ETH Price: $1,958.16 (-2.08%)

Transaction Decoder

Block:
11127920 at Oct-25-2020 09:11:42 PM +UTC
Transaction Fee:
0.080327042920243102 ETH $157.29
Gas Used:
5,381,914 Gas / 14.925367243 Gwei

Emitted Events:

23 ChiToken.Transfer( from=0x0000000000000000000000000000000000000000, to=[Receiver] 0xd33d46d87f500aeec8c1a1cbcbb43f952d453be4, value=144 )
24 ChiToken.Transfer( from=[Receiver] 0xd33d46d87f500aeec8c1a1cbcbb43f952d453be4, to=Mooniswap, value=144 )
25 ChiToken.Approval( owner=[Receiver] 0xd33d46d87f500aeec8c1a1cbcbb43f952d453be4, spender=Mooniswap, value=115792089237316195423570985008687907853269984665640564039457584007913129331343 )
26 Mooniswap.Swapped( account=[Receiver] 0xd33d46d87f500aeec8c1a1cbcbb43f952d453be4, src=ChiToken, dst=0x0000000000000000000000000000000000000000, amount=144, result=81006752259611960, srcBalance=260090, dstBalance=146393827552276798399, totalSupply=425954166268085996567, referral=[Sender] 0x73311e949c9f474727347872f5ed274f10d6eea6 )

Account State Difference:

  Address   Before After State Difference Code
0x00000000...b0eF1fA1c
0x041851E5...Cb719504a
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x077F1976...9A2c47286
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x07F1A0d2...B42f8D112
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x0cBc98B0...F77657928
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x0e127A62...F5b59EFdc
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x0F95C499...746C9B405
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x1352a2E0...73b737e63
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x138AE86E...5420C4E15
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x168a1e28...fC81cE994
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x17607b7E...620a35726
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x17Fb6590...eef609113
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x186Ec4bb...b6E800c84
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x18de7EAE...Ce2404E56
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x1bA6F1a4...e6471D7D3
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x1C28f4BF...be64Fe251
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x1F38A300...607F0e10B
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x1f99Ac68...C1f40AECa
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x254267Bc...e493db4EC
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x262E018D...eD72c4801
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x2a1aD425...c3066430C
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x2B5B35D8...0eaf74c51
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x2E308Fb1...45F304a07
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x2e97e8Dc...56C6c7b32
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x3034A885...bcDa03535
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x327f70bd...9ed502176
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x333a2777...afb80619a
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x33708997...a6e8F9466
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x341da966...aecb6a03F
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x39Ff81f8...94d989476
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x3a252cA6...41E99fbD3
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x3B00C454...697805FCb
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x4077B295...5ab046E5E
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x43AdFB3F...747DAC6FD
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x43c51c75...A989b0fB2
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x44e3b24C...0DbfF716D
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x4819fB60...F185f72E4
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x4bD3E475...63c64dB10
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x4f141605...258281428
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x52FAB03E...8Fc106343
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x547522Fa...07CaE9C41
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x54913fd9...232F73C38
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x57136A6C...F0C6b33F1
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x578e56dE...11E39DCb7
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x57aDBcDb...E790A2482
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x57b47e66...43Af29De6
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x5972Bc41...f2df8bDd4
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x59aa575A...460d81137
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x59af38aB...347c20568
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
(Spark Pool)
26.663225850098201823 Eth26.743552893018444925 Eth0.080327042920243102
0x5B1fC243...4eC29b786 146.393827552276798399 Eth146.312820800017186439 Eth0.08100675225961196
0x5bE93596...3b95e51c7
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x5d7a97D0...37d9113C7
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x5F07458c...68fE52697
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x5F39af2E...B20B08899
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x5fA9f521...138F5B118
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x60B3bF4D...ACb9eF5b2
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x617a38E5...E53318a69
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x63F662eE...401377A55
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x66B922dE...D7D5f9BC3
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x66d74a07...1470B85e4
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x67dcfe70...e527b0Fb4
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x6bEd73d3...Ed680857F
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x6c5F9f5F...d0504161A
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x6dC1398b...977D029Ab
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x70274083...2D56D9d80
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x73311e94...F10d6EEA6
0.462267436599256638 Eth
Nonce: 2168
0.462947145938625496 Eth
Nonce: 2169
0.000679709339368858
0x7b9989fe...24Cc2bb0f
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x7BcA4E23...E0F2EFa92
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x7Bcdfd7F...0D4DBabD6
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x7D4d3B10...f05dB201e
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x7Deb656D...9443ff93a
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x7E3BA0Ba...224a61604
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x800a3790...817e2eE02
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x8159d509...3c13Fc1Ae
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x845E45A5...F20e9115D
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x84D47d6f...4cF5afbC1
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x865D8Aa9...1956E1003
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x8B83aa7E...210f0a374
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x8dEfEB63...7d9Ef5c03
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x8e7e7d77...6f023Ec85
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x8Ecbd016...0fa627Fb7
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x91D02Dc9...5e2eA1950
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x9287a74a...63087A895
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x9b9a227c...1bD9B045d
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x9b9Fd06D...97b91B82d
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x9dc63Ad1...AA2EBa06D
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x9E79957F...BBA90BF2F
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0x9E96d6C6...Faf740cdc
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xa0a55E2c...DF46360d3
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xa109108c...93b3251e5
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xa13d3f04...f255Ff54d
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xA4719d66...f72A4FE5D
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xa542A1cF...a6d18393e
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xa85841B8...9A64564d9
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xb2b3b267...bC571C03b
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xb2B755A1...60fcACA4f
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xB4035aa9...229a9BCCD
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xb431b119...5c75d0239
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xB52acA46...E3a2CE830
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xb64068Ea...0D2C27299
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xbAe8A813...aB7d87c04
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xBbc809aE...03F9e1241
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xBc2F656c...07068582d
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xC044891b...8912bFdcC
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xC04a0053...7c57FB286
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xc0d397E9...7Ceb1d24E
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xC2d7959d...D9276Aa7d
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xC36002bA...c50848F92
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xC4Fa4847...29494D58e
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xC572e382...3cEBf11b6
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xC658E7D3...76dd3c47F
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xC8d6536A...4c350f147
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xcAb3785e...2a222Ba83
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xcaFC7217...673fE1F18
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xcCeA699F...88baF229f
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xD217527f...EF12740d4
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xD2880318...9de3Ce267
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xD2fCCbDc...738a51826
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xD35cf174...e7a33EC51
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xD47e4463...D51E94e9F
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xd59E949D...8bf7A425b
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xd80f3F5f...cE055e2Be
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xd969e904...21779BF2E
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xDcfa6b38...fee3c331b
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xDedF39f6...fab01aE6C
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xe0F2896c...2dbdbd625
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xE1504bbD...45874a1c9
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xe1860032...f38475609
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xe1e45FF9...8246BCB94
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xe5a6316d...FDa74975b
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xE737620F...f7493Eb83
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xEa9Fe881...fC7D90444
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xEcEa92BA...0C8310F1E
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xeD8a1dA9...638422361
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xEdA476b3...34a1263FB
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xEe42F3Fb...72706C5bd
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xeE976550...5c47A6a30
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xeeFbafF2...1cfECe0c7
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xF2150456...2776Fe1D5
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xf3ec7033...F46f75975
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xf7F3d902...2313D8751
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xF8D46056...7F45a42B9
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xf96B0648...f2A97c13b
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xf9e66b09...0da0Aa567
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xfC6C38f2...08F54bc03
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xFf1Fbc6E...6ffb398a0
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287
0xFfDBB8a6...B5aeFaFC2
0 Eth
Nonce: 0
0 Eth
Nonce: 1
From: 0 To: 159722012652671806229941900564286316604591491724287

Execution Trace

