ETH Price: $1,979.99 (-4.83%)
Gas: 0.04 Gwei

Transaction Decoder

Block:
23728630 at Nov-04-2025 09:27:11 PM +UTC
Transaction Fee:
0.000713448805811322 ETH $1.41
Gas Used:
185,451 Gas / 3.847101422 Gwei

Emitted Events:

215 WstETH.Transfer( from=[Sender] 0xf7b4728d623ba580e64b7b69d61885bd20249da1, to=[Receiver] 0xb773bcc5b325ad9ac6b36e1a046ad4466833a16e, value=49457447620960000 )
216 WstETH.Approval( owner=[Sender] 0xf7b4728d623ba580e64b7b69d61885bd20249da1, spender=[Receiver] 0xb773bcc5b325ad9ac6b36e1a046ad4466833a16e, value=0 )
217 AxelarGasServiceProxy.0x999d431b58761213cf53af96262b67a069cbd963499fd8effd1e21556217b841( 0x999d431b58761213cf53af96262b67a069cbd963499fd8effd1e21556217b841, 0x000000000000000000000000b773bcc5b325ad9ac6b36e1a046ad4466833a16e, 0x90ec257f5e6083de074e84d318658800e7e70cf5753ed2587c8d2df05e0e172e, 00000000000000000000000000000000000000000000000000000000000000c0, 0000000000000000000000000000000000000000000000000000000000000100, 0000000000000000000000000000000000000000000000000000000000000180, 00000000000000000000000000000000000000000000000000afb5495cf1df00, 000000000000000000000000000000000000000000000000000027380b0bf096, 000000000000000000000000f7b4728d623ba580e64b7b69d61885bd20249da1, 0000000000000000000000000000000000000000000000000000000000000007, 6e657574726f6e00000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000042, 6e657574726f6e317a76657375647364667875737a30366a7a74707068346433, 683578367665676c71737370786e733276326a716d6c396e687977736b636339, 3233000000000000000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000006, 7773744554480000000000000000000000000000000000000000000000000000 )
218 WstETH.Transfer( from=[Receiver] 0xb773bcc5b325ad9ac6b36e1a046ad4466833a16e, to=0x4F4495243837681061C4743b74B3eEdf548D56A5, value=49457447620960000 )
219 WstETH.Approval( owner=[Receiver] 0xb773bcc5b325ad9ac6b36e1a046ad4466833a16e, spender=0x4F4495243837681061C4743b74B3eEdf548D56A5, value=115792089237316195423570985008687907853269984665640564039455661467485754130646 )
220 0x4f4495243837681061c4743b74b3eedf548d56a5.0x7e50569d26be643bda7757722291ec66b1be66d8283474ae3fab5a98f878a7a2( 0x7e50569d26be643bda7757722291ec66b1be66d8283474ae3fab5a98f878a7a2, 0x000000000000000000000000b773bcc5b325ad9ac6b36e1a046ad4466833a16e, 0x90ec257f5e6083de074e84d318658800e7e70cf5753ed2587c8d2df05e0e172e, 00000000000000000000000000000000000000000000000000000000000000a0, 00000000000000000000000000000000000000000000000000000000000000e0, 0000000000000000000000000000000000000000000000000000000000000160, 0000000000000000000000000000000000000000000000000000000000000a80, 00000000000000000000000000000000000000000000000000afb5495cf1df00, 0000000000000000000000000000000000000000000000000000000000000007, 6e657574726f6e00000000000000000000000000000000000000000000000000, 0000000000000000000000000000000000000000000000000000000000000042, 6e657574726f6e317a76657375647364667875737a30366a7a74707068346433, 683578367665676c71737370786e733276326a716d6c396e687977736b636339, 3233000000000000000000000000000000000000000000000000000000000000, 00000000000000000000000000000000000000000000000000000000000008f6, 000000027b22737761705f616e645f616374696f6e5f776974685f7265636f76, 6572223a7b22757365725f73776170223a7b22737761705f65786163745f6173, 7365745f696e223a7b22737761705f76656e75655f6e616d65223a226e657574, 726f6e2d6c69646f2d736174656c6c697465222c226f7065726174696f6e7322, 3a5b7b22706f6f6c223a226e657574726f6e31756737343071726b7175787a72, 6b326868323971726c7833736b746b666d6c336a65376a757573633274653778, 6d767373636e73306e32777279222c2264656e6f6d5f696e223a226962632f34, 4430343038353136373737373635394331313738344133353644364230443133, 4435433746304345373746374442313135324645303341324445324342463222, 2c2264656e6f6d5f6f7574223a22666163746f72792f6e657574726f6e317567, 37343071726b7175787a726b326868323971726c7833736b746b666d6c336a65, 376a7575736332746537786d767373636e73306e327772792f77737445544822, 7d5d7d7d2c226d696e5f6173736574223a7b226e6174697665223a7b2264656e, 6f6d223a22666163746f72792f6e657574726f6e31756737343071726b717578, 7a726b326868323971726c7833736b746b666d6c336a65376a75757363327465, 37786d767373636e73306e327772792f777374455448222c22616d6f756e7422, 3a223439343535353932383930323336333333227d7d2c2274696d656f75745f, 74696d657374616d70223a313736323239333939383034393031313531352c22, 706f73745f737761705f616374696f6e223a7b226962635f7472616e73666572, 223a7b226962635f696e666f223a7b22736f757263655f6368616e6e656c223a, 226368616e6e656c2d3130222c227265636569766572223a226f736d6f31366d, 78766378727138746b756376616674636d34673767757166646b78396a653637, 77686537222c22666565223a7b22726563765f666565223a5b5d2c2261636b5f, 666565223a5b7b2264656e6f6d223a22756e74726e222c22616d6f756e74223a, 22313030303030227d5d2c2274696d656f75745f666565223a5b7b2264656e6f, 6d223a22756e74726e222c22616d6f756e74223a22313030303030227d5d7d2c, 226d656d6f223a22222c227265636f7665725f61646472657373223a226e6575, 74726f6e31366d78766378727138746b756376616674636d3467376775716664, 6b78396a656b3635393474227d2c226665655f73776170223a7b22737761705f, 76656e75655f6e616d65223a226e657574726f6e2d617374726f706f7274222c, 226f7065726174696f6e73223a5b7b22706f6f6c223a226e657574726f6e3167, 68686c7a367a6663333372343975307937377533736866356138386863773277, 72707776756b793775637332347965397970716a3361797075222c2264656e6f, 6d5f696e223a226962632f344430343038353136373737373635394331313738, 3441333536443642304431334435433746304345373746374442313135324645, 3033413244453243424632222c2264656e6f6d5f6f7574223a22666163746f72, 792f6e657574726f6e31756737343071726b7175787a726b326868323971726c, 7833736b746b666d6c336a65376a7575736332746537786d767373636e73306e, 327772792f777374455448227d2c7b22706f6f6c223a226e657574726f6e3174, 73656b38393670707768397a6d7678326b63636b36306577726e67346172786e, 63326532796b6636707232756e306333633973386d65686d38222c2264656e6f, 6d5f696e223a22666163746f72792f6e657574726f6e31756737343071726b71, 75787a726b326868323971726c7833736b746b666d6c336a65376a7575736332, 746537786d767373636e73306e327772792f777374455448222c2264656e6f6d, 5f6f7574223a226962632f423535394138304436323234394338414130374133, 3830453241324245413645354341394136463037394339313243334139453942, 3439343130354534463831227d2c7b22706f6f6c223a226e657574726f6e316c, 343874737132373238747a30756d68376c343035743630683077746874773930, 3874653970666d636667766b7538636d32657374396871336a222c2264656e6f, 6d5f696e223a226962632f423535394138304436323234394338414130374133, 3830453241324245413645354341394136463037394339313243334139453942, 3439343130354534463831222c2264656e6f6d5f6f7574223a226962632f4334, 4346463436464436444533354341344346344345303331453634334338464443, 394241344239394145353938453942304544393846453341323331394639227d, 2c7b22706f6f6c223a226e657574726f6e31346d73736334796375646b6d7a6e, 73747774636d336173737234677434306e713338783538617035373474686772, 78677a72307170786875396d222c2264656e6f6d5f696e223a226962632f4334, 4346463436464436444533354341344346344345303331453634334338464443, 394241344239394145353938453942304544393846453341323331394639222c, 2264656e6f6d5f6f7574223a22666163746f72792f6e657574726f6e31743571, 726a747972796838677a7438303071723576796c6868326638636d7834776d7a, 396d632f75676f6464617264227d2c7b22706f6f6c223a226e657574726f6e31, 3037797974663634756c396c36616a7865766679647433706b78787761303932, 34737267743975357165617a6e71337863613271777037326c73222c2264656e, 6f6d5f696e223a22666163746f72792f6e657574726f6e31743571726a747972, 796838677a7438303071723576796c6868326638636d7834776d7a396d632f75, 676f6464617264222c2264656e6f6d5f6f7574223a22756e74726e227d5d2c22, 726566756e645f61646472657373223a226e657574726f6e31366d7876637872, 7138746b756376616674636d34673767757166646b78396a656b363539347422, 7d7d7d2c22616666696c6961746573223a5b5d2c227265636f766572795f6164, 6472223a226e657574726f6e31366d78766378727138746b756376616674636d, 34673767757166646b78396a656b3635393474227d7d00000000000000000000, 0000000000000000000000000000000000000000000000000000000000000006, 7773744554480000000000000000000000000000000000000000000000000000 )

Account State Difference:

  Address   Before After State Difference Code
0x2d5d7d31...09a082712
(Axelar: Gas Service)
0.099209113062099538 Eth0.09925223471908324 Eth0.000043121656983702
(Titan Builder)
2.384234444079157519 Eth2.384316207212256187 Eth0.000081763133098668
0x7f39C581...c935E2Ca0
0xf7b4728D...d20249DA1
0.001438343410494207 Eth
Nonce: 14
0.000681772947699183 Eth
Nonce: 15
0.000756570462795024

Execution Trace

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

              File 2 of 4: AxelarGasServiceProxy
              // Sources flattened with hardhat v2.9.9 https://hardhat.org
              
              // File contracts/interfaces/IUpgradable.sol
              
              // SPDX-License-Identifier: MIT
              
              pragma solidity 0.8.9;
              
              // General interface for upgradable contracts
              interface IUpgradable {
                  error NotOwner();
                  error InvalidOwner();
                  error InvalidCodeHash();
                  error InvalidImplementation();
                  error SetupFailed();
                  error NotProxy();
              
                  event Upgraded(address indexed newImplementation);
                  event OwnershipTransferred(address indexed newOwner);
              
                  // Get current owner
                  function owner() external view returns (address);
              
                  function contractId() external pure returns (bytes32);
              
                  function implementation() external view returns (address);
              
                  function upgrade(
                      address newImplementation,
                      bytes32 newImplementationCodeHash,
                      bytes calldata params
                  ) external;
              
                  function setup(bytes calldata data) external;
              }
              
              
              // File contracts/util/Proxy.sol
              
              contract Proxy {
                  error InvalidImplementation();
                  error SetupFailed();
                  error EtherNotAccepted();
                  error NotOwner();
                  error AlreadyInitialized();
              
                  // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  // keccak256('owner')
                  bytes32 internal constant _OWNER_SLOT = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0;
              
                  constructor() {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          sstore(_OWNER_SLOT, caller())
                      }
                  }
              
                  function init(
                      address implementationAddress,
                      address newOwner,
                      bytes memory params
                  ) external {
                      address owner;
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          owner := sload(_OWNER_SLOT)
                      }
                      if (msg.sender != owner) revert NotOwner();
                      if (implementation() != address(0)) revert AlreadyInitialized();
                      if (IUpgradable(implementationAddress).contractId() != contractId()) revert InvalidImplementation();
              
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          sstore(_IMPLEMENTATION_SLOT, implementationAddress)
                          sstore(_OWNER_SLOT, newOwner)
                      }
                      // solhint-disable-next-line avoid-low-level-calls
                      (bool success, ) = implementationAddress.delegatecall(
                          // keccak('setup(bytes)') selector
                          abi.encodeWithSelector(0x9ded06df, params)
                      );
                      if (!success) revert SetupFailed();
                  }
              
                  // solhint-disable-next-line no-empty-blocks
                  function contractId() internal pure virtual returns (bytes32) {}
              
                  function implementation() public view returns (address implementation_) {
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          implementation_ := sload(_IMPLEMENTATION_SLOT)
                      }
                  }
              
                  // solhint-disable-next-line no-empty-blocks
                  function setup(bytes calldata data) public {}
              
                  // solhint-disable-next-line no-complex-fallback
                  fallback() external payable {
                      address implementaion_ = implementation();
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          calldatacopy(0, 0, calldatasize())
              
                          let result := delegatecall(gas(), implementaion_, 0, calldatasize(), 0, 0)
                          returndatacopy(0, 0, returndatasize())
              
                          switch result
                          case 0 {
                              revert(0, returndatasize())
                          }
                          default {
                              return(0, returndatasize())
                          }
                      }
                  }
              
                  receive() external payable virtual {
                      revert EtherNotAccepted();
                  }
              }
              
              
              // File contracts/gas-service/AxelarGasServiceProxy.sol
              
              contract AxelarGasServiceProxy is Proxy {
                  function contractId() internal pure override returns (bytes32) {
                      return keccak256('axelar-gas-service');
                  }
              }

              File 3 of 4: AxelarGateway
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { IGovernable } from './IGovernable.sol';
              import { IImplementation } from './IImplementation.sol';
              interface IAxelarGateway is IImplementation, IGovernable {
                  /**********\\
                  |* Errors *|
                  \\**********/
                  error NotSelf();
                  error InvalidCodeHash();
                  error SetupFailed();
                  error InvalidAuthModule();
                  error InvalidTokenDeployer();
                  error InvalidAmount();
                  error InvalidChainId();
                  error InvalidCommands();
                  error TokenDoesNotExist(string symbol);
                  error TokenAlreadyExists(string symbol);
                  error TokenDeployFailed(string symbol);
                  error TokenContractDoesNotExist(address token);
                  error BurnFailed(string symbol);
                  error MintFailed(string symbol);
                  error InvalidSetMintLimitsParams();
                  error ExceedMintLimit(string symbol);
                  /**********\\
                  |* Events *|
                  \\**********/
                  event TokenSent(
                      address indexed sender,
                      string destinationChain,
                      string destinationAddress,
                      string symbol,
                      uint256 amount
                  );
                  event ContractCall(
                      address indexed sender,
                      string destinationChain,
                      string destinationContractAddress,
                      bytes32 indexed payloadHash,
                      bytes payload
                  );
                  event ContractCallWithToken(
                      address indexed sender,
                      string destinationChain,
                      string destinationContractAddress,
                      bytes32 indexed payloadHash,
                      bytes payload,
                      string symbol,
                      uint256 amount
                  );
                  event Executed(bytes32 indexed commandId);
                  event TokenDeployed(string symbol, address tokenAddresses);
                  event ContractCallApproved(
                      bytes32 indexed commandId,
                      string sourceChain,
                      string sourceAddress,
                      address indexed contractAddress,
                      bytes32 indexed payloadHash,
                      bytes32 sourceTxHash,
                      uint256 sourceEventIndex
                  );
                  event ContractCallApprovedWithMint(
                      bytes32 indexed commandId,
                      string sourceChain,
                      string sourceAddress,
                      address indexed contractAddress,
                      bytes32 indexed payloadHash,
                      string symbol,
                      uint256 amount,
                      bytes32 sourceTxHash,
                      uint256 sourceEventIndex
                  );
                  event ContractCallExecuted(bytes32 indexed commandId);
                  event TokenMintLimitUpdated(string symbol, uint256 limit);
                  event OperatorshipTransferred(bytes newOperatorsData);
                  event Upgraded(address indexed implementation);
                  /********************\\
                  |* Public Functions *|
                  \\********************/
                  function sendToken(
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      string calldata symbol,
                      uint256 amount
                  ) external;
                  function callContract(
                      string calldata destinationChain,
                      string calldata contractAddress,
                      bytes calldata payload
                  ) external;
                  function callContractWithToken(
                      string calldata destinationChain,
                      string calldata contractAddress,
                      bytes calldata payload,
                      string calldata symbol,
                      uint256 amount
                  ) external;
                  function isContractCallApproved(
                      bytes32 commandId,
                      string calldata sourceChain,
                      string calldata sourceAddress,
                      address contractAddress,
                      bytes32 payloadHash
                  ) external view returns (bool);
                  function isContractCallAndMintApproved(
                      bytes32 commandId,
                      string calldata sourceChain,
                      string calldata sourceAddress,
                      address contractAddress,
                      bytes32 payloadHash,
                      string calldata symbol,
                      uint256 amount
                  ) external view returns (bool);
                  function validateContractCall(
                      bytes32 commandId,
                      string calldata sourceChain,
                      string calldata sourceAddress,
                      bytes32 payloadHash
                  ) external returns (bool);
                  function validateContractCallAndMint(
                      bytes32 commandId,
                      string calldata sourceChain,
                      string calldata sourceAddress,
                      bytes32 payloadHash,
                      string calldata symbol,
                      uint256 amount
                  ) external returns (bool);
                  /***********\\
                  |* Getters *|
                  \\***********/
                  function authModule() external view returns (address);
                  function tokenDeployer() external view returns (address);
                  function tokenMintLimit(string memory symbol) external view returns (uint256);
                  function tokenMintAmount(string memory symbol) external view returns (uint256);
                  function allTokensFrozen() external view returns (bool);
                  function implementation() external view returns (address);
                  function tokenAddresses(string memory symbol) external view returns (address);
                  function tokenFrozen(string memory symbol) external view returns (bool);
                  function isCommandExecuted(bytes32 commandId) external view returns (bool);
                  /************************\\
                  |* Governance Functions *|
                  \\************************/
                  function setTokenMintLimits(string[] calldata symbols, uint256[] calldata limits) external;
                  function upgrade(
                      address newImplementation,
                      bytes32 newImplementationCodeHash,
                      bytes calldata setupParams
                  ) external;
                  /**********************\\
                  |* External Functions *|
                  \\**********************/
                  function execute(bytes calldata input) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              // General interface for upgradable contracts
              interface IContractIdentifier {
                  /**
                   * @notice Returns the contract ID. It can be used as a check during upgrades.
                   * @dev Meant to be overridden in derived contracts.
                   * @return bytes32 The contract ID
                   */
                  function contractId() external pure returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  error InvalidAccount();
                  /**
                   * @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.8.0;
              /**
               * @title IGovernable Interface
               * @notice This is an interface used by the AxelarGateway contract to manage governance and mint limiter roles.
               */
              interface IGovernable {
                  error NotGovernance();
                  error NotMintLimiter();
                  error InvalidGovernance();
                  error InvalidMintLimiter();
                  event GovernanceTransferred(address indexed previousGovernance, address indexed newGovernance);
                  event MintLimiterTransferred(address indexed previousGovernance, address indexed newGovernance);
                  /**
                   * @notice Returns the governance address.
                   * @return address of the governance
                   */
                  function governance() external view returns (address);
                  /**
                   * @notice Returns the mint limiter address.
                   * @return address of the mint limiter
                   */
                  function mintLimiter() external view returns (address);
                  /**
                   * @notice Transfer the governance role to another address.
                   * @param newGovernance The new governance address
                   */
                  function transferGovernance(address newGovernance) external;
                  /**
                   * @notice Transfer the mint limiter role to another address.
                   * @param newGovernance The new mint limiter address
                   */
                  function transferMintLimiter(address newGovernance) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { IContractIdentifier } from './IContractIdentifier.sol';
              interface IImplementation is IContractIdentifier {
                  error NotProxy();
                  function setup(bytes calldata data) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @title IOwnable Interface
               * @notice IOwnable is an interface that abstracts the implementation of a
               * contract with ownership control features. It's commonly used in upgradable
               * contracts and includes the functionality to get current owner, transfer
               * ownership, and propose and accept ownership.
               */
              interface IOwnable {
                  error NotOwner();
                  error InvalidOwner();
                  error InvalidOwnerAddress();
                  event OwnershipTransferStarted(address indexed newOwner);
                  event OwnershipTransferred(address indexed newOwner);
                  /**
                   * @notice Returns the current owner of the contract.
                   * @return address The address of the current owner
                   */
                  function owner() external view returns (address);
                  /**
                   * @notice Returns the address of the pending owner of the contract.
                   * @return address The address of the pending owner
                   */
                  function pendingOwner() external view returns (address);
                  /**
                   * @notice Transfers ownership of the contract to a new address
                   * @param newOwner The address to transfer ownership to
                   */
                  function transferOwnership(address newOwner) external;
                  /**
                   * @notice Proposes to transfer the contract's ownership to a new address.
                   * The new owner needs to accept the ownership explicitly.
                   * @param newOwner The address to transfer ownership to
                   */
                  function proposeOwnership(address newOwner) external;
                  /**
                   * @notice Transfers ownership to the pending owner.
                   * @dev Can only be called by the pending owner
                   */
                  function acceptOwnership() external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              library ContractAddress {
                  function isContract(address contractAddress) internal view returns (bool) {
                      bytes32 existingCodeHash = contractAddress.codehash;
                      // https://eips.ethereum.org/EIPS/eip-1052
                      // keccak256('') == 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470
                      return
                          existingCodeHash != bytes32(0) &&
                          existingCodeHash != 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { IERC20 } from '../interfaces/IERC20.sol';
              error TokenTransferFailed();
              /*
               * @title SafeTokenCall
               * @dev This library is used for performing safe token transfers.
               */
              library SafeTokenCall {
                  /*
                   * @notice Make a safe call to a token contract.
                   * @param token The token contract to interact with.
                   * @param callData The function call data.
                   * @throws TokenTransferFailed error if transfer of token is not successful.
                   */
                  function safeCall(IERC20 token, bytes memory callData) internal {
                      (bool success, bytes memory returnData) = address(token).call(callData);
                      bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
                      if (!transferred || address(token).code.length == 0) revert TokenTransferFailed();
                  }
              }
              /*
               * @title SafeTokenTransfer
               * @dev This library safely transfers tokens from the contract to a recipient.
               */
              library SafeTokenTransfer {
                  /*
                   * @notice Transfer tokens to a recipient.
                   * @param token The token contract.
                   * @param receiver The recipient of the tokens.
                   * @param amount The amount of tokens to transfer.
                   */
                  function safeTransfer(
                      IERC20 token,
                      address receiver,
                      uint256 amount
                  ) internal {
                      SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transfer.selector, receiver, amount));
                  }
              }
              /*
               * @title SafeTokenTransferFrom
               * @dev This library helps to safely transfer tokens on behalf of a token holder.
               */
              library SafeTokenTransferFrom {
                  /*
                   * @notice Transfer tokens on behalf of a token holder.
                   * @param token The token contract.
                   * @param from The address of the token holder.
                   * @param to The address the tokens are to be sent to.
                   * @param amount The amount of tokens to be transferred.
                   */
                  function safeTransferFrom(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 amount
                  ) internal {
                      SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, amount));
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { IImplementation } from '../interfaces/IImplementation.sol';
              /**
               * @title Implementation
               * @notice This contract serves as a base for other contracts and enforces a proxy-first access restriction.
               * @dev Derived contracts must implement the setup function.
               */
              abstract contract Implementation is IImplementation {
                  address private immutable implementationAddress;
                  /**
                   * @dev Contract constructor that sets the implementation address to the address of this contract.
                   */
                  constructor() {
                      implementationAddress = address(this);
                  }
                  /**
                   * @dev Modifier to require the caller to be the proxy contract.
                   * Reverts if the caller is the current contract (i.e., the implementation contract itself).
                   */
                  modifier onlyProxy() {
                      if (implementationAddress == address(this)) revert NotProxy();
                      _;
                  }
                  /**
                   * @notice Initializes contract parameters.
                   * This function is intended to be overridden by derived contracts.
                   * The overriding function must have the onlyProxy modifier.
                   * @param params The parameters to be used for initialization
                   */
                  function setup(bytes calldata params) external virtual;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';
              import { IImplementation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IImplementation.sol';
              import { IContractIdentifier } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IContractIdentifier.sol';
              import { IAxelarGateway } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGateway.sol';
              import { SafeTokenCall, SafeTokenTransfer, SafeTokenTransferFrom } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol';
              import { ContractAddress } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/ContractAddress.sol';
              import { Implementation } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Implementation.sol';
              import { IAxelarAuth } from './interfaces/IAxelarAuth.sol';
              import { IBurnableMintableCappedERC20 } from './interfaces/IBurnableMintableCappedERC20.sol';
              import { ITokenDeployer } from './interfaces/ITokenDeployer.sol';
              import { ECDSA } from './ECDSA.sol';
              import { DepositHandler } from './DepositHandler.sol';
              import { EternalStorage } from './EternalStorage.sol';
              /**
               * @title AxelarGateway Contract
               * @notice This contract serves as the gateway for cross-chain contract calls,
               * and token transfers within the Axelar network.
               * It includes functions for sending tokens, calling contracts, and validating contract calls.
               * The contract is managed via the decentralized governance mechanism on the Axelar network.
               * @dev EternalStorage is used to simplify storage for upgradability, and InterchainGovernance module is used for governance.
               */
              contract AxelarGateway is IAxelarGateway, Implementation, EternalStorage {
                  using SafeTokenCall for IERC20;
                  using SafeTokenTransfer for IERC20;
                  using SafeTokenTransferFrom for IERC20;
                  using ContractAddress for address;
                  error InvalidImplementation();
                  enum TokenType {
                      InternalBurnable,
                      InternalBurnableFrom,
                      External
                  }
                  /**
                   * @dev Deprecated slots. Should not be reused.
                   */
                  // bytes32 internal constant KEY_ALL_TOKENS_FROZEN = keccak256('all-tokens-frozen');
                  // bytes32 internal constant PREFIX_TOKEN_FROZEN = keccak256('token-frozen');
                  /**
                   * @dev Storage slot with the address of the current implementation. `keccak256('eip1967.proxy.implementation') - 1`.
                   */
                  bytes32 internal constant KEY_IMPLEMENTATION = bytes32(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc);
                  /**
                   * @dev Storage slot with the address of the current governance. keccak256('governance')) - 1
                   */
                  bytes32 internal constant KEY_GOVERNANCE = bytes32(0xabea6fd3db56a6e6d0242111b43ebb13d1c42709651c032c7894962023a1f909);
                  /**
                   * @dev Storage slot with the address of the current mint limiter. keccak256('mint-limiter')) - 1
                   */
                  bytes32 internal constant KEY_MINT_LIMITER = bytes32(0x627f0c11732837b3240a2de89c0b6343512886dd50978b99c76a68c6416a4d92);
                  bytes32 internal constant PREFIX_COMMAND_EXECUTED = keccak256('command-executed');
                  bytes32 internal constant PREFIX_TOKEN_ADDRESS = keccak256('token-address');
                  bytes32 internal constant PREFIX_TOKEN_TYPE = keccak256('token-type');
                  bytes32 internal constant PREFIX_CONTRACT_CALL_APPROVED = keccak256('contract-call-approved');
                  bytes32 internal constant PREFIX_CONTRACT_CALL_APPROVED_WITH_MINT = keccak256('contract-call-approved-with-mint');
                  bytes32 internal constant PREFIX_TOKEN_MINT_LIMIT = keccak256('token-mint-limit');
                  bytes32 internal constant PREFIX_TOKEN_MINT_AMOUNT = keccak256('token-mint-amount');
                  bytes32 internal constant SELECTOR_BURN_TOKEN = keccak256('burnToken');
                  bytes32 internal constant SELECTOR_DEPLOY_TOKEN = keccak256('deployToken');
                  bytes32 internal constant SELECTOR_MINT_TOKEN = keccak256('mintToken');
                  bytes32 internal constant SELECTOR_APPROVE_CONTRACT_CALL = keccak256('approveContractCall');
                  bytes32 internal constant SELECTOR_APPROVE_CONTRACT_CALL_WITH_MINT = keccak256('approveContractCallWithMint');
                  bytes32 internal constant SELECTOR_TRANSFER_OPERATORSHIP = keccak256('transferOperatorship');
                  address public immutable authModule;
                  address public immutable tokenDeployer;
                  /**
                   * @notice Constructs the AxelarGateway contract.
                   * @param authModule_ The address of the authentication module
                   * @param tokenDeployer_ The address of the token deployer
                   */
                  constructor(address authModule_, address tokenDeployer_) {
                      if (authModule_.code.length == 0) revert InvalidAuthModule();
                      if (tokenDeployer_.code.length == 0) revert InvalidTokenDeployer();
                      authModule = authModule_;
                      tokenDeployer = tokenDeployer_;
                  }
                  /**
                   * @notice Ensures that the caller of the function is the gateway contract itself.
                   */
                  modifier onlySelf() {
                      if (msg.sender != address(this)) revert NotSelf();
                      _;
                  }
                  /**
                   * @notice Ensures that the caller of the function is the governance address.
                   */
                  modifier onlyGovernance() {
                      if (msg.sender != getAddress(KEY_GOVERNANCE)) revert NotGovernance();
                      _;
                  }
                  /**
                   * @notice Ensures that the caller of the function is either the mint limiter or governance.
                   */
                  modifier onlyMintLimiter() {
                      if (msg.sender != getAddress(KEY_MINT_LIMITER) && msg.sender != getAddress(KEY_GOVERNANCE)) revert NotMintLimiter();
                      _;
                  }
                  /******************\\
                  |* Public Methods *|
                  \\******************/
                  /**
                   * @notice Send the specified token to the destination chain and address.
                   * @param destinationChain The chain to send tokens to. A registered chain name on Axelar must be used here
                   * @param destinationAddress The address on the destination chain to send tokens to
                   * @param symbol The symbol of the token to send
                   * @param amount The amount of tokens to send
                   */
                  function sendToken(
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      string calldata symbol,
                      uint256 amount
                  ) external {
                      _burnTokenFrom(msg.sender, symbol, amount);
                      emit TokenSent(msg.sender, destinationChain, destinationAddress, symbol, amount);
                  }
                  /**
                   * @notice Calls a contract on the specified destination chain with a given payload.
                   * This function is the entry point for general message passing between chains.
                   * @param destinationChain The chain where the destination contract exists. A registered chain name on Axelar must be used here
                   * @param destinationContractAddress The address of the contract to call on the destination chain
                   * @param payload The payload to be sent to the destination contract, usually representing an encoded function call with arguments
                   */
                  function callContract(
                      string calldata destinationChain,
                      string calldata destinationContractAddress,
                      bytes calldata payload
                  ) external {
                      emit ContractCall(msg.sender, destinationChain, destinationContractAddress, keccak256(payload), payload);
                  }
                  /**
                   * @notice Calls a contract on the specified destination chain with a given payload and token amount.
                   * This function is the entry point for general message passing with token transfer between chains.
                   * @param destinationChain The chain where the destination contract exists. A registered chain name on Axelar must be used here
                   * @param destinationContractAddress The address of the contract to call with tokens on the destination chain
                   * @param payload The payload to be sent to the destination contract, usually representing an encoded function call with arguments
                   * @param symbol The symbol of the token to be sent with the call
                   * @param amount The amount of tokens to be sent with the call
                   */
                  function callContractWithToken(
                      string calldata destinationChain,
                      string calldata destinationContractAddress,
                      bytes calldata payload,
                      string calldata symbol,
                      uint256 amount
                  ) external {
                      _burnTokenFrom(msg.sender, symbol, amount);
                      emit ContractCallWithToken(msg.sender, destinationChain, destinationContractAddress, keccak256(payload), payload, symbol, amount);
                  }
                  /**
                   * @notice Checks whether a contract call has been approved by the gateway.
                   * @param commandId The gateway command ID
                   * @param sourceChain The source chain of the contract call
                   * @param sourceAddress The source address of the contract call
                   * @param contractAddress The contract address that will be called
                   * @param payloadHash The hash of the payload for that will be sent with the call
                   * @return bool A boolean value indicating whether the contract call has been approved by the gateway.
                   */
                  function isContractCallApproved(
                      bytes32 commandId,
                      string calldata sourceChain,
                      string calldata sourceAddress,
                      address contractAddress,
                      bytes32 payloadHash
                  ) external view override returns (bool) {
                      return getBool(_getIsContractCallApprovedKey(commandId, sourceChain, sourceAddress, contractAddress, payloadHash));
                  }
                  /**
                   * @notice Checks whether a contract call with token has been approved by the gateway.
                   * @param commandId The gateway command ID
                   * @param sourceChain The source chain of the contract call
                   * @param sourceAddress The source address of the contract call
                   * @param contractAddress The contract address that will be called, and where tokens will be sent
                   * @param payloadHash The hash of the payload for that will be sent with the call
                   * @param symbol The symbol of the token to be sent with the call
                   * @param amount The amount of tokens to be sent with the call
                   * @return bool A boolean value indicating whether the contract call with token has been approved by the gateway.
                   */
                  function isContractCallAndMintApproved(
                      bytes32 commandId,
                      string calldata sourceChain,
                      string calldata sourceAddress,
                      address contractAddress,
                      bytes32 payloadHash,
                      string calldata symbol,
                      uint256 amount
                  ) external view override returns (bool) {
                      return
                          getBool(
                              _getIsContractCallApprovedWithMintKey(commandId, sourceChain, sourceAddress, contractAddress, payloadHash, symbol, amount)
                          );
                  }
                  /**
                   * @notice Called on the destination chain gateway by the recipient of the cross-chain contract call to validate it and only allow execution
                   * if this function returns true.
                   * @dev Once validated, the gateway marks the message as executed so the contract call is not executed twice.
                   * @param commandId The gateway command ID
                   * @param sourceChain The source chain of the contract call
                   * @param sourceAddress The source address of the contract call
                   * @param payloadHash The hash of the payload for that will be sent with the call
                   * @return valid True if the contract call is approved, false otherwise
                   */
                  function validateContractCall(
                      bytes32 commandId,
                      string calldata sourceChain,
                      string calldata sourceAddress,
                      bytes32 payloadHash
                  ) external override returns (bool valid) {
                      bytes32 key = _getIsContractCallApprovedKey(commandId, sourceChain, sourceAddress, msg.sender, payloadHash);
                      valid = getBool(key);
                      if (valid) {
                          _setBool(key, false);
                          emit ContractCallExecuted(commandId);
                      }
                  }
                  /**
                   * @notice Called on the destination chain gateway to validate the approval of a contract call with token transfer and only
                   * allow execution if this function returns true.
                   * @dev Once validated, the gateway marks the message as executed so the contract call with token is not executed twice.
                   * @param commandId The gateway command ID
                   * @param sourceChain The source chain of the contract call
                   * @param sourceAddress The source address of the contract call
                   * @param payloadHash The hash of the payload for that will be sent with the call
                   * @param symbol The symbol of the token to be sent with the call
                   * @param amount The amount of tokens to be sent with the call
                   * @return valid True if the contract call with token is approved, false otherwise
                   */
                  function validateContractCallAndMint(
                      bytes32 commandId,
                      string calldata sourceChain,
                      string calldata sourceAddress,
                      bytes32 payloadHash,
                      string calldata symbol,
                      uint256 amount
                  ) external override returns (bool valid) {
                      bytes32 key = _getIsContractCallApprovedWithMintKey(commandId, sourceChain, sourceAddress, msg.sender, payloadHash, symbol, amount);
                      valid = getBool(key);
                      if (valid) {
                          // Prevent re-entrancy
                          _setBool(key, false);
                          emit ContractCallExecuted(commandId);
                          _mintToken(symbol, msg.sender, amount);
                      }
                  }
                  /***********\\
                  |* Getters *|
                  \\***********/
                  /**
                   * @notice Gets the address of governance, should be the address of InterchainGovernance.
                   * @return address The address of governance.
                   */
                  function governance() public view override returns (address) {
                      return getAddress(KEY_GOVERNANCE);
                  }
                  /**
                   * @notice Gets the address of the mint limiter, should be the address of Multisig.
                   * @return address The address of the mint limiter.
                   */
                  function mintLimiter() public view override returns (address) {
                      return getAddress(KEY_MINT_LIMITER);
                  }
                  /**
                   * @notice Gets the transfer limit for a specific token symbol within the configured epoch.
                   * @param symbol The symbol of the token
                   * @return uint The transfer limit for the given token.
                   */
                  function tokenMintLimit(string memory symbol) public view override returns (uint256) {
                      return getUint(_getTokenMintLimitKey(symbol));
                  }
                  /**
                   * @notice Gets the transfer amount for a specific token symbol within the configured epoch.
                   * @param symbol The symbol of the token
                   * @return uint The transfer amount for the given token.
                   */
                  function tokenMintAmount(string memory symbol) public view override returns (uint256) {
                      return getUint(_getTokenMintAmountKey(symbol, block.timestamp / 6 hours));
                  }
                  /**
                   * @dev This function is kept around to keep things working for internal
                   * tokens that were deployed before the token freeze functionality was removed
                   */
                  function allTokensFrozen() external pure override returns (bool) {
                      return false;
                  }
                  /**
                   * @notice Gets the address of the gateway implementation contract.
                   * @return address The address of the gateway implementation.
                   */
                  function implementation() public view override returns (address) {
                      return getAddress(KEY_IMPLEMENTATION);
                  }
                  /**
                   * @notice Gets the address of a specific token using its symbol.
                   * @param symbol The symbol of the token
                   * @return address The address of the token associated with the given symbol.
                   */
                  function tokenAddresses(string memory symbol) public view override returns (address) {
                      return getAddress(_getTokenAddressKey(symbol));
                  }
                  /**
                   * @dev Deprecated. This function is kept around to keep things working for internal tokens that were deployed before the token freeze functionality was removed
                   */
                  function tokenFrozen(string memory) external pure override returns (bool) {
                      return false;
                  }
                  /**
                   * @notice Checks whether a command with a given command ID has been executed.
                   * @param commandId The command ID to check
                   * @return bool True if the command has been executed, false otherwise
                   */
                  function isCommandExecuted(bytes32 commandId) public view override returns (bool) {
                      return getBool(_getIsCommandExecutedKey(commandId));
                  }
                  /**
                   * @notice Gets the contract ID of the Axelar Gateway.
                   * @return bytes32 The keccak256 hash of the string 'axelar-gateway'
                   */
                  function contractId() public pure returns (bytes32) {
                      return keccak256('axelar-gateway');
                  }
                  /************************\\
                  |* Governance Functions *|
                  \\************************/
                  /**
                   * @notice Transfers the governance role to a new address.
                   * @param newGovernance The address to transfer the governance role to.
                   * @dev Only the current governance entity can call this function.
                   */
                  function transferGovernance(address newGovernance) external override onlyGovernance {
                      if (newGovernance == address(0)) revert InvalidGovernance();
                      _transferGovernance(newGovernance);
                  }
                  /**
                   * @notice Transfers the mint limiter role to a new address.
                   * @param newMintLimiter The address to transfer the mint limiter role to.
                   * @dev Only the current mint limiter or the governance address can call this function.
                   */
                  function transferMintLimiter(address newMintLimiter) external override onlyMintLimiter {
                      if (newMintLimiter == address(0)) revert InvalidMintLimiter();
                      _transferMintLimiter(newMintLimiter);
                  }
                  /**
                   * @notice Sets the transfer limits for an array of tokens.
                   * @param symbols The array of token symbols to set the transfer limits for
                   * @param limits The array of transfer limits corresponding to the symbols
                   * @dev Only the mint limiter or the governance address can call this function.
                   */
                  function setTokenMintLimits(string[] calldata symbols, uint256[] calldata limits) external override onlyMintLimiter {
                      uint256 length = symbols.length;
                      if (length != limits.length) revert InvalidSetMintLimitsParams();
                      for (uint256 i; i < length; ++i) {
                          string memory symbol = symbols[i];
                          uint256 limit = limits[i];
                          if (tokenAddresses(symbol) == address(0)) revert TokenDoesNotExist(symbol);
                          _setTokenMintLimit(symbol, limit);
                      }
                  }
                  /**
                   * @notice Upgrades the contract to a new implementation.
                   * @param newImplementation The address of the new implementation
                   * @param newImplementationCodeHash The code hash of the new implementation
                   * @param setupParams Optional setup params for the new implementation
                   * @dev Only the governance address can call this function.
                   */
                  function upgrade(
                      address newImplementation,
                      bytes32 newImplementationCodeHash,
                      bytes calldata setupParams
                  ) external override onlyGovernance {
                      if (newImplementationCodeHash != newImplementation.codehash) revert InvalidCodeHash();
                      if (contractId() != IContractIdentifier(newImplementation).contractId()) revert InvalidImplementation();
                      emit Upgraded(newImplementation);
                      _setImplementation(newImplementation);
                      if (setupParams.length != 0) {
                          // slither-disable-next-line controlled-delegatecall
                          (bool success, ) = newImplementation.delegatecall(abi.encodeWithSelector(IImplementation.setup.selector, setupParams));
                          if (!success) revert SetupFailed();
                      }
                  }
                  /**********************\\
                  |* External Functions *|
                  \\**********************/
                  /**
                   * @notice Sets up the governance and mint limiter roles, and transfers operatorship if necessary.
                   * This function is called by the proxy during initial deployment, and optionally called during gateway upgrades.
                   * @param params The encoded parameters containing the governance and mint limiter addresses, as well as the new operator data.
                   * @dev Not publicly accessible as it's overshadowed in the proxy.
                   */
                  function setup(bytes calldata params) external override(IImplementation, Implementation) onlyProxy {
                      (address governance_, address mintLimiter_, bytes memory newOperatorsData) = abi.decode(params, (address, address, bytes));
                      if (governance_ != address(0)) _transferGovernance(governance_);
                      if (mintLimiter_ != address(0)) _transferMintLimiter(mintLimiter_);
                      if (newOperatorsData.length != 0) {
                          emit OperatorshipTransferred(newOperatorsData);
                          IAxelarAuth(authModule).transferOperatorship(newOperatorsData);
                      }
                  }
                  /**
                   * @notice Executes a batch of commands signed by the Axelar network. There are a finite set of command types that can be executed.
                   * @param input The encoded input containing the data for the batch of commands, as well as the proof that verifies the integrity of the data.
                   * @dev Each command has a corresponding commandID that is guaranteed to be unique from the Axelar network.
                   * @dev This function allows retrying a commandID if the command initially failed to be processed.
                   * @dev Ignores unknown commands or duplicate commandIDs.
                   * @dev Emits an Executed event for successfully executed commands.
                   */
                  // slither-disable-next-line cyclomatic-complexity
                  function execute(bytes calldata input) external override {
                      (bytes memory data, bytes memory proof) = abi.decode(input, (bytes, bytes));
                      bytes32 messageHash = ECDSA.toEthSignedMessageHash(keccak256(data));
                      // returns true for current operators
                      // slither-disable-next-line reentrancy-no-eth
                      bool allowOperatorshipTransfer = IAxelarAuth(authModule).validateProof(messageHash, proof);
                      uint256 chainId;
                      bytes32[] memory commandIds;
                      string[] memory commands;
                      bytes[] memory params;
                      (chainId, commandIds, commands, params) = abi.decode(data, (uint256, bytes32[], string[], bytes[]));
                      if (chainId != block.chainid) revert InvalidChainId();
                      uint256 commandsLength = commandIds.length;
                      if (commandsLength != commands.length || commandsLength != params.length) revert InvalidCommands();
                      for (uint256 i; i < commandsLength; ++i) {
                          bytes32 commandId = commandIds[i];
                          // Ignore if duplicate commandId received
                          if (isCommandExecuted(commandId)) continue;
                          bytes4 commandSelector;
                          bytes32 commandHash = keccak256(abi.encodePacked(commands[i]));
                          if (commandHash == SELECTOR_DEPLOY_TOKEN) {
                              commandSelector = AxelarGateway.deployToken.selector;
                          } else if (commandHash == SELECTOR_MINT_TOKEN) {
                              commandSelector = AxelarGateway.mintToken.selector;
                          } else if (commandHash == SELECTOR_APPROVE_CONTRACT_CALL) {
                              commandSelector = AxelarGateway.approveContractCall.selector;
                          } else if (commandHash == SELECTOR_APPROVE_CONTRACT_CALL_WITH_MINT) {
                              commandSelector = AxelarGateway.approveContractCallWithMint.selector;
                          } else if (commandHash == SELECTOR_BURN_TOKEN) {
                              commandSelector = AxelarGateway.burnToken.selector;
                          } else if (commandHash == SELECTOR_TRANSFER_OPERATORSHIP) {
                              if (!allowOperatorshipTransfer) continue;
                              allowOperatorshipTransfer = false;
                              commandSelector = AxelarGateway.transferOperatorship.selector;
                          } else {
                              // Ignore unknown commands
                              continue;
                          }
                          // Prevent a re-entrancy from executing this command before it can be marked as successful.
                          _setCommandExecuted(commandId, true);
                          // slither-disable-next-line calls-loop,reentrancy-no-eth
                          (bool success, ) = address(this).call(abi.encodeWithSelector(commandSelector, params[i], commandId));
                          // slither-disable-next-line reentrancy-events
                          if (success) emit Executed(commandId);
                          else _setCommandExecuted(commandId, false);
                      }
                  }
                  /******************\\
                  |* Self Functions *|
                  \\******************/
                  /**
                   * @notice Deploys a new token or registers an existing token in the gateway contract itself.
                   * @param params Encoded parameters including the token name, symbol, decimals, cap, token address, and mint limit
                   * @dev If the token address is not specified, a new token is deployed and registed as InternalBurnableFrom
                   * @dev If the token address is specified, the token is marked as External.
                   * @dev Emits a TokenDeployed event with the symbol and token address.
                   */
                  function deployToken(bytes calldata params, bytes32) external onlySelf {
                      (string memory name, string memory symbol, uint8 decimals, uint256 cap, address tokenAddress, uint256 mintLimit) = abi.decode(
                          params,
                          (string, string, uint8, uint256, address, uint256)
                      );
                      // Ensure that this symbol has not been taken.
                      if (tokenAddresses(symbol) != address(0)) revert TokenAlreadyExists(symbol);
                      _setTokenMintLimit(symbol, mintLimit);
                      if (tokenAddress == address(0)) {
                          // If token address is not specified, it indicates a request to deploy one.
                          bytes32 salt = keccak256(abi.encodePacked(symbol));
                          _setTokenType(symbol, TokenType.InternalBurnableFrom);
                          // slither-disable-next-line reentrancy-no-eth,controlled-delegatecall
                          (bool success, bytes memory data) = tokenDeployer.delegatecall(
                              abi.encodeWithSelector(ITokenDeployer.deployToken.selector, name, symbol, decimals, cap, salt)
                          );
                          if (!success) revert TokenDeployFailed(symbol);
                          tokenAddress = abi.decode(data, (address));
                      } else {
                          // If token address is specified, ensure that there is a contact at the specified address.
                          if (tokenAddress.code.length == uint256(0)) revert TokenContractDoesNotExist(tokenAddress);
                          // Mark that this symbol is an external token, which is needed to differentiate between operations on mint and burn.
                          _setTokenType(symbol, TokenType.External);
                      }
                      // slither-disable-next-line reentrancy-events
                      emit TokenDeployed(symbol, tokenAddress);
                      _setTokenAddress(symbol, tokenAddress);
                  }
                  /**
                   * @notice Transfers a specific amount of tokens to an account, based on the provided symbol.
                   * @param params Encoded parameters including the token symbol, recipient address, and amount to mint.
                   * @dev This function will revert if the token is not registered with the gatewaty.
                   * @dev If the token type is External, a safe transfer is performed to the recipient account.
                   * @dev If the token type is Internal (InternalBurnable or InternalBurnableFrom), the mint function is called on the token address.
                   */
                  function mintToken(bytes calldata params, bytes32) external onlySelf {
                      (string memory symbol, address account, uint256 amount) = abi.decode(params, (string, address, uint256));
                      _mintToken(symbol, account, amount);
                  }
                  /**
                   * @notice Burns tokens of a given symbol, either through an external deposit handler or a token defined burn method.
                   * @param params Encoded parameters including the token symbol and a salt value for the deposit handler
                   */
                  function burnToken(bytes calldata params, bytes32) external onlySelf {
                      (string memory symbol, bytes32 salt) = abi.decode(params, (string, bytes32));
                      address tokenAddress = tokenAddresses(symbol);
                      if (tokenAddress == address(0)) revert TokenDoesNotExist(symbol);
                      if (_getTokenType(symbol) == TokenType.External) {
                          address depositHandlerAddress = _getCreate2Address(salt, keccak256(abi.encodePacked(type(DepositHandler).creationCode)));
                          if (depositHandlerAddress.isContract()) return;
                          DepositHandler depositHandler = new DepositHandler{ salt: salt }();
                          (bool success, bytes memory returnData) = depositHandler.execute(
                              tokenAddress,
                              abi.encodeWithSelector(IERC20.transfer.selector, address(this), IERC20(tokenAddress).balanceOf(address(depositHandler)))
                          );
                          if (!success || (returnData.length != uint256(0) && !abi.decode(returnData, (bool)))) revert BurnFailed(symbol);
                          // NOTE: `depositHandler` must always be destroyed in the same runtime context that it is deployed.
                          depositHandler.destroy(address(this));
                      } else {
                          IBurnableMintableCappedERC20(tokenAddress).burn(salt);
                      }
                  }
                  /**
                   * @notice Approves a contract call.
                   * @param params Encoded parameters including the source chain, source address, contract address, payload hash, transaction hash, and event index
                   * @param commandId to associate with the approval
                   */
                  function approveContractCall(bytes calldata params, bytes32 commandId) external onlySelf {
                      (
                          string memory sourceChain,
                          string memory sourceAddress,
                          address contractAddress,
                          bytes32 payloadHash,
                          bytes32 sourceTxHash,
                          uint256 sourceEventIndex
                      ) = abi.decode(params, (string, string, address, bytes32, bytes32, uint256));
                      _setContractCallApproved(commandId, sourceChain, sourceAddress, contractAddress, payloadHash);
                      emit ContractCallApproved(commandId, sourceChain, sourceAddress, contractAddress, payloadHash, sourceTxHash, sourceEventIndex);
                  }
                  /**
                   * @notice Approves a contract call with token transfer.
                   * @param params Encoded parameters including the source chain, source address, contract address, payload hash, token symbol,
                   * token amount, transaction hash, and event index.
                   * @param commandId to associate with the approval
                   */
                  function approveContractCallWithMint(bytes calldata params, bytes32 commandId) external onlySelf {
                      (
                          string memory sourceChain,
                          string memory sourceAddress,
                          address contractAddress,
                          bytes32 payloadHash,
                          string memory symbol,
                          uint256 amount,
                          bytes32 sourceTxHash,
                          uint256 sourceEventIndex
                      ) = abi.decode(params, (string, string, address, bytes32, string, uint256, bytes32, uint256));
                      _setContractCallApprovedWithMint(commandId, sourceChain, sourceAddress, contractAddress, payloadHash, symbol, amount);
                      emit ContractCallApprovedWithMint(
                          commandId,
                          sourceChain,
                          sourceAddress,
                          contractAddress,
                          payloadHash,
                          symbol,
                          amount,
                          sourceTxHash,
                          sourceEventIndex
                      );
                  }
                  /**
                   * @notice Transfers operatorship with the provided data by calling the transferOperatorship function on the auth module.
                   * @param newOperatorsData Encoded data for the new operators
                   */
                  function transferOperatorship(bytes calldata newOperatorsData, bytes32) external onlySelf {
                      emit OperatorshipTransferred(newOperatorsData);
                      IAxelarAuth(authModule).transferOperatorship(newOperatorsData);
                  }
                  /********************\\
                  |* Internal Methods *|
                  \\********************/
                  function _mintToken(
                      string memory symbol,
                      address account,
                      uint256 amount
                  ) internal {
                      address tokenAddress = tokenAddresses(symbol);
                      if (tokenAddress == address(0)) revert TokenDoesNotExist(symbol);
                      _setTokenMintAmount(symbol, tokenMintAmount(symbol) + amount);
                      if (_getTokenType(symbol) == TokenType.External) {
                          IERC20(tokenAddress).safeTransfer(account, amount);
                      } else {
                          IBurnableMintableCappedERC20(tokenAddress).mint(account, amount);
                      }
                  }
                  /**
                   * @notice Burns or locks a specific amount of tokens from a sender's account based on the provided symbol.
                   * @param sender Address of the account from which to burn the tokens
                   * @param symbol Symbol of the token to burn
                   * @param amount Amount of tokens to burn
                   * @dev Depending on the token type (External, InternalBurnableFrom, or InternalBurnable), the function either
                   * transfers the tokens to gateway contract itself or calls a burn function on the token contract.
                   */
                  function _burnTokenFrom(
                      address sender,
                      string memory symbol,
                      uint256 amount
                  ) internal {
                      address tokenAddress = tokenAddresses(symbol);
                      if (tokenAddress == address(0)) revert TokenDoesNotExist(symbol);
                      if (amount == 0) revert InvalidAmount();
                      TokenType tokenType = _getTokenType(symbol);
                      if (tokenType == TokenType.External) {
                          IERC20(tokenAddress).safeTransferFrom(sender, address(this), amount);
                      } else if (tokenType == TokenType.InternalBurnableFrom) {
                          IERC20(tokenAddress).safeCall(abi.encodeWithSelector(IBurnableMintableCappedERC20.burnFrom.selector, sender, amount));
                      } else {
                          IERC20(tokenAddress).safeTransferFrom(sender, IBurnableMintableCappedERC20(tokenAddress).depositAddress(bytes32(0)), amount);
                          IBurnableMintableCappedERC20(tokenAddress).burn(bytes32(0));
                      }
                  }
                  /********************\\
                  |* Pure Key Getters *|
                  \\********************/
                  function _getTokenMintLimitKey(string memory symbol) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked(PREFIX_TOKEN_MINT_LIMIT, symbol));
                  }
                  function _getTokenMintAmountKey(string memory symbol, uint256 day) internal pure returns (bytes32) {
                      return keccak256(abi.encode(PREFIX_TOKEN_MINT_AMOUNT, symbol, day));
                  }
                  function _getTokenTypeKey(string memory symbol) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked(PREFIX_TOKEN_TYPE, symbol));
                  }
                  function _getTokenAddressKey(string memory symbol) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked(PREFIX_TOKEN_ADDRESS, symbol));
                  }
                  function _getIsCommandExecutedKey(bytes32 commandId) internal pure returns (bytes32) {
                      return keccak256(abi.encodePacked(PREFIX_COMMAND_EXECUTED, commandId));
                  }
                  function _getIsContractCallApprovedKey(
                      bytes32 commandId,
                      string memory sourceChain,
                      string memory sourceAddress,
                      address contractAddress,
                      bytes32 payloadHash
                  ) internal pure returns (bytes32) {
                      return keccak256(abi.encode(PREFIX_CONTRACT_CALL_APPROVED, commandId, sourceChain, sourceAddress, contractAddress, payloadHash));
                  }
                  function _getIsContractCallApprovedWithMintKey(
                      bytes32 commandId,
                      string memory sourceChain,
                      string memory sourceAddress,
                      address contractAddress,
                      bytes32 payloadHash,
                      string memory symbol,
                      uint256 amount
                  ) internal pure returns (bytes32) {
                      return
                          keccak256(
                              abi.encode(
                                  PREFIX_CONTRACT_CALL_APPROVED_WITH_MINT,
                                  commandId,
                                  sourceChain,
                                  sourceAddress,
                                  contractAddress,
                                  payloadHash,
                                  symbol,
                                  amount
                              )
                          );
                  }
                  /********************\\
                  |* Internal Getters *|
                  \\********************/
                  function _getCreate2Address(bytes32 salt, bytes32 codeHash) internal view returns (address) {
                      return address(uint160(uint256(keccak256(abi.encodePacked(bytes1(0xff), address(this), salt, codeHash)))));
                  }
                  function _getTokenType(string memory symbol) internal view returns (TokenType) {
                      return TokenType(getUint(_getTokenTypeKey(symbol)));
                  }
                  /********************\\
                  |* Internal Setters *|
                  \\********************/
                  function _setTokenMintLimit(string memory symbol, uint256 limit) internal {
                      emit TokenMintLimitUpdated(symbol, limit);
                      _setUint(_getTokenMintLimitKey(symbol), limit);
                  }
                  function _setTokenMintAmount(string memory symbol, uint256 amount) internal {
                      uint256 limit = tokenMintLimit(symbol);
                      if (limit > 0 && amount > limit) revert ExceedMintLimit(symbol);
                      _setUint(_getTokenMintAmountKey(symbol, block.timestamp / 6 hours), amount);
                  }
                  function _setTokenType(string memory symbol, TokenType tokenType) internal {
                      _setUint(_getTokenTypeKey(symbol), uint256(tokenType));
                  }
                  function _setTokenAddress(string memory symbol, address tokenAddress) internal {
                      _setAddress(_getTokenAddressKey(symbol), tokenAddress);
                  }
                  function _setCommandExecuted(bytes32 commandId, bool executed) internal {
                      _setBool(_getIsCommandExecutedKey(commandId), executed);
                  }
                  function _setContractCallApproved(
                      bytes32 commandId,
                      string memory sourceChain,
                      string memory sourceAddress,
                      address contractAddress,
                      bytes32 payloadHash
                  ) internal {
                      _setBool(_getIsContractCallApprovedKey(commandId, sourceChain, sourceAddress, contractAddress, payloadHash), true);
                  }
                  function _setContractCallApprovedWithMint(
                      bytes32 commandId,
                      string memory sourceChain,
                      string memory sourceAddress,
                      address contractAddress,
                      bytes32 payloadHash,
                      string memory symbol,
                      uint256 amount
                  ) internal {
                      _setBool(
                          _getIsContractCallApprovedWithMintKey(commandId, sourceChain, sourceAddress, contractAddress, payloadHash, symbol, amount),
                          true
                      );
                  }
                  function _setImplementation(address newImplementation) internal {
                      _setAddress(KEY_IMPLEMENTATION, newImplementation);
                  }
                  function _transferGovernance(address newGovernance) internal {
                      emit GovernanceTransferred(getAddress(KEY_GOVERNANCE), newGovernance);
                      _setAddress(KEY_GOVERNANCE, newGovernance);
                  }
                  function _transferMintLimiter(address newMintLimiter) internal {
                      emit MintLimiterTransferred(getAddress(KEY_MINT_LIMITER), newMintLimiter);
                      _setAddress(KEY_MINT_LIMITER, newMintLimiter);
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.9;
              contract DepositHandler {
                  error IsLocked();
                  error NotContract();
                  uint256 internal constant IS_NOT_LOCKED = uint256(1);
                  uint256 internal constant IS_LOCKED = uint256(2);
                  uint256 internal _lockedStatus = IS_NOT_LOCKED;
                  modifier noReenter() {
                      if (_lockedStatus == IS_LOCKED) revert IsLocked();
                      _lockedStatus = IS_LOCKED;
                      _;
                      _lockedStatus = IS_NOT_LOCKED;
                  }
                  function execute(address callee, bytes calldata data) external noReenter returns (bool success, bytes memory returnData) {
                      if (callee.code.length == 0) revert NotContract();
                      (success, returnData) = callee.call(data);
                  }
                  // NOTE: The gateway should always destroy the `DepositHandler` in the same runtime context that deploys it.
                  function destroy(address etherDestination) external noReenter {
                      selfdestruct(payable(etherDestination));
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.9;
              /**
               * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
               *
               * These functions can be used to verify that a message was signed by the holder
               * of the private keys of a given address.
               */
              library ECDSA {
                  error InvalidSignatureLength();
                  error InvalidS();
                  error InvalidV();
                  error InvalidSignature();
                  /**
                   * @dev Returns the address that signed a hashed message (`hash`) with
                   * `signature`. This address can then be used for verification purposes.
                   *
                   * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures:
                   * this function rejects them by requiring the `s` value to be in the lower
                   * half order, and the `v` value to be either 27 or 28.
                   *
                   * IMPORTANT: `hash` _must_ be the result of a hash operation for the
                   * verification to be secure: it is possible to craft signatures that
                   * recover to arbitrary addresses for non-hashed data. A safe way to ensure
                   * this is by receiving a hash of the original message (which may otherwise
                   * be too long), and then calling {toEthSignedMessageHash} on it.
                   */
                  function recover(bytes32 hash, bytes memory signature) internal pure returns (address signer) {
                      // Check the signature length
                      if (signature.length != 65) revert InvalidSignatureLength();
                      // Divide the signature in r, s and v variables
                      bytes32 r;
                      bytes32 s;
                      uint8 v;
                      // ecrecover takes the signature parameters, and the only way to get them
                      // currently is to use assembly.
                      // solhint-disable-next-line no-inline-assembly
                      assembly {
                          r := mload(add(signature, 0x20))
                          s := mload(add(signature, 0x40))
                          v := byte(0, mload(add(signature, 0x60)))
                      }
                      // 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 InvalidS();
                      if (v != 27 && v != 28) revert InvalidV();
                      // If the signature is valid (and not malleable), return the signer address
                      if ((signer = ecrecover(hash, v, r, s)) == address(0)) revert InvalidSignature();
                  }
                  /**
                   * @dev Returns an Ethereum Signed Message, created from a `hash`. This
                   * replicates the behavior of the
                   * https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_sign[`eth_sign`]
                   * JSON-RPC method.
                   *
                   * See {recover}.
                   */
                  function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32) {
                      // 32 is the length in bytes of hash,
                      // enforced by the type signature above
                      return keccak256(abi.encodePacked('\\x19Ethereum Signed Message:\
              32', hash));
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity 0.8.9;
              /**
               * @title EternalStorage
               * @dev This contract holds all the necessary state variables to carry out the storage of any contract.
               */
              contract EternalStorage {
                  mapping(bytes32 => uint256) private _uintStorage;
                  mapping(bytes32 => string) private _stringStorage;
                  mapping(bytes32 => address) private _addressStorage;
                  mapping(bytes32 => bytes) private _bytesStorage;
                  mapping(bytes32 => bool) private _boolStorage;
                  mapping(bytes32 => int256) private _intStorage;
                  // *** Getter Methods ***
                  function getUint(bytes32 key) public view returns (uint256) {
                      return _uintStorage[key];
                  }
                  function getString(bytes32 key) public view returns (string memory) {
                      return _stringStorage[key];
                  }
                  function getAddress(bytes32 key) public view returns (address) {
                      return _addressStorage[key];
                  }
                  function getBytes(bytes32 key) public view returns (bytes memory) {
                      return _bytesStorage[key];
                  }
                  function getBool(bytes32 key) public view returns (bool) {
                      return _boolStorage[key];
                  }
                  function getInt(bytes32 key) public view returns (int256) {
                      return _intStorage[key];
                  }
                  // *** Setter Methods ***
                  function _setUint(bytes32 key, uint256 value) internal {
                      _uintStorage[key] = value;
                  }
                  function _setString(bytes32 key, string memory value) internal {
                      _stringStorage[key] = value;
                  }
                  function _setAddress(bytes32 key, address value) internal {
                      _addressStorage[key] = value;
                  }
                  function _setBytes(bytes32 key, bytes memory value) internal {
                      _bytesStorage[key] = value;
                  }
                  function _setBool(bytes32 key, bool value) internal {
                      _boolStorage[key] = value;
                  }
                  function _setInt(bytes32 key, int256 value) internal {
                      _intStorage[key] = value;
                  }
                  // *** Delete Methods ***
                  function _deleteUint(bytes32 key) internal {
                      delete _uintStorage[key];
                  }
                  function _deleteString(bytes32 key) internal {
                      delete _stringStorage[key];
                  }
                  function _deleteAddress(bytes32 key) internal {
                      delete _addressStorage[key];
                  }
                  function _deleteBytes(bytes32 key) internal {
                      delete _bytesStorage[key];
                  }
                  function _deleteBool(bytes32 key) internal {
                      delete _boolStorage[key];
                  }
                  function _deleteInt(bytes32 key) internal {
                      delete _intStorage[key];
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { IOwnable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IOwnable.sol';
              interface IAxelarAuth is IOwnable {
                  function validateProof(bytes32 messageHash, bytes calldata proof) external returns (bool currentOperators);
                  function transferOperatorship(bytes calldata params) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.9;
              import { IERC20Burn } from './IERC20Burn.sol';
              import { IERC20BurnFrom } from './IERC20BurnFrom.sol';
              import { IMintableCappedERC20 } from './IMintableCappedERC20.sol';
              interface IBurnableMintableCappedERC20 is IERC20Burn, IERC20BurnFrom, IMintableCappedERC20 {
                  function depositAddress(bytes32 salt) external view returns (address);
                  function burn(bytes32 salt) external;
                  function burnFrom(address account, uint256 amount) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.9;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  error InvalidAccount();
                  /**
                   * @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.8.9;
              interface IERC20Burn {
                  function burn(bytes32 salt) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.9;
              interface IERC20BurnFrom {
                  function burnFrom(address account, uint256 amount) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.9;
              interface IERC20Permit {
                  function DOMAIN_SEPARATOR() external view returns (bytes32);
                  function nonces(address account) external view returns (uint256);
                  function permit(
                      address issuer,
                      address spender,
                      uint256 value,
                      uint256 deadline,
                      uint8 v,
                      bytes32 r,
                      bytes32 s
                  ) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.9;
              import { IERC20 } from './IERC20.sol';
              import { IERC20Permit } from './IERC20Permit.sol';
              import { IOwnable } from './IOwnable.sol';
              interface IMintableCappedERC20 is IERC20, IERC20Permit, IOwnable {
                  error CapExceeded();
                  function cap() external view returns (uint256);
                  function mint(address account, uint256 amount) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.9;
              interface IOwnable {
                  error NotOwner();
                  error InvalidOwner();
                  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
                  function owner() external view returns (address);
                  function transferOwnership(address newOwner) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              interface ITokenDeployer {
                  function deployToken(
                      string calldata name,
                      string calldata symbol,
                      uint8 decimals,
                      uint256 cap,
                      bytes32 salt
                  ) external returns (address tokenAddress);
              }
              

              File 4 of 4: AxelarGasService
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { GasEstimationType, GasInfo } from '../types/GasEstimationTypes.sol';
              import { IInterchainGasEstimation } from '../interfaces/IInterchainGasEstimation.sol';
              /**
               * @title InterchainGasEstimation
               * @notice This is an abstract contract that allows for estimating gas fees for cross-chain communication on the Axelar network.
               */
              abstract contract InterchainGasEstimation is IInterchainGasEstimation {
                  // keccak256('GasEstimate.Slot') - 1
                  bytes32 internal constant GAS_SERVICE_SLOT = 0x2fa150da4c9f4c3a28593398c65313dd42f63d0530ec6db4a2b46e6d837a3902;
                  // 68 bytes for the TX RLP encoding overhead
                  uint256 internal constant TX_ENCODING_OVERHEAD = 68;
                  // GMP executeWithToken call parameters
                  // 4 bytes for method selector, 32 bytes for the commandId, 96 bytes for the sourceChain, 128 bytes for the sourceAddress, 96 bytes for token symbol, 32 bytes for amount
                  // Expecting most of the calldata bytes to be zeroes. So multiplying by 8 as a weighted average of 4 and 16
                  uint256 internal constant GMP_CALLDATA_SIZE = 4 + 32 + 96 + 128 + 96 + 32; // 388 bytes
                  struct GasServiceStorage {
                      mapping(string => GasInfo) gasPrices;
                  }
                  /**
                   * @notice Returns the gas price for a specific chain.
                   * @param chain The name of the chain
                   * @return gasInfo The gas info for the chain
                   */
                  function getGasInfo(string calldata chain) external view returns (GasInfo memory) {
                      return _storage().gasPrices[chain];
                  }
                  /**
                   * @notice Sets the gas price for a specific chain.
                   * @dev This function is called by the gas oracle to update the gas price for a specific chain.
                   * @param chain The name of the chain
                   * @param gasInfo The gas info for the chain
                   */
                  function _setGasInfo(string calldata chain, GasInfo calldata gasInfo) internal {
                      emit GasInfoUpdated(chain, gasInfo);
                      _storage().gasPrices[chain] = gasInfo;
                  }
                  /**
                   * @notice Estimates the gas fee for a contract call on a destination chain.
                   * @param destinationChain Axelar registered name of the destination chain
                   * param destinationAddress Destination contract address being called
                   * @param executionGasLimit The gas limit to be used for the destination contract execution,
                   *        e.g. pass in 200k if your app consumes needs upto 200k for this contract call
                   * param params Additional parameters for the gas estimation
                   * @return gasEstimate The cross-chain gas estimate, in terms of source chain's native gas token that should be forwarded to the gas service.
                   */
                  function estimateGasFee(
                      string calldata destinationChain,
                      string calldata, /* destinationAddress */
                      bytes calldata payload,
                      uint256 executionGasLimit,
                      bytes calldata /* params */
                  ) public view returns (uint256 gasEstimate) {
                      GasInfo storage gasInfo = _storage().gasPrices[destinationChain];
                      GasEstimationType gasEstimationType = GasEstimationType(gasInfo.gasEstimationType);
                      gasEstimate = gasInfo.axelarBaseFee + (executionGasLimit * gasInfo.relativeGasPrice);
                      // if chain is L2, compute L1 data fee using L1 gas price info
                      if (gasEstimationType != GasEstimationType.Default) {
                          GasInfo storage l1GasInfo = _storage().gasPrices['ethereum'];
                          gasEstimate += computeL1DataFee(gasEstimationType, payload, gasInfo, l1GasInfo);
                      }
                  }
                  /**
                   * @notice Computes the additional L1 data fee for an L2 destination chain.
                   * @param gasEstimationType The gas estimation type
                   * @param payload The payload of the contract call
                   * @param l1GasInfo The L1 gas info
                   * @return l1DataFee The L1 to L2 data fee
                   */
                  function computeL1DataFee(
                      GasEstimationType gasEstimationType,
                      bytes calldata payload,
                      GasInfo storage gasInfo,
                      GasInfo storage l1GasInfo
                  ) internal view returns (uint256) {
                      if (gasEstimationType == GasEstimationType.OptimismEcotone) {
                          return optimismEcotoneL1Fee(payload, gasInfo, l1GasInfo);
                      }
                      if (gasEstimationType == GasEstimationType.OptimismBedrock) {
                          return optimismBedrockL1Fee(payload, gasInfo, l1GasInfo);
                      }
                      if (gasEstimationType == GasEstimationType.Arbitrum) {
                          return arbitrumL1Fee(payload, gasInfo, l1GasInfo);
                      }
                      if (gasEstimationType == GasEstimationType.Scroll) {
                          return scrollL1Fee(payload, gasInfo, l1GasInfo);
                      }
                      revert UnsupportedEstimationType(gasEstimationType);
                  }
                  /**
                   * @notice Computes the L1 to L2 fee for an OP chain with Ecotone gas model.
                   * @param payload The payload of the contract call
                   * @param gasInfo Destination chain gas info
                   * @param l1GasInfo The L1 gas info
                   * @return l1DataFee The L1 to L2 data fee
                   */
                  function optimismEcotoneL1Fee(
                      bytes calldata payload,
                      GasInfo storage gasInfo,
                      GasInfo storage l1GasInfo
                  ) internal view returns (uint256 l1DataFee) {
                      /* Optimism Ecotone gas model https://docs.optimism.io/stack/transactions/fees#ecotone
                           tx_compressed_size = ((count_zero_bytes(tx_data) * 4 + count_non_zero_bytes(tx_data) * 16)) / 16
                           weighted_gas_price = 16 * base_fee_scalar*base_fee + blob_base_fee_scalar * blob_base_fee
                           l1_data_fee = tx_compressed_size * weighted_gas_price
                         Reference implementation:
                           https://github.com/ethereum-optimism/optimism/blob/876e16ad04968f0bb641eb76f98eb77e7e1a3e16/packages/contracts-bedrock/src/L2/GasPriceOracle.sol#L138
                      */
                      // The new base_fee_scalar is currently set to 0.001368
                      // We are setting it to un upper bound of 0.0015 to account for possible fluctuations
                      uint256 scalarPrecision = 10**6;
                      // The blob_base_fee_scalar is currently set to 0.810949. Setting it to 0.9 as an upper bound
                      // https://eips.ethereum.org/EIPS/eip-4844
                      uint256 blobBaseFeeScalar = 9 * 10**5; // 0.9 multiplied by scalarPrecision
                      // Calculating transaction size in bytes that will later be divided by 16 to compress the size
                      uint256 txSize = _l1TxSize(payload);
                      uint256 weightedGasPrice = 16 *
                          gasInfo.l1FeeScalar *
                          l1GasInfo.relativeGasPrice +
                          blobBaseFeeScalar *
                          l1GasInfo.relativeBlobBaseFee;
                      l1DataFee = (weightedGasPrice * txSize) / (16 * scalarPrecision); // 16 for txSize compression and scalar precision conversion
                  }
                  /**
                   * @notice Computes the L1 to L2 fee for an OP chain with Bedrock gas model.
                   * @param payload The payload of the contract call
                   * @param gasInfo Destination chain gas info
                   * @param l1GasInfo The L1 gas info
                   * @return l1DataFee The L1 to L2 data fee
                   */
                  function optimismBedrockL1Fee(
                      bytes calldata payload,
                      GasInfo storage gasInfo,
                      GasInfo storage l1GasInfo
                  ) internal view returns (uint256 l1DataFee) {
                      // Resembling OP Bedrock gas price model
                      // https://docs.optimism.io/stack/transactions/fees#bedrock
                      // https://docs-v2.mantle.xyz/devs/concepts/tx-fee/ef
                      // Reference https://github.com/mantlenetworkio/mantle-v2/blob/a29f01045191344b0ba89542215e6a02bd5e7fcc/packages/contracts-bedrock/contracts/L2/GasPriceOracle.sol#L98-L105
                      uint256 overhead = 188;
                      uint256 precision = 1e6;
                      uint256 txSize = _l1TxSize(payload) + overhead;
                      return (l1GasInfo.relativeGasPrice * txSize * gasInfo.l1FeeScalar) / precision;
                  }
                  /**
                   * @notice Computes the L1 to L2 fee for a contract call on the Arbitrum chain.
                   * @param payload The payload of the contract call
                   * param gasInfo Destination chain gas info
                   * @param l1GasInfo The L1 gas info
                   * @return l1DataFee The L1 to L2 data fee
                   */
                  function arbitrumL1Fee(
                      bytes calldata payload,
                      GasInfo storage, /* gasInfo */
                      GasInfo storage l1GasInfo
                  ) internal view returns (uint256 l1DataFee) {
                      // https://docs.arbitrum.io/build-decentralized-apps/how-to-estimate-gas
                      // https://docs.arbitrum.io/arbos/l1-pricing
                      // Reference https://github.com/OffchainLabs/nitro/blob/master/arbos/l1pricing/l1pricing.go#L565-L578
                      uint256 oneInBips = 10000;
                      uint256 txDataNonZeroGasEIP2028 = 16;
                      uint256 estimationPaddingUnits = 16 * txDataNonZeroGasEIP2028;
                      uint256 estimationPaddingBasisPoints = 100;
                      uint256 l1Bytes = TX_ENCODING_OVERHEAD + GMP_CALLDATA_SIZE + payload.length;
                      // Brotli baseline compression rate as 2x
                      uint256 units = (txDataNonZeroGasEIP2028 * l1Bytes) / 2;
                      return
                          (l1GasInfo.relativeGasPrice *
                              (units + estimationPaddingUnits) *
                              (oneInBips + estimationPaddingBasisPoints)) / oneInBips;
                  }
                  /**
                   * @notice Computes the L1 to L2 fee for a contract call on the Scroll chain.
                   * @param payload The payload of the contract call
                   * @param gasInfo Destination chain gas info
                   * @param l1GasInfo The L1 gas info
                   * @return l1DataFee The L1 to L2 data fee
                   */
                  function scrollL1Fee(
                      bytes calldata payload,
                      GasInfo storage gasInfo,
                      GasInfo storage l1GasInfo
                  ) internal view returns (uint256 l1DataFee) {
                      // https://docs.scroll.io/en/developers/guides/estimating-gas-and-tx-fees/
                      // Reference https://github.com/scroll-tech/scroll/blob/af2913903b181f3492af1c62b4da4c1c99cc552d/contracts/src/L2/predeploys/L1GasPriceOracle.sol#L63-L86
                      uint256 overhead = 2500;
                      uint256 precision = 1e9;
                      uint256 txSize = _l1TxSize(payload) + overhead + (4 * 16);
                      return (l1GasInfo.relativeGasPrice * txSize * gasInfo.l1FeeScalar) / precision;
                  }
                  /**
                   * @notice Computes the transaction size for an L1 transaction
                   * @param payload The payload of the contract call
                   * @return txSize The transaction size
                   */
                  function _l1TxSize(bytes calldata payload) private pure returns (uint256 txSize) {
                      txSize = TX_ENCODING_OVERHEAD * 16;
                      // GMP executeWithToken call parameters
                      // Expecting most of the calldata bytes to be zeroes. So multiplying by 8 as a weighted average of 4 and 16
                      txSize += GMP_CALLDATA_SIZE * 8;
                      uint256 length = payload.length;
                      for (uint256 i; i < length; ++i) {
                          if (payload[i] == 0) {
                              txSize += 4; // 4 for each zero byte
                          } else {
                              txSize += 16; // 16 for each non-zero byte
                          }
                      }
                  }
                  /**
                   * @notice Get the storage slot for the GasServiceStorage struct
                   */
                  function _storage() private pure returns (GasServiceStorage storage slot) {
                      assembly {
                          slot.slot := GAS_SERVICE_SLOT
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { GasInfo } from '../types/GasEstimationTypes.sol';
              import { IInterchainGasEstimation } from './IInterchainGasEstimation.sol';
              import { IUpgradable } from './IUpgradable.sol';
              /**
               * @title IAxelarGasService Interface
               * @notice This is an interface for the AxelarGasService contract which manages gas payments
               * and refunds for cross-chain communication on the Axelar network.
               * @dev This interface inherits IUpgradable
               */
              interface IAxelarGasService is IInterchainGasEstimation, IUpgradable {
                  error InvalidAddress();
                  error NotCollector();
                  error InvalidAmounts();
                  error InvalidGasUpdates();
                  error InvalidParams();
                  error InsufficientGasPayment(uint256 required, uint256 provided);
                  event GasPaidForContractCall(
                      address indexed sourceAddress,
                      string destinationChain,
                      string destinationAddress,
                      bytes32 indexed payloadHash,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  );
                  event GasPaidForContractCallWithToken(
                      address indexed sourceAddress,
                      string destinationChain,
                      string destinationAddress,
                      bytes32 indexed payloadHash,
                      string symbol,
                      uint256 amount,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  );
                  event NativeGasPaidForContractCall(
                      address indexed sourceAddress,
                      string destinationChain,
                      string destinationAddress,
                      bytes32 indexed payloadHash,
                      uint256 gasFeeAmount,
                      address refundAddress
                  );
                  event NativeGasPaidForContractCallWithToken(
                      address indexed sourceAddress,
                      string destinationChain,
                      string destinationAddress,
                      bytes32 indexed payloadHash,
                      string symbol,
                      uint256 amount,
                      uint256 gasFeeAmount,
                      address refundAddress
                  );
                  event GasPaidForExpressCall(
                      address indexed sourceAddress,
                      string destinationChain,
                      string destinationAddress,
                      bytes32 indexed payloadHash,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  );
                  event GasPaidForExpressCallWithToken(
                      address indexed sourceAddress,
                      string destinationChain,
                      string destinationAddress,
                      bytes32 indexed payloadHash,
                      string symbol,
                      uint256 amount,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  );
                  event NativeGasPaidForExpressCall(
                      address indexed sourceAddress,
                      string destinationChain,
                      string destinationAddress,
                      bytes32 indexed payloadHash,
                      uint256 gasFeeAmount,
                      address refundAddress
                  );
                  event NativeGasPaidForExpressCallWithToken(
                      address indexed sourceAddress,
                      string destinationChain,
                      string destinationAddress,
                      bytes32 indexed payloadHash,
                      string symbol,
                      uint256 amount,
                      uint256 gasFeeAmount,
                      address refundAddress
                  );
                  event GasAdded(
                      bytes32 indexed txHash,
                      uint256 indexed logIndex,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  );
                  event NativeGasAdded(bytes32 indexed txHash, uint256 indexed logIndex, uint256 gasFeeAmount, address refundAddress);
                  event ExpressGasAdded(
                      bytes32 indexed txHash,
                      uint256 indexed logIndex,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  );
                  event NativeExpressGasAdded(
                      bytes32 indexed txHash,
                      uint256 indexed logIndex,
                      uint256 gasFeeAmount,
                      address refundAddress
                  );
                  event Refunded(
                      bytes32 indexed txHash,
                      uint256 indexed logIndex,
                      address payable receiver,
                      address token,
                      uint256 amount
                  );
                  /**
                   * @notice Pay for gas for any type of contract execution on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
                   * @dev If estimateOnChain is true, the function will estimate the gas cost and revert if the payment is insufficient.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call
                   * @param executionGasLimit The gas limit for the contract call
                   * @param estimateOnChain Flag to enable on-chain gas estimation
                   * @param refundAddress The address where refunds, if any, should be sent
                   * @param params Additional parameters for gas payment. This can be left empty for normal contract call payments.
                   */
                  function payGas(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      uint256 executionGasLimit,
                      bool estimateOnChain,
                      address refundAddress,
                      bytes calldata params
                  ) external payable;
                  /**
                   * @notice Pay for gas using ERC20 tokens for a contract call on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call
                   * @param gasToken The address of the ERC20 token used to pay for gas
                   * @param gasFeeAmount The amount of tokens to pay for gas
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payGasForContractCall(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  ) external;
                  /**
                   * @notice Pay for gas using ERC20 tokens for a contract call with tokens on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call with tokens will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call with tokens
                   * @param symbol The symbol of the token to be sent with the call
                   * @param amount The amount of tokens to be sent with the call
                   * @param gasToken The address of the ERC20 token used to pay for gas
                   * @param gasFeeAmount The amount of tokens to pay for gas
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payGasForContractCallWithToken(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      string calldata symbol,
                      uint256 amount,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  ) external;
                  /**
                   * @notice Pay for gas using native currency for a contract call on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payNativeGasForContractCall(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      address refundAddress
                  ) external payable;
                  /**
                   * @notice Pay for gas using native currency for a contract call with tokens on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call with tokens will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call with tokens
                   * @param symbol The symbol of the token to be sent with the call
                   * @param amount The amount of tokens to be sent with the call
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payNativeGasForContractCallWithToken(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      string calldata symbol,
                      uint256 amount,
                      address refundAddress
                  ) external payable;
                  /**
                   * @notice Pay for gas using ERC20 tokens for an express contract call on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to express execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call
                   * @param gasToken The address of the ERC20 token used to pay for gas
                   * @param gasFeeAmount The amount of tokens to pay for gas
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payGasForExpressCall(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  ) external;
                  /**
                   * @notice Pay for gas using ERC20 tokens for an express contract call with tokens on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to express execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call with tokens will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call with tokens
                   * @param symbol The symbol of the token to be sent with the call
                   * @param amount The amount of tokens to be sent with the call
                   * @param gasToken The address of the ERC20 token used to pay for gas
                   * @param gasFeeAmount The amount of tokens to pay for gas
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payGasForExpressCallWithToken(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      string calldata symbol,
                      uint256 amount,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  ) external;
                  /**
                   * @notice Pay for gas using native currency for an express contract call on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payNativeGasForExpressCall(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      address refundAddress
                  ) external payable;
                  /**
                   * @notice Pay for gas using native currency for an express contract call with tokens on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call with tokens will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call with tokens
                   * @param symbol The symbol of the token to be sent with the call
                   * @param amount The amount of tokens to be sent with the call
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payNativeGasForExpressCallWithToken(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      string calldata symbol,
                      uint256 amount,
                      address refundAddress
                  ) external payable;
                  /**
                   * @notice Add additional gas payment using ERC20 tokens after initiating a cross-chain call.
                   * @dev This function can be called on the source chain after calling the gateway to execute a remote contract.
                   * @param txHash The transaction hash of the cross-chain call
                   * @param logIndex The log index for the cross-chain call
                   * @param gasToken The ERC20 token address used to add gas
                   * @param gasFeeAmount The amount of tokens to add as gas
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function addGas(
                      bytes32 txHash,
                      uint256 logIndex,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  ) external;
                  /**
                   * @notice Add additional gas payment using native currency after initiating a cross-chain call.
                   * @dev This function can be called on the source chain after calling the gateway to execute a remote contract.
                   * @param txHash The transaction hash of the cross-chain call
                   * @param logIndex The log index for the cross-chain call
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function addNativeGas(
                      bytes32 txHash,
                      uint256 logIndex,
                      address refundAddress
                  ) external payable;
                  /**
                   * @notice Add additional gas payment using ERC20 tokens after initiating an express cross-chain call.
                   * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract.
                   * @param txHash The transaction hash of the cross-chain call
                   * @param logIndex The log index for the cross-chain call
                   * @param gasToken The ERC20 token address used to add gas
                   * @param gasFeeAmount The amount of tokens to add as gas
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function addExpressGas(
                      bytes32 txHash,
                      uint256 logIndex,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  ) external;
                  /**
                   * @notice Add additional gas payment using native currency after initiating an express cross-chain call.
                   * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract.
                   * @param txHash The transaction hash of the cross-chain call
                   * @param logIndex The log index for the cross-chain call
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function addNativeExpressGas(
                      bytes32 txHash,
                      uint256 logIndex,
                      address refundAddress
                  ) external payable;
                  /**
                   * @notice Updates the gas price for a specific chain.
                   * @dev This function is called by the gas oracle to update the gas prices for a specific chains.
                   * @param chains Array of chain names
                   * @param gasUpdates Array of gas updates
                   */
                  function updateGasInfo(string[] calldata chains, GasInfo[] calldata gasUpdates) external;
                  /**
                   * @notice Allows the gasCollector to collect accumulated fees from the contract.
                   * @dev Use address(0) as the token address for native currency.
                   * @param receiver The address to receive the collected fees
                   * @param tokens Array of token addresses to be collected
                   * @param amounts Array of amounts to be collected for each respective token address
                   */
                  function collectFees(
                      address payable receiver,
                      address[] calldata tokens,
                      uint256[] calldata amounts
                  ) external;
                  /**
                   * @notice Refunds gas payment to the receiver in relation to a specific cross-chain transaction.
                   * @dev Only callable by the gasCollector.
                   * @dev Use address(0) as the token address to refund native currency.
                   * @param txHash The transaction hash of the cross-chain call
                   * @param logIndex The log index for the cross-chain call
                   * @param receiver The address to receive the refund
                   * @param token The token address to be refunded
                   * @param amount The amount to refund
                   */
                  function refund(
                      bytes32 txHash,
                      uint256 logIndex,
                      address payable receiver,
                      address token,
                      uint256 amount
                  ) external;
                  /**
                   * @notice Returns the address of the designated gas collector.
                   * @return address of the gas collector
                   */
                  function gasCollector() external returns (address);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              // General interface for upgradable contracts
              interface IContractIdentifier {
                  /**
                   * @notice Returns the contract ID. It can be used as a check during upgrades.
                   * @dev Meant to be overridden in derived contracts.
                   * @return bytes32 The contract ID
                   */
                  function contractId() external pure returns (bytes32);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @dev Interface of the ERC20 standard as defined in the EIP.
               */
              interface IERC20 {
                  error InvalidAccount();
                  /**
                   * @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.8.0;
              import { IContractIdentifier } from './IContractIdentifier.sol';
              interface IImplementation is IContractIdentifier {
                  error NotProxy();
                  function setup(bytes calldata data) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { GasEstimationType, GasInfo } from '../types/GasEstimationTypes.sol';
              /**
               * @title IInterchainGasEstimation Interface
               * @notice This is an interface for the InterchainGasEstimation contract
               * which allows for estimating gas fees for cross-chain communication on the Axelar network.
               */
              interface IInterchainGasEstimation {
                  error UnsupportedEstimationType(GasEstimationType gasEstimationType);
                  /**
                   * @notice Event emitted when the gas price for a specific chain is updated.
                   * @param chain The name of the chain
                   * @param info The gas info for the chain
                   */
                  event GasInfoUpdated(string chain, GasInfo info);
                  /**
                   * @notice Returns the gas price for a specific chain.
                   * @param chain The name of the chain
                   * @return gasInfo The gas info for the chain
                   */
                  function getGasInfo(string calldata chain) external view returns (GasInfo memory);
                  /**
                   * @notice Estimates the gas fee for a cross-chain contract call.
                   * @param destinationChain Axelar registered name of the destination chain
                   * @param destinationAddress Destination contract address being called
                   * @param executionGasLimit The gas limit to be used for the destination contract execution,
                   *        e.g. pass in 200k if your app consumes needs upto 200k for this contract call
                   * @param params Additional parameters for the gas estimation
                   * @return gasEstimate The cross-chain gas estimate, in terms of source chain's native gas token that should be forwarded to the gas service.
                   */
                  function estimateGasFee(
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      uint256 executionGasLimit,
                      bytes calldata params
                  ) external view returns (uint256 gasEstimate);
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @title IOwnable Interface
               * @notice IOwnable is an interface that abstracts the implementation of a
               * contract with ownership control features. It's commonly used in upgradable
               * contracts and includes the functionality to get current owner, transfer
               * ownership, and propose and accept ownership.
               */
              interface IOwnable {
                  error NotOwner();
                  error InvalidOwner();
                  error InvalidOwnerAddress();
                  event OwnershipTransferStarted(address indexed newOwner);
                  event OwnershipTransferred(address indexed newOwner);
                  /**
                   * @notice Returns the current owner of the contract.
                   * @return address The address of the current owner
                   */
                  function owner() external view returns (address);
                  /**
                   * @notice Returns the address of the pending owner of the contract.
                   * @return address The address of the pending owner
                   */
                  function pendingOwner() external view returns (address);
                  /**
                   * @notice Transfers ownership of the contract to a new address
                   * @param newOwner The address to transfer ownership to
                   */
                  function transferOwnership(address newOwner) external;
                  /**
                   * @notice Proposes to transfer the contract's ownership to a new address.
                   * The new owner needs to accept the ownership explicitly.
                   * @param newOwner The address to transfer ownership to
                   */
                  function proposeOwnership(address newOwner) external;
                  /**
                   * @notice Transfers ownership to the pending owner.
                   * @dev Can only be called by the pending owner
                   */
                  function acceptOwnership() external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { IOwnable } from './IOwnable.sol';
              import { IImplementation } from './IImplementation.sol';
              // General interface for upgradable contracts
              interface IUpgradable is IOwnable, IImplementation {
                  error InvalidCodeHash();
                  error InvalidImplementation();
                  error SetupFailed();
                  event Upgraded(address indexed newImplementation);
                  function implementation() external view returns (address);
                  function upgrade(
                      address newImplementation,
                      bytes32 newImplementationCodeHash,
                      bytes calldata params
                  ) external;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              error NativeTransferFailed();
              /*
               * @title SafeNativeTransfer
               * @dev This library is used for performing safe native value transfers in Solidity by utilizing inline assembly.
               */
              library SafeNativeTransfer {
                  /*
                   * @notice Perform a native transfer to a given address.
                   * @param receiver The recipient address to which the amount will be sent.
                   * @param amount The amount of native value to send.
                   * @throws NativeTransferFailed error if transfer is not successful.
                   */
                  function safeNativeTransfer(address receiver, uint256 amount) internal {
                      bool success;
                      assembly {
                          success := call(gas(), receiver, amount, 0, 0, 0, 0)
                      }
                      if (!success) revert NativeTransferFailed();
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { IERC20 } from '../interfaces/IERC20.sol';
              error TokenTransferFailed();
              /*
               * @title SafeTokenCall
               * @dev This library is used for performing safe token transfers.
               */
              library SafeTokenCall {
                  /*
                   * @notice Make a safe call to a token contract.
                   * @param token The token contract to interact with.
                   * @param callData The function call data.
                   * @throws TokenTransferFailed error if transfer of token is not successful.
                   */
                  function safeCall(IERC20 token, bytes memory callData) internal {
                      (bool success, bytes memory returnData) = address(token).call(callData);
                      bool transferred = success && (returnData.length == uint256(0) || abi.decode(returnData, (bool)));
                      if (!transferred || address(token).code.length == 0) revert TokenTransferFailed();
                  }
              }
              /*
               * @title SafeTokenTransfer
               * @dev This library safely transfers tokens from the contract to a recipient.
               */
              library SafeTokenTransfer {
                  /*
                   * @notice Transfer tokens to a recipient.
                   * @param token The token contract.
                   * @param receiver The recipient of the tokens.
                   * @param amount The amount of tokens to transfer.
                   */
                  function safeTransfer(
                      IERC20 token,
                      address receiver,
                      uint256 amount
                  ) internal {
                      SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transfer.selector, receiver, amount));
                  }
              }
              /*
               * @title SafeTokenTransferFrom
               * @dev This library helps to safely transfer tokens on behalf of a token holder.
               */
              library SafeTokenTransferFrom {
                  /*
                   * @notice Transfer tokens on behalf of a token holder.
                   * @param token The token contract.
                   * @param from The address of the token holder.
                   * @param to The address the tokens are to be sent to.
                   * @param amount The amount of tokens to be transferred.
                   */
                  function safeTransferFrom(
                      IERC20 token,
                      address from,
                      address to,
                      uint256 amount
                  ) internal {
                      SafeTokenCall.safeCall(token, abi.encodeWithSelector(IERC20.transferFrom.selector, from, to, amount));
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              /**
               * @title GasEstimationType
               * @notice This enum represents the gas estimation types for different chains.
               */
              enum GasEstimationType {
                  Default,
                  OptimismEcotone,
                  OptimismBedrock,
                  Arbitrum,
                  Scroll
              }
              /**
               * @title GasInfo
               * @notice This struct represents the gas pricing information for a specific chain.
               * @dev Smaller uint types are used for efficient struct packing to save storage costs.
               */
              struct GasInfo {
                  /// @dev Custom gas pricing rule, such as L1 data fee on L2s
                  uint64 gasEstimationType;
                  /// @dev Scalar value needed for specific gas estimation types, expected to be less than 1e10
                  uint64 l1FeeScalar;
                  /// @dev Axelar base fee for cross-chain message approval on destination, in terms of source native gas token
                  uint128 axelarBaseFee;
                  /// @dev Gas price of destination chain, in terms of the source chain token, i.e dest_gas_price * dest_token_market_price / src_token_market_price
                  uint128 relativeGasPrice;
                  /// @dev Needed for specific gas estimation types. Blob base fee of destination chain, in terms of the source chain token, i.e dest_blob_base_fee * dest_token_market_price / src_token_market_price
                  uint128 relativeBlobBaseFee;
                  /// @dev Axelar express fee for express execution, in terms of source chain token
                  uint128 expressFee;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { IImplementation } from '../interfaces/IImplementation.sol';
              /**
               * @title Implementation
               * @notice This contract serves as a base for other contracts and enforces a proxy-first access restriction.
               * @dev Derived contracts must implement the setup function.
               */
              abstract contract Implementation is IImplementation {
                  address private immutable implementationAddress;
                  /**
                   * @dev Contract constructor that sets the implementation address to the address of this contract.
                   */
                  constructor() {
                      implementationAddress = address(this);
                  }
                  /**
                   * @dev Modifier to require the caller to be the proxy contract.
                   * Reverts if the caller is the current contract (i.e., the implementation contract itself).
                   */
                  modifier onlyProxy() {
                      if (implementationAddress == address(this)) revert NotProxy();
                      _;
                  }
                  /**
                   * @notice Initializes contract parameters.
                   * This function is intended to be overridden by derived contracts.
                   * The overriding function must have the onlyProxy modifier.
                   * @param params The parameters to be used for initialization
                   */
                  function setup(bytes calldata params) external virtual;
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { IImplementation } from '../interfaces/IImplementation.sol';
              import { IUpgradable } from '../interfaces/IUpgradable.sol';
              import { Ownable } from '../utils/Ownable.sol';
              import { Implementation } from './Implementation.sol';
              /**
               * @title Upgradable Contract
               * @notice This contract provides an interface for upgradable smart contracts and includes the functionality to perform upgrades.
               */
              abstract contract Upgradable is Ownable, Implementation, IUpgradable {
                  // bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)
                  bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
                  /**
                   * @notice Constructor sets the implementation address to the address of the contract itself
                   * @dev This is used in the onlyProxy modifier to prevent certain functions from being called directly
                   * on the implementation contract itself.
                   * @dev The owner is initially set as address(1) because the actual owner is set within the proxy. It is not
                   * set as the zero address because Ownable is designed to throw an error for ownership transfers to the zero address.
                   */
                  constructor() Ownable(address(1)) {}
                  /**
                   * @notice Returns the address of the current implementation
                   * @return implementation_ Address of the current implementation
                   */
                  function implementation() public view returns (address implementation_) {
                      assembly {
                          implementation_ := sload(_IMPLEMENTATION_SLOT)
                      }
                  }
                  /**
                   * @notice Upgrades the contract to a new implementation
                   * @param newImplementation The address of the new implementation contract
                   * @param newImplementationCodeHash The codehash of the new implementation contract
                   * @param params Optional setup parameters for the new implementation contract
                   * @dev This function is only callable by the owner.
                   */
                  function upgrade(
                      address newImplementation,
                      bytes32 newImplementationCodeHash,
                      bytes calldata params
                  ) external override onlyOwner {
                      if (IUpgradable(newImplementation).contractId() != IUpgradable(implementation()).contractId())
                          revert InvalidImplementation();
                      if (newImplementationCodeHash != newImplementation.codehash) revert InvalidCodeHash();
                      assembly {
                          sstore(_IMPLEMENTATION_SLOT, newImplementation)
                      }
                      emit Upgraded(newImplementation);
                      if (params.length > 0) {
                          // slither-disable-next-line controlled-delegatecall
                          (bool success, ) = newImplementation.delegatecall(abi.encodeWithSelector(this.setup.selector, params));
                          if (!success) revert SetupFailed();
                      }
                  }
                  /**
                   * @notice Sets up the contract with initial data
                   * @param data Initialization data for the contract
                   * @dev This function is only callable by the proxy contract.
                   */
                  function setup(bytes calldata data) external override(IImplementation, Implementation) onlyProxy {
                      _setup(data);
                  }
                  /**
                   * @notice Internal function to set up the contract with initial data
                   * @param data Initialization data for the contract
                   * @dev This function should be implemented in derived contracts.
                   */
                  function _setup(bytes calldata data) internal virtual {}
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { IOwnable } from '../interfaces/IOwnable.sol';
              /**
               * @title Ownable
               * @notice A contract module which provides a basic access control mechanism, where
               * there is an account (an owner) that can be granted exclusive access to
               * specific functions.
               *
               * The owner account is set through ownership transfer. This module makes
               * it possible to transfer the ownership of the contract to a new account in one
               * step, as well as to an interim pending owner. In the second flow the ownership does not
               * change until the pending owner accepts the ownership transfer.
               */
              abstract contract Ownable is IOwnable {
                  // keccak256('owner')
                  bytes32 internal constant _OWNER_SLOT = 0x02016836a56b71f0d02689e69e326f4f4c1b9057164ef592671cf0d37c8040c0;
                  // keccak256('ownership-transfer')
                  bytes32 internal constant _OWNERSHIP_TRANSFER_SLOT =
                      0x9855384122b55936fbfb8ca5120e63c6537a1ac40caf6ae33502b3c5da8c87d1;
                  /**
                   * @notice Initializes the contract by transferring ownership to the owner parameter.
                   * @param _owner Address to set as the initial owner of the contract
                   */
                  constructor(address _owner) {
                      _transferOwnership(_owner);
                  }
                  /**
                   * @notice Modifier that throws an error if called by any account other than the owner.
                   */
                  modifier onlyOwner() {
                      if (owner() != msg.sender) revert NotOwner();
                      _;
                  }
                  /**
                   * @notice Returns the current owner of the contract.
                   * @return owner_ The current owner of the contract
                   */
                  function owner() public view returns (address owner_) {
                      assembly {
                          owner_ := sload(_OWNER_SLOT)
                      }
                  }
                  /**
                   * @notice Returns the pending owner of the contract.
                   * @return owner_ The pending owner of the contract
                   */
                  function pendingOwner() public view returns (address owner_) {
                      assembly {
                          owner_ := sload(_OWNERSHIP_TRANSFER_SLOT)
                      }
                  }
                  /**
                   * @notice Transfers ownership of the contract to a new account `newOwner`.
                   * @dev Can only be called by the current owner.
                   * @param newOwner The address to transfer ownership to
                   */
                  function transferOwnership(address newOwner) external virtual onlyOwner {
                      _transferOwnership(newOwner);
                  }
                  /**
                   * @notice Propose to transfer ownership of the contract to a new account `newOwner`.
                   * @dev Can only be called by the current owner. The ownership does not change
                   * until the new owner accepts the ownership transfer.
                   * @param newOwner The address to transfer ownership to
                   */
                  function proposeOwnership(address newOwner) external virtual onlyOwner {
                      if (newOwner == address(0)) revert InvalidOwnerAddress();
                      emit OwnershipTransferStarted(newOwner);
                      assembly {
                          sstore(_OWNERSHIP_TRANSFER_SLOT, newOwner)
                      }
                  }
                  /**
                   * @notice Accepts ownership of the contract.
                   * @dev Can only be called by the pending owner
                   */
                  function acceptOwnership() external virtual {
                      address newOwner = pendingOwner();
                      if (newOwner != msg.sender) revert InvalidOwner();
                      _transferOwnership(newOwner);
                  }
                  /**
                   * @notice Internal function to transfer ownership of the contract to a new account `newOwner`.
                   * @dev Called in the constructor to set the initial owner.
                   * @param newOwner The address to transfer ownership to
                   */
                  function _transferOwnership(address newOwner) internal virtual {
                      if (newOwner == address(0)) revert InvalidOwnerAddress();
                      emit OwnershipTransferred(newOwner);
                      assembly {
                          sstore(_OWNER_SLOT, newOwner)
                          sstore(_OWNERSHIP_TRANSFER_SLOT, 0)
                      }
                  }
              }
              // SPDX-License-Identifier: MIT
              pragma solidity ^0.8.0;
              import { IERC20 } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IERC20.sol';
              import { IAxelarGasService } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/interfaces/IAxelarGasService.sol';
              import { InterchainGasEstimation, GasInfo } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/gas-estimation/InterchainGasEstimation.sol';
              import { Upgradable } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/Upgradable.sol';
              import { SafeTokenTransfer, SafeTokenTransferFrom } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeTransfer.sol';
              import { SafeNativeTransfer } from '@axelar-network/axelar-gmp-sdk-solidity/contracts/libs/SafeNativeTransfer.sol';
              /**
               * @title AxelarGasService
               * @notice This contract manages gas payments and refunds for cross-chain communication on the Axelar network.
               * @dev The owner address of this contract should be the microservice that pays for gas.
               * @dev Users pay gas for cross-chain calls, and the gasCollector can collect accumulated fees and/or refund users if needed.
               */
              contract AxelarGasService is InterchainGasEstimation, Upgradable, IAxelarGasService {
                  using SafeTokenTransfer for IERC20;
                  using SafeTokenTransferFrom for IERC20;
                  using SafeNativeTransfer for address payable;
                  address public immutable gasCollector;
                  /**
                   * @notice Constructs the AxelarGasService contract.
                   * @param gasCollector_ The address of the gas collector
                   */
                  constructor(address gasCollector_) {
                      gasCollector = gasCollector_;
                  }
                  /**
                   * @notice Modifier that ensures the caller is the designated gas collector.
                   */
                  modifier onlyCollector() {
                      if (msg.sender != gasCollector) revert NotCollector();
                      _;
                  }
                  /**
                   * @notice Pay for gas for any type of contract execution on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
                   * @dev If estimateOnChain is true, the function will estimate the gas cost and revert if the payment is insufficient.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call
                   * @param executionGasLimit The gas limit for the contract call
                   * @param estimateOnChain Flag to enable on-chain gas estimation
                   * @param refundAddress The address where refunds, if any, should be sent
                   * @param params Additional parameters for gas payment
                   */
                  function payGas(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      uint256 executionGasLimit,
                      bool estimateOnChain,
                      address refundAddress,
                      bytes calldata params
                  ) external payable override {
                      if (params.length > 0) {
                          revert InvalidParams();
                      }
                      if (estimateOnChain) {
                          uint256 gasEstimate = estimateGasFee(destinationChain, destinationAddress, payload, executionGasLimit, params);
                          if (gasEstimate > msg.value) {
                              revert InsufficientGasPayment(gasEstimate, msg.value);
                          }
                      }
                      emit NativeGasPaidForContractCall(sender, destinationChain, destinationAddress, keccak256(payload), msg.value, refundAddress);
                  }
                  /**
                   * @notice Pay for gas using ERC20 tokens for a contract call on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call
                   * @param gasToken The address of the ERC20 token used to pay for gas
                   * @param gasFeeAmount The amount of tokens to pay for gas
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payGasForContractCall(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  ) external override {
                      emit GasPaidForContractCall(
                          sender,
                          destinationChain,
                          destinationAddress,
                          keccak256(payload),
                          gasToken,
                          gasFeeAmount,
                          refundAddress
                      );
                      IERC20(gasToken).safeTransferFrom(msg.sender, address(this), gasFeeAmount);
                  }
                  /**
                   * @notice Pay for gas using ERC20 tokens for a contract call with tokens on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call with tokens will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call with tokens
                   * @param symbol The symbol of the token to be sent with the call
                   * @param amount The amount of tokens to be sent with the call
                   * @param gasToken The address of the ERC20 token used to pay for gas
                   * @param gasFeeAmount The amount of tokens to pay for gas
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payGasForContractCallWithToken(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      string memory symbol,
                      uint256 amount,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  ) external override {
                      emit GasPaidForContractCallWithToken(
                          sender,
                          destinationChain,
                          destinationAddress,
                          keccak256(payload),
                          symbol,
                          amount,
                          gasToken,
                          gasFeeAmount,
                          refundAddress
                      );
                      IERC20(gasToken).safeTransferFrom(msg.sender, address(this), gasFeeAmount);
                  }
                  /**
                   * @notice Pay for gas using native currency for a contract call on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payNativeGasForContractCall(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      address refundAddress
                  ) external payable override {
                      emit NativeGasPaidForContractCall(sender, destinationChain, destinationAddress, keccak256(payload), msg.value, refundAddress);
                  }
                  /**
                   * @notice Pay for gas using native currency for a contract call with tokens on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call with tokens will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call with tokens
                   * @param symbol The symbol of the token to be sent with the call
                   * @param amount The amount of tokens to be sent with the call
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payNativeGasForContractCallWithToken(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      string calldata symbol,
                      uint256 amount,
                      address refundAddress
                  ) external payable override {
                      emit NativeGasPaidForContractCallWithToken(
                          sender,
                          destinationChain,
                          destinationAddress,
                          keccak256(payload),
                          symbol,
                          amount,
                          msg.value,
                          refundAddress
                      );
                  }
                  /**
                   * @notice Pay for gas using ERC20 tokens for an express contract call on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to express execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call
                   * @param gasToken The address of the ERC20 token used to pay for gas
                   * @param gasFeeAmount The amount of tokens to pay for gas
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payGasForExpressCall(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  ) external override {
                      emit GasPaidForExpressCall(sender, destinationChain, destinationAddress, keccak256(payload), gasToken, gasFeeAmount, refundAddress);
                      IERC20(gasToken).safeTransferFrom(msg.sender, address(this), gasFeeAmount);
                  }
                  /**
                   * @notice Pay for gas using ERC20 tokens for an express contract call with tokens on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to express execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call with tokens will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call with tokens
                   * @param symbol The symbol of the token to be sent with the call
                   * @param amount The amount of tokens to be sent with the call
                   * @param gasToken The address of the ERC20 token used to pay for gas
                   * @param gasFeeAmount The amount of tokens to pay for gas
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payGasForExpressCallWithToken(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      string memory symbol,
                      uint256 amount,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  ) external override {
                      emit GasPaidForExpressCallWithToken(
                          sender,
                          destinationChain,
                          destinationAddress,
                          keccak256(payload),
                          symbol,
                          amount,
                          gasToken,
                          gasFeeAmount,
                          refundAddress
                      );
                      IERC20(gasToken).safeTransferFrom(msg.sender, address(this), gasFeeAmount);
                  }
                  /**
                   * @notice Pay for gas using native currency for an express contract call on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payNativeGasForExpressCall(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      address refundAddress
                  ) external payable override {
                      emit NativeGasPaidForExpressCall(sender, destinationChain, destinationAddress, keccak256(payload), msg.value, refundAddress);
                  }
                  /**
                   * @notice Pay for gas using native currency for an express contract call with tokens on a destination chain.
                   * @dev This function is called on the source chain before calling the gateway to execute a remote contract.
                   * @param sender The address making the payment
                   * @param destinationChain The target chain where the contract call with tokens will be made
                   * @param destinationAddress The target address on the destination chain
                   * @param payload Data payload for the contract call with tokens
                   * @param symbol The symbol of the token to be sent with the call
                   * @param amount The amount of tokens to be sent with the call
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function payNativeGasForExpressCallWithToken(
                      address sender,
                      string calldata destinationChain,
                      string calldata destinationAddress,
                      bytes calldata payload,
                      string calldata symbol,
                      uint256 amount,
                      address refundAddress
                  ) external payable override {
                      emit NativeGasPaidForExpressCallWithToken(
                          sender,
                          destinationChain,
                          destinationAddress,
                          keccak256(payload),
                          symbol,
                          amount,
                          msg.value,
                          refundAddress
                      );
                  }
                  /**
                   * @notice Add additional gas payment using ERC20 tokens after initiating a cross-chain call.
                   * @dev This function can be called on the source chain after calling the gateway to execute a remote contract.
                   * @param txHash The transaction hash of the cross-chain call
                   * @param logIndex The log index for the cross-chain call
                   * @param gasToken The ERC20 token address used to add gas
                   * @param gasFeeAmount The amount of tokens to add as gas
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function addGas(
                      bytes32 txHash,
                      uint256 logIndex,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  ) external override {
                      emit GasAdded(txHash, logIndex, gasToken, gasFeeAmount, refundAddress);
                      IERC20(gasToken).safeTransferFrom(msg.sender, address(this), gasFeeAmount);
                  }
                  /**
                   * @notice Add additional gas payment using native currency after initiating a cross-chain call.
                   * @dev This function can be called on the source chain after calling the gateway to execute a remote contract.
                   * @param txHash The transaction hash of the cross-chain call
                   * @param logIndex The log index for the cross-chain call
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function addNativeGas(
                      bytes32 txHash,
                      uint256 logIndex,
                      address refundAddress
                  ) external payable override {
                      emit NativeGasAdded(txHash, logIndex, msg.value, refundAddress);
                  }
                  /**
                   * @notice Add additional gas payment using ERC20 tokens after initiating an express cross-chain call.
                   * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract.
                   * @param txHash The transaction hash of the cross-chain call
                   * @param logIndex The log index for the cross-chain call
                   * @param gasToken The ERC20 token address used to add gas
                   * @param gasFeeAmount The amount of tokens to add as gas
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function addExpressGas(
                      bytes32 txHash,
                      uint256 logIndex,
                      address gasToken,
                      uint256 gasFeeAmount,
                      address refundAddress
                  ) external override {
                      emit ExpressGasAdded(txHash, logIndex, gasToken, gasFeeAmount, refundAddress);
                      IERC20(gasToken).safeTransferFrom(msg.sender, address(this), gasFeeAmount);
                  }
                  /**
                   * @notice Add additional gas payment using native currency after initiating an express cross-chain call.
                   * @dev This function can be called on the source chain after calling the gateway to express execute a remote contract.
                   * @param txHash The transaction hash of the cross-chain call
                   * @param logIndex The log index for the cross-chain call
                   * @param refundAddress The address where refunds, if any, should be sent
                   */
                  function addNativeExpressGas(
                      bytes32 txHash,
                      uint256 logIndex,
                      address refundAddress
                  ) external payable override {
                      emit NativeExpressGasAdded(txHash, logIndex, msg.value, refundAddress);
                  }
                  /**
                   * @notice Updates the gas price for a specific chain.
                   * @dev This function is called by the gas oracle to update the gas prices for a specific chains.
                   * @param chains Array of chain names
                   * @param gasUpdates Array of gas updates
                   */
                  function updateGasInfo(string[] calldata chains, GasInfo[] calldata gasUpdates) external onlyCollector {
                      uint256 chainsLength = chains.length;
                      if (chainsLength != gasUpdates.length) revert InvalidGasUpdates();
                      for (uint256 i; i < chainsLength; i++) {
                          string calldata chain = chains[i];
                          GasInfo calldata gasUpdate = gasUpdates[i];
                          _setGasInfo(chain, gasUpdate);
                      }
                  }
                  /**
                   * @notice Allows the gasCollector to collect accumulated fees from the contract.
                   * @dev Use address(0) as the token address for native currency.
                   * @param receiver The address to receive the collected fees
                   * @param tokens Array of token addresses to be collected
                   * @param amounts Array of amounts to be collected for each respective token address
                   */
                  function collectFees(
                      address payable receiver,
                      address[] calldata tokens,
                      uint256[] calldata amounts
                  ) external onlyCollector {
                      if (receiver == address(0)) revert InvalidAddress();
                      uint256 tokensLength = tokens.length;
                      if (tokensLength != amounts.length) revert InvalidAmounts();
                      for (uint256 i; i < tokensLength; i++) {
                          address token = tokens[i];
                          uint256 amount = amounts[i];
                          if (amount == 0) revert InvalidAmounts();
                          if (token == address(0)) {
                              if (amount <= address(this).balance) receiver.safeNativeTransfer(amount);
                          } else {
                              // slither-disable-next-line calls-loop
                              if (amount <= IERC20(token).balanceOf(address(this))) IERC20(token).safeTransfer(receiver, amount);
                          }
                      }
                  }
                  /**
                   * @dev Deprecated refund function, kept for backward compatibility.
                   */
                  function refund(
                      address payable receiver,
                      address token,
                      uint256 amount
                  ) external onlyCollector {
                      _refund(bytes32(0), 0, receiver, token, amount);
                  }
                  /**
                   * @notice Refunds gas payment to the receiver in relation to a specific cross-chain transaction.
                   * @dev Only callable by the gasCollector.
                   * @dev Use address(0) as the token address to refund native currency.
                   * @param txHash The transaction hash of the cross-chain call
                   * @param logIndex The log index for the cross-chain call
                   * @param receiver The address to receive the refund
                   * @param token The token address to be refunded
                   * @param amount The amount to refund
                   */
                  function refund(
                      bytes32 txHash,
                      uint256 logIndex,
                      address payable receiver,
                      address token,
                      uint256 amount
                  ) external onlyCollector {
                      _refund(txHash, logIndex, receiver, token, amount);
                  }
                  /**
                   * @dev Internal function to implement gas refund logic.
                   */
                  function _refund(
                      bytes32 txHash,
                      uint256 logIndex,
                      address payable receiver,
                      address token,
                      uint256 amount
                  ) private {
                      if (receiver == address(0)) revert InvalidAddress();
                      emit Refunded(txHash, logIndex, receiver, token, amount);
                      if (token == address(0)) {
                          receiver.safeNativeTransfer(amount);
                      } else {
                          IERC20(token).safeTransfer(receiver, amount);
                      }
                  }
                  /**
                   * @notice Returns a unique identifier for the contract.
                   * @return bytes32 Hash of the contract identifier
                   */
                  function contractId() external pure returns (bytes32) {
                      return keccak256('axelar-gas-service');
                  }
              }