0xd33d46d87f500aeec8c1a1cbcbb43f952d453be4.0ebd4c66( )
  • UniswapV2Router02.getAmountsOut( amountIn=144, path=[0x0000000000004946c0e9F43F4Dee607b0eF1fA1c, 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2] ) => ( amounts=[144, 80718033559400253] )
    • UniswapV2Pair.STATICCALL( )
    • Mooniswap.getReturn( src=0x0000000000004946c0e9F43F4Dee607b0eF1fA1c, dst=0x0000000000000000000000000000000000000000, amount=144 ) => ( 81006752259611960 )
      • ChiToken.balanceOf( account=0x5B1fC2435B1f7C16c206e7968C0e8524eC29b786 ) => ( 260090 )
      • MooniFactory.STATICCALL( )
      • ChiToken.mint( value=144 )
        • 0xf3ec7033b19b3b75b3828686c70f3d3f46f75975.746d4946( )
        • 0x5f39af2e7803ec5d7e63e7ab9d76dc0b20b08899.746d4946( )
        • 0xf9e66b098c90e417dabae12f6827ec20da0aa567.746d4946( )
        • 0xf7f3d902c427e0f6fa4e0e363075dbf2313d8751.746d4946( )
        • 0xc4fa484761904f52ecc843f1a9b3ab429494d58e.746d4946( )
        • 0x7bca4e23911de924c5f483c998a6d40e0f2efa92.746d4946( )
        • 0xc658e7d3bf0db1ccac444739d2b16f176dd3c47f.746d4946( )
        • 0x2e97e8dc06a7dcbd24f17ee699a5f2256c6c7b32.746d4946( )
        • 0xeefbaff249c30601b39de81e70947351cfece0c7.746d4946( )
        • 0xbc2f656cfe6d78f329f330af758a22407068582d.746d4946( )
        • 0xe1e45ff99837135e715f15405e45e0a8246bcb94.746d4946( )
        • 0x254267bc43e757f54e52dd23cce66d0e493db4ec.746d4946( )
        • 0x57b47e66bd2bb6785e3ebb632a8e2d743af29de6.746d4946( )
        • 0xd59e949df5dba91338fc8eca13a04948bf7a425b.746d4946( )
        • 0xd969e904a5dddc3488281e6621958bd21779bf2e.746d4946( )
        • 0x67dcfe706792fde44f80cc494dc85d6e527b0fb4.746d4946( )
        • 0xd35cf174fa8a2a5cbc3eba8e797655fe7a33ec51.746d4946( )
        • 0xa13d3f04257dcd96ead15a4c779167ff255ff54d.746d4946( )
        • 0xd47e4463f805e5f343469f151676a4dd51e94e9f.746d4946( )
        • 0x578e56de5fcf3cfdfdd9f619367f22711e39dcb7.746d4946( )
        • 0xd217527f441ac728df0e04208685b17ef12740d4.746d4946( )
        • 0x84d47d6f847c52bac46cb7a6b6903514cf5afbc1.746d4946( )
        • 0xeda476b3793171c4526d85d6502ecee34a1263fb.746d4946( )
        • 0x138ae86e37d1e6caf4d62bf7bcb615b5420c4e15.746d4946( )
        • 0x18de7eae4e46d580d3983c4e9d055a3ce2404e56.746d4946( )
        • 0xe1504bbdb2e1c1d20e41e2be415219045874a1c9.746d4946( )
        • 0x8defeb63faaf8573cb6089236d8b4147d9ef5c03.746d4946( )
        • 0xc36002bac2f444798af756da85192e3c50848f92.746d4946( )
        • 0xee976550c4c5a94093cc378c628a22e5c47a6a30.746d4946( )
        • 0x5fa9f521db62ed67722999d4e6dc432138f5b118.746d4946( )
        • 0x9b9fd06de4b64f9fe64d9956072395397b91b82d.746d4946( )
        • 0x43adfb3f3bca480ef9ea0d67c7f2fca747dac6fd.746d4946( )
        • 0x66b922de73f0d0227009c834753866bd7d5f9bc3.746d4946( )
        • 0xdedf39f64550d9286465a28e8d200b1fab01ae6c.746d4946( )
        • 0xea9fe8816a9b8da3ae2bedf7c020f35fc7d90444.746d4946( )
        • 0x60b3bf4d36a6c2176644b2bdb9fb8c8acb9ef5b2.746d4946( )
        • 0x52fab03e3bb07f0571125b13070f2828fc106343.746d4946( )
        • 0xa0a55e2cac68f1ca27df93dd3501f69df46360d3.746d4946( )
        • 0x66d74a074040c68c6fdd528a7d569bf1470b85e4.746d4946( )
        • 0x59aa575ac4c5b8ae5a6926b88a5d2f5460d81137.746d4946( )
        • 0xe1860032e2ef0102cb6d3699a85dab7f38475609.746d4946( )
        • 0xbbc809ae1ffd596c2697879d343a4da03f9e1241.746d4946( )
        • 0x8159d509d24c23fda617079a000cb1a3c13fc1ae.746d4946( )
        • 0x7deb656d9e52deddd4b7f71b9c467679443ff93a.746d4946( )
        • 0xf96b0648f05952c7dcf89f46a4b2755f2a97c13b.746d4946( )
        • 0x3034a88591c2c0baca4cda7707f447dbcda03535.746d4946( )
        • 0x9287a74ab6b2982c4dbf471bcd81ce263087a895.746d4946( )
        • 0x865d8aa904696b0a358c3e93bf39df91956e1003.746d4946( )
        • 0x59af38ab984570fe33db3d3c1350b78347c20568.746d4946( )
        • 0xdcfa6b389e320bd2bfbfdeb569eed3afee3c331b.746d4946( )
        • 0xa542a1cf836da08810dc65dcdbcae63a6d18393e.746d4946( )
        • 0x041851e5d526903984e7ba6a4a7dcc3cb719504a.746d4946( )
        • 0x0cbc98b009fb149198134efc0ea4031f77657928.746d4946( )
        • 0x5be935961419599436fec6b29ab43103b95e51c7.746d4946( )
        • 0x1352a2e04378815f11ae1c8ad4abd3473b737e63.746d4946( )
        • 0xf8d46056d6d0e44d04338fb8a9c9b1d7f45a42b9.746d4946( )
        • 0x7bcdfd7f79a0de6e8aef4d0dd9b625d0d4dbabd6.746d4946( )
        • 0x845e45a539fdfcc5bccf9f9a90b3fc0f20e9115d.746d4946( )
        • 0x17607b7e4602847290e939ea758930f620a35726.746d4946( )
        • 0x2e308fb15b908e390b1a09a59b8cd4645f304a07.746d4946( )
        • 0x7e3ba0baf2160748e0944ce5097ab48224a61604.746d4946( )
        • 0x5972bc41ed25092bdde567066381fe3f2df8bdd4.746d4946( )
        • 0xecea92bac4afb37eb55431ca40da52c0c8310f1e.746d4946( )
        • 0x39ff81f8e295a425abc519d504f63fe94d989476.746d4946( )
        • 0x341da966e7cf0c0abacf6d7365f6eb4aecb6a03f.746d4946( )
        • 0x63f662eec8d1e03c687a9bc8ebce728401377a55.746d4946( )
        • 0xb64068ea20c4c069e0ed9eeb7df5ef70d2c27299.746d4946( )
        • 0xf2150456376855b3cd57afa1e69daff2776fe1d5.746d4946( )
        • 0xc044891bfde124e1d412ec5893702578912bfdcc.746d4946( )
        • 0x168a1e285ac7a280647b5827054a232fc81ce994.746d4946( )
        • 0xc572e38212d577b6596b691310d72993cebf11b6.746d4946( )
        • 0x617a38e5c133995ae05c14234b68441e53318a69.746d4946( )
        • 0x70274083b597a18e4101bd90ac64bb12d56d9d80.746d4946( )
        • 0xcafc7217efa3aa9e3bdabe6ae48418b673fe1f18.746d4946( )
        • 0xffdbb8a6eb10cf19eb439e57a7b36d0b5aefafc2.746d4946( )
        • 0x2a1ad425650301315bd98c01fa22097c3066430c.746d4946( )
        • 0xfc6c38f2d7c209ea5941c307228368408f54bc03.746d4946( )
        • 0x333a2777a5a4eea62b8f25c6c5b03f7afb80619a.746d4946( )
        • 0x57adbcdba37fe277d832eef02726969e790a2482.746d4946( )
        • 0x44e3b24c0944b7dcb2893eaa94be3ac0dbff716d.746d4946( )
        • 0x7d4d3b1005435a82bead2e98b17779ff05db201e.746d4946( )
        • 0xee42f3fbf4ad941fa67579066c807dd72706c5bd.746d4946( )
        • 0xcab3785ec853a43030a49fe666c0f562a222ba83.746d4946( )
        • 0x1c28f4bf90b0494c05d4524874af46bbe64fe251.746d4946( )
        • 0x4819fb60ea619da6037485e33fff32cf185f72e4.746d4946( )
        • 0x1f99ac6869d4cd8fb4129b764bffdf8c1f40aeca.746d4946( )
        • 0xc8d6536a462ae958d8cb9ae10a6d1b94c350f147.746d4946( )
        • 0x9b9a227c43cd214560bc5fcee51b2c61bd9b045d.746d4946( )
        • 0x4f14160512eaf0b8b7254f7e94b0ed4258281428.746d4946( )
        • 0x6bed73d329949fb1fccc65203c9485ced680857f.746d4946( )
        • 0x0f95c499c47cde8d341b6f8773be30a746c9b405.746d4946( )
        • 0xb2b3b267f10c82254c2249b734dc8dcbc571c03b.746d4946( )
        • 0x186ec4bbed7ce24bf0cfaf155b6922eb6e800c84.746d4946( )
        • 0xb4035aa988845060777cabbdb88cdec229a9bccd.746d4946( )
        • 0xe737620f31658eae718151e1471abc1f7493eb83.746d4946( )
        • 0xe5a6316d83b6e680658efe4e999d9d5fda74975b.746d4946( )
        • 0x7b9989fee38c8468faa7420bbd991ea24cc2bb0f.746d4946( )
        • 0x262e018d96a79b9943b1b280d949655ed72c4801.746d4946( )
        • 0xd2880318c143ec10f1c4cd73821bbef9de3ce267.746d4946( )
        • 0x5d7a97d02850030e804e57a85bd04db37d9113c7.746d4946( )
        • 0xb2b755a177e7e6eb635845293532dc660fcaca4f.746d4946( )
        • 0x800a379000e8796996181ad30f8315f817e2ee02.746d4946( )
        • 0xff1fbc6e0f173f2960b5d9f631eea826ffb398a0.746d4946( )
        • 0x91d02dc97c9c31fb4b1408cf513504f5e2ea1950.746d4946( )
        • 0x33708997b60b3afd1b56e0082aae4bfa6e8f9466.746d4946( )
        • 0xd2fccbdc0b93b456445f770020c05de738a51826.746d4946( )
        • 0x4077b29555d7f1f1d9d9e7e9ac5c6875ab046e5e.746d4946( )
        • 0x43c51c751526a13b2145d3d139f03eba989b0fb2.746d4946( )
        • 0x1ba6f1a42be46f0bf1fb8912cc036b2e6471d7d3.746d4946( )
        • 0x547522fa0cef3920c5314fbc129a54407cae9c41.746d4946( )
        • 0x8ecbd016ceddeef78d0e6ae15473a4c0fa627fb7.746d4946( )
        • 0xccea699fd3f41ddf047adeb7f06b02f88baf229f.746d4946( )
        • 0xb431b1197b61615336311c4d51abaa05c75d0239.746d4946( )
        • 0x3b00c4543c69b87ffb7592cc264a00b697805fcb.746d4946( )
        • 0x8b83aa7e60c08360ce6517380bf9027210f0a374.746d4946( )
        • 0x57136a6ceb6e6b22895fa24db1065bbf0c6b33f1.746d4946( )
        • 0xd80f3f5fdeb310c42415c22deadd5aece055e2be.746d4946( )
        • 0xa109108c33a39f5a7ebcf3766b2e88393b3251e5.746d4946( )
        • 0x9e79957f8fe5b8ee8d02f183c6f85b0bba90bf2f.746d4946( )
        • 0x9e96d6c67a0b679cdf2b1b3f4d32ca8faf740cdc.746d4946( )
        • 0x8e7e7d774eb53bdab0ad991e812e8f76f023ec85.746d4946( )
        • 0x2b5b35d8aeff571ade60fb18f9335e20eaf74c51.746d4946( )
        • 0x0e127a62b3eb510b12f1dd4f013639af5b59efdc.746d4946( )
        • 0x6c5f9f5f6f3bdccf901b6c86fc6c40ad0504161a.746d4946( )
        • 0xbae8a8139a80ccb60808da0f48c0113ab7d87c04.746d4946( )
        • 0x6dc1398b03f43d8b43d5d8b14a1fff7977d029ab.746d4946( )
        • 0x3a252ca63d256b5e57a016e754b5f2441e99fbd3.746d4946( )
        • 0x4bd3e475475f87333027b3fe4df1d7663c64db10.746d4946( )
        • 0x327f70bda066ce8d7d904a7851c3b3e9ed502176.746d4946( )
        • 0xc04a00537b8a7f6ed81832298457da37c57fb286.746d4946( )
        • 0x07f1a0d2dec0b0b0de3451e04995daab42f8d112.746d4946( )
        • 0xc0d397e95acde6ff8534814ecf25a4c7ceb1d24e.746d4946( )
        • 0x1f38a300f389cbce57e289aba859e12607f0e10b.746d4946( )
        • 0xed8a1da9aefee6e89696e0e799fc5e5638422361.746d4946( )
        • 0xc2d7959d3fdf5aa524b490889eec1e4d9276aa7d.746d4946( )
        • 0xe0f2896c1aca5053fbfbea47ac577df2dbdbd625.746d4946( )
        • 0xb52aca4605b448b65f56cfa196df8e6e3a2ce830.746d4946( )
        • 0x17fb6590c0d0f92754673ad1d125792eef609113.746d4946( )
        • 0x5f07458cd5f258dd850c2260627f83568fe52697.746d4946( )
        • 0x077f197633b5a051db4927fd7b9d1889a2c47286.746d4946( )
        • 0x54913fd943c998a6826a19915bd29d6232f73c38.746d4946( )
        • 0xa4719d66da7b1ab62cd6a3b46686e1cf72a4fe5d.746d4946( )
        • 0x9dc63ad1f5f11d342773f3fa3c77144aa2eba06d.746d4946( )
        • 0xa85841b8c54cbb51167a54726f73bbb9a64564d9.746d4946( )
        • Mooniswap.swap( src=0x0000000000004946c0e9F43F4Dee607b0eF1fA1c, dst=0x0000000000000000000000000000000000000000, amount=144, minReturn=0, referral=0x73311e949c9F474727347872F5eD274F10d6EEA6 ) => ( result=81006752259611960 )
          • ChiToken.balanceOf( account=0x5B1fC2435B1f7C16c206e7968C0e8524eC29b786 ) => ( 260090 )
          • ChiToken.transferFrom( sender=0xd33d46D87F500aEeC8c1a1CbCBB43F952D453be4, recipient=0x5B1fC2435B1f7C16c206e7968C0e8524eC29b786, amount=144 ) => ( True )
          • ChiToken.balanceOf( account=0x5B1fC2435B1f7C16c206e7968C0e8524eC29b786 ) => ( 260234 )
          • MooniFactory.STATICCALL( )
          • ETH 0.08100675225961196 0xd33d46d87f500aeec8c1a1cbcbb43f952d453be4.CALL( )
          • ETH 0.08100675225961196 0x73311e949c9f474727347872f5ed274f10d6eea6.CALL( )
            File 1 of 5: ChiToken
            /*
                                                                             ,╖╗#▒▓▓▓▓▓╣╬╣▓▓▓▓▒#╗╗╓,
                                                                       ,╗@▓╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬▓▓╗╖
                                                                   ╓#▓╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╝▀╠╠▄╣╝╜"""╙╙▀╝╝╣╬╬╬╬▓▌╖
                                                                ╓▓╣╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬▀`╓å▓▓▓╙  ,▄▓▓██▓▓▓▄▄▄▄▄╠╠╙╠▄▄
                                                             ╓@╣╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬▌  ê`'     *▀▀▀▀▀▀▀▓██████████████▄
                                                           ╔▓╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬                      ╙▀████████████▌
                                                         ╓▓╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬                         ╙████████████▌
                                                       ,▓╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬▓▀                          ╗▄█████████████▄
                                                      é╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬▓▌   #╙                       ╙▀█████████████▓
                                                     ╣╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬▀              ╙▓╬╣▓▄            ╙▀▀███████████µ
                                                    ▓╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬▀╣╝╙             ╒▓╬╬╬╬╬╬▓         ╙████████████████µ
                                                   ▓╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬▌              ╖╖╖▄▓╬╬╬╬╬╬╬▓         █████████████████µ
                                                  ╣╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬        ,#▓╣╬╬▓╬╬╬╬╬╬╬╬╬╬╬╬▌         ▓█████████████████
                                                 ]╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╓╖     ]╣╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╣╨          ██████████████████▌
                                                 ▓╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬▓▌╖,   ╙╠╠▓╬╬╬╬╬╬╬╬╬▓╝╙           ╫███████████████████
                                                ]╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╝▀╙              ▓████████████████████▌
                                                ║╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╝▀╙`                 ▄███████████████████████
                                                ╟╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬▓╝▀╙                     ,▄█████████████████████████
                                                ╟╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╝╜`                       ▄▓████████████████████████████
                                                ║╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╣▀`                     ,▄▄▓████████████████████████████████
                                                ▐╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬▓╙                       ,,,▄╠▓██████████████████████████████▌
                                                 ╣╬╬╬╬╬╬╬╬╬╬╬╬╬╬▓╙╔▒`               ╓▄▓████████████████████████████████████████⌐
                                                 ╚╬╬╬╬╬╬╬╬╬╬╬╬╬▓▓╣▓              ▄▓████████████████████████████████████████████
                                                  ▓╬╬╬╬╬╬╬╬╬╬╬╬╬╬╬             ▄██████████████████████████████████████████████⌐
                                                   ╣╬╬╬╬╬╬╬╬╬╬╬╬╬╛           ▄███████████████████████████████████████████████▌
                                                   └╣╬╬╬╬╬╬╬╬╬╬╬▓           ▄███████████████████████████████████████████████▌
                                                    └▓╬╬╬╬╬╬╬╬╬╬Γ          ]███████████████████████████████████████████████▀
                                                      ╣╬╬╬╬╬╬╬╬╬⌐          ╫████████████████████████████████▀▀▀▀▀▓████████╜
                                                       ╙╬╬╬╬╬╬╬╬⌐          ╟███████████████████████████▀╙    ,▄▓▓▓▓▓████▓
                                                         ╫╬╬╬╬╬╬b           ████████████████████████▀`    ,Φ▀▀█████████╙
                                                           ╫╬╬╬╬▌╟           ██████████████████▀╓▀─       ▄▓█████████▀
                                                             ╚╣╬▓╣▓           └▀████████████▀`         ╓▓█████████▓╙
                                                               ╙╝╬╬▓             .▄▄▓█▀▀▀`           ▄▓█████████▀
                                                                  ╙▀▓▄                            ƒ,▓███████▀▀
                                                                      "                        ╓▓█▓█████▀▀└
                                                                                           ╓▄▓████▀▀╙└
            
             ██████╗██╗  ██╗██╗     ██████╗  █████╗ ███████╗████████╗ ██████╗ ██╗  ██╗███████╗███╗   ██╗    ██████╗ ██╗   ██╗     ██╗██╗███╗   ██╗ ██████╗██╗  ██╗
            ██╔════╝██║  ██║██║    ██╔════╝ ██╔══██╗██╔════╝╚══██╔══╝██╔═══██╗██║ ██╔╝██╔════╝████╗  ██║    ██╔══██╗╚██╗ ██╔╝    ███║██║████╗  ██║██╔════╝██║  ██║
            ██║     ███████║██║    ██║  ███╗███████║███████╗   ██║   ██║   ██║█████╔╝ █████╗  ██╔██╗ ██║    ██████╔╝ ╚████╔╝     ╚██║██║██╔██╗ ██║██║     ███████║
            ██║     ██╔══██║██║    ██║   ██║██╔══██║╚════██║   ██║   ██║   ██║██╔═██╗ ██╔══╝  ██║╚██╗██║    ██╔══██╗  ╚██╔╝       ██║██║██║╚██╗██║██║     ██╔══██║
            ╚██████╗██║  ██║██║    ╚██████╔╝██║  ██║███████║   ██║   ╚██████╔╝██║  ██╗███████╗██║ ╚████║    ██████╔╝   ██║        ██║██║██║ ╚████║╚██████╗██║  ██║
             ╚═════╝╚═╝  ╚═╝╚═╝     ╚═════╝ ╚═╝  ╚═╝╚══════╝   ╚═╝    ╚═════╝ ╚═╝  ╚═╝╚══════╝╚═╝  ╚═══╝    ╚═════╝    ╚═╝        ╚═╝╚═╝╚═╝  ╚═══╝ ╚═════╝╚═╝  ╚═╝
            
                                                                         Copyright by 1inch Corporation
                                                                             https://1inch.exchange
            
            ---
            Deployer wallet address:
            0x7E1E3334130355799F833ffec2D731BCa3E68aF6
            
            Signed raw transaction for chainId 1:
            0xf90d7f808506fc23ac00830bd0fa8080b90d2c608060405234801561001057600080fd5b50610d0c806100206000396000f3fe608060405234801561001057600080fd5b506004361061010b5760003560e01c806370a08231116100a2578063a9059cbb11610071578063a9059cbb14610305578063b0ac19a014610331578063d89135cd1461036a578063d8ccd0f314610372578063dd62ed3e1461038f5761010b565b806370a08231146102b057806395d89b41146102d6578063a0712d68146102de578063a2309ff8146102fd5761010b565b806323b872dd116100de57806323b872dd14610213578063313ce567146102495780635f2e2b45146102675780636366b936146102935761010b565b806306fdde0314610110578063079d229f1461018d578063095ea7b3146101cb57806318160ddd1461020b575b600080fd5b6101186103bd565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561015257818101518382015260200161013a565b50505050905090810190601f16801561017f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101b9600480360360408110156101a357600080fd5b506001600160a01b0381351690602001356103ee565b60408051918252519081900360200190f35b6101f7600480360360408110156101e157600080fd5b506001600160a01b03813516906020013561041f565b604080519115158252519081900360200190f35b6101b9610435565b6101f76004803603606081101561022957600080fd5b506001600160a01b03813581169160208101359091169060400135610453565b6102516104c2565b6040805160ff9092168252519081900360200190f35b6101b96004803603604081101561027d57600080fd5b506001600160a01b0381351690602001356104c7565b6101b9600480360360208110156102a957600080fd5b50356104e2565b6101b9600480360360208110156102c657600080fd5b50356001600160a01b03166104ff565b61011861051a565b6102fb600480360360208110156102f457600080fd5b5035610539565b005b6101b961070d565b6101f76004803603604081101561031b57600080fd5b506001600160a01b038135169060200135610713565b61034e6004803603602081101561034757600080fd5b5035610720565b604080516001600160a01b039092168252519081900360200190f35b6101b961078b565b6101b96004803603602081101561038857600080fd5b5035610791565b6101b9600480360360408110156103a557600080fd5b506001600160a01b03813581169160200135166107aa565b60405180604001604052806015815260200174086d0d2408ec2e6e8ded6cadc40c4f24062d2dcc6d605b1b81525081565b60006104188361041361040985610404886104ff565b6107d5565b61040487336107aa565b6104c7565b9392505050565b600061042c3384846107eb565b50600192915050565b600061044e60035460025461084d90919063ffffffff16565b905090565b600061046084848461088f565b6104b884336104b385604051806060016040528060288152602001610c8b602891396001600160a01b038a166000908152600160209081526040808320338452909152902054919063ffffffff61096116565b6107eb565b5060019392505050565b600081565b60006104d383836109f8565b6104dc82610a59565b50919050565b60006104f96104f483610404336104ff565b610791565b92915050565b6001600160a01b031660009081526020819052604090205490565b6040518060400160405280600381526020016243484960e81b81525081565b6002547f746d4946c0e9f43f4dee607b0ef1fa1c3318585733ff6000526015600bf30000600052602082045b80156106d85781601e600080f55060018201601e600080f55060028201601e600080f55060038201601e600080f55060048201601e600080f55060058201601e600080f55060068201601e600080f55060078201601e600080f55060088201601e600080f55060098201601e600080f550600a8201601e600080f550600b8201601e600080f550600c8201601e600080f550600d8201601e600080f550600e8201601e600080f550600f8201601e600080f55060108201601e600080f55060118201601e600080f55060128201601e600080f55060138201601e600080f55060148201601e600080f55060158201601e600080f55060168201601e600080f55060178201601e600080f55060188201601e600080f55060198201601e600080f550601a8201601e600080f550601b8201601e600080f550601c8201601e600080f550601d8201601e600080f550601e8201601e600080f550601f8201601e600080f5506020919091019060001901610565565b50601f82165b80156106fc5781601e600080f55060019190910190600019016106de565b506107073383610ad1565b60025550565b60025481565b600061042c33848461088f565b604080516001600160f81b03196020808301919091523060601b602183015260358201939093527f3c1644c68e5d6cb380c36d1bf847fdbc0c7ac28030025a2fc5e63cce23c16348605580830191909152825180830390910181526075909101909152805191012090565b60035481565b600061079d3383610b50565b6107a682610a59565b5090565b6001600160a01b03918216600090815260016020908152604080832093909416825291909152205490565b60008183106107e45781610418565b5090919050565b6001600160a01b03808416600081815260016020908152604080832094871680845294825291829020859055815185815291517f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259281900390910190a3505050565b600061041883836040518060400160405280601e81526020017f536166654d6174683a207375627472616374696f6e206f766572666c6f770000815250610961565b6108d281604051806060016040528060268152602001610c65602691396001600160a01b038616600090815260208190526040902054919063ffffffff61096116565b6001600160a01b038085166000908152602081905260408082209390935590841681522054610907908263ffffffff610be816565b6001600160a01b038084166000818152602081815260409182902094909455805185815290519193928716927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef92918290030190a3505050565b600081848411156109f05760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156109b557818101518382015260200161099d565b50505050905090810190601f1680156109e25780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b505050900390565b610a028282610b50565b610a5582336104b384604051806060016040528060248152602001610cb3602491396001600160a01b0388166000908152600160209081526040808320338452909152902054919063ffffffff61096116565b5050565b60035460005b82811015610aca57610a72818301610720565b6040516001600160a01b039190911690600081818181865af19150503d8060008114610aba576040519150601f19603f3d011682016040523d82523d6000602084013e610abf565b606091505b505050600101610a5f565b5001600355565b6001600160a01b038216600090815260208190526040902054610afa908263ffffffff610be816565b6001600160a01b0383166000818152602081815260408083209490945583518581529351929391927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9281900390910190a35050565b610b9381604051806060016040528060228152602001610c43602291396001600160a01b038516600090815260208190526040902054919063ffffffff61096116565b6001600160a01b038316600081815260208181526040808320949094558351858152935191937fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef929081900390910190a35050565b600082820183811015610418576040805162461bcd60e51b815260206004820152601b60248201527f536166654d6174683a206164646974696f6e206f766572666c6f770000000000604482015290519081900360640190fdfe45524332303a206275726e20616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e7420657863656564732062616c616e636545524332303a207472616e7366657220616d6f756e74206578636565647320616c6c6f77616e636545524332303a206275726e20616d6f756e74206578636565647320616c6c6f77616e6365a2646970667358221220687f814fb4c0b3c6abd66ebdb1f1eabcf69becf92a382c3af453e0b21c3d15b564736f6c6343000608003325a00ed87a047b4e415bd7f8cf7a7ce5a1c204125df1cedc35c7bdcb71bd2a29a35ea02db2490337fa6c425f1b3d74b7b217de8b394adb3f571827629c06dc16364b66
            ---
            */
            // File: @openzeppelin/contracts/math/Math.sol
            
            pragma solidity ^0.6.0;
            
            /**
             * @dev Standard math utilities missing in the Solidity language.
             */
            library Math {
                /**
                 * @dev Returns the largest of two numbers.
                 */
                function max(uint256 a, uint256 b) internal pure returns (uint256) {
                    return a >= b ? a : b;
                }
            
                /**
                 * @dev Returns the smallest of two numbers.
                 */
                function min(uint256 a, uint256 b) internal pure returns (uint256) {
                    return a < b ? a : b;
                }
            
                /**
                 * @dev Returns the average of two numbers. The result is rounded towards
                 * zero.
                 */
                function average(uint256 a, uint256 b) internal pure returns (uint256) {
                    // (a + b) / 2 can overflow, so we distribute
                    return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
                }
            }
            
            // File: @openzeppelin/contracts/math/SafeMath.sol
            
            pragma solidity ^0.6.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, 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) {
                    return sub(a, b, "SafeMath: subtraction overflow");
                }
            
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                 * overflow (when the result is negative).
                 *
                 * 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);
                    uint256 c = a - b;
            
                    return c;
                }
            
                /**
                 * @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) {
                    // 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 0;
                    }
            
                    uint256 c = a * b;
                    require(c / a == b, "SafeMath: multiplication overflow");
            
                    return c;
                }
            
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts 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) {
                    return div(a, b, "SafeMath: division by zero");
                }
            
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts with custom message 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, string memory errorMessage) internal pure returns (uint256) {
                    // Solidity only automatically asserts when dividing by 0
                    require(b > 0, errorMessage);
                    uint256 c = a / b;
                    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
            
                    return c;
                }
            
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts 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) {
                    return mod(a, b, "SafeMath: modulo by zero");
                }
            
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts with custom message 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, string memory errorMessage) internal pure returns (uint256) {
                    require(b != 0, errorMessage);
                    return a % b;
                }
            }
            
            // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
            
            pragma solidity ^0.6.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: contracts/ChiToken.sol
            
            pragma solidity ^0.6.0;
            
            
            
            
            
            abstract contract ERC20WithoutTotalSupply is IERC20 {
                using SafeMath for uint256;
            
                mapping(address => uint256) private _balances;
                mapping(address => mapping(address => uint256)) private _allowances;
            
                function balanceOf(address account) public view override returns (uint256) {
                    return _balances[account];
                }
            
                function allowance(address owner, address spender) public view override returns (uint256) {
                    return _allowances[owner][spender];
                }
            
                function transfer(address recipient, uint256 amount) public override returns (bool) {
                    _transfer(msg.sender, recipient, amount);
                    return true;
                }
            
                function approve(address spender, uint256 amount) public override returns (bool) {
                    _approve(msg.sender, spender, amount);
                    return true;
                }
            
                function transferFrom(address sender, address recipient, uint256 amount) public override returns (bool) {
                    _transfer(sender, recipient, amount);
                    _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount, "ERC20: transfer amount exceeds allowance"));
                    return true;
                }
            
                function _transfer(address sender, address recipient, uint256 amount) internal {
                    _balances[sender] = _balances[sender].sub(amount, "ERC20: transfer amount exceeds balance");
                    _balances[recipient] = _balances[recipient].add(amount);
                    emit Transfer(sender, recipient, amount);
                }
            
                function _approve(address owner, address spender, uint256 amount) internal {
                    _allowances[owner][spender] = amount;
                    emit Approval(owner, spender, amount);
                }
            
                function _mint(address account, uint256 amount) internal {
                    _balances[account] = _balances[account].add(amount);
                    emit Transfer(address(0), account, amount);
                }
            
                function _burn(address account, uint256 amount) internal {
                    _balances[account] = _balances[account].sub(amount, "ERC20: burn amount exceeds balance");
                    emit Transfer(account, address(0), amount);
                }
            
                function _burnFrom(address account, uint256 amount) internal {
                    _burn(account, amount);
                    _approve(account, msg.sender, _allowances[account][msg.sender].sub(amount, "ERC20: burn amount exceeds allowance"));
                }
            }
            
            
            contract ChiToken is IERC20, ERC20WithoutTotalSupply {
                string constant public name = "Chi Gastoken by 1inch";
                string constant public symbol = "CHI";
                uint8 constant public decimals = 0;
            
                uint256 public totalMinted;
                uint256 public totalBurned;
            
                function totalSupply() public view override returns(uint256) {
                    return totalMinted.sub(totalBurned);
                }
            
                function mint(uint256 value) public {
                    uint256 offset = totalMinted;
                    assembly {
                        mstore(0, 0x746d4946c0e9F43F4Dee607b0eF1fA1c3318585733ff6000526015600bf30000)
            
                        for {let i := div(value, 32)} i {i := sub(i, 1)} {
                            pop(create2(0, 0, 30, add(offset, 0))) pop(create2(0, 0, 30, add(offset, 1)))
                            pop(create2(0, 0, 30, add(offset, 2))) pop(create2(0, 0, 30, add(offset, 3)))
                            pop(create2(0, 0, 30, add(offset, 4))) pop(create2(0, 0, 30, add(offset, 5)))
                            pop(create2(0, 0, 30, add(offset, 6))) pop(create2(0, 0, 30, add(offset, 7)))
                            pop(create2(0, 0, 30, add(offset, 8))) pop(create2(0, 0, 30, add(offset, 9)))
                            pop(create2(0, 0, 30, add(offset, 10))) pop(create2(0, 0, 30, add(offset, 11)))
                            pop(create2(0, 0, 30, add(offset, 12))) pop(create2(0, 0, 30, add(offset, 13)))
                            pop(create2(0, 0, 30, add(offset, 14))) pop(create2(0, 0, 30, add(offset, 15)))
                            pop(create2(0, 0, 30, add(offset, 16))) pop(create2(0, 0, 30, add(offset, 17)))
                            pop(create2(0, 0, 30, add(offset, 18))) pop(create2(0, 0, 30, add(offset, 19)))
                            pop(create2(0, 0, 30, add(offset, 20))) pop(create2(0, 0, 30, add(offset, 21)))
                            pop(create2(0, 0, 30, add(offset, 22))) pop(create2(0, 0, 30, add(offset, 23)))
                            pop(create2(0, 0, 30, add(offset, 24))) pop(create2(0, 0, 30, add(offset, 25)))
                            pop(create2(0, 0, 30, add(offset, 26))) pop(create2(0, 0, 30, add(offset, 27)))
                            pop(create2(0, 0, 30, add(offset, 28))) pop(create2(0, 0, 30, add(offset, 29)))
                            pop(create2(0, 0, 30, add(offset, 30))) pop(create2(0, 0, 30, add(offset, 31)))
                            offset := add(offset, 32)
                        }
            
                        for {let i := and(value, 0x1F)} i {i := sub(i, 1)} {
                            pop(create2(0, 0, 30, offset))
                            offset := add(offset, 1)
                        }
                    }
            
                    _mint(msg.sender, value);
                    totalMinted = offset;
                }
            
                function computeAddress2(uint256 salt) public view returns (address) {
                    bytes32 _data = keccak256(
                        abi.encodePacked(bytes1(0xff), address(this), salt, bytes32(0x3c1644c68e5d6cb380c36d1bf847fdbc0c7ac28030025a2fc5e63cce23c16348))
                    );
                    return address(uint256(_data));
                }
            
                function _destroyChildren(uint256 value) internal {
                    uint256 _totalBurned = totalBurned;
                    for (uint256 i = 0; i < value; i++) {
                        computeAddress2(_totalBurned + i).call("");
                    }
                    totalBurned = _totalBurned + value;
                }
            
                function free(uint256 value) public returns (uint256)  {
                    _burn(msg.sender, value);
                    _destroyChildren(value);
                    return value;
                }
            
                function freeUpTo(uint256 value) public returns (uint256) {
                    return free(Math.min(value, balanceOf(msg.sender)));
                }
            
                function freeFrom(address from, uint256 value) public returns (uint256) {
                    _burnFrom(from, value);
                    _destroyChildren(value);
                    return value;
                }
            
                function freeFromUpTo(address from, uint256 value) public returns (uint256) {
                    return freeFrom(from, Math.min(Math.min(value, balanceOf(from)), allowance(from, msg.sender)));
                }
            }

            File 2 of 5: Mooniswap
            /*
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNXK0OxdoollccccclodkOKNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWX0kdlc;'..      .,:loxkk0KXNWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNKkoc,..        .':ox0XNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXOd:'.          .;lxKNWMMMMMMMMMMMMMMMWWNNNNNNNNWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNOo:.           .,lkXWMMMMMMMMMWXKOxddol:;;,''.....'',,;:cldxO0XWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXxc'            .;d0NMMMMMMMWXOxl:,..                            ..,:ldOKNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXx:.            .;dKWMMMMMWN0dc,.                                          .,cdOXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNkc.             'l0NMMMMMN0d:'.                                                   .:d0NMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMW0o'             .;xXMMMMMNOo,.              .....''',,'''....                           .'lkXWMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMNOc.             .:kNMMMMW0o,         ..,:loxk0KKXXNNNWWWNNNNXKK0kxol:,..                      .cONMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMNk;.             .;ONMMMMXx;.      .,cdkKNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNKOdc;.                    ,oKWMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMNk;               ,kNMMMW0l.     ':dOXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXOd:'                  .cOWMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMWO:               .dXMMMW0c.   .;oONWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNOo;.                .:ONMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMWKc.               :0WMMWKl.  .;dKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNWMMMMMMMMMMWKx:.               .:OWMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMNx.               .dNMMMXd.  ,o0WMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMN0kxOXWMMMMMMMMWKd;.              .cKWMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMW0:                ,OWMMWO, .:kNMMMMMMMMMMMMMMMMMMMMMMWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMWXxc,:d0NMMMMMMMMNOc.              .xNMMMMMMMMMMMMMM
            MMMMMMMMMMMMMNx.                :KMMMXo..c0WMMMMMMMMMMMMMMMMMMMMXxl:;,;;:codkKNMMMMMMMMMMMMMMMMMMMMMMNk;..,o0WMMMMMMMWKo.              :KMMMMMMMMMMMMM
            MMMMMMMMMMMMXl.                cXMMW0:.c0WMMMMMMMMMMMMWWNNXXXNNWXd'..;;'..   .,lkNMMW00NMMMMMMMMMMMMMMMNk;  .;dKWMMMMMMWKo.             'kWMMMMMMMMMMM
            MMMMMMMMMMMK:                 cXMMWk;;OWMMMMMMMMMN0xoc;,'.....';cll'.oKNX0xol,   ,xXNc.,xXMMMMMMMMMMMMMMMNx'   .l0WMMMMMMWKl.            .dNMMMMMMMMMM
            MMMMMMMMMM0;                 cXMMWk:dXMMMMMMMMW0o,.  ..,:clllllcc:;,..;0WMMMMNO:.  ;Ol.  ;0MMMMMMMMMMMMMMMMXl.   .:OWMMMMMMWO;            .lNMMMMMMMMM
            MMMMMMMMM0,                 ;KMMWklkNWMMMMMMMKl.     .';coxOXWMMMMWNKOdxKWMMMMMWk,  .:l:  ,0MMMMMMMMMMMMMMMMWO,    .c0WMMMMMMXo.            lXMMMMMMMM
            MMMMMMMM0,                 '0MMNx,''lXMMMMMWk'   .,cdxkkkxdxxOKXWMMMMMMWNXNMMMMMM0,  .kK;  cNMMMMMMN0XMMMMMMMMKc     .oXMMMMMMWO,            lNMMMMMMM
            MMMMMMM0;                 .xWMKc. . .kWMMMNd.  .l0NMMMMMMMMMMWNXNWMMMMMMMWNNWMMMMMk. 'OWd. .OMMMMXx:oXMMMMMMMMMNo.     ,kWMMMMMMK:           .dWMMMMMM
            MMMMMMX:                  cXM0; .l0o..:ool,  .lKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMX; ;XMO. .dWWKo'.:KMMMMMMMMMMMWx.     .oXMMMMMMXc           .kWMMMMM
            MMMMMNl                  .OMX: .oNMWOc'...':dKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNc.dWMO.  lOl.  ,0MMMMMMMMMMMMMWx.      :KMMMMMMXl           ;KMMMMM
            MMMMWx.                  lNMx. ;XMMMMMNXKXNWMWNWMMMMMMMMMMMMMMMXO0WMMMMMMMMMMMMMMMK::XMWx.  ...'.'OMMMMMMMMMMMMMMMWd.      ;0MMMMMMXc           oWMMMM
            MMMMK,                  .kMNl  cNMMMMMMMWNX0kxONMMMMMMMMMMMMMMWd..,cok0NWMMMMMMMMM0xKMMX;   .cd;'kWMMMMMMMMMMMMMMMMNo       ,0MMMMMMK:          '0MMMM
            MMMWo                   .cl:. .,ooolllllc::ld0WMMMMMMMMMMMMMMMNl      .':oOXNMMMMMWMMWO:  ;d00:.xWMMMMMMMMMMMMMMMMMMX:       ;KMMMMMM0'          oWMMM
            MMM0'                    .':ok0KKK0OkxxkOKNWMMMMMMMMMMMMMMMMMMWo          ..;OWMMMMWO:.  ,OWK:.dWMMMMMMMMMMMMMMMMMMMMO'       :XMMMMMWd.         ,KMMM
            MMWo                   .l0NWMMMMMMMMMMMMMMMMMMMMMMMMWXKWMMMMW0o'             .kWMWO:. .cOOOO:.oNMMMMMMMMMNXWMMMMMMMMMWo       .oWMMMMMX:         .kMMM
            MMK,                   .OMMMMMMMMMMMMMMMMMMMMMMMMN0d:.cXMMMKc.                .ok:. ..;KMMX:.lNMMMMMMMMMWxlKMMMMMMMMMM0,       'OMMMMMWk.         oWMM
            MMx.                   'OXXNMMMMMMMMMMMMMMMMMWXkl,.   lWMWO'                       ,O0dd0Kc .kMMMMMMMMMWk' cXMMMMMMMMMWl        lNMMMMMX;         cNMM
            MNl                    .dOKNMMMMMMMMMMMMMMWKxc.       lWM0,                        'xXWN0:   ;0MMMWN0xl,.   'cdOXWMMMMMk.       'OMMMMMWd.        ;XMM
            MX;                    ;XMMMMMMMMNOkXMMWKx;.          ,KWo                           .:c,     ;KMMWN0ko;.   'cdOXWMMMMMK,        dWMMMMMO.        ,KMM
            M0'                    ,KMMMMXKN0:.oWW0c.              ;d;                                     lNMMMMMMWO'.dNMMMMMMMMMMNc        :XMMMMMK,        ;KMM
            MO.                    '0MMM0lkO, 'ONd.                                                        .oNMMMMMMWkdNMMMMMMMMMMMWo        ,KMMMMMX:        ;XMM
            Mk.                    .OMM0,,x;  ;Ol                                     ..',.                 ;KMMMMMMMNNMMMMMMMMMMMMWo        '0MMMMMNc        cNMM
            Mx.                    .dW0, ,:.  ,c.                                      .:0Xk:.              lNMMMMMMMMMMMMMMMMMMMMMWo        .OMMMMMNc        oWMM
            Mk.                     c0:  ..    .                                      .,xOd0NO;            '0MMMMMMMMMMMMMWWMMMMMMMWo        .OMMMMMN:       .kMMM
            Mk.                     .:.                     .,.                       .cx: .lKXl           lWMMMMMMMMMMMWNxlONMMMMMNc        '0MMMMMX;       ;XMMM
            M0'                                           .:c.                      .. .,:;,,l0Xl         .xMMMMMMMMMMMWNk,.:ONMMMMX;        ;XMMMMM0'      .dWMMM
            MK;                                          'dl.                        .....''',:k0d;.       oWMMMMMMMMMMMMWOo0MMMMMMO.        oWMMMMMx.      ,KMMMM
            MNc                                         :kl.                           ......   ,c;.       :XMMMMMMMMMMMMMWWWMMMMMWo        .kMMMMMNc      .dWMMMM
            MWd.                                       c0l.                                                .OMMMMMMMMMMMMMMMMMMMMMK;        :XMMMMMO'      :XMMMMM
            MM0'                                      :0d.                                         .        lNMMMMMMMMMMMMMMMMMMMWd.       .kMMMMMNl      '0MMMMMM
            MMNl                                     '0k.                    ..                    'c.      .OMMMMMMMMMMMMMMMMMMM0,        lNMMMMMO.     .kWMMMMMM
            MMMO.                                   .dK;                    ;0O;                    :d'      ;KMMMMMMMMMMMMMMMMMNl        ,KMMMMMX:     .xWMMMWXNM
            MMMNc                                   ,0d.                   .OMMNk:.                 .x0xo,    cXMMMMMMMMMMMMMMMWd.       'OMMMMMNo     .dWMMMWOkNM
            MMMMO'                                  c0;                   .dWMMMMW0l.                ;dooxl.   :0WMMMMMMMMMMMMWk.       .kWMMMMWx.    .xWMMMWxc0MM
            MMMMWd.                                 ok.                   :XMMMMMMMWO,                  .:dc    'OWMMMMMMMMMMWO'       .kWMMMMWk.    'kWMMMNo,dWMM
            MMMMMX:                                 cd.                  .kWMMMMMMMMWk.         ..      .,cc.    :XMMMMMMMMMWk'       'OWMMMMWk.    :0MMMMKc.cXMMM
            MMMMMM0,                                .'.                  :XMMMMMMMMMMNc      'c;ll.       ..     .OMMMMMMMMWx.       :KMMMMMNx.   .oNMMMWO, ;KMMMM
            MMMMMMWO'                                                   .xWMMMMMMMMMMMk:.    ,0KOOc              .OMMMMMMMNo.      .oXMMMMMXo.   ;OWMMMXl. 'OMMMMM
            MMMMMMMWk.                                                  ,0MMMMMMMMMMMMX0:     .:d00l.            lNMMMMMW0:       ;OWMMMMW0:   'xNMMMNk,  'OWMMMMM
            MMMMMMMMWx.                                                 :XMMMMMNNMMMMMMWk.       .',.          'xNMMMMMNx.      ,xNMMMMMNx.  .oXMMMWO:.  'OWMMMMMM
            MMMMMMMMMWk.                                                cNMMMMNdxNMMMMMMWO:.                .;xXMMMMMWO:      'dXMMMMMWO;  .oKWMMW0c.   'OWMMMMMMM
            MMMMMMMMMMWO,                                               cNWNKk:..:kKNWMMMMWKxc'.         .,oONMMMMMW0c.    .;xXMMMMMWKl..,dXWMMNOc.    ;0WMMMMMMMM
            MMMMMMMMMMMMK:                                              cNWN0o'  'oONWMMMMMMMWN0dc;''';lxKNMMMMMMW0l.    .cONMMMMMWKl'.ckNMMMNk;.     cKMMMMMMMMMM
            MMMMMMMMMMMMMXo.                                            cNMMMMKccKMMMMMMMMMMMMMMMMWNNNWMMMMMMMMNOc.   .:xXWMMMMMWOl;:dKWMMW0o'      .dNMMMMMMMMMMM
            MMMMMMMMMMMMMMWk'                                           lNMMMMMXXMMMMMMMMMMMMMMMMMMMMMMMMMMMWKx;. .,lkXWMMMMMWXOdoxKWMMWKd;.       ;OWMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMKl.                                         oWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXkc,';lxKNMMMMMMMWX0OOXWMMN0d;.        .oXMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMWk;                                        lXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXOxdxOXWMMMMMMMMMMMWNWMMWKxl,.         .:0WMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMXd'                                       .:ok0NWMMMMMMMMMMMMMMMMMMMMMMMWNWWMMMMMMMMMMMMMMMMWX0xl,.            ,kNMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMXo.                                         .':ldOKNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXKOdc;..              'xNMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMWKo'                                             .';ccldxO0KXXNWWWWWWWWWNNXKKOkxol:,..                  ,xXMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMWXd,                                                    ....',,,,,,,,,'....                        .;kNMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMNk:.                                                                                          .l0WMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKo,.                                                                                    .:xXWMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNOo,                                                                               .;dKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNOo;.                                                                        .:xKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKxc'.                                                                .;oOXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMN0xl;.                                                       .':oOXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXOdl;'.                                           ..,cokKNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNKOxoc;,...                          ..';:ldk0XWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWWXK0Okxdollcccc:::cccclloddkO0KXNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
                ███╗   ███╗ ██████╗  ██████╗ ███╗   ██╗██╗███████╗██╗    ██╗ █████╗ ██████╗ ███████╗██╗  ██╗ ██████╗██╗  ██╗ █████╗ ███╗   ██╗ ██████╗ ███████╗
                ████╗ ████║██╔═══██╗██╔═══██╗████╗  ██║██║██╔════╝██║    ██║██╔══██╗██╔══██╗██╔════╝╚██╗██╔╝██╔════╝██║  ██║██╔══██╗████╗  ██║██╔════╝ ██╔════╝
                ██╔████╔██║██║   ██║██║   ██║██╔██╗ ██║██║███████╗██║ █╗ ██║███████║██████╔╝█████╗   ╚███╔╝ ██║     ███████║███████║██╔██╗ ██║██║  ███╗█████╗
                ██║╚██╔╝██║██║   ██║██║   ██║██║╚██╗██║██║╚════██║██║███╗██║██╔══██║██╔═══╝ ██╔══╝   ██╔██╗ ██║     ██╔══██║██╔══██║██║╚██╗██║██║   ██║██╔══╝
                ██║ ╚═╝ ██║╚██████╔╝╚██████╔╝██║ ╚████║██║███████║╚███╔███╔╝██║  ██║██║██╗  ███████╗██╔╝ ██╗╚██████╗██║  ██║██║  ██║██║ ╚████║╚██████╔╝███████╗
                ╚═╝     ╚═╝ ╚═════╝  ╚═════╝ ╚═╝  ╚═══╝╚═╝╚══════╝ ╚══╝╚══╝ ╚═╝  ╚═╝╚═╝╚═╝  ╚══════╝╚═╝  ╚═╝ ╚═════╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═══╝ ╚═════╝ ╚══════╝
            
                                                                ██████╗ ██╗   ██╗     ██╗██╗███╗   ██╗ ██████╗██╗  ██╗
                                                                ██╔══██╗╚██╗ ██╔╝    ███║██║████╗  ██║██╔════╝██║  ██║
                                                                ██████╔╝ ╚████╔╝     ╚██║██║██╔██╗ ██║██║     ███████║
                                                                ██╔══██╗  ╚██╔╝       ██║██║██║╚██╗██║██║     ██╔══██║
                                                                ██████╔╝   ██║        ██║██║██║ ╚████║╚██████╗██║  ██║
                                                                ╚═════╝    ╚═╝        ╚═╝╚═╝╚═╝  ╚═══╝ ╚═════╝╚═╝  ╚═╝
            */
            // File: @openzeppelin/contracts/GSN/Context.sol
            
            // SPDX-License-Identifier: MIT
            
            pragma solidity ^0.6.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/access/Ownable.sol
            
            
            pragma solidity ^0.6.0;
            
            /**
             * @dev Contract module which provides a basic access control mechanism, where
             * there is an account (an owner) that can be granted exclusive access to
             * specific functions.
             *
             * By default, the owner account will be the one that deploys the contract. This
             * can later be changed with {transferOwnership}.
             *
             * This module is used through inheritance. It will make available the modifier
             * `onlyOwner`, which can be applied to your functions to restrict their use to
             * the owner.
             */
            contract Ownable is Context {
                address private _owner;
            
                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
            
                /**
                 * @dev Initializes the contract setting the deployer as the initial owner.
                 */
                constructor () internal {
                    address msgSender = _msgSender();
                    _owner = msgSender;
                    emit OwnershipTransferred(address(0), msgSender);
                }
            
                /**
                 * @dev Returns the address of the current owner.
                 */
                function owner() public view returns (address) {
                    return _owner;
                }
            
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                    require(_owner == _msgSender(), "Ownable: caller is not the owner");
                    _;
                }
            
                /**
                 * @dev Leaves the contract without owner. It will not be possible to call
                 * `onlyOwner` functions anymore. Can only be called by the current owner.
                 *
                 * NOTE: Renouncing ownership will leave the contract without an owner,
                 * thereby removing any functionality that is only available to the owner.
                 */
                function renounceOwnership() public virtual onlyOwner {
                    emit OwnershipTransferred(_owner, address(0));
                    _owner = address(0);
                }
            
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 * Can only be called by the current owner.
                 */
                function transferOwnership(address newOwner) public virtual onlyOwner {
                    require(newOwner != address(0), "Ownable: new owner is the zero address");
                    emit OwnershipTransferred(_owner, newOwner);
                    _owner = newOwner;
                }
            }
            
            // File: @openzeppelin/contracts/utils/ReentrancyGuard.sol
            
            
            pragma solidity ^0.6.0;
            
            /**
             * @dev Contract module that helps prevent reentrant calls to a function.
             *
             * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
             * available, which can be applied to functions to make sure there are no nested
             * (reentrant) calls to them.
             *
             * Note that because there is a single `nonReentrant` guard, functions marked as
             * `nonReentrant` may not call one another. This can be worked around by making
             * those functions `private`, and then adding `external` `nonReentrant` entry
             * points to them.
             *
             * TIP: If you would like to learn more about reentrancy and alternative ways
             * to protect against it, check out our blog post
             * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
             */
            contract ReentrancyGuard {
                // Booleans are more expensive than uint256 or any type that takes up a full
                // word because each write operation emits an extra SLOAD to first read the
                // slot's contents, replace the bits taken up by the boolean, and then write
                // back. This is the compiler's defense against contract upgrades and
                // pointer aliasing, and it cannot be disabled.
            
                // The values being non-zero value makes deployment a bit more expensive,
                // but in exchange the refund on every call to nonReentrant will be lower in
                // amount. Since refunds are capped to a percentage of the total
                // transaction's gas, it is best to keep them low in cases like this one, to
                // increase the likelihood of the full refund coming into effect.
                uint256 private constant _NOT_ENTERED = 1;
                uint256 private constant _ENTERED = 2;
            
                uint256 private _status;
            
                constructor () internal {
                    _status = _NOT_ENTERED;
                }
            
                /**
                 * @dev Prevents a contract from calling itself, directly or indirectly.
                 * Calling a `nonReentrant` function from another `nonReentrant`
                 * function is not supported. It is possible to prevent this from happening
                 * by making the `nonReentrant` function external, and make it call a
                 * `private` function that does the actual work.
                 */
                modifier nonReentrant() {
                    // On the first call to nonReentrant, _notEntered will be true
                    require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
            
                    // Any calls to nonReentrant after this point will fail
                    _status = _ENTERED;
            
                    _;
            
                    // By storing the original value once again, a refund is triggered (see
                    // https://eips.ethereum.org/EIPS/eip-2200)
                    _status = _NOT_ENTERED;
                }
            }
            
            // File: @openzeppelin/contracts/math/Math.sol
            
            
            pragma solidity ^0.6.0;
            
            /**
             * @dev Standard math utilities missing in the Solidity language.
             */
            library Math {
                /**
                 * @dev Returns the largest of two numbers.
                 */
                function max(uint256 a, uint256 b) internal pure returns (uint256) {
                    return a >= b ? a : b;
                }
            
                /**
                 * @dev Returns the smallest of two numbers.
                 */
                function min(uint256 a, uint256 b) internal pure returns (uint256) {
                    return a < b ? a : b;
                }
            
                /**
                 * @dev Returns the average of two numbers. The result is rounded towards
                 * zero.
                 */
                function average(uint256 a, uint256 b) internal pure returns (uint256) {
                    // (a + b) / 2 can overflow, so we distribute
                    return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
                }
            }
            
            // File: @openzeppelin/contracts/math/SafeMath.sol
            
            
            pragma solidity ^0.6.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, 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) {
                    return sub(a, b, "SafeMath: subtraction overflow");
                }
            
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                 * overflow (when the result is negative).
                 *
                 * 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);
                    uint256 c = a - b;
            
                    return c;
                }
            
                /**
                 * @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) {
                    // 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 0;
                    }
            
                    uint256 c = a * b;
                    require(c / a == b, "SafeMath: multiplication overflow");
            
                    return c;
                }
            
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts 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) {
                    return div(a, b, "SafeMath: division by zero");
                }
            
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts with custom message 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, string memory errorMessage) internal pure returns (uint256) {
                    require(b > 0, errorMessage);
                    uint256 c = a / b;
                    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
            
                    return c;
                }
            
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts 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) {
                    return mod(a, b, "SafeMath: modulo by zero");
                }
            
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts with custom message 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, string memory errorMessage) internal pure returns (uint256) {
                    require(b != 0, errorMessage);
                    return a % b;
                }
            }
            
            // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
            
            
            pragma solidity ^0.6.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/utils/Address.sol
            
            
            pragma solidity ^0.6.2;
            
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                    // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                    // for accounts without code, i.e. `keccak256('')`
                    bytes32 codehash;
                    bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                    // solhint-disable-next-line no-inline-assembly
                    assembly { codehash := extcodehash(account) }
                    return (codehash != accountHash && codehash != 0x0);
                }
            
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
            
                    // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                    (bool success, ) = recipient.call{ value: amount }("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
            
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain`call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                  return functionCall(target, data, "Address: low-level call failed");
                }
            
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                    return _functionCallWithValue(target, data, 0, errorMessage);
                }
            
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
            
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    return _functionCallWithValue(target, data, value, errorMessage);
                }
            
                function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
                    require(isContract(target), "Address: call to non-contract");
            
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
            
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            
            // File: @openzeppelin/contracts/token/ERC20/ERC20.sol
            
            
            pragma solidity ^0.6.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;
                using Address for address;
            
                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 returns (string memory) {
                    return _name;
                }
            
                /**
                 * @dev Returns the symbol of the token, usually a shorter version of the
                 * name.
                 */
                function symbol() public view 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 returns (uint8) {
                    return _decimals;
                }
            
                /**
                 * @dev See {IERC20-totalSupply}.
                 */
                function totalSupply() public view override returns (uint256) {
                    return _totalSupply;
                }
            
                /**
                 * @dev See {IERC20-balanceOf}.
                 */
                function balanceOf(address account) public view 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 is 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 {
                    _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/token/ERC20/SafeERC20.sol
            
            
            pragma solidity ^0.6.0;
            
            
            
            
            /**
             * @title SafeERC20
             * @dev Wrappers around ERC20 operations that throw on failure (when the token
             * contract returns false). Tokens that return no value (and instead revert or
             * throw on failure) are also supported, non-reverting calls are assumed to be
             * successful.
             * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
             * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
             */
            library SafeERC20 {
                using SafeMath for uint256;
                using Address for address;
            
                function safeTransfer(IERC20 token, address to, uint256 value) internal {
                    _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                }
            
                function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                    _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                }
            
                /**
                 * @dev Deprecated. This function has issues similar to the ones found in
                 * {IERC20-approve}, and its usage is discouraged.
                 *
                 * Whenever possible, use {safeIncreaseAllowance} and
                 * {safeDecreaseAllowance} instead.
                 */
                function safeApprove(IERC20 token, address spender, uint256 value) internal {
                    // safeApprove should only be called when setting an initial allowance,
                    // or when resetting it to zero. To increase and decrease it, use
                    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                    // solhint-disable-next-line max-line-length
                    require((value == 0) || (token.allowance(address(this), spender) == 0),
                        "SafeERC20: approve from non-zero to non-zero allowance"
                    );
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                }
            
                function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                    uint256 newAllowance = token.allowance(address(this), spender).add(value);
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                }
            
                function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                    uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                }
            
                /**
                 * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                 * on the return value: the return value is optional (but if data is returned, it must not be false).
                 * @param token The token targeted by the call.
                 * @param data The call data (encoded using abi.encode or one of its variants).
                 */
                function _callOptionalReturn(IERC20 token, bytes memory data) private {
                    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                    // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                    // the target address contains contract code and also asserts for success in the low-level call.
            
                    bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                    if (returndata.length > 0) { // Return data is optional
                        // solhint-disable-next-line max-line-length
                        require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                    }
                }
            }
            
            // File: contracts/libraries/UniERC20.sol
            
            
            pragma solidity ^0.6.0;
            
            
            
            
            
            library UniERC20 {
                using SafeMath for uint256;
                using SafeERC20 for IERC20;
            
                function isETH(IERC20 token) internal pure returns(bool) {
                    return (address(token) == address(0));
                }
            
                function uniBalanceOf(IERC20 token, address account) internal view returns (uint256) {
                    if (isETH(token)) {
                        return account.balance;
                    } else {
                        return token.balanceOf(account);
                    }
                }
            
                function uniTransfer(IERC20 token, address payable to, uint256 amount) internal {
                    if (amount > 0) {
                        if (isETH(token)) {
                            to.transfer(amount);
                        } else {
                            token.safeTransfer(to, amount);
                        }
                    }
                }
            
                function uniTransferFromSenderToThis(IERC20 token, uint256 amount) internal {
                    if (amount > 0) {
                        if (isETH(token)) {
                            require(msg.value >= amount, "UniERC20: not enough value");
                            if (msg.value > amount) {
                                // Return remainder if exist
                                msg.sender.transfer(msg.value.sub(amount));
                            }
                        } else {
                            token.safeTransferFrom(msg.sender, address(this), amount);
                        }
                    }
                }
            
                function uniSymbol(IERC20 token) internal view returns(string memory) {
                    if (isETH(token)) {
                        return "ETH";
                    }
            
                    (bool success, bytes memory data) = address(token).staticcall{ gas: 20000 }(
                        abi.encodeWithSignature("symbol()")
                    );
                    if (!success) {
                        (success, data) = address(token).staticcall{ gas: 20000 }(
                            abi.encodeWithSignature("SYMBOL()")
                        );
                    }
            
                    if (success && data.length >= 96) {
                        (uint256 offset, uint256 len) = abi.decode(data, (uint256, uint256));
                        if (offset == 0x20 && len > 0 && len <= 256) {
                            return string(abi.decode(data, (bytes)));
                        }
                    }
            
                    if (success && data.length == 32) {
                        uint len = 0;
                        while (len < data.length && data[len] >= 0x20 && data[len] <= 0x7E) {
                            len++;
                        }
            
                        if (len > 0) {
                            bytes memory result = new bytes(len);
                            for (uint i = 0; i < len; i++) {
                                result[i] = data[i];
                            }
                            return string(result);
                        }
                    }
            
                    return _toHex(address(token));
                }
            
                function _toHex(address account) private pure returns(string memory) {
                    return _toHex(abi.encodePacked(account));
                }
            
                function _toHex(bytes memory data) private pure returns(string memory) {
                    bytes memory str = new bytes(2 + data.length * 2);
                    str[0] = "0";
                    str[1] = "x";
                    uint j = 2;
                    for (uint i = 0; i < data.length; i++) {
                        uint a = uint8(data[i]) >> 4;
                        uint b = uint8(data[i]) & 0x0f;
                        str[j++] = byte(uint8(a + 48 + (a/10)*39));
                        str[j++] = byte(uint8(b + 48 + (b/10)*39));
                    }
            
                    return string(str);
                }
            }
            
            // File: contracts/libraries/Sqrt.sol
            
            
            pragma solidity ^0.6.0;
            
            
            library Sqrt {
                // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
                function sqrt(uint256 y) internal pure returns (uint256) {
                    if (y > 3) {
                        uint256 z = y;
                        uint256 x = y / 2 + 1;
                        while (x < z) {
                            z = x;
                            x = (y / x + x) / 2;
                        }
                        return z;
                    } else if (y != 0) {
                        return 1;
                    } else {
                        return 0;
                    }
                }
            }
            
            // File: contracts/Mooniswap.sol
            
            
            pragma solidity ^0.6.0;
            
            
            
            
            
            
            
            
            
            interface IFactory {
                function fee() external view returns(uint256);
            }
            
            
            library VirtualBalance {
                using SafeMath for uint256;
            
                struct Data {
                    uint216 balance;
                    uint40 time;
                }
            
                uint256 public constant DECAY_PERIOD = 5 minutes;
            
                function set(VirtualBalance.Data storage self, uint256 balance) internal {
                    self.balance = uint216(balance);
                    self.time = uint40(block.timestamp);
                }
            
                function update(VirtualBalance.Data storage self, uint256 realBalance) internal {
                    set(self, current(self, realBalance));
                }
            
                function scale(VirtualBalance.Data storage self, uint256 realBalance, uint256 num, uint256 denom) internal {
                    set(self, current(self, realBalance).mul(num).add(denom.sub(1)).div(denom));
                }
            
                function current(VirtualBalance.Data memory self, uint256 realBalance) internal view returns(uint256) {
                    uint256 timePassed = Math.min(DECAY_PERIOD, block.timestamp.sub(self.time));
                    uint256 timeRemain = DECAY_PERIOD.sub(timePassed);
                    return uint256(self.balance).mul(timeRemain).add(
                        realBalance.mul(timePassed)
                    ).div(DECAY_PERIOD);
                }
            }
            
            
            contract Mooniswap is ERC20, ReentrancyGuard, Ownable {
                using Sqrt for uint256;
                using SafeMath for uint256;
                using UniERC20 for IERC20;
                using VirtualBalance for VirtualBalance.Data;
            
                struct Balances {
                    uint256 src;
                    uint256 dst;
                }
            
                struct SwapVolumes {
                    uint128 confirmed;
                    uint128 result;
                }
            
                event Deposited(
                    address indexed account,
                    uint256 amount
                );
            
                event Withdrawn(
                    address indexed account,
                    uint256 amount
                );
            
                event Swapped(
                    address indexed account,
                    address indexed src,
                    address indexed dst,
                    uint256 amount,
                    uint256 result,
                    uint256 srcBalance,
                    uint256 dstBalance,
                    uint256 totalSupply,
                    address referral
                );
            
                uint256 public constant REFERRAL_SHARE = 20; // 1/share = 5% of LPs revenue
                uint256 public constant BASE_SUPPLY = 1000;  // Total supply on first deposit
                uint256 public constant FEE_DENOMINATOR = 1e18;
            
                IFactory public factory;
                IERC20[] public tokens;
                mapping(IERC20 => bool) public isToken;
                mapping(IERC20 => SwapVolumes) public volumes;
                mapping(IERC20 => VirtualBalance.Data) public virtualBalancesForAddition;
                mapping(IERC20 => VirtualBalance.Data) public virtualBalancesForRemoval;
            
                constructor(IERC20[] memory assets, string memory name, string memory symbol) public ERC20(name, symbol) {
                    require(bytes(name).length > 0, "Mooniswap: name is empty");
                    require(bytes(symbol).length > 0, "Mooniswap: symbol is empty");
                    require(assets.length == 2, "Mooniswap: only 2 tokens allowed");
            
                    factory = IFactory(msg.sender);
                    tokens = assets;
                    for (uint i = 0; i < assets.length; i++) {
                        require(!isToken[assets[i]], "Mooniswap: duplicate tokens");
                        isToken[assets[i]] = true;
                    }
                }
            
                function fee() public view returns(uint256) {
                    return factory.fee();
                }
            
                function getTokens() external view returns(IERC20[] memory) {
                    return tokens;
                }
            
                function decayPeriod() external pure returns(uint256) {
                    return VirtualBalance.DECAY_PERIOD;
                }
            
                function getBalanceForAddition(IERC20 token) public view returns(uint256) {
                    uint256 balance = token.uniBalanceOf(address(this));
                    return Math.max(virtualBalancesForAddition[token].current(balance), balance);
                }
            
                function getBalanceForRemoval(IERC20 token) public view returns(uint256) {
                    uint256 balance = token.uniBalanceOf(address(this));
                    return Math.min(virtualBalancesForRemoval[token].current(balance), balance);
                }
            
                function getReturn(IERC20 src, IERC20 dst, uint256 amount) external view returns(uint256) {
                    return _getReturn(src, dst, amount, getBalanceForAddition(src), getBalanceForRemoval(dst));
                }
            
                function deposit(uint256[] calldata amounts, uint256[] calldata minAmounts) external payable nonReentrant returns(uint256 fairSupply) {
                    IERC20[] memory _tokens = tokens;
                    require(amounts.length == _tokens.length, "Mooniswap: wrong amounts length");
                    require(msg.value == (_tokens[0].isETH() ? amounts[0] : (_tokens[1].isETH() ? amounts[1] : 0)), "Mooniswap: wrong value usage");
            
                    uint256[] memory realBalances = new uint256[](amounts.length);
                    for (uint i = 0; i < realBalances.length; i++) {
                        realBalances[i] = _tokens[i].uniBalanceOf(address(this)).sub(_tokens[i].isETH() ? msg.value : 0);
                    }
            
                    uint256 totalSupply = totalSupply();
                    if (totalSupply == 0) {
                        fairSupply = BASE_SUPPLY.mul(99);
                        _mint(address(this), BASE_SUPPLY); // Donate up to 1%
            
                        // Use the greatest token amount but not less than 99k for the initial supply
                        for (uint i = 0; i < amounts.length; i++) {
                            fairSupply = Math.max(fairSupply, amounts[i]);
                        }
                    }
                    else {
                        // Pre-compute fair supply
                        fairSupply = type(uint256).max;
                        for (uint i = 0; i < amounts.length; i++) {
                            fairSupply = Math.min(fairSupply, totalSupply.mul(amounts[i]).div(realBalances[i]));
                        }
                    }
            
                    uint256 fairSupplyCached = fairSupply;
                    for (uint i = 0; i < amounts.length; i++) {
                        require(amounts[i] > 0, "Mooniswap: amount is zero");
                        uint256 amount = (totalSupply == 0) ? amounts[i] :
                            realBalances[i].mul(fairSupplyCached).add(totalSupply - 1).div(totalSupply);
                        require(amount >= minAmounts[i], "Mooniswap: minAmount not reached");
            
                        _tokens[i].uniTransferFromSenderToThis(amount);
                        if (totalSupply > 0) {
                            uint256 confirmed = _tokens[i].uniBalanceOf(address(this)).sub(realBalances[i]);
                            fairSupply = Math.min(fairSupply, totalSupply.mul(confirmed).div(realBalances[i]));
                        }
                    }
            
                    if (totalSupply > 0) {
                        for (uint i = 0; i < amounts.length; i++) {
                            virtualBalancesForRemoval[_tokens[i]].scale(realBalances[i], totalSupply.add(fairSupply), totalSupply);
                            virtualBalancesForAddition[_tokens[i]].scale(realBalances[i], totalSupply.add(fairSupply), totalSupply);
                        }
                    }
            
                    require(fairSupply > 0, "Mooniswap: result is not enough");
                    _mint(msg.sender, fairSupply);
            
                    emit Deposited(msg.sender, fairSupply);
                }
            
                function withdraw(uint256 amount, uint256[] memory minReturns) external nonReentrant {
                    uint256 totalSupply = totalSupply();
                    _burn(msg.sender, amount);
            
                    for (uint i = 0; i < tokens.length; i++) {
                        IERC20 token = tokens[i];
            
                        uint256 preBalance = token.uniBalanceOf(address(this));
                        uint256 value = preBalance.mul(amount).div(totalSupply);
                        token.uniTransfer(msg.sender, value);
                        require(i >= minReturns.length || value >= minReturns[i], "Mooniswap: result is not enough");
            
                        virtualBalancesForAddition[token].scale(preBalance, totalSupply.sub(amount), totalSupply);
                        virtualBalancesForRemoval[token].scale(preBalance, totalSupply.sub(amount), totalSupply);
                    }
            
                    emit Withdrawn(msg.sender, amount);
                }
            
                function swap(IERC20 src, IERC20 dst, uint256 amount, uint256 minReturn, address referral) external payable nonReentrant returns(uint256 result) {
                    require(msg.value == (src.isETH() ? amount : 0), "Mooniswap: wrong value usage");
            
                    Balances memory balances = Balances({
                        src: src.uniBalanceOf(address(this)).sub(src.isETH() ? msg.value : 0),
                        dst: dst.uniBalanceOf(address(this))
                    });
            
                    // catch possible airdrops and external balance changes for deflationary tokens
                    uint256 srcAdditionBalance = Math.max(virtualBalancesForAddition[src].current(balances.src), balances.src);
                    uint256 dstRemovalBalance = Math.min(virtualBalancesForRemoval[dst].current(balances.dst), balances.dst);
            
                    src.uniTransferFromSenderToThis(amount);
                    uint256 confirmed = src.uniBalanceOf(address(this)).sub(balances.src);
                    result = _getReturn(src, dst, confirmed, srcAdditionBalance, dstRemovalBalance);
                    require(result > 0 && result >= minReturn, "Mooniswap: return is not enough");
                    dst.uniTransfer(msg.sender, result);
            
                    // Update virtual balances to the same direction only at imbalanced state
                    if (srcAdditionBalance != balances.src) {
                        virtualBalancesForAddition[src].set(srcAdditionBalance.add(confirmed));
                    }
                    if (dstRemovalBalance != balances.dst) {
                        virtualBalancesForRemoval[dst].set(dstRemovalBalance.sub(result));
                    }
            
                    // Update virtual balances to the opposite direction
                    virtualBalancesForRemoval[src].update(balances.src);
                    virtualBalancesForAddition[dst].update(balances.dst);
            
                    if (referral != address(0)) {
                        uint256 invariantRatio = uint256(1e36);
                        invariantRatio = invariantRatio.mul(balances.src.add(confirmed)).div(balances.src);
                        invariantRatio = invariantRatio.mul(balances.dst.sub(result)).div(balances.dst);
                        if (invariantRatio > 1e36) {
                            // calculate share only if invariant increased
                            uint256 referralShare = invariantRatio.sqrt().sub(1e18).mul(totalSupply()).div(1e18).div(REFERRAL_SHARE);
                            if (referralShare > 0) {
                                _mint(referral, referralShare);
                            }
                        }
                    }
            
                    emit Swapped(msg.sender, address(src), address(dst), confirmed, result, balances.src, balances.dst, totalSupply(), referral);
            
                    // Overflow of uint128 is desired
                    volumes[src].confirmed += uint128(confirmed);
                    volumes[src].result += uint128(result);
                }
            
                function rescueFunds(IERC20 token, uint256 amount) external nonReentrant onlyOwner {
                    uint256[] memory balances = new uint256[](tokens.length);
                    for (uint i = 0; i < balances.length; i++) {
                        balances[i] = tokens[i].uniBalanceOf(address(this));
                    }
            
                    token.uniTransfer(msg.sender, amount);
            
                    for (uint i = 0; i < balances.length; i++) {
                        require(tokens[i].uniBalanceOf(address(this)) >= balances[i], "Mooniswap: access denied");
                    }
                    require(balanceOf(address(this)) >= BASE_SUPPLY, "Mooniswap: access denied");
                }
            
                function _getReturn(IERC20 src, IERC20 dst, uint256 amount, uint256 srcBalance, uint256 dstBalance) internal view returns(uint256) {
                    if (isToken[src] && isToken[dst] && src != dst && amount > 0) {
                        uint256 taxedAmount = amount.sub(amount.mul(fee()).div(FEE_DENOMINATOR));
                        return taxedAmount.mul(dstBalance).div(srcBalance.add(taxedAmount));
                    }
                }
            }

            File 3 of 5: UniswapV2Router02
            pragma solidity =0.6.6;
            
            interface IUniswapV2Factory {
                event PairCreated(address indexed token0, address indexed token1, address pair, uint);
            
                function feeTo() external view returns (address);
                function feeToSetter() external view returns (address);
            
                function getPair(address tokenA, address tokenB) external view returns (address pair);
                function allPairs(uint) external view returns (address pair);
                function allPairsLength() external view returns (uint);
            
                function createPair(address tokenA, address tokenB) external returns (address pair);
            
                function setFeeTo(address) external;
                function setFeeToSetter(address) external;
            }
            
            interface IUniswapV2Pair {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external pure returns (string memory);
                function symbol() external pure returns (string memory);
                function decimals() external pure returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            
                function DOMAIN_SEPARATOR() external view returns (bytes32);
                function PERMIT_TYPEHASH() external pure returns (bytes32);
                function nonces(address owner) external view returns (uint);
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
            
                event Mint(address indexed sender, uint amount0, uint amount1);
                event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
                event Swap(
                    address indexed sender,
                    uint amount0In,
                    uint amount1In,
                    uint amount0Out,
                    uint amount1Out,
                    address indexed to
                );
                event Sync(uint112 reserve0, uint112 reserve1);
            
                function MINIMUM_LIQUIDITY() external pure returns (uint);
                function factory() external view returns (address);
                function token0() external view returns (address);
                function token1() external view returns (address);
                function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
                function price0CumulativeLast() external view returns (uint);
                function price1CumulativeLast() external view returns (uint);
                function kLast() external view returns (uint);
            
                function mint(address to) external returns (uint liquidity);
                function burn(address to) external returns (uint amount0, uint amount1);
                function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
                function skim(address to) external;
                function sync() external;
            
                function initialize(address, address) external;
            }
            
            interface IUniswapV2Router01 {
                function factory() external pure returns (address);
                function WETH() external pure returns (address);
            
                function addLiquidity(
                    address tokenA,
                    address tokenB,
                    uint amountADesired,
                    uint amountBDesired,
                    uint amountAMin,
                    uint amountBMin,
                    address to,
                    uint deadline
                ) external returns (uint amountA, uint amountB, uint liquidity);
                function addLiquidityETH(
                    address token,
                    uint amountTokenDesired,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline
                ) external payable returns (uint amountToken, uint amountETH, uint liquidity);
                function removeLiquidity(
                    address tokenA,
                    address tokenB,
                    uint liquidity,
                    uint amountAMin,
                    uint amountBMin,
                    address to,
                    uint deadline
                ) external returns (uint amountA, uint amountB);
                function removeLiquidityETH(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline
                ) external returns (uint amountToken, uint amountETH);
                function removeLiquidityWithPermit(
                    address tokenA,
                    address tokenB,
                    uint liquidity,
                    uint amountAMin,
                    uint amountBMin,
                    address to,
                    uint deadline,
                    bool approveMax, uint8 v, bytes32 r, bytes32 s
                ) external returns (uint amountA, uint amountB);
                function removeLiquidityETHWithPermit(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline,
                    bool approveMax, uint8 v, bytes32 r, bytes32 s
                ) external returns (uint amountToken, uint amountETH);
                function swapExactTokensForTokens(
                    uint amountIn,
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external returns (uint[] memory amounts);
                function swapTokensForExactTokens(
                    uint amountOut,
                    uint amountInMax,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external returns (uint[] memory amounts);
                function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
                    external
                    payable
                    returns (uint[] memory amounts);
                function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
                    external
                    returns (uint[] memory amounts);
                function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
                    external
                    returns (uint[] memory amounts);
                function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
                    external
                    payable
                    returns (uint[] memory amounts);
            
                function quote(uint amountA, uint reserveA, uint reserveB) external pure returns (uint amountB);
                function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) external pure returns (uint amountOut);
                function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) external pure returns (uint amountIn);
                function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts);
                function getAmountsIn(uint amountOut, address[] calldata path) external view returns (uint[] memory amounts);
            }
            
            interface IUniswapV2Router02 is IUniswapV2Router01 {
                function removeLiquidityETHSupportingFeeOnTransferTokens(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline
                ) external returns (uint amountETH);
                function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline,
                    bool approveMax, uint8 v, bytes32 r, bytes32 s
                ) external returns (uint amountETH);
            
                function swapExactTokensForTokensSupportingFeeOnTransferTokens(
                    uint amountIn,
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external;
                function swapExactETHForTokensSupportingFeeOnTransferTokens(
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external payable;
                function swapExactTokensForETHSupportingFeeOnTransferTokens(
                    uint amountIn,
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external;
            }
            
            interface IERC20 {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external view returns (string memory);
                function symbol() external view returns (string memory);
                function decimals() external view returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            }
            
            interface IWETH {
                function deposit() external payable;
                function transfer(address to, uint value) external returns (bool);
                function withdraw(uint) external;
            }
            
            contract UniswapV2Router02 is IUniswapV2Router02 {
                using SafeMath for uint;
            
                address public immutable override factory;
                address public immutable override WETH;
            
                modifier ensure(uint deadline) {
                    require(deadline >= block.timestamp, 'UniswapV2Router: EXPIRED');
                    _;
                }
            
                constructor(address _factory, address _WETH) public {
                    factory = _factory;
                    WETH = _WETH;
                }
            
                receive() external payable {
                    assert(msg.sender == WETH); // only accept ETH via fallback from the WETH contract
                }
            
                // **** ADD LIQUIDITY ****
                function _addLiquidity(
                    address tokenA,
                    address tokenB,
                    uint amountADesired,
                    uint amountBDesired,
                    uint amountAMin,
                    uint amountBMin
                ) internal virtual returns (uint amountA, uint amountB) {
                    // create the pair if it doesn't exist yet
                    if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
                        IUniswapV2Factory(factory).createPair(tokenA, tokenB);
                    }
                    (uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
                    if (reserveA == 0 && reserveB == 0) {
                        (amountA, amountB) = (amountADesired, amountBDesired);
                    } else {
                        uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);
                        if (amountBOptimal <= amountBDesired) {
                            require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
                            (amountA, amountB) = (amountADesired, amountBOptimal);
                        } else {
                            uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);
                            assert(amountAOptimal <= amountADesired);
                            require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
                            (amountA, amountB) = (amountAOptimal, amountBDesired);
                        }
                    }
                }
                function addLiquidity(
                    address tokenA,
                    address tokenB,
                    uint amountADesired,
                    uint amountBDesired,
                    uint amountAMin,
                    uint amountBMin,
                    address to,
                    uint deadline
                ) external virtual override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) {
                    (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
                    address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
                    TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
                    TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
                    liquidity = IUniswapV2Pair(pair).mint(to);
                }
                function addLiquidityETH(
                    address token,
                    uint amountTokenDesired,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline
                ) external virtual override payable ensure(deadline) returns (uint amountToken, uint amountETH, uint liquidity) {
                    (amountToken, amountETH) = _addLiquidity(
                        token,
                        WETH,
                        amountTokenDesired,
                        msg.value,
                        amountTokenMin,
                        amountETHMin
                    );
                    address pair = UniswapV2Library.pairFor(factory, token, WETH);
                    TransferHelper.safeTransferFrom(token, msg.sender, pair, amountToken);
                    IWETH(WETH).deposit{value: amountETH}();
                    assert(IWETH(WETH).transfer(pair, amountETH));
                    liquidity = IUniswapV2Pair(pair).mint(to);
                    // refund dust eth, if any
                    if (msg.value > amountETH) TransferHelper.safeTransferETH(msg.sender, msg.value - amountETH);
                }
            
                // **** REMOVE LIQUIDITY ****
                function removeLiquidity(
                    address tokenA,
                    address tokenB,
                    uint liquidity,
                    uint amountAMin,
                    uint amountBMin,
                    address to,
                    uint deadline
                ) public virtual override ensure(deadline) returns (uint amountA, uint amountB) {
                    address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
                    IUniswapV2Pair(pair).transferFrom(msg.sender, pair, liquidity); // send liquidity to pair
                    (uint amount0, uint amount1) = IUniswapV2Pair(pair).burn(to);
                    (address token0,) = UniswapV2Library.sortTokens(tokenA, tokenB);
                    (amountA, amountB) = tokenA == token0 ? (amount0, amount1) : (amount1, amount0);
                    require(amountA >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
                    require(amountB >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
                }
                function removeLiquidityETH(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline
                ) public virtual override ensure(deadline) returns (uint amountToken, uint amountETH) {
                    (amountToken, amountETH) = removeLiquidity(
                        token,
                        WETH,
                        liquidity,
                        amountTokenMin,
                        amountETHMin,
                        address(this),
                        deadline
                    );
                    TransferHelper.safeTransfer(token, to, amountToken);
                    IWETH(WETH).withdraw(amountETH);
                    TransferHelper.safeTransferETH(to, amountETH);
                }
                function removeLiquidityWithPermit(
                    address tokenA,
                    address tokenB,
                    uint liquidity,
                    uint amountAMin,
                    uint amountBMin,
                    address to,
                    uint deadline,
                    bool approveMax, uint8 v, bytes32 r, bytes32 s
                ) external virtual override returns (uint amountA, uint amountB) {
                    address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
                    uint value = approveMax ? uint(-1) : liquidity;
                    IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
                    (amountA, amountB) = removeLiquidity(tokenA, tokenB, liquidity, amountAMin, amountBMin, to, deadline);
                }
                function removeLiquidityETHWithPermit(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline,
                    bool approveMax, uint8 v, bytes32 r, bytes32 s
                ) external virtual override returns (uint amountToken, uint amountETH) {
                    address pair = UniswapV2Library.pairFor(factory, token, WETH);
                    uint value = approveMax ? uint(-1) : liquidity;
                    IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
                    (amountToken, amountETH) = removeLiquidityETH(token, liquidity, amountTokenMin, amountETHMin, to, deadline);
                }
            
                // **** REMOVE LIQUIDITY (supporting fee-on-transfer tokens) ****
                function removeLiquidityETHSupportingFeeOnTransferTokens(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline
                ) public virtual override ensure(deadline) returns (uint amountETH) {
                    (, amountETH) = removeLiquidity(
                        token,
                        WETH,
                        liquidity,
                        amountTokenMin,
                        amountETHMin,
                        address(this),
                        deadline
                    );
                    TransferHelper.safeTransfer(token, to, IERC20(token).balanceOf(address(this)));
                    IWETH(WETH).withdraw(amountETH);
                    TransferHelper.safeTransferETH(to, amountETH);
                }
                function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens(
                    address token,
                    uint liquidity,
                    uint amountTokenMin,
                    uint amountETHMin,
                    address to,
                    uint deadline,
                    bool approveMax, uint8 v, bytes32 r, bytes32 s
                ) external virtual override returns (uint amountETH) {
                    address pair = UniswapV2Library.pairFor(factory, token, WETH);
                    uint value = approveMax ? uint(-1) : liquidity;
                    IUniswapV2Pair(pair).permit(msg.sender, address(this), value, deadline, v, r, s);
                    amountETH = removeLiquidityETHSupportingFeeOnTransferTokens(
                        token, liquidity, amountTokenMin, amountETHMin, to, deadline
                    );
                }
            
                // **** SWAP ****
                // requires the initial amount to have already been sent to the first pair
                function _swap(uint[] memory amounts, address[] memory path, address _to) internal virtual {
                    for (uint i; i < path.length - 1; i++) {
                        (address input, address output) = (path[i], path[i + 1]);
                        (address token0,) = UniswapV2Library.sortTokens(input, output);
                        uint amountOut = amounts[i + 1];
                        (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOut) : (amountOut, uint(0));
                        address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
                        IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output)).swap(
                            amount0Out, amount1Out, to, new bytes(0)
                        );
                    }
                }
                function swapExactTokensForTokens(
                    uint amountIn,
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external virtual override ensure(deadline) returns (uint[] memory amounts) {
                    amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
                    require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
                    TransferHelper.safeTransferFrom(
                        path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
                    );
                    _swap(amounts, path, to);
                }
                function swapTokensForExactTokens(
                    uint amountOut,
                    uint amountInMax,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external virtual override ensure(deadline) returns (uint[] memory amounts) {
                    amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
                    require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
                    TransferHelper.safeTransferFrom(
                        path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
                    );
                    _swap(amounts, path, to);
                }
                function swapExactETHForTokens(uint amountOutMin, address[] calldata path, address to, uint deadline)
                    external
                    virtual
                    override
                    payable
                    ensure(deadline)
                    returns (uint[] memory amounts)
                {
                    require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
                    amounts = UniswapV2Library.getAmountsOut(factory, msg.value, path);
                    require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
                    IWETH(WETH).deposit{value: amounts[0]}();
                    assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
                    _swap(amounts, path, to);
                }
                function swapTokensForExactETH(uint amountOut, uint amountInMax, address[] calldata path, address to, uint deadline)
                    external
                    virtual
                    override
                    ensure(deadline)
                    returns (uint[] memory amounts)
                {
                    require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
                    amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
                    require(amounts[0] <= amountInMax, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
                    TransferHelper.safeTransferFrom(
                        path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
                    );
                    _swap(amounts, path, address(this));
                    IWETH(WETH).withdraw(amounts[amounts.length - 1]);
                    TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
                }
                function swapExactTokensForETH(uint amountIn, uint amountOutMin, address[] calldata path, address to, uint deadline)
                    external
                    virtual
                    override
                    ensure(deadline)
                    returns (uint[] memory amounts)
                {
                    require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
                    amounts = UniswapV2Library.getAmountsOut(factory, amountIn, path);
                    require(amounts[amounts.length - 1] >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
                    TransferHelper.safeTransferFrom(
                        path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]
                    );
                    _swap(amounts, path, address(this));
                    IWETH(WETH).withdraw(amounts[amounts.length - 1]);
                    TransferHelper.safeTransferETH(to, amounts[amounts.length - 1]);
                }
                function swapETHForExactTokens(uint amountOut, address[] calldata path, address to, uint deadline)
                    external
                    virtual
                    override
                    payable
                    ensure(deadline)
                    returns (uint[] memory amounts)
                {
                    require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
                    amounts = UniswapV2Library.getAmountsIn(factory, amountOut, path);
                    require(amounts[0] <= msg.value, 'UniswapV2Router: EXCESSIVE_INPUT_AMOUNT');
                    IWETH(WETH).deposit{value: amounts[0]}();
                    assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amounts[0]));
                    _swap(amounts, path, to);
                    // refund dust eth, if any
                    if (msg.value > amounts[0]) TransferHelper.safeTransferETH(msg.sender, msg.value - amounts[0]);
                }
            
                // **** SWAP (supporting fee-on-transfer tokens) ****
                // requires the initial amount to have already been sent to the first pair
                function _swapSupportingFeeOnTransferTokens(address[] memory path, address _to) internal virtual {
                    for (uint i; i < path.length - 1; i++) {
                        (address input, address output) = (path[i], path[i + 1]);
                        (address token0,) = UniswapV2Library.sortTokens(input, output);
                        IUniswapV2Pair pair = IUniswapV2Pair(UniswapV2Library.pairFor(factory, input, output));
                        uint amountInput;
                        uint amountOutput;
                        { // scope to avoid stack too deep errors
                        (uint reserve0, uint reserve1,) = pair.getReserves();
                        (uint reserveInput, uint reserveOutput) = input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
                        amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput);
                        amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);
                        }
                        (uint amount0Out, uint amount1Out) = input == token0 ? (uint(0), amountOutput) : (amountOutput, uint(0));
                        address to = i < path.length - 2 ? UniswapV2Library.pairFor(factory, output, path[i + 2]) : _to;
                        pair.swap(amount0Out, amount1Out, to, new bytes(0));
                    }
                }
                function swapExactTokensForTokensSupportingFeeOnTransferTokens(
                    uint amountIn,
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                ) external virtual override ensure(deadline) {
                    TransferHelper.safeTransferFrom(
                        path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn
                    );
                    uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
                    _swapSupportingFeeOnTransferTokens(path, to);
                    require(
                        IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
                        'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
                    );
                }
                function swapExactETHForTokensSupportingFeeOnTransferTokens(
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                )
                    external
                    virtual
                    override
                    payable
                    ensure(deadline)
                {
                    require(path[0] == WETH, 'UniswapV2Router: INVALID_PATH');
                    uint amountIn = msg.value;
                    IWETH(WETH).deposit{value: amountIn}();
                    assert(IWETH(WETH).transfer(UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn));
                    uint balanceBefore = IERC20(path[path.length - 1]).balanceOf(to);
                    _swapSupportingFeeOnTransferTokens(path, to);
                    require(
                        IERC20(path[path.length - 1]).balanceOf(to).sub(balanceBefore) >= amountOutMin,
                        'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT'
                    );
                }
                function swapExactTokensForETHSupportingFeeOnTransferTokens(
                    uint amountIn,
                    uint amountOutMin,
                    address[] calldata path,
                    address to,
                    uint deadline
                )
                    external
                    virtual
                    override
                    ensure(deadline)
                {
                    require(path[path.length - 1] == WETH, 'UniswapV2Router: INVALID_PATH');
                    TransferHelper.safeTransferFrom(
                        path[0], msg.sender, UniswapV2Library.pairFor(factory, path[0], path[1]), amountIn
                    );
                    _swapSupportingFeeOnTransferTokens(path, address(this));
                    uint amountOut = IERC20(WETH).balanceOf(address(this));
                    require(amountOut >= amountOutMin, 'UniswapV2Router: INSUFFICIENT_OUTPUT_AMOUNT');
                    IWETH(WETH).withdraw(amountOut);
                    TransferHelper.safeTransferETH(to, amountOut);
                }
            
                // **** LIBRARY FUNCTIONS ****
                function quote(uint amountA, uint reserveA, uint reserveB) public pure virtual override returns (uint amountB) {
                    return UniswapV2Library.quote(amountA, reserveA, reserveB);
                }
            
                function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut)
                    public
                    pure
                    virtual
                    override
                    returns (uint amountOut)
                {
                    return UniswapV2Library.getAmountOut(amountIn, reserveIn, reserveOut);
                }
            
                function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut)
                    public
                    pure
                    virtual
                    override
                    returns (uint amountIn)
                {
                    return UniswapV2Library.getAmountIn(amountOut, reserveIn, reserveOut);
                }
            
                function getAmountsOut(uint amountIn, address[] memory path)
                    public
                    view
                    virtual
                    override
                    returns (uint[] memory amounts)
                {
                    return UniswapV2Library.getAmountsOut(factory, amountIn, path);
                }
            
                function getAmountsIn(uint amountOut, address[] memory path)
                    public
                    view
                    virtual
                    override
                    returns (uint[] memory amounts)
                {
                    return UniswapV2Library.getAmountsIn(factory, amountOut, path);
                }
            }
            
            // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
            
            library SafeMath {
                function add(uint x, uint y) internal pure returns (uint z) {
                    require((z = x + y) >= x, 'ds-math-add-overflow');
                }
            
                function sub(uint x, uint y) internal pure returns (uint z) {
                    require((z = x - y) <= x, 'ds-math-sub-underflow');
                }
            
                function mul(uint x, uint y) internal pure returns (uint z) {
                    require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
                }
            }
            
            library UniswapV2Library {
                using SafeMath for uint;
            
                // returns sorted token addresses, used to handle return values from pairs sorted in this order
                function sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) {
                    require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
                    (token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA);
                    require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
                }
            
                // calculates the CREATE2 address for a pair without making any external calls
                function pairFor(address factory, address tokenA, address tokenB) internal pure returns (address pair) {
                    (address token0, address token1) = sortTokens(tokenA, tokenB);
                    pair = address(uint(keccak256(abi.encodePacked(
                            hex'ff',
                            factory,
                            keccak256(abi.encodePacked(token0, token1)),
                            hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash
                        ))));
                }
            
                // fetches and sorts the reserves for a pair
                function getReserves(address factory, address tokenA, address tokenB) internal view returns (uint reserveA, uint reserveB) {
                    (address token0,) = sortTokens(tokenA, tokenB);
                    (uint reserve0, uint reserve1,) = IUniswapV2Pair(pairFor(factory, tokenA, tokenB)).getReserves();
                    (reserveA, reserveB) = tokenA == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
                }
            
                // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
                function quote(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint amountB) {
                    require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
                    require(reserveA > 0 && reserveB > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
                    amountB = amountA.mul(reserveB) / reserveA;
                }
            
                // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
                function getAmountOut(uint amountIn, uint reserveIn, uint reserveOut) internal pure returns (uint amountOut) {
                    require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
                    require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
                    uint amountInWithFee = amountIn.mul(997);
                    uint numerator = amountInWithFee.mul(reserveOut);
                    uint denominator = reserveIn.mul(1000).add(amountInWithFee);
                    amountOut = numerator / denominator;
                }
            
                // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
                function getAmountIn(uint amountOut, uint reserveIn, uint reserveOut) internal pure returns (uint amountIn) {
                    require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
                    require(reserveIn > 0 && reserveOut > 0, 'UniswapV2Library: INSUFFICIENT_LIQUIDITY');
                    uint numerator = reserveIn.mul(amountOut).mul(1000);
                    uint denominator = reserveOut.sub(amountOut).mul(997);
                    amountIn = (numerator / denominator).add(1);
                }
            
                // performs chained getAmountOut calculations on any number of pairs
                function getAmountsOut(address factory, uint amountIn, address[] memory path) internal view returns (uint[] memory amounts) {
                    require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
                    amounts = new uint[](path.length);
                    amounts[0] = amountIn;
                    for (uint i; i < path.length - 1; i++) {
                        (uint reserveIn, uint reserveOut) = getReserves(factory, path[i], path[i + 1]);
                        amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
                    }
                }
            
                // performs chained getAmountIn calculations on any number of pairs
                function getAmountsIn(address factory, uint amountOut, address[] memory path) internal view returns (uint[] memory amounts) {
                    require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
                    amounts = new uint[](path.length);
                    amounts[amounts.length - 1] = amountOut;
                    for (uint i = path.length - 1; i > 0; i--) {
                        (uint reserveIn, uint reserveOut) = getReserves(factory, path[i - 1], path[i]);
                        amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
                    }
                }
            }
            
            // helper methods for interacting with ERC20 tokens and sending ETH that do not consistently return true/false
            library TransferHelper {
                function safeApprove(address token, address to, uint value) internal {
                    // bytes4(keccak256(bytes('approve(address,uint256)')));
                    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
                    require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED');
                }
            
                function safeTransfer(address token, address to, uint value) internal {
                    // bytes4(keccak256(bytes('transfer(address,uint256)')));
                    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
                    require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
                }
            
                function safeTransferFrom(address token, address from, address to, uint value) internal {
                    // bytes4(keccak256(bytes('transferFrom(address,address,uint256)')));
                    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
                    require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
                }
            
                function safeTransferETH(address to, uint value) internal {
                    (bool success,) = to.call{value:value}(new bytes(0));
                    require(success, 'TransferHelper: ETH_TRANSFER_FAILED');
                }
            }

            File 4 of 5: UniswapV2Pair
            // File: contracts/interfaces/IUniswapV2Pair.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2Pair {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external pure returns (string memory);
                function symbol() external pure returns (string memory);
                function decimals() external pure returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            
                function DOMAIN_SEPARATOR() external view returns (bytes32);
                function PERMIT_TYPEHASH() external pure returns (bytes32);
                function nonces(address owner) external view returns (uint);
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
            
                event Mint(address indexed sender, uint amount0, uint amount1);
                event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
                event Swap(
                    address indexed sender,
                    uint amount0In,
                    uint amount1In,
                    uint amount0Out,
                    uint amount1Out,
                    address indexed to
                );
                event Sync(uint112 reserve0, uint112 reserve1);
            
                function MINIMUM_LIQUIDITY() external pure returns (uint);
                function factory() external view returns (address);
                function token0() external view returns (address);
                function token1() external view returns (address);
                function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
                function price0CumulativeLast() external view returns (uint);
                function price1CumulativeLast() external view returns (uint);
                function kLast() external view returns (uint);
            
                function mint(address to) external returns (uint liquidity);
                function burn(address to) external returns (uint amount0, uint amount1);
                function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
                function skim(address to) external;
                function sync() external;
            
                function initialize(address, address) external;
            }
            
            // File: contracts/interfaces/IUniswapV2ERC20.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2ERC20 {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external pure returns (string memory);
                function symbol() external pure returns (string memory);
                function decimals() external pure returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            
                function DOMAIN_SEPARATOR() external view returns (bytes32);
                function PERMIT_TYPEHASH() external pure returns (bytes32);
                function nonces(address owner) external view returns (uint);
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
            }
            
            // File: contracts/libraries/SafeMath.sol
            
            pragma solidity =0.5.16;
            
            // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
            
            library SafeMath {
                function add(uint x, uint y) internal pure returns (uint z) {
                    require((z = x + y) >= x, 'ds-math-add-overflow');
                }
            
                function sub(uint x, uint y) internal pure returns (uint z) {
                    require((z = x - y) <= x, 'ds-math-sub-underflow');
                }
            
                function mul(uint x, uint y) internal pure returns (uint z) {
                    require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
                }
            }
            
            // File: contracts/UniswapV2ERC20.sol
            
            pragma solidity =0.5.16;
            
            
            
            contract UniswapV2ERC20 is IUniswapV2ERC20 {
                using SafeMath for uint;
            
                string public constant name = 'Uniswap V2';
                string public constant symbol = 'UNI-V2';
                uint8 public constant decimals = 18;
                uint  public totalSupply;
                mapping(address => uint) public balanceOf;
                mapping(address => mapping(address => uint)) public allowance;
            
                bytes32 public DOMAIN_SEPARATOR;
                // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
                bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
                mapping(address => uint) public nonces;
            
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                constructor() public {
                    uint chainId;
                    assembly {
                        chainId := chainid
                    }
                    DOMAIN_SEPARATOR = keccak256(
                        abi.encode(
                            keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                            keccak256(bytes(name)),
                            keccak256(bytes('1')),
                            chainId,
                            address(this)
                        )
                    );
                }
            
                function _mint(address to, uint value) internal {
                    totalSupply = totalSupply.add(value);
                    balanceOf[to] = balanceOf[to].add(value);
                    emit Transfer(address(0), to, value);
                }
            
                function _burn(address from, uint value) internal {
                    balanceOf[from] = balanceOf[from].sub(value);
                    totalSupply = totalSupply.sub(value);
                    emit Transfer(from, address(0), value);
                }
            
                function _approve(address owner, address spender, uint value) private {
                    allowance[owner][spender] = value;
                    emit Approval(owner, spender, value);
                }
            
                function _transfer(address from, address to, uint value) private {
                    balanceOf[from] = balanceOf[from].sub(value);
                    balanceOf[to] = balanceOf[to].add(value);
                    emit Transfer(from, to, value);
                }
            
                function approve(address spender, uint value) external returns (bool) {
                    _approve(msg.sender, spender, value);
                    return true;
                }
            
                function transfer(address to, uint value) external returns (bool) {
                    _transfer(msg.sender, to, value);
                    return true;
                }
            
                function transferFrom(address from, address to, uint value) external returns (bool) {
                    if (allowance[from][msg.sender] != uint(-1)) {
                        allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
                    }
                    _transfer(from, to, value);
                    return true;
                }
            
                function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
                    require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
                    bytes32 digest = keccak256(
                        abi.encodePacked(
                            '\x19\x01',
                            DOMAIN_SEPARATOR,
                            keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                        )
                    );
                    address recoveredAddress = ecrecover(digest, v, r, s);
                    require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
                    _approve(owner, spender, value);
                }
            }
            
            // File: contracts/libraries/Math.sol
            
            pragma solidity =0.5.16;
            
            // a library for performing various math operations
            
            library Math {
                function min(uint x, uint y) internal pure returns (uint z) {
                    z = x < y ? x : y;
                }
            
                // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
                function sqrt(uint y) internal pure returns (uint z) {
                    if (y > 3) {
                        z = y;
                        uint x = y / 2 + 1;
                        while (x < z) {
                            z = x;
                            x = (y / x + x) / 2;
                        }
                    } else if (y != 0) {
                        z = 1;
                    }
                }
            }
            
            // File: contracts/libraries/UQ112x112.sol
            
            pragma solidity =0.5.16;
            
            // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
            
            // range: [0, 2**112 - 1]
            // resolution: 1 / 2**112
            
            library UQ112x112 {
                uint224 constant Q112 = 2**112;
            
                // encode a uint112 as a UQ112x112
                function encode(uint112 y) internal pure returns (uint224 z) {
                    z = uint224(y) * Q112; // never overflows
                }
            
                // divide a UQ112x112 by a uint112, returning a UQ112x112
                function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
                    z = x / uint224(y);
                }
            }
            
            // File: contracts/interfaces/IERC20.sol
            
            pragma solidity >=0.5.0;
            
            interface IERC20 {
                event Approval(address indexed owner, address indexed spender, uint value);
                event Transfer(address indexed from, address indexed to, uint value);
            
                function name() external view returns (string memory);
                function symbol() external view returns (string memory);
                function decimals() external view returns (uint8);
                function totalSupply() external view returns (uint);
                function balanceOf(address owner) external view returns (uint);
                function allowance(address owner, address spender) external view returns (uint);
            
                function approve(address spender, uint value) external returns (bool);
                function transfer(address to, uint value) external returns (bool);
                function transferFrom(address from, address to, uint value) external returns (bool);
            }
            
            // File: contracts/interfaces/IUniswapV2Factory.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2Factory {
                event PairCreated(address indexed token0, address indexed token1, address pair, uint);
            
                function feeTo() external view returns (address);
                function feeToSetter() external view returns (address);
            
                function getPair(address tokenA, address tokenB) external view returns (address pair);
                function allPairs(uint) external view returns (address pair);
                function allPairsLength() external view returns (uint);
            
                function createPair(address tokenA, address tokenB) external returns (address pair);
            
                function setFeeTo(address) external;
                function setFeeToSetter(address) external;
            }
            
            // File: contracts/interfaces/IUniswapV2Callee.sol
            
            pragma solidity >=0.5.0;
            
            interface IUniswapV2Callee {
                function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
            }
            
            // File: contracts/UniswapV2Pair.sol
            
            pragma solidity =0.5.16;
            
            
            
            
            
            
            
            
            contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
                using SafeMath  for uint;
                using UQ112x112 for uint224;
            
                uint public constant MINIMUM_LIQUIDITY = 10**3;
                bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
            
                address public factory;
                address public token0;
                address public token1;
            
                uint112 private reserve0;           // uses single storage slot, accessible via getReserves
                uint112 private reserve1;           // uses single storage slot, accessible via getReserves
                uint32  private blockTimestampLast; // uses single storage slot, accessible via getReserves
            
                uint public price0CumulativeLast;
                uint public price1CumulativeLast;
                uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
            
                uint private unlocked = 1;
                modifier lock() {
                    require(unlocked == 1, 'UniswapV2: LOCKED');
                    unlocked = 0;
                    _;
                    unlocked = 1;
                }
            
                function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
                    _reserve0 = reserve0;
                    _reserve1 = reserve1;
                    _blockTimestampLast = blockTimestampLast;
                }
            
                function _safeTransfer(address token, address to, uint value) private {
                    (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
                    require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
                }
            
                event Mint(address indexed sender, uint amount0, uint amount1);
                event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
                event Swap(
                    address indexed sender,
                    uint amount0In,
                    uint amount1In,
                    uint amount0Out,
                    uint amount1Out,
                    address indexed to
                );
                event Sync(uint112 reserve0, uint112 reserve1);
            
                constructor() public {
                    factory = msg.sender;
                }
            
                // called once by the factory at time of deployment
                function initialize(address _token0, address _token1) external {
                    require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
                    token0 = _token0;
                    token1 = _token1;
                }
            
                // update reserves and, on the first call per block, price accumulators
                function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
                    require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
                    uint32 blockTimestamp = uint32(block.timestamp % 2**32);
                    uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
                    if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
                        // * never overflows, and + overflow is desired
                        price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
                        price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
                    }
                    reserve0 = uint112(balance0);
                    reserve1 = uint112(balance1);
                    blockTimestampLast = blockTimestamp;
                    emit Sync(reserve0, reserve1);
                }
            
                // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
                function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
                    address feeTo = IUniswapV2Factory(factory).feeTo();
                    feeOn = feeTo != address(0);
                    uint _kLast = kLast; // gas savings
                    if (feeOn) {
                        if (_kLast != 0) {
                            uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
                            uint rootKLast = Math.sqrt(_kLast);
                            if (rootK > rootKLast) {
                                uint numerator = totalSupply.mul(rootK.sub(rootKLast));
                                uint denominator = rootK.mul(5).add(rootKLast);
                                uint liquidity = numerator / denominator;
                                if (liquidity > 0) _mint(feeTo, liquidity);
                            }
                        }
                    } else if (_kLast != 0) {
                        kLast = 0;
                    }
                }
            
                // this low-level function should be called from a contract which performs important safety checks
                function mint(address to) external lock returns (uint liquidity) {
                    (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                    uint balance0 = IERC20(token0).balanceOf(address(this));
                    uint balance1 = IERC20(token1).balanceOf(address(this));
                    uint amount0 = balance0.sub(_reserve0);
                    uint amount1 = balance1.sub(_reserve1);
            
                    bool feeOn = _mintFee(_reserve0, _reserve1);
                    uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                    if (_totalSupply == 0) {
                        liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
                       _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
                    } else {
                        liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
                    }
                    require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
                    _mint(to, liquidity);
            
                    _update(balance0, balance1, _reserve0, _reserve1);
                    if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                    emit Mint(msg.sender, amount0, amount1);
                }
            
                // this low-level function should be called from a contract which performs important safety checks
                function burn(address to) external lock returns (uint amount0, uint amount1) {
                    (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                    address _token0 = token0;                                // gas savings
                    address _token1 = token1;                                // gas savings
                    uint balance0 = IERC20(_token0).balanceOf(address(this));
                    uint balance1 = IERC20(_token1).balanceOf(address(this));
                    uint liquidity = balanceOf[address(this)];
            
                    bool feeOn = _mintFee(_reserve0, _reserve1);
                    uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
                    amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
                    amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
                    require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
                    _burn(address(this), liquidity);
                    _safeTransfer(_token0, to, amount0);
                    _safeTransfer(_token1, to, amount1);
                    balance0 = IERC20(_token0).balanceOf(address(this));
                    balance1 = IERC20(_token1).balanceOf(address(this));
            
                    _update(balance0, balance1, _reserve0, _reserve1);
                    if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
                    emit Burn(msg.sender, amount0, amount1, to);
                }
            
                // this low-level function should be called from a contract which performs important safety checks
                function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
                    require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
                    (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
                    require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
            
                    uint balance0;
                    uint balance1;
                    { // scope for _token{0,1}, avoids stack too deep errors
                    address _token0 = token0;
                    address _token1 = token1;
                    require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
                    if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
                    if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
                    if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
                    balance0 = IERC20(_token0).balanceOf(address(this));
                    balance1 = IERC20(_token1).balanceOf(address(this));
                    }
                    uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
                    uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
                    require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
                    { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
                    uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
                    uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
                    require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
                    }
            
                    _update(balance0, balance1, _reserve0, _reserve1);
                    emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
                }
            
                // force balances to match reserves
                function skim(address to) external lock {
                    address _token0 = token0; // gas savings
                    address _token1 = token1; // gas savings
                    _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
                    _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
                }
            
                // force reserves to match balances
                function sync() external lock {
                    _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
                }
            }

            File 5 of 5: MooniFactory
            /*
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNXK0OxdoollccccclodkOKNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWX0kdlc;'..      .,:loxkk0KXNWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNKkoc,..        .':ox0XNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXOd:'.          .;lxKNWMMMMMMMMMMMMMMMWWNNNNNNNNWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNOo:.           .,lkXWMMMMMMMMMWXKOxddol:;;,''.....'',,;:cldxO0XWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXxc'            .;d0NMMMMMMMWXOxl:,..                            ..,:ldOKNMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXx:.            .;dKWMMMMMWN0dc,.                                          .,cdOXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNkc.             'l0NMMMMMN0d:'.                                                   .:d0NMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMW0o'             .;xXMMMMMNOo,.              .....''',,'''....                           .'lkXWMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMNOc.             .:kNMMMMW0o,         ..,:loxk0KKXXNNNWWWNNNNXKK0kxol:,..                      .cONMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMNk;.             .;ONMMMMXx;.      .,cdkKNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNKOdc;.                    ,oKWMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMNk;               ,kNMMMW0l.     ':dOXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXOd:'                  .cOWMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMWO:               .dXMMMW0c.   .;oONWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNOo;.                .:ONMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMWKc.               :0WMMWKl.  .;dKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNWMMMMMMMMMMWKx:.               .:OWMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMNx.               .dNMMMXd.  ,o0WMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMN0kxOXWMMMMMMMMWKd;.              .cKWMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMW0:                ,OWMMWO, .:kNMMMMMMMMMMMMMMMMMMMMMMWWWWMMMMMMMMMMMMMMMMMMMMMMMMMMWXxc,:d0NMMMMMMMMNOc.              .xNMMMMMMMMMMMMMM
            MMMMMMMMMMMMMNx.                :KMMMXo..c0WMMMMMMMMMMMMMMMMMMMMXxl:;,;;:codkKNMMMMMMMMMMMMMMMMMMMMMMNk;..,o0WMMMMMMMWKo.              :KMMMMMMMMMMMMM
            MMMMMMMMMMMMXl.                cXMMW0:.c0WMMMMMMMMMMMMWWNNXXXNNWXd'..;;'..   .,lkNMMW00NMMMMMMMMMMMMMMMNk;  .;dKWMMMMMMWKo.             'kWMMMMMMMMMMM
            MMMMMMMMMMMK:                 cXMMWk;;OWMMMMMMMMMN0xoc;,'.....';cll'.oKNX0xol,   ,xXNc.,xXMMMMMMMMMMMMMMMNx'   .l0WMMMMMMWKl.            .dNMMMMMMMMMM
            MMMMMMMMMM0;                 cXMMWk:dXMMMMMMMMW0o,.  ..,:clllllcc:;,..;0WMMMMNO:.  ;Ol.  ;0MMMMMMMMMMMMMMMMXl.   .:OWMMMMMMWO;            .lNMMMMMMMMM
            MMMMMMMMM0,                 ;KMMWklkNWMMMMMMMKl.     .';coxOXWMMMMWNKOdxKWMMMMMWk,  .:l:  ,0MMMMMMMMMMMMMMMMWO,    .c0WMMMMMMXo.            lXMMMMMMMM
            MMMMMMMM0,                 '0MMNx,''lXMMMMMWk'   .,cdxkkkxdxxOKXWMMMMMMWNXNMMMMMM0,  .kK;  cNMMMMMMN0XMMMMMMMMKc     .oXMMMMMMWO,            lNMMMMMMM
            MMMMMMM0;                 .xWMKc. . .kWMMMNd.  .l0NMMMMMMMMMMWNXNWMMMMMMMWNNWMMMMMk. 'OWd. .OMMMMXx:oXMMMMMMMMMNo.     ,kWMMMMMMK:           .dWMMMMMM
            MMMMMMX:                  cXM0; .l0o..:ool,  .lKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMX; ;XMO. .dWWKo'.:KMMMMMMMMMMMWx.     .oXMMMMMMXc           .kWMMMMM
            MMMMMNl                  .OMX: .oNMWOc'...':dKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNc.dWMO.  lOl.  ,0MMMMMMMMMMMMMWx.      :KMMMMMMXl           ;KMMMMM
            MMMMWx.                  lNMx. ;XMMMMMNXKXNWMWNWMMMMMMMMMMMMMMMXO0WMMMMMMMMMMMMMMMK::XMWx.  ...'.'OMMMMMMMMMMMMMMMWd.      ;0MMMMMMXc           oWMMMM
            MMMMK,                  .kMNl  cNMMMMMMMWNX0kxONMMMMMMMMMMMMMMWd..,cok0NWMMMMMMMMM0xKMMX;   .cd;'kWMMMMMMMMMMMMMMMMNo       ,0MMMMMMK:          '0MMMM
            MMMWo                   .cl:. .,ooolllllc::ld0WMMMMMMMMMMMMMMMNl      .':oOXNMMMMMWMMWO:  ;d00:.xWMMMMMMMMMMMMMMMMMMX:       ;KMMMMMM0'          oWMMM
            MMM0'                    .':ok0KKK0OkxxkOKNWMMMMMMMMMMMMMMMMMMWo          ..;OWMMMMWO:.  ,OWK:.dWMMMMMMMMMMMMMMMMMMMMO'       :XMMMMMWd.         ,KMMM
            MMWo                   .l0NWMMMMMMMMMMMMMMMMMMMMMMMMWXKWMMMMW0o'             .kWMWO:. .cOOOO:.oNMMMMMMMMMNXWMMMMMMMMMWo       .oWMMMMMX:         .kMMM
            MMK,                   .OMMMMMMMMMMMMMMMMMMMMMMMMN0d:.cXMMMKc.                .ok:. ..;KMMX:.lNMMMMMMMMMWxlKMMMMMMMMMM0,       'OMMMMMWk.         oWMM
            MMx.                   'OXXNMMMMMMMMMMMMMMMMMWXkl,.   lWMWO'                       ,O0dd0Kc .kMMMMMMMMMWk' cXMMMMMMMMMWl        lNMMMMMX;         cNMM
            MNl                    .dOKNMMMMMMMMMMMMMMWKxc.       lWM0,                        'xXWN0:   ;0MMMWN0xl,.   'cdOXWMMMMMk.       'OMMMMMWd.        ;XMM
            MX;                    ;XMMMMMMMMNOkXMMWKx;.          ,KWo                           .:c,     ;KMMWN0ko;.   'cdOXWMMMMMK,        dWMMMMMO.        ,KMM
            M0'                    ,KMMMMXKN0:.oWW0c.              ;d;                                     lNMMMMMMWO'.dNMMMMMMMMMMNc        :XMMMMMK,        ;KMM
            MO.                    '0MMM0lkO, 'ONd.                                                        .oNMMMMMMWkdNMMMMMMMMMMMWo        ,KMMMMMX:        ;XMM
            Mk.                    .OMM0,,x;  ;Ol                                     ..',.                 ;KMMMMMMMNNMMMMMMMMMMMMWo        '0MMMMMNc        cNMM
            Mx.                    .dW0, ,:.  ,c.                                      .:0Xk:.              lNMMMMMMMMMMMMMMMMMMMMMWo        .OMMMMMNc        oWMM
            Mk.                     c0:  ..    .                                      .,xOd0NO;            '0MMMMMMMMMMMMMWWMMMMMMMWo        .OMMMMMN:       .kMMM
            Mk.                     .:.                     .,.                       .cx: .lKXl           lWMMMMMMMMMMMWNxlONMMMMMNc        '0MMMMMX;       ;XMMM
            M0'                                           .:c.                      .. .,:;,,l0Xl         .xMMMMMMMMMMMWNk,.:ONMMMMX;        ;XMMMMM0'      .dWMMM
            MK;                                          'dl.                        .....''',:k0d;.       oWMMMMMMMMMMMMWOo0MMMMMMO.        oWMMMMMx.      ,KMMMM
            MNc                                         :kl.                           ......   ,c;.       :XMMMMMMMMMMMMMWWWMMMMMWo        .kMMMMMNc      .dWMMMM
            MWd.                                       c0l.                                                .OMMMMMMMMMMMMMMMMMMMMMK;        :XMMMMMO'      :XMMMMM
            MM0'                                      :0d.                                         .        lNMMMMMMMMMMMMMMMMMMMWd.       .kMMMMMNl      '0MMMMMM
            MMNl                                     '0k.                    ..                    'c.      .OMMMMMMMMMMMMMMMMMMM0,        lNMMMMMO.     .kWMMMMMM
            MMMO.                                   .dK;                    ;0O;                    :d'      ;KMMMMMMMMMMMMMMMMMNl        ,KMMMMMX:     .xWMMMWXNM
            MMMNc                                   ,0d.                   .OMMNk:.                 .x0xo,    cXMMMMMMMMMMMMMMMWd.       'OMMMMMNo     .dWMMMWOkNM
            MMMMO'                                  c0;                   .dWMMMMW0l.                ;dooxl.   :0WMMMMMMMMMMMMWk.       .kWMMMMWx.    .xWMMMWxc0MM
            MMMMWd.                                 ok.                   :XMMMMMMMWO,                  .:dc    'OWMMMMMMMMMMWO'       .kWMMMMWk.    'kWMMMNo,dWMM
            MMMMMX:                                 cd.                  .kWMMMMMMMMWk.         ..      .,cc.    :XMMMMMMMMMWk'       'OWMMMMWk.    :0MMMMKc.cXMMM
            MMMMMM0,                                .'.                  :XMMMMMMMMMMNc      'c;ll.       ..     .OMMMMMMMMWx.       :KMMMMMNx.   .oNMMMWO, ;KMMMM
            MMMMMMWO'                                                   .xWMMMMMMMMMMMk:.    ,0KOOc              .OMMMMMMMNo.      .oXMMMMMXo.   ;OWMMMXl. 'OMMMMM
            MMMMMMMWk.                                                  ,0MMMMMMMMMMMMX0:     .:d00l.            lNMMMMMW0:       ;OWMMMMW0:   'xNMMMNk,  'OWMMMMM
            MMMMMMMMWx.                                                 :XMMMMMNNMMMMMMWk.       .',.          'xNMMMMMNx.      ,xNMMMMMNx.  .oXMMMWO:.  'OWMMMMMM
            MMMMMMMMMWk.                                                cNMMMMNdxNMMMMMMWO:.                .;xXMMMMMWO:      'dXMMMMMWO;  .oKWMMW0c.   'OWMMMMMMM
            MMMMMMMMMMWO,                                               cNWNKk:..:kKNWMMMMWKxc'.         .,oONMMMMMW0c.    .;xXMMMMMWKl..,dXWMMNOc.    ;0WMMMMMMMM
            MMMMMMMMMMMMK:                                              cNWN0o'  'oONWMMMMMMMWN0dc;''';lxKNMMMMMMW0l.    .cONMMMMMWKl'.ckNMMMNk;.     cKMMMMMMMMMM
            MMMMMMMMMMMMMXo.                                            cNMMMMKccKMMMMMMMMMMMMMMMMWNNNWMMMMMMMMNOc.   .:xXWMMMMMWOl;:dKWMMW0o'      .dNMMMMMMMMMMM
            MMMMMMMMMMMMMMWk'                                           lNMMMMMXXMMMMMMMMMMMMMMMMMMMMMMMMMMMWKx;. .,lkXWMMMMMWXOdoxKWMMWKd;.       ;OWMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMKl.                                         oWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXkc,';lxKNMMMMMMMWX0OOXWMMN0d;.        .oXMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMWk;                                        lXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXOxdxOXWMMMMMMMMMMMWNWMMWKxl,.         .:0WMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMXd'                                       .:ok0NWMMMMMMMMMMMMMMMMMMMMMMMWNWWMMMMMMMMMMMMMMMMWX0xl,.            ,kNMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMXo.                                         .':ldOKNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXKOdc;..              'xNMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMWKo'                                             .';ccldxO0KXXNWWWWWWWWWNNXKKOkxol:,..                  ,xXMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMWXd,                                                    ....',,,,,,,,,'....                        .;kNMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMNk:.                                                                                          .l0WMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKo,.                                                                                    .:xXWMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNOo,                                                                               .;dKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMNOo;.                                                                        .:xKWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWKxc'.                                                                .;oOXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMN0xl;.                                                       .':oOXWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWXOdl;'.                                           ..,cokKNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWNKOxoc;,...                          ..';:ldk0XWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMWWXK0Okxdollcccc:::cccclloddkO0KXNWMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
            MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
                ███╗   ███╗ ██████╗  ██████╗ ███╗   ██╗██╗███████╗██╗    ██╗ █████╗ ██████╗ ███████╗██╗  ██╗ ██████╗██╗  ██╗ █████╗ ███╗   ██╗ ██████╗ ███████╗
                ████╗ ████║██╔═══██╗██╔═══██╗████╗  ██║██║██╔════╝██║    ██║██╔══██╗██╔══██╗██╔════╝╚██╗██╔╝██╔════╝██║  ██║██╔══██╗████╗  ██║██╔════╝ ██╔════╝
                ██╔████╔██║██║   ██║██║   ██║██╔██╗ ██║██║███████╗██║ █╗ ██║███████║██████╔╝█████╗   ╚███╔╝ ██║     ███████║███████║██╔██╗ ██║██║  ███╗█████╗
                ██║╚██╔╝██║██║   ██║██║   ██║██║╚██╗██║██║╚════██║██║███╗██║██╔══██║██╔═══╝ ██╔══╝   ██╔██╗ ██║     ██╔══██║██╔══██║██║╚██╗██║██║   ██║██╔══╝
                ██║ ╚═╝ ██║╚██████╔╝╚██████╔╝██║ ╚████║██║███████║╚███╔███╔╝██║  ██║██║██╗  ███████╗██╔╝ ██╗╚██████╗██║  ██║██║  ██║██║ ╚████║╚██████╔╝███████╗
                ╚═╝     ╚═╝ ╚═════╝  ╚═════╝ ╚═╝  ╚═══╝╚═╝╚══════╝ ╚══╝╚══╝ ╚═╝  ╚═╝╚═╝╚═╝  ╚══════╝╚═╝  ╚═╝ ╚═════╝╚═╝  ╚═╝╚═╝  ╚═╝╚═╝  ╚═══╝ ╚═════╝ ╚══════╝
            
                                                                ██████╗ ██╗   ██╗     ██╗██╗███╗   ██╗ ██████╗██╗  ██╗
                                                                ██╔══██╗╚██╗ ██╔╝    ███║██║████╗  ██║██╔════╝██║  ██║
                                                                ██████╔╝ ╚████╔╝     ╚██║██║██╔██╗ ██║██║     ███████║
                                                                ██╔══██╗  ╚██╔╝       ██║██║██║╚██╗██║██║     ██╔══██║
                                                                ██████╔╝   ██║        ██║██║██║ ╚████║╚██████╗██║  ██║
                                                                ╚═════╝    ╚═╝        ╚═╝╚═╝╚═╝  ╚═══╝ ╚═════╝╚═╝  ╚═╝
            */
            // File: @openzeppelin/contracts/GSN/Context.sol
            
            // SPDX-License-Identifier: MIT
            
            pragma solidity ^0.6.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/access/Ownable.sol
            
            
            pragma solidity ^0.6.0;
            
            /**
             * @dev Contract module which provides a basic access control mechanism, where
             * there is an account (an owner) that can be granted exclusive access to
             * specific functions.
             *
             * By default, the owner account will be the one that deploys the contract. This
             * can later be changed with {transferOwnership}.
             *
             * This module is used through inheritance. It will make available the modifier
             * `onlyOwner`, which can be applied to your functions to restrict their use to
             * the owner.
             */
            contract Ownable is Context {
                address private _owner;
            
                event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
            
                /**
                 * @dev Initializes the contract setting the deployer as the initial owner.
                 */
                constructor () internal {
                    address msgSender = _msgSender();
                    _owner = msgSender;
                    emit OwnershipTransferred(address(0), msgSender);
                }
            
                /**
                 * @dev Returns the address of the current owner.
                 */
                function owner() public view returns (address) {
                    return _owner;
                }
            
                /**
                 * @dev Throws if called by any account other than the owner.
                 */
                modifier onlyOwner() {
                    require(_owner == _msgSender(), "Ownable: caller is not the owner");
                    _;
                }
            
                /**
                 * @dev Leaves the contract without owner. It will not be possible to call
                 * `onlyOwner` functions anymore. Can only be called by the current owner.
                 *
                 * NOTE: Renouncing ownership will leave the contract without an owner,
                 * thereby removing any functionality that is only available to the owner.
                 */
                function renounceOwnership() public virtual onlyOwner {
                    emit OwnershipTransferred(_owner, address(0));
                    _owner = address(0);
                }
            
                /**
                 * @dev Transfers ownership of the contract to a new account (`newOwner`).
                 * Can only be called by the current owner.
                 */
                function transferOwnership(address newOwner) public virtual onlyOwner {
                    require(newOwner != address(0), "Ownable: new owner is the zero address");
                    emit OwnershipTransferred(_owner, newOwner);
                    _owner = newOwner;
                }
            }
            
            // File: @openzeppelin/contracts/math/SafeMath.sol
            
            
            pragma solidity ^0.6.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, 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) {
                    return sub(a, b, "SafeMath: subtraction overflow");
                }
            
                /**
                 * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
                 * overflow (when the result is negative).
                 *
                 * 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);
                    uint256 c = a - b;
            
                    return c;
                }
            
                /**
                 * @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) {
                    // 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 0;
                    }
            
                    uint256 c = a * b;
                    require(c / a == b, "SafeMath: multiplication overflow");
            
                    return c;
                }
            
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts 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) {
                    return div(a, b, "SafeMath: division by zero");
                }
            
                /**
                 * @dev Returns the integer division of two unsigned integers. Reverts with custom message 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, string memory errorMessage) internal pure returns (uint256) {
                    require(b > 0, errorMessage);
                    uint256 c = a / b;
                    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
            
                    return c;
                }
            
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts 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) {
                    return mod(a, b, "SafeMath: modulo by zero");
                }
            
                /**
                 * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
                 * Reverts with custom message 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, string memory errorMessage) internal pure returns (uint256) {
                    require(b != 0, errorMessage);
                    return a % b;
                }
            }
            
            // File: @openzeppelin/contracts/token/ERC20/IERC20.sol
            
            
            pragma solidity ^0.6.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/utils/Address.sol
            
            
            pragma solidity ^0.6.2;
            
            /**
             * @dev Collection of functions related to the address type
             */
            library Address {
                /**
                 * @dev Returns true if `account` is a contract.
                 *
                 * [IMPORTANT]
                 * ====
                 * It is unsafe to assume that an address for which this function returns
                 * false is an externally-owned account (EOA) and not a contract.
                 *
                 * Among others, `isContract` will return false for the following
                 * types of addresses:
                 *
                 *  - an externally-owned account
                 *  - a contract in construction
                 *  - an address where a contract will be created
                 *  - an address where a contract lived, but was destroyed
                 * ====
                 */
                function isContract(address account) internal view returns (bool) {
                    // According to EIP-1052, 0x0 is the value returned for not-yet created accounts
                    // and 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470 is returned
                    // for accounts without code, i.e. `keccak256('')`
                    bytes32 codehash;
                    bytes32 accountHash = 0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470;
                    // solhint-disable-next-line no-inline-assembly
                    assembly { codehash := extcodehash(account) }
                    return (codehash != accountHash && codehash != 0x0);
                }
            
                /**
                 * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
                 * `recipient`, forwarding all available gas and reverting on errors.
                 *
                 * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
                 * of certain opcodes, possibly making contracts go over the 2300 gas limit
                 * imposed by `transfer`, making them unable to receive funds via
                 * `transfer`. {sendValue} removes this limitation.
                 *
                 * https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
                 *
                 * IMPORTANT: because control is transferred to `recipient`, care must be
                 * taken to not create reentrancy vulnerabilities. Consider using
                 * {ReentrancyGuard} or the
                 * https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
                 */
                function sendValue(address payable recipient, uint256 amount) internal {
                    require(address(this).balance >= amount, "Address: insufficient balance");
            
                    // solhint-disable-next-line avoid-low-level-calls, avoid-call-value
                    (bool success, ) = recipient.call{ value: amount }("");
                    require(success, "Address: unable to send value, recipient may have reverted");
                }
            
                /**
                 * @dev Performs a Solidity function call using a low level `call`. A
                 * plain`call` is an unsafe replacement for a function call: use this
                 * function instead.
                 *
                 * If `target` reverts with a revert reason, it is bubbled up by this
                 * function (like regular Solidity function calls).
                 *
                 * Returns the raw returned data. To convert to the expected return value,
                 * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
                 *
                 * Requirements:
                 *
                 * - `target` must be a contract.
                 * - calling `target` with `data` must not revert.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data) internal returns (bytes memory) {
                  return functionCall(target, data, "Address: low-level call failed");
                }
            
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
                 * `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCall(address target, bytes memory data, string memory errorMessage) internal returns (bytes memory) {
                    return _functionCallWithValue(target, data, 0, errorMessage);
                }
            
                /**
                 * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
                 * but also transferring `value` wei to `target`.
                 *
                 * Requirements:
                 *
                 * - the calling contract must have an ETH balance of at least `value`.
                 * - the called Solidity function must be `payable`.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
                    return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
                }
            
                /**
                 * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
                 * with `errorMessage` as a fallback revert reason when `target` reverts.
                 *
                 * _Available since v3.1._
                 */
                function functionCallWithValue(address target, bytes memory data, uint256 value, string memory errorMessage) internal returns (bytes memory) {
                    require(address(this).balance >= value, "Address: insufficient balance for call");
                    return _functionCallWithValue(target, data, value, errorMessage);
                }
            
                function _functionCallWithValue(address target, bytes memory data, uint256 weiValue, string memory errorMessage) private returns (bytes memory) {
                    require(isContract(target), "Address: call to non-contract");
            
                    // solhint-disable-next-line avoid-low-level-calls
                    (bool success, bytes memory returndata) = target.call{ value: weiValue }(data);
                    if (success) {
                        return returndata;
                    } else {
                        // Look for revert reason and bubble it up if present
                        if (returndata.length > 0) {
                            // The easiest way to bubble the revert reason is using memory via assembly
            
                            // solhint-disable-next-line no-inline-assembly
                            assembly {
                                let returndata_size := mload(returndata)
                                revert(add(32, returndata), returndata_size)
                            }
                        } else {
                            revert(errorMessage);
                        }
                    }
                }
            }
            
            // File: @openzeppelin/contracts/token/ERC20/SafeERC20.sol
            
            
            pragma solidity ^0.6.0;
            
            
            
            
            /**
             * @title SafeERC20
             * @dev Wrappers around ERC20 operations that throw on failure (when the token
             * contract returns false). Tokens that return no value (and instead revert or
             * throw on failure) are also supported, non-reverting calls are assumed to be
             * successful.
             * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
             * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
             */
            library SafeERC20 {
                using SafeMath for uint256;
                using Address for address;
            
                function safeTransfer(IERC20 token, address to, uint256 value) internal {
                    _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
                }
            
                function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
                    _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
                }
            
                /**
                 * @dev Deprecated. This function has issues similar to the ones found in
                 * {IERC20-approve}, and its usage is discouraged.
                 *
                 * Whenever possible, use {safeIncreaseAllowance} and
                 * {safeDecreaseAllowance} instead.
                 */
                function safeApprove(IERC20 token, address spender, uint256 value) internal {
                    // safeApprove should only be called when setting an initial allowance,
                    // or when resetting it to zero. To increase and decrease it, use
                    // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
                    // solhint-disable-next-line max-line-length
                    require((value == 0) || (token.allowance(address(this), spender) == 0),
                        "SafeERC20: approve from non-zero to non-zero allowance"
                    );
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
                }
            
                function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                    uint256 newAllowance = token.allowance(address(this), spender).add(value);
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                }
            
                function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
                    uint256 newAllowance = token.allowance(address(this), spender).sub(value, "SafeERC20: decreased allowance below zero");
                    _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
                }
            
                /**
                 * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
                 * on the return value: the return value is optional (but if data is returned, it must not be false).
                 * @param token The token targeted by the call.
                 * @param data The call data (encoded using abi.encode or one of its variants).
                 */
                function _callOptionalReturn(IERC20 token, bytes memory data) private {
                    // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
                    // we're implementing it ourselves. We use {Address.functionCall} to perform this call, which verifies that
                    // the target address contains contract code and also asserts for success in the low-level call.
            
                    bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
                    if (returndata.length > 0) { // Return data is optional
                        // solhint-disable-next-line max-line-length
                        require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
                    }
                }
            }
            
            // File: contracts/libraries/UniERC20.sol
            
            
            pragma solidity ^0.6.0;
            
            
            
            
            
            library UniERC20 {
                using SafeMath for uint256;
                using SafeERC20 for IERC20;
            
                function isETH(IERC20 token) internal pure returns(bool) {
                    return (address(token) == address(0));
                }
            
                function uniBalanceOf(IERC20 token, address account) internal view returns (uint256) {
                    if (isETH(token)) {
                        return account.balance;
                    } else {
                        return token.balanceOf(account);
                    }
                }
            
                function uniTransfer(IERC20 token, address payable to, uint256 amount) internal {
                    if (amount > 0) {
                        if (isETH(token)) {
                            to.transfer(amount);
                        } else {
                            token.safeTransfer(to, amount);
                        }
                    }
                }
            
                function uniTransferFromSenderToThis(IERC20 token, uint256 amount) internal {
                    if (amount > 0) {
                        if (isETH(token)) {
                            require(msg.value >= amount, "UniERC20: not enough value");
                            if (msg.value > amount) {
                                // Return remainder if exist
                                msg.sender.transfer(msg.value.sub(amount));
                            }
                        } else {
                            token.safeTransferFrom(msg.sender, address(this), amount);
                        }
                    }
                }
            
                function uniSymbol(IERC20 token) internal view returns(string memory) {
                    if (isETH(token)) {
                        return "ETH";
                    }
            
                    (bool success, bytes memory data) = address(token).staticcall{ gas: 20000 }(
                        abi.encodeWithSignature("symbol()")
                    );
                    if (!success) {
                        (success, data) = address(token).staticcall{ gas: 20000 }(
                            abi.encodeWithSignature("SYMBOL()")
                        );
                    }
            
                    if (success && data.length >= 96) {
                        (uint256 offset, uint256 len) = abi.decode(data, (uint256, uint256));
                        if (offset == 0x20 && len > 0 && len <= 256) {
                            return string(abi.decode(data, (bytes)));
                        }
                    }
            
                    if (success && data.length == 32) {
                        uint len = 0;
                        while (len < data.length && data[len] >= 0x20 && data[len] <= 0x7E) {
                            len++;
                        }
            
                        if (len > 0) {
                            bytes memory result = new bytes(len);
                            for (uint i = 0; i < len; i++) {
                                result[i] = data[i];
                            }
                            return string(result);
                        }
                    }
            
                    return _toHex(address(token));
                }
            
                function _toHex(address account) private pure returns(string memory) {
                    return _toHex(abi.encodePacked(account));
                }
            
                function _toHex(bytes memory data) private pure returns(string memory) {
                    bytes memory str = new bytes(2 + data.length * 2);
                    str[0] = "0";
                    str[1] = "x";
                    uint j = 2;
                    for (uint i = 0; i < data.length; i++) {
                        uint a = uint8(data[i]) >> 4;
                        uint b = uint8(data[i]) & 0x0f;
                        str[j++] = byte(uint8(a + 48 + (a/10)*39));
                        str[j++] = byte(uint8(b + 48 + (b/10)*39));
                    }
            
                    return string(str);
                }
            }
            
            // File: @openzeppelin/contracts/utils/ReentrancyGuard.sol
            
            
            pragma solidity ^0.6.0;
            
            /**
             * @dev Contract module that helps prevent reentrant calls to a function.
             *
             * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
             * available, which can be applied to functions to make sure there are no nested
             * (reentrant) calls to them.
             *
             * Note that because there is a single `nonReentrant` guard, functions marked as
             * `nonReentrant` may not call one another. This can be worked around by making
             * those functions `private`, and then adding `external` `nonReentrant` entry
             * points to them.
             *
             * TIP: If you would like to learn more about reentrancy and alternative ways
             * to protect against it, check out our blog post
             * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
             */
            contract ReentrancyGuard {
                // Booleans are more expensive than uint256 or any type that takes up a full
                // word because each write operation emits an extra SLOAD to first read the
                // slot's contents, replace the bits taken up by the boolean, and then write
                // back. This is the compiler's defense against contract upgrades and
                // pointer aliasing, and it cannot be disabled.
            
                // The values being non-zero value makes deployment a bit more expensive,
                // but in exchange the refund on every call to nonReentrant will be lower in
                // amount. Since refunds are capped to a percentage of the total
                // transaction's gas, it is best to keep them low in cases like this one, to
                // increase the likelihood of the full refund coming into effect.
                uint256 private constant _NOT_ENTERED = 1;
                uint256 private constant _ENTERED = 2;
            
                uint256 private _status;
            
                constructor () internal {
                    _status = _NOT_ENTERED;
                }
            
                /**
                 * @dev Prevents a contract from calling itself, directly or indirectly.
                 * Calling a `nonReentrant` function from another `nonReentrant`
                 * function is not supported. It is possible to prevent this from happening
                 * by making the `nonReentrant` function external, and make it call a
                 * `private` function that does the actual work.
                 */
                modifier nonReentrant() {
                    // On the first call to nonReentrant, _notEntered will be true
                    require(_status != _ENTERED, "ReentrancyGuard: reentrant call");
            
                    // Any calls to nonReentrant after this point will fail
                    _status = _ENTERED;
            
                    _;
            
                    // By storing the original value once again, a refund is triggered (see
                    // https://eips.ethereum.org/EIPS/eip-2200)
                    _status = _NOT_ENTERED;
                }
            }
            
            // File: @openzeppelin/contracts/math/Math.sol
            
            
            pragma solidity ^0.6.0;
            
            /**
             * @dev Standard math utilities missing in the Solidity language.
             */
            library Math {
                /**
                 * @dev Returns the largest of two numbers.
                 */
                function max(uint256 a, uint256 b) internal pure returns (uint256) {
                    return a >= b ? a : b;
                }
            
                /**
                 * @dev Returns the smallest of two numbers.
                 */
                function min(uint256 a, uint256 b) internal pure returns (uint256) {
                    return a < b ? a : b;
                }
            
                /**
                 * @dev Returns the average of two numbers. The result is rounded towards
                 * zero.
                 */
                function average(uint256 a, uint256 b) internal pure returns (uint256) {
                    // (a + b) / 2 can overflow, so we distribute
                    return (a / 2) + (b / 2) + ((a % 2 + b % 2) / 2);
                }
            }
            
            // File: @openzeppelin/contracts/token/ERC20/ERC20.sol
            
            
            pragma solidity ^0.6.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;
                using Address for address;
            
                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 returns (string memory) {
                    return _name;
                }
            
                /**
                 * @dev Returns the symbol of the token, usually a shorter version of the
                 * name.
                 */
                function symbol() public view 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 returns (uint8) {
                    return _decimals;
                }
            
                /**
                 * @dev See {IERC20-totalSupply}.
                 */
                function totalSupply() public view override returns (uint256) {
                    return _totalSupply;
                }
            
                /**
                 * @dev See {IERC20-balanceOf}.
                 */
                function balanceOf(address account) public view 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 is 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 {
                    _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: contracts/libraries/Sqrt.sol
            
            
            pragma solidity ^0.6.0;
            
            
            library Sqrt {
                // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
                function sqrt(uint256 y) internal pure returns (uint256) {
                    if (y > 3) {
                        uint256 z = y;
                        uint256 x = y / 2 + 1;
                        while (x < z) {
                            z = x;
                            x = (y / x + x) / 2;
                        }
                        return z;
                    } else if (y != 0) {
                        return 1;
                    } else {
                        return 0;
                    }
                }
            }
            
            // File: contracts/Mooniswap.sol
            
            
            pragma solidity ^0.6.0;
            
            
            
            
            
            
            
            
            
            interface IFactory {
                function fee() external view returns(uint256);
            }
            
            
            library VirtualBalance {
                using SafeMath for uint256;
            
                struct Data {
                    uint216 balance;
                    uint40 time;
                }
            
                uint256 public constant DECAY_PERIOD = 5 minutes;
            
                function set(VirtualBalance.Data storage self, uint256 balance) internal {
                    self.balance = uint216(balance);
                    self.time = uint40(block.timestamp);
                }
            
                function update(VirtualBalance.Data storage self, uint256 realBalance) internal {
                    set(self, current(self, realBalance));
                }
            
                function scale(VirtualBalance.Data storage self, uint256 realBalance, uint256 num, uint256 denom) internal {
                    set(self, current(self, realBalance).mul(num).add(denom.sub(1)).div(denom));
                }
            
                function current(VirtualBalance.Data memory self, uint256 realBalance) internal view returns(uint256) {
                    uint256 timePassed = Math.min(DECAY_PERIOD, block.timestamp.sub(self.time));
                    uint256 timeRemain = DECAY_PERIOD.sub(timePassed);
                    return uint256(self.balance).mul(timeRemain).add(
                        realBalance.mul(timePassed)
                    ).div(DECAY_PERIOD);
                }
            }
            
            
            contract Mooniswap is ERC20, ReentrancyGuard, Ownable {
                using Sqrt for uint256;
                using SafeMath for uint256;
                using UniERC20 for IERC20;
                using VirtualBalance for VirtualBalance.Data;
            
                struct Balances {
                    uint256 src;
                    uint256 dst;
                }
            
                struct SwapVolumes {
                    uint128 confirmed;
                    uint128 result;
                }
            
                event Deposited(
                    address indexed account,
                    uint256 amount
                );
            
                event Withdrawn(
                    address indexed account,
                    uint256 amount
                );
            
                event Swapped(
                    address indexed account,
                    address indexed src,
                    address indexed dst,
                    uint256 amount,
                    uint256 result,
                    uint256 srcBalance,
                    uint256 dstBalance,
                    uint256 totalSupply,
                    address referral
                );
            
                uint256 public constant REFERRAL_SHARE = 20; // 1/share = 5% of LPs revenue
                uint256 public constant BASE_SUPPLY = 1000;  // Total supply on first deposit
                uint256 public constant FEE_DENOMINATOR = 1e18;
            
                IFactory public factory;
                IERC20[] public tokens;
                mapping(IERC20 => bool) public isToken;
                mapping(IERC20 => SwapVolumes) public volumes;
                mapping(IERC20 => VirtualBalance.Data) public virtualBalancesForAddition;
                mapping(IERC20 => VirtualBalance.Data) public virtualBalancesForRemoval;
            
                constructor(IERC20[] memory assets, string memory name, string memory symbol) public ERC20(name, symbol) {
                    require(bytes(name).length > 0, "Mooniswap: name is empty");
                    require(bytes(symbol).length > 0, "Mooniswap: symbol is empty");
                    require(assets.length == 2, "Mooniswap: only 2 tokens allowed");
            
                    factory = IFactory(msg.sender);
                    tokens = assets;
                    for (uint i = 0; i < assets.length; i++) {
                        require(!isToken[assets[i]], "Mooniswap: duplicate tokens");
                        isToken[assets[i]] = true;
                    }
                }
            
                function fee() public view returns(uint256) {
                    return factory.fee();
                }
            
                function getTokens() external view returns(IERC20[] memory) {
                    return tokens;
                }
            
                function decayPeriod() external pure returns(uint256) {
                    return VirtualBalance.DECAY_PERIOD;
                }
            
                function getBalanceForAddition(IERC20 token) public view returns(uint256) {
                    uint256 balance = token.uniBalanceOf(address(this));
                    return Math.max(virtualBalancesForAddition[token].current(balance), balance);
                }
            
                function getBalanceForRemoval(IERC20 token) public view returns(uint256) {
                    uint256 balance = token.uniBalanceOf(address(this));
                    return Math.min(virtualBalancesForRemoval[token].current(balance), balance);
                }
            
                function getReturn(IERC20 src, IERC20 dst, uint256 amount) external view returns(uint256) {
                    return _getReturn(src, dst, amount, getBalanceForAddition(src), getBalanceForRemoval(dst));
                }
            
                function deposit(uint256[] calldata amounts, uint256[] calldata minAmounts) external payable nonReentrant returns(uint256 fairSupply) {
                    IERC20[] memory _tokens = tokens;
                    require(amounts.length == _tokens.length, "Mooniswap: wrong amounts length");
                    require(msg.value == (_tokens[0].isETH() ? amounts[0] : (_tokens[1].isETH() ? amounts[1] : 0)), "Mooniswap: wrong value usage");
            
                    uint256[] memory realBalances = new uint256[](amounts.length);
                    for (uint i = 0; i < realBalances.length; i++) {
                        realBalances[i] = _tokens[i].uniBalanceOf(address(this)).sub(_tokens[i].isETH() ? msg.value : 0);
                    }
            
                    uint256 totalSupply = totalSupply();
                    if (totalSupply == 0) {
                        fairSupply = BASE_SUPPLY.mul(99);
                        _mint(address(this), BASE_SUPPLY); // Donate up to 1%
            
                        // Use the greatest token amount but not less than 99k for the initial supply
                        for (uint i = 0; i < amounts.length; i++) {
                            fairSupply = Math.max(fairSupply, amounts[i]);
                        }
                    }
                    else {
                        // Pre-compute fair supply
                        fairSupply = type(uint256).max;
                        for (uint i = 0; i < amounts.length; i++) {
                            fairSupply = Math.min(fairSupply, totalSupply.mul(amounts[i]).div(realBalances[i]));
                        }
                    }
            
                    uint256 fairSupplyCached = fairSupply;
                    for (uint i = 0; i < amounts.length; i++) {
                        require(amounts[i] > 0, "Mooniswap: amount is zero");
                        uint256 amount = (totalSupply == 0) ? amounts[i] :
                            realBalances[i].mul(fairSupplyCached).add(totalSupply - 1).div(totalSupply);
                        require(amount >= minAmounts[i], "Mooniswap: minAmount not reached");
            
                        _tokens[i].uniTransferFromSenderToThis(amount);
                        if (totalSupply > 0) {
                            uint256 confirmed = _tokens[i].uniBalanceOf(address(this)).sub(realBalances[i]);
                            fairSupply = Math.min(fairSupply, totalSupply.mul(confirmed).div(realBalances[i]));
                        }
                    }
            
                    if (totalSupply > 0) {
                        for (uint i = 0; i < amounts.length; i++) {
                            virtualBalancesForRemoval[_tokens[i]].scale(realBalances[i], totalSupply.add(fairSupply), totalSupply);
                            virtualBalancesForAddition[_tokens[i]].scale(realBalances[i], totalSupply.add(fairSupply), totalSupply);
                        }
                    }
            
                    require(fairSupply > 0, "Mooniswap: result is not enough");
                    _mint(msg.sender, fairSupply);
            
                    emit Deposited(msg.sender, fairSupply);
                }
            
                function withdraw(uint256 amount, uint256[] memory minReturns) external nonReentrant {
                    uint256 totalSupply = totalSupply();
                    _burn(msg.sender, amount);
            
                    for (uint i = 0; i < tokens.length; i++) {
                        IERC20 token = tokens[i];
            
                        uint256 preBalance = token.uniBalanceOf(address(this));
                        uint256 value = preBalance.mul(amount).div(totalSupply);
                        token.uniTransfer(msg.sender, value);
                        require(i >= minReturns.length || value >= minReturns[i], "Mooniswap: result is not enough");
            
                        virtualBalancesForAddition[token].scale(preBalance, totalSupply.sub(amount), totalSupply);
                        virtualBalancesForRemoval[token].scale(preBalance, totalSupply.sub(amount), totalSupply);
                    }
            
                    emit Withdrawn(msg.sender, amount);
                }
            
                function swap(IERC20 src, IERC20 dst, uint256 amount, uint256 minReturn, address referral) external payable nonReentrant returns(uint256 result) {
                    require(msg.value == (src.isETH() ? amount : 0), "Mooniswap: wrong value usage");
            
                    Balances memory balances = Balances({
                        src: src.uniBalanceOf(address(this)).sub(src.isETH() ? msg.value : 0),
                        dst: dst.uniBalanceOf(address(this))
                    });
            
                    // catch possible airdrops and external balance changes for deflationary tokens
                    uint256 srcAdditionBalance = Math.max(virtualBalancesForAddition[src].current(balances.src), balances.src);
                    uint256 dstRemovalBalance = Math.min(virtualBalancesForRemoval[dst].current(balances.dst), balances.dst);
            
                    src.uniTransferFromSenderToThis(amount);
                    uint256 confirmed = src.uniBalanceOf(address(this)).sub(balances.src);
                    result = _getReturn(src, dst, confirmed, srcAdditionBalance, dstRemovalBalance);
                    require(result > 0 && result >= minReturn, "Mooniswap: return is not enough");
                    dst.uniTransfer(msg.sender, result);
            
                    // Update virtual balances to the same direction only at imbalanced state
                    if (srcAdditionBalance != balances.src) {
                        virtualBalancesForAddition[src].set(srcAdditionBalance.add(confirmed));
                    }
                    if (dstRemovalBalance != balances.dst) {
                        virtualBalancesForRemoval[dst].set(dstRemovalBalance.sub(result));
                    }
            
                    // Update virtual balances to the opposite direction
                    virtualBalancesForRemoval[src].update(balances.src);
                    virtualBalancesForAddition[dst].update(balances.dst);
            
                    if (referral != address(0)) {
                        uint256 invariantRatio = uint256(1e36);
                        invariantRatio = invariantRatio.mul(balances.src.add(confirmed)).div(balances.src);
                        invariantRatio = invariantRatio.mul(balances.dst.sub(result)).div(balances.dst);
                        if (invariantRatio > 1e36) {
                            // calculate share only if invariant increased
                            uint256 referralShare = invariantRatio.sqrt().sub(1e18).mul(totalSupply()).div(1e18).div(REFERRAL_SHARE);
                            if (referralShare > 0) {
                                _mint(referral, referralShare);
                            }
                        }
                    }
            
                    emit Swapped(msg.sender, address(src), address(dst), confirmed, result, balances.src, balances.dst, totalSupply(), referral);
            
                    // Overflow of uint128 is desired
                    volumes[src].confirmed += uint128(confirmed);
                    volumes[src].result += uint128(result);
                }
            
                function rescueFunds(IERC20 token, uint256 amount) external nonReentrant onlyOwner {
                    uint256[] memory balances = new uint256[](tokens.length);
                    for (uint i = 0; i < balances.length; i++) {
                        balances[i] = tokens[i].uniBalanceOf(address(this));
                    }
            
                    token.uniTransfer(msg.sender, amount);
            
                    for (uint i = 0; i < balances.length; i++) {
                        require(tokens[i].uniBalanceOf(address(this)) >= balances[i], "Mooniswap: access denied");
                    }
                    require(balanceOf(address(this)) >= BASE_SUPPLY, "Mooniswap: access denied");
                }
            
                function _getReturn(IERC20 src, IERC20 dst, uint256 amount, uint256 srcBalance, uint256 dstBalance) internal view returns(uint256) {
                    if (isToken[src] && isToken[dst] && src != dst && amount > 0) {
                        uint256 taxedAmount = amount.sub(amount.mul(fee()).div(FEE_DENOMINATOR));
                        return taxedAmount.mul(dstBalance).div(srcBalance.add(taxedAmount));
                    }
                }
            }
            
            // File: contracts/MooniFactory.sol
            
            
            pragma solidity ^0.6.0;
            
            
            
            
            
            contract MooniFactory is Ownable {
                using UniERC20 for IERC20;
            
                event Deployed(
                    address indexed mooniswap,
                    address indexed token1,
                    address indexed token2
                );
            
                uint256 public constant MAX_FEE = 0.003e18; // 0.3%
            
                uint256 public fee;
                Mooniswap[] public allPools;
                mapping(Mooniswap => bool) public isPool;
                mapping(IERC20 => mapping(IERC20 => Mooniswap)) public pools;
            
                function getAllPools() external view returns(Mooniswap[] memory) {
                    return allPools;
                }
            
                function setFee(uint256 newFee) external onlyOwner {
                    require(newFee <= MAX_FEE, "Factory: fee should be <= 0.3%");
                    fee = newFee;
                }
            
                function deploy(IERC20 tokenA, IERC20 tokenB) public returns(Mooniswap pool) {
                    require(tokenA != tokenB, "Factory: not support same tokens");
                    require(pools[tokenA][tokenB] == Mooniswap(0), "Factory: pool already exists");
            
                    (IERC20 token1, IERC20 token2) = sortTokens(tokenA, tokenB);
                    IERC20[] memory tokens = new IERC20[](2);
                    tokens[0] = token1;
                    tokens[1] = token2;
            
                    string memory symbol1 = token1.uniSymbol();
                    string memory symbol2 = token2.uniSymbol();
            
                    pool = new Mooniswap(
                        tokens,
                        string(abi.encodePacked("Mooniswap V1 (", symbol1, "-", symbol2, ")")),
                        string(abi.encodePacked("MOON-V1-", symbol1, "-", symbol2))
                    );
            
                    pool.transferOwnership(owner());
                    pools[token1][token2] = pool;
                    pools[token2][token1] = pool;
                    allPools.push(pool);
                    isPool[pool] = true;
            
                    emit Deployed(
                        address(pool),
                        address(token1),
                        address(token2)
                    );
                }
            
                function sortTokens(IERC20 tokenA, IERC20 tokenB) public pure returns(IERC20, IERC20) {
                    if (tokenA < tokenB) {
                        return (tokenA, tokenB);
                    }
                    return (tokenB, tokenA);
                }
            }