diff --git a/artifacts/contracts/CunaFinanceBsc.sol/CunaFinanceBsc.json b/artifacts/contracts/CunaFinanceBsc.sol/CunaFinanceBsc.json index cdcba62..4d50b1d 100644 --- a/artifacts/contracts/CunaFinanceBsc.sol/CunaFinanceBsc.json +++ b/artifacts/contracts/CunaFinanceBsc.sol/CunaFinanceBsc.json @@ -536,43 +536,15 @@ "internalType": "uint256", "name": "createdAt", "type": "uint256" - } - ], - "name": "createVesting", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" }, { "internalType": "uint256", - "name": "amount", + "name": "claimedAmount", "type": "uint256" }, { "internalType": "uint256", - "name": "bonus", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lockedUntil", - "type": "uint256" - }, - { - "internalType": "address", - "name": "token", - "type": "address" - }, - { - "internalType": "uint256", - "name": "usdAmount", + "name": "claimedBonus", "type": "uint256" } ], @@ -1898,6 +1870,69 @@ "stateMutability": "nonpayable", "type": "function" }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint256", + "name": "vestingIndex", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "bonus", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lockedUntil", + "type": "uint256" + }, + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "uint256", + "name": "usdAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastClaimed", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "createdAt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "claimedAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "claimedBonus", + "type": "uint256" + } + ], + "name": "updateVesting", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, { "inputs": [ { @@ -2146,8 +2181,8 @@ "type": "function" } ], - "bytecode": "0x60806040523480156200001157600080fd5b506200001c62000022565b620000d6565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000735760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620000d35780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b615d8480620000e66000396000f3fe608060405234801561001057600080fd5b506004361061036e5760003560e01c8062159da6146103735780630137451814610399578063022914a7146103cf5780630519da3214610402578063092c76101461040b5780630a84096a1461042b5780630a910a6d1461043e5780630c7d63861461044757806313baee5b14610451578063173825d9146104715780631764303d146104845780631aefa2d1146104975780631eb9e53e146104aa5780632ded58aa146104bd5780632e46fed8146104c65780633ba8396e146104ef5780633c92f98d146105125780633e4bc6bc146105345780633f35e7221461053d57806343a32f891461055057806343c7c011146105cb578063441a4175146105de5780634a61f1e5146105ff57806351e624721461061f57806351f6cf2f14610632578063549e61d3146106535780635811622714610666578063592d1dd11461067957806361d1080b1461069957806362cd6a09146106a157806367a74ddc146106b85780636ef569a5146106cb5780637065cb48146106d457806374d1c8e3146106e757806375060a0b146106fa5780637a0c6dc01461071a5780637bc221ac1461073a5780637e6d99261461074d57806380259e691461077657806380ca0ecf146107895780638129fc1c1461079c57806382b75681146107a457806384e8520a146107b7578063853e0df2146107d75780638939d787146107ea5780638f82818f146107f35780639437e32e14610813578063953d16bf1461082657806396fd111a146108395780639cb6f556146108595780639f3a676c1461086c578063a0d46758146108b8578063aaf4b04d146108d8578063ac97b417146108eb578063b6c3dc4c146108fe578063b92a349f1461091e578063bc0bc6ba14610931578063bd84477d14610951578063bed9757e146109b9578063c0c07d17146109da578063c2676603146109ed578063c32d3ae2146109f5578063c6b61e4c14610a28578063c7b530b014610a98578063cc573a9114610ab8578063ce13d09014610af4578063cfcf331914610b07578063d532bdfe14610b27578063da1b436414610b3a578063e88f8e6614610b5a578063eacdc5ff14610b6d578063eb44e0a314610b76578063ef5d9ae814610b89578063f2bb563014610ba9578063fe2f50d014610bbc578063fee6018c14610bc5578063ffecf51614610bd8575b600080fd5b6103866103813660046152c2565b610beb565b6040519081526020015b60405180910390f35b6103c26103a73660046152c2565b6004602052600090815260409020546001600160a01b031681565b60405161039091906152e4565b6103f26103dd3660046152c2565b60006020819052908152604090205460ff1681565b6040519015158152602001610390565b61038660075481565b6103866104193660046152c2565b60066020526000908152604090205481565b6103866104393660046152f8565b610c85565b61038660125481565b61044f610dbd565b005b61038661045f3660046152c2565b600c6020526000908152604090205481565b61044f61047f3660046152c2565b610f83565b61044f610492366004615366565b611074565b61044f6104a53660046153a7565b6112b2565b6103866104b83660046152c2565b6112e6565b61038660115481565b6103866104d43660046152c2565b6001600160a01b03166000908152601c602052604090205490565b6103f26104fd3660046152c2565b60016020526000908152604090205460ff1681565b610525610520366004615366565b61132a565b604051610390939291906153fb565b610386601d5481565b61044f61054b3660046152f8565b61151b565b61059c61055e3660046152f8565b60146020908152600092835260408084209091529082529020805460018201546002830154600390930154919290916001600160a01b039091169084565b604051610390949392919093845260208401929092526001600160a01b03166040830152606082015260800190565b61044f6105d93660046152c2565b611590565b6105f16105ec3660046153a7565b6118fe565b604051610390929190615431565b61038661060d3660046152c2565b60186020526000908152604090205481565b61044f61062d3660046153a7565b611936565b6106456106403660046152f8565b61196a565b60405161039092919061544a565b61044f610661366004615458565b6119a6565b61044f6106743660046154c3565b611d13565b6103866106873660046152c2565b60056020526000908152604090205481565b601b54610386565b6106a9611f7d565b60405161039093929190615511565b61044f6106c63660046155b3565b6121a4565b61038660165481565b61044f6106e23660046152c2565b612201565b61044f6106f53660046155e6565b6122d3565b6103866107083660046152c2565b600a6020526000908152604090205481565b61072d6107283660046152c2565b6124b6565b6040516103909190615651565b6103866107483660046152c2565b61259d565b61038661075b3660046152c2565b6001600160a01b031660009081526017602052604090205490565b61044f6107843660046153a7565b612771565b6103866107973660046152f8565b6127a5565b61044f6128b1565b61044f6107b23660046153a7565b612ad9565b6103866107c53660046152c2565b600d6020526000908152604090205481565b61044f6107e53660046153a7565b612b6b565b61038660135481565b6103866108013660046152c2565b60176020526000908152604090205481565b61044f6108213660046156ff565b612be9565b61044f6108343660046153a7565b612c30565b61084c6108473660046154c3565b612f1e565b6040516103909190615791565b61044f6108673660046153a7565b6130bd565b61087f61087a3660046153a7565b61334c565b6040805196875260208701959095529385019290925260608401526001600160a01b0390811660808401521660a082015260c001610390565b6108cb6108c63660046152f8565b61339f565b60405161039091906157df565b61044f6108e63660046153a7565b613403565b61044f6108f93660046153a7565b613489565b61091161090c3660046152f8565b61387b565b6040516103909190615819565b61044f61092c366004615827565b61398d565b61094461093f3660046153a7565b613b2e565b604051610390919061585a565b61096461095f3660046152f8565b613bd2565b604080519a8b5260208b0199909952978901969096526060880194909452608087019290925260a086015260c08501526001600160a01b031660e0840152151561010083015261012082015261014001610390565b6109cc6109c73660046152f8565b613c50565b604051610390929190615868565b61044f6109e836600461588d565b613df6565b6103e7610386565b610a08610a033660046152c2565b613fb0565b604080519485526020850193909352918301526060820152608001610390565b610a6b610a363660046153a7565b600b60205260009081526040902080546001820154600283015460038401546004850154600590950154939492939192909186565b604080519687526020870195909552938501929092526060840152608083015260a082015260c001610390565b610aab610aa63660046152c2565b614004565b60405161039091906158bf565b610acb610ac63660046152f8565b614099565b604080519485526020850193909352918301526001600160a01b03166060820152608001610390565b61044f610b023660046153a7565b6140e8565b610b1a610b153660046154c3565b61411c565b6040516103909190615901565b61044f610b35366004615366565b6142e5565b610386610b483660046152c2565b600e6020526000908152604090205481565b610525610b683660046152c2565b61451e565b61038660105481565b61044f610b843660046152f8565b6146f6565b610386610b973660046152c2565b601c6020526000908152604090205481565b61044f610bb73660046154c3565b614c6a565b61038660155481565b61044f610bd336600461597b565b614d24565b61044f610be63660046152c2565b614eeb565b600080610bf7836112e6565b6001600160a01b0384166000908152600e6020526040902054909150805b601054811015610c7d578215610c6b576000818152600b602052604081206004015461271090610c4590866159f1565b610c4f9190615a08565b9050610c5b8186615a2a565b9450610c678185615a3d565b9350505b80610c7581615a50565b915050610c15565b505050919050565b6001600160a01b0382166000908152600260205260408120805482919084908110610cb257610cb2615a69565b906000526020600020906009020190506000816006015442610cd49190615a3d565b60078301549091506001600160a01b03166000805b6001600160a01b038316600090815260036020526040902054811015610db0576001600160a01b0383166000908152600360205260408120805483908110610d3357610d33615a69565b6000918252602082206002909102018054600182015460088a015492945090929091606490610d6490600a906159f1565b610d6e9190615a08565b9050828810610d9b57612710610d8483836159f1565b610d8e9190615a08565b610d989087615a2a565b95505b5050505080610da990615a50565b9050610ce9565b5093505050505b92915050565b610dc5614f67565b6000610dd033610beb565b905060008111610dfb5760405162461bcd60e51b8152600401610df290615a7f565b60405180910390fd5b336000908152600c602052604081208054839290610e1a908490615a3d565b925050819055508060116000828254610e339190615a3d565b9091555050601054336000908152600e6020908152604080832093909355601c90529081208054839290610e68908490615a2a565b909155505060088054906000610e7d83615a50565b9091555050336000908152600f6020908152604091829020825160808101845260085481529182018490526007549092820190610eba9042615a2a565b8152600080516020615ccf833981519152602091820181905283546001808201865560009586528386208551600490930201918255848401519082015560408401516002820155606090930151600390930180546001600160a01b0319166001600160a01b0390941693909317909255908252600a9052600080516020615c6f8339815191528054839290610f50908490615a2a565b90915550506040518181523390600080516020615c8f8339815191529060200160405180910390a250610f81614f9d565b565b3360009081526020819052604090205460ff16610fb25760405162461bcd60e51b8152600401610df290615aa9565b6001600160a01b03811660009081526020819052604090205460ff166110065760405162461bcd60e51b81526020600482015260096024820152682737ba1037bbb732b960b91b6044820152606401610df2565b336001600160a01b038216036110535760405162461bcd60e51b815260206004820152601260248201527121b0b73737ba103932b6b7bb329039b2b63360711b6044820152606401610df2565b6001600160a01b03166000908152602081905260409020805460ff19169055565b61107c614f67565b6000805b8281101561122b57600084848381811061109c5761109c615a69565b336000908152600260209081526040909120549102929092013592505081106110c55750611219565b3360009081526002602052604081208054839081106110e6576110e6615a69565b9060005260206000209060090201905060006111023384610c85565b9050816004015481101561111857505050611219565b600082600401548261112a9190615a3d565b90508060000361113d5750505050611219565b808360040160008282546111519190615a2a565b9091555061116190508187615a2a565b336000908152600f6020526040908190208151608081019092529197508061118c87620f4240615a2a565b8152602001838152602001600754426111a59190615a2a565b8152600080516020615ccf833981519152602091820152825460018082018555600094855293829020835160049092020190815590820151928101929092556040810151600283015560600151600390910180546001600160a01b0319166001600160a01b03909216919091179055505050505b8061122381615a50565b915050611080565b5080156112a557600080516020615ccf8339815191526000908152600a602052600080516020615c6f833981519152805483929061126a908490615a2a565b909155505060405181815233907f4e69fdc49495bcab2b4375781457ba16653a90eb4ffb6588351bdc39071433e29060200160405180910390a25b506112ae614f9d565b5050565b3360009081526020819052604090205460ff166112e15760405162461bcd60e51b8152600401610df290615aa9565b601655565b6001600160a01b0381166000908152600c60209081526040808320546018909252822054808211611318576000611322565b6113228183615a3d565b949350505050565b606080600083806001600160401b0381111561134857611348615ad1565b604051908082528060200260200182016040528015611371578160200160208202803683370190505b509350806001600160401b0381111561138c5761138c615ad1565b6040519080825280602002602001820160405280156113b5578160200160208202803683370190505b50925060005b818110156115125760008787838181106113d7576113d7615a69565b90506020020160208101906113ec91906152c2565b6001600160a01b038116600090815260066020526040902054875191925090819088908590811061141f5761141f615a69565b6020908102919091018101919091526001600160a01b03808416600090815260049283905260408082205490516302c68be360e31b815291939216916316345f189161146d918791016152e4565b602060405180830381865afa15801561148a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114ae9190615ae7565b90506000670de0b6b3a76400006114c584846159f1565b6114cf9190615a08565b9050808886815181106114e4576114e4615a69565b60209081029190910101526114f98188615a2a565b965050505050808061150a90615a50565b9150506113bb565b50509250925092565b3360009081526020819052604090205460ff1661154a5760405162461bcd60e51b8152600401610df290615aa9565b61155e6001600160a01b0383163383614fae565b6040518181526001600160a01b038316903390600080516020615d0f8339815191529060200160405180910390a35050565b611598614f67565b6000805b336000908152600260205260409020548110156116a3573360009081526002602052604081208054839081106115d4576115d4615a69565b6000918252602090912060099091020160078101549091506001600160a01b03858116911614801561161257506007810154600160a01b900460ff16155b1561169057600061162333846127a5565b9050816003015481111561168e5760008260030154826116439190615a3d565b905061164f8186615a2a565b9450808360030160008282546116659190615a2a565b9091555050825460038401541061168c5760078301805460ff60a01b1916600160a01b1790555b505b505b508061169b81615a50565b91505061159c565b50600081116116c45760405162461bcd60e51b8152600401610df290615a7f565b33600090815260056020526040902054156117d1576001600160a01b0380831660009081526004602081905260408083205490516302c68be360e31b81529293670de0b6b3a764000093869392909116916316345f1891611727918991016152e4565b602060405180830381865afa158015611744573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117689190615ae7565b61177291906159f1565b61177c9190615a08565b3360009081526005602052604090205490915081106117aa57336000908152600560205260408120556117cf565b33600090815260056020526040812080548392906117c9908490615a3d565b90915550505b505b6001600160a01b038216600090815260066020526040812080548392906117f9908490615a3d565b9091555050336000908152600f60205260408082208151608081019092526009805491938392919061182a83615a50565b919050558152602001838152602001600754426118479190615a2a565b81526001600160a01b0385811660209283018190528454600180820187556000968752848720865160049093020191825585850151908201556040808601516002830155606090950151600390910180546001600160a01b03191691909316179091558352600a9052812080548392906118c2908490615a2a565b90915550506040513390600080516020615caf833981519152906118ea90849060009061544a565b60405180910390a2506118fb614f9d565b50565b6019818154811061190e57600080fd5b6000918252602090912060029091020180546001909101546001600160a01b03909116915082565b3360009081526020819052604090205460ff166119655760405162461bcd60e51b8152600401610df290615aa9565b601555565b6003602052816000526040600020818154811061198657600080fd5b600091825260209091206002909102018054600190910154909250905082565b3360009081526001602052604090205460ff166119d55760405162461bcd60e51b8152600401610df290615aa9565b828114611a1c5760405162461bcd60e51b8152602060048201526015602482015274082e4e4c2f240d8cadccee8d040dad2e6dac2e8c6d605b1b6044820152606401610df2565b82611a585760405162461bcd60e51b815260206004820152600c60248201526b456d7074792061727261797360a01b6044820152606401610df2565b60005b83811015611d0c576000858583818110611a7757611a77615a69565b9050602002016020810190611a8c91906152c2565b6001600160a01b031603611ab25760405162461bcd60e51b8152600401610df290615b00565b6000838383818110611ac657611ac6615a69565b9050602002013511611aea5760405162461bcd60e51b8152600401610df290615b29565b828282818110611afc57611afc615a69565b90506020020135600c6000878785818110611b1957611b19615a69565b9050602002016020810190611b2e91906152c2565b6001600160a01b03166001600160a01b0316815260200190815260200160002054601154611b5c9190615a3d565b611b669190615a2a565b601155600d6000868684818110611b7f57611b7f615a69565b9050602002016020810190611b9491906152c2565b6001600160a01b03166001600160a01b0316815260200190815260200160002054600003611c1b57828282818110611bce57611bce615a69565b90506020020135600d6000878785818110611beb57611beb615a69565b9050602002016020810190611c0091906152c2565b6001600160a01b031681526020810191909152604001600020555b828282818110611c2d57611c2d615a69565b90506020020135600c6000878785818110611c4a57611c4a615a69565b9050602002016020810190611c5f91906152c2565b6001600160a01b03168152602081019190915260400160002055848482818110611c8b57611c8b615a69565b9050602002016020810190611ca091906152c2565b6001600160a01b03167fec7e3594982826a1f90c8fc76513357b83a691b7f4e38b8be04f3d40f9b15839848484818110611cdc57611cdc615a69565b90506020020135604051611cf291815260200190565b60405180910390a280611d0481615a50565b915050611a5b565b5050505050565b611d1b614f67565b60008211611d5b5760405162461bcd60e51b815260206004820152600d60248201526c496e76616c69642076616c756560981b6044820152606401610df2565b60008111611d7b5760405162461bcd60e51b8152600401610df290615b51565b601554821015611dc35760405162461bcd60e51b815260206004820152601360248201527256616c75652062656c6f77206d696e696d756d60681b6044820152606401610df2565b6000611dce336112e6565b905080831115611df05760405162461bcd60e51b8152600401610df290615b7d565b60088054906000611e0083615a50565b90915550506008543360009081526018602052604081208054869290611e27908490615a2a565b909155505060408051608081018252858152602080820186815233838501818152426060860190815260008381526014865287812089825286528781209651875593516001808801919091559151600280880180546001600160a01b039384166001600160a01b03199182161790915592516003909801979097558751808901909852928752938601878152601980548084018255948190529651939095027f944998273e477b495144fb8794c914197f3ccb46be2900f4698fd0ef743c969581018054949093169390941692909217905591517f944998273e477b495144fb8794c914197f3ccb46be2900f4698fd0ef743c9696909101559054611f2c9190615a3d565b336000818152601a6020908152604080832086845290915290819020929092559051600080516020615cef83398151915290611f6b908690859061544a565b60405180910390a250506112ae614f9d565b60195460609081908190806001600160401b03811115611f9f57611f9f615ad1565b604051908082528060200260200182016040528015611fc8578160200160208202803683370190505b509350806001600160401b03811115611fe357611fe3615ad1565b60405190808252806020026020018201604052801561200c578160200160208202803683370190505b509250806001600160401b0381111561202757612027615ad1565b60405190808252806020026020018201604052801561206057816020015b61204d615204565b8152602001906001900390816120455790505b50915060005b8181101561219d5760006019828154811061208357612083615a69565b60009182526020918290206040805180820190915260029092020180546001600160a01b03168083526001909101549282019290925287519092508790849081106120d0576120d0615a69565b60200260200101906001600160a01b031690816001600160a01b031681525050806020015185838151811061210757612107615a69565b60209081029190910181019190915281516001600160a01b039081166000908152601483526040808220858501518352845290819020815160808101835281548152600182015494810194909452600281015490921690830152600301546060820152845185908490811061217e5761217e615a69565b602002602001018190525050808061219590615a50565b915050612066565b5050909192565b3360009081526020819052604090205460ff166121d35760405162461bcd60e51b8152600401610df290615aa9565b6001600160a01b03918216600090815260046020526040902080546001600160a01b03191691909216179055565b3360009081526020819052604090205460ff166122305760405162461bcd60e51b8152600401610df290615aa9565b6001600160a01b0381166122565760405162461bcd60e51b8152600401610df290615b00565b6001600160a01b03811660009081526020819052604090205460ff16156122af5760405162461bcd60e51b815260206004820152600d60248201526c20b63932b0b23c9037bbb732b960991b6044820152606401610df2565b6001600160a01b03166000908152602081905260409020805460ff19166001179055565b3360009081526001602052604090205460ff166123025760405162461bcd60e51b8152600401610df290615aa9565b60026000896001600160a01b03166001600160a01b031681526020019081526020016000206040518061014001604052808981526020018881526020018781526020016000815260200160008152602001848152602001838152602001866001600160a01b03168152602001600015158152602001858152509080600181540180825580915050600190039060005260206000209060090201600090919091909150600082015181600001556020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c0820151816006015560e08201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506101008201518160070160146101000a81548160ff0219169083151502179055506101208201518160080155505082600560008a6001600160a01b03166001600160a01b03168152602001908152602001600020600082825461247a9190615a2a565b90915550506001600160a01b038416600090815260066020526040812080548992906124a7908490615a2a565b90915550505050505050505050565b6001600160a01b0381166000908152600260209081526040808320805482518185028101850190935280835260609492939192909184015b8282101561259257600084815260209081902060408051610140810182526009860290920180548352600180820154848601526002820154928401929092526003810154606084015260048101546080840152600581015460a0840152600681015460c084015260078101546001600160a01b03811660e0850152600160a01b900460ff1615156101008401526008015461012083015290835290920191016124ee565b505050509050919050565b6001600160a01b038116600090815260026020526040812054815b8181101561276a576001600160a01b03841660009081526002602052604081208054839081106125ea576125ea615a69565b600091825260209182902060408051610140810182526009909302909101805483526001810154938301939093526002830154908201526003820154606082015260048201546080820152600582015460a0820152600682015460c082015260078201546001600160a01b03811660e083015260ff600160a01b909104161515610100820181905260089092015461012082015291506127575760e0810180516001600160a01b03908116600090815260046020819052604080832054945190516302c68be360e31b81529294909316926316345f18926126cb92016152e4565b602060405180830381865afa1580156126e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061270c9190615ae7565b90506000826060015183600001516127249190615a3d565b90506000670de0b6b3a764000061273b83856159f1565b6127459190615a08565b90506127518188615a2a565b96505050505b508061276281615a50565b9150506125b8565b5050919050565b3360009081526020819052604090205460ff166127a05760405162461bcd60e51b8152600401610df290615aa9565b601d55565b6001600160a01b03821660009081526002602052604081208054829190849081106127d2576127d2615a69565b9060005260206000209060090201905060008160060154426127f49190615a3d565b60078301549091506001600160a01b03166000805b6001600160a01b038316600090815260036020526040902054811015610db0576001600160a01b038316600090815260036020526040812080548390811061285357612853615a69565b60009182526020909120600290910201805460018201549192509081871061289d578754612710906128869083906159f1565b6128909190615a08565b61289a9086615a2a565b94505b505050806128aa90615a50565b9050612809565b60006128bb61500b565b805490915060ff600160401b82041615906001600160401b03166000811580156128e25750825b90506000826001600160401b031660011480156128fe5750303b155b90508115801561290c575080155b1561292a5760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b0319166001178555831561295357845460ff60401b1916600160401b1785555b61295b615034565b3360009081526020818152604082208054600160ff1991821681179092557f4cfa3c6903140891ecff713e237b1f1746362568dfb5e8e9bc37719e4f37e0c580548216831790557f3e1f9e6abae6b647fdc3c5896ef1871ceb3c98e9e72852da3f17d161a860f76c80548216831790557f3517cbb3edfcef55b3b32be7021bc73ef070f4a8bad0d4b389b96924cf17b7418054821683179055738a9281ecece9b599c2f42d829c3d0d8e74b7083e84527f968a1791ad31618c63b086103baa804af57c3ca0efa33a191010fbb7741579fc80549091169091179055600c905269021e19e0c9bab24000007f649cbac87391465cfe24c65b052f0edf3b8486f136e0b91262d76d1f34d7826b8190556011805491929091612a7c908490615a2a565b90915550506201fa4060075560646013558315611d0c57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050505050565b3360009081526020819052604090205460ff16612b085760405162461bcd60e51b8152600401610df290615aa9565b60008111612b665760405162461bcd60e51b815260206004820152602560248201527f4d61782070657263656e74616765206d75737420626520677265617465722074604482015264068616e20360dc1b6064820152608401610df2565b601355565b3360009081526020819052604090205460ff16612b9a5760405162461bcd60e51b8152600401610df290615aa9565b612bb3600080516020615ccf8339815191523383614fae565b604051818152600080516020615ccf833981519152903390600080516020615d0f8339815191529060200160405180910390a350565b3360009081526001602052604090205460ff16612c185760405162461bcd60e51b8152600401610df290615aa9565b612c2886868686868642426122d3565b505050505050565b33600090815260146020908152604080832084845290915290208054612c685760405162461bcd60e51b8152600401610df290615bad565b60028101546001600160a01b03163314612c945760405162461bcd60e51b8152600401610df290615bd8565b805460165460009061271090612caa90846159f1565b612cb49190615a08565b33600090815260186020526040812080549293508492909190612cd8908490615a3d565b90915550508015612d2157336000908152600c602052604081208054839290612d02908490615a3d565b925050819055508060116000828254612d1b9190615a3d565b90915550505b8015612d6b57336001600160a01b03167f4725a4d4de9bff212d0885095e27515072f73f427df55e52f37f241321ef88f98286604051612d6292919061544a565b60405180910390a25b336000818152601460209081526040808320888452825280832083815560018082018590556002820180546001600160a01b03191690556003909101849055938352601a82528083208884529091528120546019549092612dcb91615a3d565b9050808214612e8c57600060198281548110612de957612de9615a69565b60009182526020918290206040805180820190915260029092020180546001600160a01b03168252600101549181019190915260198054919250829185908110612e3557612e35615a69565b6000918252602080832084516002939093020180546001600160a01b0319166001600160a01b03938416178155938101516001909401939093558351168152601a8252604080822093830151825292909152208290555b6019805480612e9d57612e9d615c00565b600082815260208082206002600019949094019384020180546001600160a01b03191681556001018290559190925533808352601a825260408084208a855283528084209390935591518881527f73d12dec3eb3b445b6c9feb2fd559ba7c852c525bc1e59d8f7ff760c55df041d91015b60405180910390a2505050505050565b606081831115612f605760405162461bcd60e51b815260206004820152600d60248201526c496e76616c69642072616e676560981b6044820152606401610df2565b6010548210612fa75760405162461bcd60e51b8152602060048201526013602482015272115b9908195c1bd8da081b9bdd08199bdd5b99606a1b6044820152606401610df2565b6000612fb38484615a3d565b612fbe906001615a2a565b90506000816001600160401b03811115612fda57612fda615ad1565b60405190808252806020026020018201604052801561301357816020015b613000615235565b815260200190600190039081612ff85790505b50905060005b828110156130b457600b600061302f8389615a2a565b81526020019081526020016000206040518060c0016040529081600082015481526020016001820154815260200160028201548152602001600382015481526020016004820154815260200160058201548152505082828151811061309657613096615a69565b602002602001018190525080806130ac90615a50565b915050613019565b50949350505050565b6130c5614f67565b600081116130e55760405162461bcd60e51b8152600401610df290615b29565b60006012541161312e5760405162461bcd60e51b81526020600482015260146024820152734275796f7574206e6f7420617661696c61626c6560601b6044820152606401610df2565b6000613139336112e6565b90508082111561315b5760405162461bcd60e51b8152600401610df290615b7d565b61271061316a826109c46159f1565b6131749190615a08565b8210156131b45760405162461bcd60e51b815260206004820152600e60248201526d416d6f756e7420746f6f206c6f7760901b6044820152606401610df2565b6000612710601254846131c791906159f1565b6131d19190615a08565b336000908152600c60205260408120805492935085929091906131f5908490615a3d565b92505081905550826011600082825461320e9190615a3d565b9091555050336000908152601c602052604081208054839290613232908490615a2a565b90915550506008805490600061324783615a50565b9091555050336000908152600f60209081526040918290208251608081018452600854815291820184905260075490928201906132849042615a2a565b8152600080516020615ccf833981519152602091820181905283546001808201865560009586528386208551600490930201918255848401519082015560408401516002820155606090930151600390930180546001600160a01b0319166001600160a01b0390941693909317909255908252600a9052600080516020615c6f833981519152805483929061331a908490615a2a565b90915550506040518181523390600080516020615c8f8339815191529060200160405180910390a250506118fb614f9d565b601b818154811061335c57600080fd5b6000918252602090912060069091020180546001820154600283015460038401546004850154600590950154939550919390926001600160a01b03918216911686565b6133a7615204565b506001600160a01b03918216600090815260146020908152604080832093835292815290829020825160808101845281548152600182015492810192909252600281015490931691810191909152600390910154606082015290565b3360009081526020819052604090205460ff166134325760405162461bcd60e51b8152600401610df290615aa9565b6127108111156134845760405162461bcd60e51b815260206004820152601d60248201527f50657263656e746167652063616e6e6f742065786365656420313030250000006044820152606401610df2565b601255565b613491614f67565b3360009081526002602052604090205481106134e75760405162461bcd60e51b8152602060048201526015602482015274092dcecc2d8d2c840eccae6e8d2dcce40d2dcc8caf605b1b6044820152606401610df2565b33600090815260026020526040812080548390811061350857613508615a69565b906000526020600020906009020190508060070160149054906101000a900460ff161561356a5760405162461bcd60e51b815260206004820152601060248201526f56657374696e6720636f6d706c65746560801b6044820152606401610df2565b600061357633846127a5565b905081600301548110156135c35760405162461bcd60e51b8152602060048201526014602482015273125b9d985b1a590818db185a5b48185b5bdd5b9d60621b6044820152606401610df2565b60008260030154826135d59190615a3d565b9050600081116135f75760405162461bcd60e51b8152600401610df290615a7f565b8083600301600082825461360b9190615a2a565b909155505082546003840154106136325760078301805460ff60a01b1916600160a01b1790555b33600090815260056020526040902054156137435760078301546001600160a01b0390811660008181526004602081905260408083205490516302c68be360e31b81529294670de0b6b3a764000094879492909116926316345f18926136999291016152e4565b602060405180830381865afa1580156136b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136da9190615ae7565b6136e491906159f1565b6136ee9190615a08565b33600090815260056020526040902054909150811061371c5733600090815260056020526040812055613741565b336000908152600560205260408120805483929061373b908490615a3d565b90915550505b505b60078301546001600160a01b03166000908152600660205260408120805483929061376f908490615a3d565b9091555050336000908152600f6020526040808220815160808101909252600980549193839291906137a083615a50565b919050558152602001838152602001600754426137bd9190615a2a565b81526007860180546001600160a01b039081166020938401528454600180820187556000968752848720865160049093020191825585850151908201556040808601516002830155606090950151600390910180546001600160a01b0319169183169190911790559054168352600a905281208054839290613840908490615a2a565b90915550506040513390600080516020615caf8339815191529061386890849060009061544a565b60405180910390a25050506118fb614f9d565b6138af604051806080016040528060008152602001600081526020016000815260200160006001600160a01b031681525090565b6001600160a01b0383166000908152600f60205260408120905b815481101561397457838282815481106138e5576138e5615a69565b906000526020600020906004020160000154036139625781818154811061390e5761390e615a69565b6000918252602091829020604080516080810182526004909302909101805483526001810154938301939093526002830154908201526003909101546001600160a01b031660608201529250610db7915050565b8061396c81615a50565b9150506138c9565b5060405162461bcd60e51b8152600401610df290615c16565b3360009081526020819052604090205460ff166139bc5760405162461bcd60e51b8152600401610df290615aa9565b6001600160a01b0383166139e25760405162461bcd60e51b8152600401610df290615c3f565b6000811180156139f457506127108111155b613a355760405162461bcd60e51b8152602060048201526012602482015271496e76616c69642070657263656e7461676560701b6044820152606401610df2565b6001600160a01b0383166000908152600360205260408120613a569161526b565b6000825b612710821015613af35782612710613a728285615a2a565b1115613a8757613a8483612710615a3d565b90505b6001600160a01b0386166000908152600360209081526040808320815180830190925285825281830185815281546001818101845592865293909420915160029093029091019182559151910155613adf8184615a2a565b9250613aeb8583615a2a565b915050613a5a565b6040516001600160a01b038616907fde4b6ccc38b84f88129403b65a309f9b1c41d4c316bc2118d7614e449b9d4c4590600090a25050505050565b613b36615235565b6010548210613b795760405162461bcd60e51b815260206004820152600f60248201526e115c1bd8da081b9bdd08199bdd5b99608a1b6044820152606401610df2565b506000908152600b6020908152604091829020825160c08101845281548152600182015492810192909252600281015492820192909252600382015460608201526004820154608082015260059091015460a082015290565b60026020528160005260406000208181548110613bee57600080fd5b6000918252602090912060099091020180546001820154600283015460038401546004850154600586015460068701546007880154600890980154969950949750929591949093916001600160a01b03811691600160a01b90910460ff16908a565b6001600160a01b038216600090815260026020526040812080546060928392909185908110613c8157613c81615a69565b600091825260208083206007600990930201918201546001600160a01b03168084526003909152604083205491935091816001600160401b03811115613cc957613cc9615ad1565b604051908082528060200260200182016040528015613cf2578160200160208202803683370190505b5090506000826001600160401b03811115613d0f57613d0f615ad1565b604051908082528060200260200182016040528015613d38578160200160208202803683370190505b50905060005b83811015613de5576001600160a01b0385166000908152600360205260408120805483908110613d7057613d70615a69565b9060005260206000209060020201905080600001548760060154613d949190615a2a565b848381518110613da657613da6615a69565b6020026020010181815250508060010154838381518110613dc957613dc9615a69565b602090810291909101015250613dde81615a50565b9050613d3e565b5090955093505050505b9250929050565b3360009081526020819052604090205460ff16613e255760405162461bcd60e51b8152600401610df290615aa9565b60008115613e5a57600082613e3c866127106159f1565b613e469190615a08565b9050601d54811115613e5857601d8190555b505b60105415613ea0576000600b60006001601054613e779190615a3d565b81526020019081526020016000209050613e9c85848360010154846002015488615044565b9150505b601354811115613eeb5760405162461bcd60e51b81526020600482015260166024820152750aadcd8dec6d640e0cae4c6cadce8c2ceca40d0d2ced60531b6044820152606401610df2565b6040805160c08101825286815260208082018781528284018681526060808501898152608086018881524260a08801908152601080546000908152600b89528a90209851895595516001890155935160028801559051600387015551600486015590516005909401939093555483518881529182018590529281018690527feadbedb993dfca23e4c79bf4fa5fe531c2e0e926258fabb8445e8bc5c472780f910160405180910390a260108054906000613fa483615a50565b91905055505050505050565b6000806000806000613fc186610beb565b90506000613fce876112e6565b6001600160a01b03979097166000908152600c6020908152604080832054600d9092529091205490989297965094509092505050565b6001600160a01b0381166000908152600f60209081526040808320805482518185028101850190935280835260609492939192909184015b8282101561259257600084815260209081902060408051608081018252600486029092018054835260018082015484860152600282015492840192909252600301546001600160a01b03166060830152908352909201910161403c565b600f60205281600052604060002081815481106140b557600080fd5b6000918252602090912060049091020180546001820154600283015460039093015491945092506001600160a01b031684565b3360009081526020819052604090205460ff166141175760405162461bcd60e51b8152600401610df290615aa9565b600755565b601b54606090831061416c5760405162461bcd60e51b8152602060048201526019602482015278537461727420696e646578206f7574206f6620626f756e647360381b6044820152606401610df2565b60006141788385615a2a565b601b5490915081111561418a5750601b545b60006141968583615a3d565b6001600160401b038111156141ad576141ad615ad1565b60405190808252806020026020018201604052801561422957816020015b6142166040518060c001604052806000815260200160008152602001600081526020016000815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b8152602001906001900390816141cb5790505b509050845b828110156130b457601b818154811061424957614249615a69565b60009182526020918290206040805160c0810182526006909302909101805483526001810154938301939093526002830154908201526003820154606082015260048201546001600160a01b03908116608083015260059092015490911660a0820152826142b78884615a3d565b815181106142c7576142c7615a69565b602002602001018190525080806142dd90615a50565b91505061422e565b6142ed614f67565b336000908152600f6020526040902080546143405760405162461bcd60e51b81526020600482015260136024820152724e6f207374616b657320617661696c61626c6560681b6044820152606401610df2565b816143855760405162461bcd60e51b8152602060048201526015602482015274139bc81cdd185ad94812511cc81c1c9bdd9a591959605a1b6044820152606401610df2565b60005b828110156145145760008484838181106143a4576143a4615a69565b9050602002013590506000805b84548110156144e15760008582815481106143ce576143ce615a69565b906000526020600020906004020190508381600001541480156143f5575060008160010154115b156144ce57806002015442101561443d5760405162461bcd60e51b815260206004820152600c60248201526b14dd185ad9481b1bd8dad95960a21b6044820152606401610df2565b60018101805460038301546000928390556001600160a01b0316808352600a60205260408320805492939192849290614477908490615a3d565b9091555061449190506001600160a01b0382163384614fae565b336001600160a01b0316600080516020615d2f83398151915283886040516144ba92919061544a565b60405180910390a2600194505050506144e1565b50806144d981615a50565b9150506143b1565b50806144ff5760405162461bcd60e51b8152600401610df290615c16565b5050808061450c90615a50565b915050614388565b50506112ae614f9d565b60608060008061452d856112e6565b6001600160a01b0386166000908152600e602052604081205460105492935091614558908390615a3d565b9050806000036145895750506040805160008082526020820181815282840190935290955090935091506146ef9050565b806001600160401b038111156145a1576145a1615ad1565b6040519080825280602002602001820160405280156145ca578160200160208202803683370190505b509550806001600160401b038111156145e5576145e5615ad1565b60405190808252806020026020018201604052801561460e578160200160208202803683370190505b50945060005b818110156146ea5760006146288285615a2a565b90508088838151811061463d5761463d615a69565b602090810291909101015284156146b6576000818152600b60205260408120600401546127109061466e90886159f1565b6146789190615a08565b90508088848151811061468d5761468d615a69565b60209081029190910101526146a28188615a2a565b96506146ae8187615a3d565b9550506146d7565b60008783815181106146ca576146ca615a69565b6020026020010181815250505b50806146e281615a50565b915050614614565b505050505b9193909250565b6146fe614f67565b6001600160a01b03821660009081526014602090815260408083208484529091529020805461473f5760405162461bcd60e51b8152600401610df290615bad565b336001600160a01b038416036147905760405162461bcd60e51b815260206004820152601660248201527543616e6e6f7420627579206f776e206c697374696e6760501b6044820152606401610df2565b80546001820154600383015460008284116147ac5760006147b6565b6147b68385615a3d565b90506000846147c7836127106159f1565b6147d19190615a08565b905060006127106147e283806159f1565b6147ec9190615a08565b905060006127106147fd83896159f1565b6148079190615a08565b905060006148158289615a3d565b9050614831600080516020615ccf833981519152338d8a6150f2565b6001600160a01b038b166000908152600c6020526040812080548a9290614859908490615a3d565b9091555050336000908152600c60205260408120805483929061487d908490615a2a565b90915550506001600160a01b038b16600090815260186020526040812080548a92906148aa908490615a3d565b9091555050336000908152600d6020526040812080548392906148ce908490615a2a565b9250508190555081601160008282546148e79190615a3d565b90915550506001600160a01b038b1660009081526017602052604081208054899290614914908490615a2a565b92505081905550601b6040518060c001604052808881526020014281526020018a81526020018981526020018d6001600160a01b03168152602001336001600160a01b031681525090806001815401808255809150506001900390600052602060002090600602016000909190919091506000820151816000015560208201518160010155604082015181600201556060820151816003015560808201518160040160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060a08201518160050160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505050601460008c6001600160a01b03166001600160a01b0316815260200190815260200160002060008b815260200190815260200160002060008082016000905560018201600090556002820160006101000a8154906001600160a01b030219169055600382016000905550506000601a60008d6001600160a01b03166001600160a01b0316815260200190815260200160002060008c815260200190815260200160002054905060006001601980549050614ac39190615a3d565b9050808214614b8457600060198281548110614ae157614ae1615a69565b60009182526020918290206040805180820190915260029092020180546001600160a01b03168252600101549181019190915260198054919250829185908110614b2d57614b2d615a69565b6000918252602080832084516002939093020180546001600160a01b0319166001600160a01b03938416178155938101516001909401939093558351168152601a8252604080822093830151825292909152208290555b6019805480614b9557614b95615c00565b6001900381819060005260206000209060020201600080820160006101000a8154906001600160a01b030219169055600182016000905550509055601a60008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008d815260200190815260200160002060009055336001600160a01b03168d6001600160a01b03167f7bb39d095b04a9986ed34adf14d74c33294d0a9e807f02bf634d532507422eba8b8f604051614c4f92919061544a565b60405180910390a350505050505050505050506112ae614f9d565b33600090815260146020908152604080832085845290915290208054614ca25760405162461bcd60e51b8152600401610df290615bad565b60028101546001600160a01b03163314614cce5760405162461bcd60e51b8152600401610df290615bd8565b60008211614cee5760405162461bcd60e51b8152600401610df290615b51565b600181018290556040513390600080516020615cef83398151915290614d17908590879061544a565b60405180910390a2505050565b3360009081526001602052604090205460ff16614d535760405162461bcd60e51b8152600401610df290615aa9565b6001600160a01b038516614da05760405162461bcd60e51b8152602060048201526014602482015273496e76616c69642075736572206164647265737360601b6044820152606401610df2565b60008411614dc05760405162461bcd60e51b8152600401610df290615b29565b6001600160a01b038216614de65760405162461bcd60e51b8152600401610df290615c3f565b60088054906000614df683615a50565b9190505550600081614e0a57600854614e1a565b600854614e1a90620f4240615a2a565b6001600160a01b038781166000908152600f6020908152604080832081516080810183528681528084018c81528184018c81528b881660608401818152855460018082018855968a52888a209551600490910290950194855592519484019490945551600283015551600390910180546001600160a01b0319169190961617909455928252600a905290812080549293508792909190614ebb908490615a2a565b92505081905550856001600160a01b0316600080516020615d2f8339815191528683604051612f0e92919061544a565b3360009081526020819052604090205460ff16614f1a5760405162461bcd60e51b8152600401610df290615aa9565b6001600160a01b038116614f405760405162461bcd60e51b8152600401610df290615b00565b6001600160a01b03166000908152600160208190526040909120805460ff19169091179055565b6000614f71615131565b805490915060011901614f9757604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b6000614fa7615131565b6001905550565b61500683846001600160a01b031663a9059cbb8585604051602401614fd4929190615431565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050615155565b505050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610db7565b61503c6151bd565b610f816151e2565b6000821580615051575084155b1561505e575060006150e9565b60008561506d886127106159f1565b6150779190615a08565b9050600084615088876127106159f1565b6150929190615a08565b9050601d548210156150a9576000925050506150e9565b8082116150bb576000925050506150e9565b60006150c78284615a3d565b905060006127106150d887846159f1565b6150e29190615a08565b9450505050505b95945050505050565b6040516001600160a01b03848116602483015283811660448301526064820183905261512b9186918216906323b872dd90608401614fd4565b50505050565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0090565b600080602060008451602086016000885af180615178576040513d6000823e3d81fd5b50506000513d9150811561519057806001141561519d565b6001600160a01b0384163b155b1561512b5783604051635274afe760e01b8152600401610df291906152e4565b6151c56151ea565b610f8157604051631afcd79f60e31b815260040160405180910390fd5b614f9d6151bd565b60006151f461500b565b54600160401b900460ff16919050565b6040518060800160405280600081526020016000815260200160006001600160a01b03168152602001600081525090565b6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b50805460008255600202906000526020600020908101906118fb91905b808211156152a25760008082556001820155600201615288565b5090565b80356001600160a01b03811681146152bd57600080fd5b919050565b6000602082840312156152d457600080fd5b6152dd826152a6565b9392505050565b6001600160a01b0391909116815260200190565b6000806040838503121561530b57600080fd5b615314836152a6565b946020939093013593505050565b60008083601f84011261533457600080fd5b5081356001600160401b0381111561534b57600080fd5b6020830191508360208260051b8501011115613def57600080fd5b6000806020838503121561537957600080fd5b82356001600160401b0381111561538f57600080fd5b61539b85828601615322565b90969095509350505050565b6000602082840312156153b957600080fd5b5035919050565b600081518084526020808501945080840160005b838110156153f0578151875295820195908201906001016153d4565b509495945050505050565b60608152600061540e60608301866153c0565b828103602084015261542081866153c0565b915050826040830152949350505050565b6001600160a01b03929092168252602082015260400190565b918252602082015260400190565b6000806000806040858703121561546e57600080fd5b84356001600160401b038082111561548557600080fd5b61549188838901615322565b909650945060208701359150808211156154aa57600080fd5b506154b787828801615322565b95989497509550505050565b600080604083850312156154d657600080fd5b50508035926020909101359150565b80518252602080820151908301526040808201516001600160a01b031690830152606090810151910152565b60608082528451908201819052600090608090818401906020808901855b838110156155545781516001600160a01b03168552938201939082019060010161552f565b50508583038187015261556783896153c0565b868103604088015287518082528289019450908201925060005b818110156155a4576155948486516154e5565b9382019392850192600101615581565b50919998505050505050505050565b600080604083850312156155c657600080fd5b6155cf836152a6565b91506155dd602084016152a6565b90509250929050565b600080600080600080600080610100898b03121561560357600080fd5b61560c896152a6565b975060208901359650604089013595506060890135945061562f60808a016152a6565b979a969950949793969560a0850135955060c08501359460e001359350915050565b602080825282518282018190526000919060409081850190868401855b828110156156f25781518051855286810151878601528581015186860152606080820151908601526080808201519086015260a0808201519086015260c0808201519086015260e0808201516001600160a01b031690860152610100808201511515908601526101209081015190850152610140909301929085019060010161566e565b5091979650505050505050565b60008060008060008060c0878903121561571857600080fd5b615721876152a6565b9550602087013594506040870135935060608701359250615744608088016152a6565b915060a087013590509295509295509295565b805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301525050565b6020808252825182820181905260009190848201906040850190845b818110156157d3576157c0838551615757565b9284019260c092909201916001016157ad565b50909695505050505050565b60808101610db782846154e5565b8051825260208082015190830152604080820151908301526060908101516001600160a01b0316910152565b60808101610db782846157ed565b60008060006060848603121561583c57600080fd5b615845846152a6565b95602085013595506040909401359392505050565b60c08101610db78284615757565b60408152600061587b60408301856153c0565b82810360208401526150e981856153c0565b600080600080608085870312156158a357600080fd5b5050823594602084013594506040840135936060013592509050565b6020808252825182820181905260009190848201906040850190845b818110156157d3576158ee8385516157ed565b92840192608092909201916001016158db565b602080825282518282018190526000919060409081850190868401855b828110156156f25781518051855286810151878601528581015186860152606080820151908601526080808201516001600160a01b039081169187019190915260a091820151169085015260c0909301929085019060010161591e565b600080600080600060a0868803121561599357600080fd5b61599c866152a6565b945060208601359350604086013592506159b8606087016152a6565b9150608086013580151581146159cd57600080fd5b809150509295509295909350565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610db757610db76159db565b600082615a2557634e487b7160e01b600052601260045260246000fd5b500490565b80820180821115610db757610db76159db565b81810381811115610db757610db76159db565b600060018201615a6257615a626159db565b5060010190565b634e487b7160e01b600052603260045260246000fd5b60208082526010908201526f4e6f7468696e6720746f20636c61696d60801b604082015260600190565b6020808252600e908201526d139bdd08185d5d1a1bdc9a5e995960921b604082015260600190565b634e487b7160e01b600052604160045260246000fd5b600060208284031215615af957600080fd5b5051919050565b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b6020808252600e908201526d125b9d985b1a5908185b5bdd5b9d60921b604082015260600190565b602080825260129082015271496e76616c69642073616c6520707269636560701b604082015260600190565b602080825260169082015275496e73756666696369656e74206e6574207374616b6560501b604082015260600190565b602080825260119082015270131a5cdd1a5b99c81b9bdd08199bdd5b99607a1b604082015260600190565b6020808252600e908201526d2737ba103a34329039b2b63632b960911b604082015260600190565b634e487b7160e01b600052603160045260246000fd5b6020808252600f908201526e14dd185ad9481b9bdd08199bdd5b99608a1b604082015260600190565b602080825260159082015274496e76616c696420746f6b656e206164647265737360581b60408201526060019056feb0775d57c3eeb070f58d410e36e9ff396813a5d76997d9a91bdf10f0798296f0a65a8b4f7f65a1063243d7f7e9e4da00ff767599acf21549ef2548a45d1695ae4a94c2c356e29a6583071e731bdacf2ca56565ba5efebcff6936eb7923b5172100000000000000000000000055d398326f99059ff775485246999027b31979558e79b7ba8dab5ebfa59b9c6af1743c3ef14863680b3cc5ac837f8d636f76031ca92ff919b850e4909ab2261d907ef955f11bc1716733a6cbece38d163a69af8a933735aa8de6d7547d0126171b2f31b9c34dd00f3ecd4be85a0ba047db4fafefa2646970667358221220c8b39bc364af48f03e5e506c54c206562f38e806902f8ca765430d8cbb08f93d64736f6c63430008140033", - "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061036e5760003560e01c8062159da6146103735780630137451814610399578063022914a7146103cf5780630519da3214610402578063092c76101461040b5780630a84096a1461042b5780630a910a6d1461043e5780630c7d63861461044757806313baee5b14610451578063173825d9146104715780631764303d146104845780631aefa2d1146104975780631eb9e53e146104aa5780632ded58aa146104bd5780632e46fed8146104c65780633ba8396e146104ef5780633c92f98d146105125780633e4bc6bc146105345780633f35e7221461053d57806343a32f891461055057806343c7c011146105cb578063441a4175146105de5780634a61f1e5146105ff57806351e624721461061f57806351f6cf2f14610632578063549e61d3146106535780635811622714610666578063592d1dd11461067957806361d1080b1461069957806362cd6a09146106a157806367a74ddc146106b85780636ef569a5146106cb5780637065cb48146106d457806374d1c8e3146106e757806375060a0b146106fa5780637a0c6dc01461071a5780637bc221ac1461073a5780637e6d99261461074d57806380259e691461077657806380ca0ecf146107895780638129fc1c1461079c57806382b75681146107a457806384e8520a146107b7578063853e0df2146107d75780638939d787146107ea5780638f82818f146107f35780639437e32e14610813578063953d16bf1461082657806396fd111a146108395780639cb6f556146108595780639f3a676c1461086c578063a0d46758146108b8578063aaf4b04d146108d8578063ac97b417146108eb578063b6c3dc4c146108fe578063b92a349f1461091e578063bc0bc6ba14610931578063bd84477d14610951578063bed9757e146109b9578063c0c07d17146109da578063c2676603146109ed578063c32d3ae2146109f5578063c6b61e4c14610a28578063c7b530b014610a98578063cc573a9114610ab8578063ce13d09014610af4578063cfcf331914610b07578063d532bdfe14610b27578063da1b436414610b3a578063e88f8e6614610b5a578063eacdc5ff14610b6d578063eb44e0a314610b76578063ef5d9ae814610b89578063f2bb563014610ba9578063fe2f50d014610bbc578063fee6018c14610bc5578063ffecf51614610bd8575b600080fd5b6103866103813660046152c2565b610beb565b6040519081526020015b60405180910390f35b6103c26103a73660046152c2565b6004602052600090815260409020546001600160a01b031681565b60405161039091906152e4565b6103f26103dd3660046152c2565b60006020819052908152604090205460ff1681565b6040519015158152602001610390565b61038660075481565b6103866104193660046152c2565b60066020526000908152604090205481565b6103866104393660046152f8565b610c85565b61038660125481565b61044f610dbd565b005b61038661045f3660046152c2565b600c6020526000908152604090205481565b61044f61047f3660046152c2565b610f83565b61044f610492366004615366565b611074565b61044f6104a53660046153a7565b6112b2565b6103866104b83660046152c2565b6112e6565b61038660115481565b6103866104d43660046152c2565b6001600160a01b03166000908152601c602052604090205490565b6103f26104fd3660046152c2565b60016020526000908152604090205460ff1681565b610525610520366004615366565b61132a565b604051610390939291906153fb565b610386601d5481565b61044f61054b3660046152f8565b61151b565b61059c61055e3660046152f8565b60146020908152600092835260408084209091529082529020805460018201546002830154600390930154919290916001600160a01b039091169084565b604051610390949392919093845260208401929092526001600160a01b03166040830152606082015260800190565b61044f6105d93660046152c2565b611590565b6105f16105ec3660046153a7565b6118fe565b604051610390929190615431565b61038661060d3660046152c2565b60186020526000908152604090205481565b61044f61062d3660046153a7565b611936565b6106456106403660046152f8565b61196a565b60405161039092919061544a565b61044f610661366004615458565b6119a6565b61044f6106743660046154c3565b611d13565b6103866106873660046152c2565b60056020526000908152604090205481565b601b54610386565b6106a9611f7d565b60405161039093929190615511565b61044f6106c63660046155b3565b6121a4565b61038660165481565b61044f6106e23660046152c2565b612201565b61044f6106f53660046155e6565b6122d3565b6103866107083660046152c2565b600a6020526000908152604090205481565b61072d6107283660046152c2565b6124b6565b6040516103909190615651565b6103866107483660046152c2565b61259d565b61038661075b3660046152c2565b6001600160a01b031660009081526017602052604090205490565b61044f6107843660046153a7565b612771565b6103866107973660046152f8565b6127a5565b61044f6128b1565b61044f6107b23660046153a7565b612ad9565b6103866107c53660046152c2565b600d6020526000908152604090205481565b61044f6107e53660046153a7565b612b6b565b61038660135481565b6103866108013660046152c2565b60176020526000908152604090205481565b61044f6108213660046156ff565b612be9565b61044f6108343660046153a7565b612c30565b61084c6108473660046154c3565b612f1e565b6040516103909190615791565b61044f6108673660046153a7565b6130bd565b61087f61087a3660046153a7565b61334c565b6040805196875260208701959095529385019290925260608401526001600160a01b0390811660808401521660a082015260c001610390565b6108cb6108c63660046152f8565b61339f565b60405161039091906157df565b61044f6108e63660046153a7565b613403565b61044f6108f93660046153a7565b613489565b61091161090c3660046152f8565b61387b565b6040516103909190615819565b61044f61092c366004615827565b61398d565b61094461093f3660046153a7565b613b2e565b604051610390919061585a565b61096461095f3660046152f8565b613bd2565b604080519a8b5260208b0199909952978901969096526060880194909452608087019290925260a086015260c08501526001600160a01b031660e0840152151561010083015261012082015261014001610390565b6109cc6109c73660046152f8565b613c50565b604051610390929190615868565b61044f6109e836600461588d565b613df6565b6103e7610386565b610a08610a033660046152c2565b613fb0565b604080519485526020850193909352918301526060820152608001610390565b610a6b610a363660046153a7565b600b60205260009081526040902080546001820154600283015460038401546004850154600590950154939492939192909186565b604080519687526020870195909552938501929092526060840152608083015260a082015260c001610390565b610aab610aa63660046152c2565b614004565b60405161039091906158bf565b610acb610ac63660046152f8565b614099565b604080519485526020850193909352918301526001600160a01b03166060820152608001610390565b61044f610b023660046153a7565b6140e8565b610b1a610b153660046154c3565b61411c565b6040516103909190615901565b61044f610b35366004615366565b6142e5565b610386610b483660046152c2565b600e6020526000908152604090205481565b610525610b683660046152c2565b61451e565b61038660105481565b61044f610b843660046152f8565b6146f6565b610386610b973660046152c2565b601c6020526000908152604090205481565b61044f610bb73660046154c3565b614c6a565b61038660155481565b61044f610bd336600461597b565b614d24565b61044f610be63660046152c2565b614eeb565b600080610bf7836112e6565b6001600160a01b0384166000908152600e6020526040902054909150805b601054811015610c7d578215610c6b576000818152600b602052604081206004015461271090610c4590866159f1565b610c4f9190615a08565b9050610c5b8186615a2a565b9450610c678185615a3d565b9350505b80610c7581615a50565b915050610c15565b505050919050565b6001600160a01b0382166000908152600260205260408120805482919084908110610cb257610cb2615a69565b906000526020600020906009020190506000816006015442610cd49190615a3d565b60078301549091506001600160a01b03166000805b6001600160a01b038316600090815260036020526040902054811015610db0576001600160a01b0383166000908152600360205260408120805483908110610d3357610d33615a69565b6000918252602082206002909102018054600182015460088a015492945090929091606490610d6490600a906159f1565b610d6e9190615a08565b9050828810610d9b57612710610d8483836159f1565b610d8e9190615a08565b610d989087615a2a565b95505b5050505080610da990615a50565b9050610ce9565b5093505050505b92915050565b610dc5614f67565b6000610dd033610beb565b905060008111610dfb5760405162461bcd60e51b8152600401610df290615a7f565b60405180910390fd5b336000908152600c602052604081208054839290610e1a908490615a3d565b925050819055508060116000828254610e339190615a3d565b9091555050601054336000908152600e6020908152604080832093909355601c90529081208054839290610e68908490615a2a565b909155505060088054906000610e7d83615a50565b9091555050336000908152600f6020908152604091829020825160808101845260085481529182018490526007549092820190610eba9042615a2a565b8152600080516020615ccf833981519152602091820181905283546001808201865560009586528386208551600490930201918255848401519082015560408401516002820155606090930151600390930180546001600160a01b0319166001600160a01b0390941693909317909255908252600a9052600080516020615c6f8339815191528054839290610f50908490615a2a565b90915550506040518181523390600080516020615c8f8339815191529060200160405180910390a250610f81614f9d565b565b3360009081526020819052604090205460ff16610fb25760405162461bcd60e51b8152600401610df290615aa9565b6001600160a01b03811660009081526020819052604090205460ff166110065760405162461bcd60e51b81526020600482015260096024820152682737ba1037bbb732b960b91b6044820152606401610df2565b336001600160a01b038216036110535760405162461bcd60e51b815260206004820152601260248201527121b0b73737ba103932b6b7bb329039b2b63360711b6044820152606401610df2565b6001600160a01b03166000908152602081905260409020805460ff19169055565b61107c614f67565b6000805b8281101561122b57600084848381811061109c5761109c615a69565b336000908152600260209081526040909120549102929092013592505081106110c55750611219565b3360009081526002602052604081208054839081106110e6576110e6615a69565b9060005260206000209060090201905060006111023384610c85565b9050816004015481101561111857505050611219565b600082600401548261112a9190615a3d565b90508060000361113d5750505050611219565b808360040160008282546111519190615a2a565b9091555061116190508187615a2a565b336000908152600f6020526040908190208151608081019092529197508061118c87620f4240615a2a565b8152602001838152602001600754426111a59190615a2a565b8152600080516020615ccf833981519152602091820152825460018082018555600094855293829020835160049092020190815590820151928101929092556040810151600283015560600151600390910180546001600160a01b0319166001600160a01b03909216919091179055505050505b8061122381615a50565b915050611080565b5080156112a557600080516020615ccf8339815191526000908152600a602052600080516020615c6f833981519152805483929061126a908490615a2a565b909155505060405181815233907f4e69fdc49495bcab2b4375781457ba16653a90eb4ffb6588351bdc39071433e29060200160405180910390a25b506112ae614f9d565b5050565b3360009081526020819052604090205460ff166112e15760405162461bcd60e51b8152600401610df290615aa9565b601655565b6001600160a01b0381166000908152600c60209081526040808320546018909252822054808211611318576000611322565b6113228183615a3d565b949350505050565b606080600083806001600160401b0381111561134857611348615ad1565b604051908082528060200260200182016040528015611371578160200160208202803683370190505b509350806001600160401b0381111561138c5761138c615ad1565b6040519080825280602002602001820160405280156113b5578160200160208202803683370190505b50925060005b818110156115125760008787838181106113d7576113d7615a69565b90506020020160208101906113ec91906152c2565b6001600160a01b038116600090815260066020526040902054875191925090819088908590811061141f5761141f615a69565b6020908102919091018101919091526001600160a01b03808416600090815260049283905260408082205490516302c68be360e31b815291939216916316345f189161146d918791016152e4565b602060405180830381865afa15801561148a573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114ae9190615ae7565b90506000670de0b6b3a76400006114c584846159f1565b6114cf9190615a08565b9050808886815181106114e4576114e4615a69565b60209081029190910101526114f98188615a2a565b965050505050808061150a90615a50565b9150506113bb565b50509250925092565b3360009081526020819052604090205460ff1661154a5760405162461bcd60e51b8152600401610df290615aa9565b61155e6001600160a01b0383163383614fae565b6040518181526001600160a01b038316903390600080516020615d0f8339815191529060200160405180910390a35050565b611598614f67565b6000805b336000908152600260205260409020548110156116a3573360009081526002602052604081208054839081106115d4576115d4615a69565b6000918252602090912060099091020160078101549091506001600160a01b03858116911614801561161257506007810154600160a01b900460ff16155b1561169057600061162333846127a5565b9050816003015481111561168e5760008260030154826116439190615a3d565b905061164f8186615a2a565b9450808360030160008282546116659190615a2a565b9091555050825460038401541061168c5760078301805460ff60a01b1916600160a01b1790555b505b505b508061169b81615a50565b91505061159c565b50600081116116c45760405162461bcd60e51b8152600401610df290615a7f565b33600090815260056020526040902054156117d1576001600160a01b0380831660009081526004602081905260408083205490516302c68be360e31b81529293670de0b6b3a764000093869392909116916316345f1891611727918991016152e4565b602060405180830381865afa158015611744573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117689190615ae7565b61177291906159f1565b61177c9190615a08565b3360009081526005602052604090205490915081106117aa57336000908152600560205260408120556117cf565b33600090815260056020526040812080548392906117c9908490615a3d565b90915550505b505b6001600160a01b038216600090815260066020526040812080548392906117f9908490615a3d565b9091555050336000908152600f60205260408082208151608081019092526009805491938392919061182a83615a50565b919050558152602001838152602001600754426118479190615a2a565b81526001600160a01b0385811660209283018190528454600180820187556000968752848720865160049093020191825585850151908201556040808601516002830155606090950151600390910180546001600160a01b03191691909316179091558352600a9052812080548392906118c2908490615a2a565b90915550506040513390600080516020615caf833981519152906118ea90849060009061544a565b60405180910390a2506118fb614f9d565b50565b6019818154811061190e57600080fd5b6000918252602090912060029091020180546001909101546001600160a01b03909116915082565b3360009081526020819052604090205460ff166119655760405162461bcd60e51b8152600401610df290615aa9565b601555565b6003602052816000526040600020818154811061198657600080fd5b600091825260209091206002909102018054600190910154909250905082565b3360009081526001602052604090205460ff166119d55760405162461bcd60e51b8152600401610df290615aa9565b828114611a1c5760405162461bcd60e51b8152602060048201526015602482015274082e4e4c2f240d8cadccee8d040dad2e6dac2e8c6d605b1b6044820152606401610df2565b82611a585760405162461bcd60e51b815260206004820152600c60248201526b456d7074792061727261797360a01b6044820152606401610df2565b60005b83811015611d0c576000858583818110611a7757611a77615a69565b9050602002016020810190611a8c91906152c2565b6001600160a01b031603611ab25760405162461bcd60e51b8152600401610df290615b00565b6000838383818110611ac657611ac6615a69565b9050602002013511611aea5760405162461bcd60e51b8152600401610df290615b29565b828282818110611afc57611afc615a69565b90506020020135600c6000878785818110611b1957611b19615a69565b9050602002016020810190611b2e91906152c2565b6001600160a01b03166001600160a01b0316815260200190815260200160002054601154611b5c9190615a3d565b611b669190615a2a565b601155600d6000868684818110611b7f57611b7f615a69565b9050602002016020810190611b9491906152c2565b6001600160a01b03166001600160a01b0316815260200190815260200160002054600003611c1b57828282818110611bce57611bce615a69565b90506020020135600d6000878785818110611beb57611beb615a69565b9050602002016020810190611c0091906152c2565b6001600160a01b031681526020810191909152604001600020555b828282818110611c2d57611c2d615a69565b90506020020135600c6000878785818110611c4a57611c4a615a69565b9050602002016020810190611c5f91906152c2565b6001600160a01b03168152602081019190915260400160002055848482818110611c8b57611c8b615a69565b9050602002016020810190611ca091906152c2565b6001600160a01b03167fec7e3594982826a1f90c8fc76513357b83a691b7f4e38b8be04f3d40f9b15839848484818110611cdc57611cdc615a69565b90506020020135604051611cf291815260200190565b60405180910390a280611d0481615a50565b915050611a5b565b5050505050565b611d1b614f67565b60008211611d5b5760405162461bcd60e51b815260206004820152600d60248201526c496e76616c69642076616c756560981b6044820152606401610df2565b60008111611d7b5760405162461bcd60e51b8152600401610df290615b51565b601554821015611dc35760405162461bcd60e51b815260206004820152601360248201527256616c75652062656c6f77206d696e696d756d60681b6044820152606401610df2565b6000611dce336112e6565b905080831115611df05760405162461bcd60e51b8152600401610df290615b7d565b60088054906000611e0083615a50565b90915550506008543360009081526018602052604081208054869290611e27908490615a2a565b909155505060408051608081018252858152602080820186815233838501818152426060860190815260008381526014865287812089825286528781209651875593516001808801919091559151600280880180546001600160a01b039384166001600160a01b03199182161790915592516003909801979097558751808901909852928752938601878152601980548084018255948190529651939095027f944998273e477b495144fb8794c914197f3ccb46be2900f4698fd0ef743c969581018054949093169390941692909217905591517f944998273e477b495144fb8794c914197f3ccb46be2900f4698fd0ef743c9696909101559054611f2c9190615a3d565b336000818152601a6020908152604080832086845290915290819020929092559051600080516020615cef83398151915290611f6b908690859061544a565b60405180910390a250506112ae614f9d565b60195460609081908190806001600160401b03811115611f9f57611f9f615ad1565b604051908082528060200260200182016040528015611fc8578160200160208202803683370190505b509350806001600160401b03811115611fe357611fe3615ad1565b60405190808252806020026020018201604052801561200c578160200160208202803683370190505b509250806001600160401b0381111561202757612027615ad1565b60405190808252806020026020018201604052801561206057816020015b61204d615204565b8152602001906001900390816120455790505b50915060005b8181101561219d5760006019828154811061208357612083615a69565b60009182526020918290206040805180820190915260029092020180546001600160a01b03168083526001909101549282019290925287519092508790849081106120d0576120d0615a69565b60200260200101906001600160a01b031690816001600160a01b031681525050806020015185838151811061210757612107615a69565b60209081029190910181019190915281516001600160a01b039081166000908152601483526040808220858501518352845290819020815160808101835281548152600182015494810194909452600281015490921690830152600301546060820152845185908490811061217e5761217e615a69565b602002602001018190525050808061219590615a50565b915050612066565b5050909192565b3360009081526020819052604090205460ff166121d35760405162461bcd60e51b8152600401610df290615aa9565b6001600160a01b03918216600090815260046020526040902080546001600160a01b03191691909216179055565b3360009081526020819052604090205460ff166122305760405162461bcd60e51b8152600401610df290615aa9565b6001600160a01b0381166122565760405162461bcd60e51b8152600401610df290615b00565b6001600160a01b03811660009081526020819052604090205460ff16156122af5760405162461bcd60e51b815260206004820152600d60248201526c20b63932b0b23c9037bbb732b960991b6044820152606401610df2565b6001600160a01b03166000908152602081905260409020805460ff19166001179055565b3360009081526001602052604090205460ff166123025760405162461bcd60e51b8152600401610df290615aa9565b60026000896001600160a01b03166001600160a01b031681526020019081526020016000206040518061014001604052808981526020018881526020018781526020016000815260200160008152602001848152602001838152602001866001600160a01b03168152602001600015158152602001858152509080600181540180825580915050600190039060005260206000209060090201600090919091909150600082015181600001556020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c0820151816006015560e08201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506101008201518160070160146101000a81548160ff0219169083151502179055506101208201518160080155505082600560008a6001600160a01b03166001600160a01b03168152602001908152602001600020600082825461247a9190615a2a565b90915550506001600160a01b038416600090815260066020526040812080548992906124a7908490615a2a565b90915550505050505050505050565b6001600160a01b0381166000908152600260209081526040808320805482518185028101850190935280835260609492939192909184015b8282101561259257600084815260209081902060408051610140810182526009860290920180548352600180820154848601526002820154928401929092526003810154606084015260048101546080840152600581015460a0840152600681015460c084015260078101546001600160a01b03811660e0850152600160a01b900460ff1615156101008401526008015461012083015290835290920191016124ee565b505050509050919050565b6001600160a01b038116600090815260026020526040812054815b8181101561276a576001600160a01b03841660009081526002602052604081208054839081106125ea576125ea615a69565b600091825260209182902060408051610140810182526009909302909101805483526001810154938301939093526002830154908201526003820154606082015260048201546080820152600582015460a0820152600682015460c082015260078201546001600160a01b03811660e083015260ff600160a01b909104161515610100820181905260089092015461012082015291506127575760e0810180516001600160a01b03908116600090815260046020819052604080832054945190516302c68be360e31b81529294909316926316345f18926126cb92016152e4565b602060405180830381865afa1580156126e8573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061270c9190615ae7565b90506000826060015183600001516127249190615a3d565b90506000670de0b6b3a764000061273b83856159f1565b6127459190615a08565b90506127518188615a2a565b96505050505b508061276281615a50565b9150506125b8565b5050919050565b3360009081526020819052604090205460ff166127a05760405162461bcd60e51b8152600401610df290615aa9565b601d55565b6001600160a01b03821660009081526002602052604081208054829190849081106127d2576127d2615a69565b9060005260206000209060090201905060008160060154426127f49190615a3d565b60078301549091506001600160a01b03166000805b6001600160a01b038316600090815260036020526040902054811015610db0576001600160a01b038316600090815260036020526040812080548390811061285357612853615a69565b60009182526020909120600290910201805460018201549192509081871061289d578754612710906128869083906159f1565b6128909190615a08565b61289a9086615a2a565b94505b505050806128aa90615a50565b9050612809565b60006128bb61500b565b805490915060ff600160401b82041615906001600160401b03166000811580156128e25750825b90506000826001600160401b031660011480156128fe5750303b155b90508115801561290c575080155b1561292a5760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b0319166001178555831561295357845460ff60401b1916600160401b1785555b61295b615034565b3360009081526020818152604082208054600160ff1991821681179092557f4cfa3c6903140891ecff713e237b1f1746362568dfb5e8e9bc37719e4f37e0c580548216831790557f3e1f9e6abae6b647fdc3c5896ef1871ceb3c98e9e72852da3f17d161a860f76c80548216831790557f3517cbb3edfcef55b3b32be7021bc73ef070f4a8bad0d4b389b96924cf17b7418054821683179055738a9281ecece9b599c2f42d829c3d0d8e74b7083e84527f968a1791ad31618c63b086103baa804af57c3ca0efa33a191010fbb7741579fc80549091169091179055600c905269021e19e0c9bab24000007f649cbac87391465cfe24c65b052f0edf3b8486f136e0b91262d76d1f34d7826b8190556011805491929091612a7c908490615a2a565b90915550506201fa4060075560646013558315611d0c57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050505050565b3360009081526020819052604090205460ff16612b085760405162461bcd60e51b8152600401610df290615aa9565b60008111612b665760405162461bcd60e51b815260206004820152602560248201527f4d61782070657263656e74616765206d75737420626520677265617465722074604482015264068616e20360dc1b6064820152608401610df2565b601355565b3360009081526020819052604090205460ff16612b9a5760405162461bcd60e51b8152600401610df290615aa9565b612bb3600080516020615ccf8339815191523383614fae565b604051818152600080516020615ccf833981519152903390600080516020615d0f8339815191529060200160405180910390a350565b3360009081526001602052604090205460ff16612c185760405162461bcd60e51b8152600401610df290615aa9565b612c2886868686868642426122d3565b505050505050565b33600090815260146020908152604080832084845290915290208054612c685760405162461bcd60e51b8152600401610df290615bad565b60028101546001600160a01b03163314612c945760405162461bcd60e51b8152600401610df290615bd8565b805460165460009061271090612caa90846159f1565b612cb49190615a08565b33600090815260186020526040812080549293508492909190612cd8908490615a3d565b90915550508015612d2157336000908152600c602052604081208054839290612d02908490615a3d565b925050819055508060116000828254612d1b9190615a3d565b90915550505b8015612d6b57336001600160a01b03167f4725a4d4de9bff212d0885095e27515072f73f427df55e52f37f241321ef88f98286604051612d6292919061544a565b60405180910390a25b336000818152601460209081526040808320888452825280832083815560018082018590556002820180546001600160a01b03191690556003909101849055938352601a82528083208884529091528120546019549092612dcb91615a3d565b9050808214612e8c57600060198281548110612de957612de9615a69565b60009182526020918290206040805180820190915260029092020180546001600160a01b03168252600101549181019190915260198054919250829185908110612e3557612e35615a69565b6000918252602080832084516002939093020180546001600160a01b0319166001600160a01b03938416178155938101516001909401939093558351168152601a8252604080822093830151825292909152208290555b6019805480612e9d57612e9d615c00565b600082815260208082206002600019949094019384020180546001600160a01b03191681556001018290559190925533808352601a825260408084208a855283528084209390935591518881527f73d12dec3eb3b445b6c9feb2fd559ba7c852c525bc1e59d8f7ff760c55df041d91015b60405180910390a2505050505050565b606081831115612f605760405162461bcd60e51b815260206004820152600d60248201526c496e76616c69642072616e676560981b6044820152606401610df2565b6010548210612fa75760405162461bcd60e51b8152602060048201526013602482015272115b9908195c1bd8da081b9bdd08199bdd5b99606a1b6044820152606401610df2565b6000612fb38484615a3d565b612fbe906001615a2a565b90506000816001600160401b03811115612fda57612fda615ad1565b60405190808252806020026020018201604052801561301357816020015b613000615235565b815260200190600190039081612ff85790505b50905060005b828110156130b457600b600061302f8389615a2a565b81526020019081526020016000206040518060c0016040529081600082015481526020016001820154815260200160028201548152602001600382015481526020016004820154815260200160058201548152505082828151811061309657613096615a69565b602002602001018190525080806130ac90615a50565b915050613019565b50949350505050565b6130c5614f67565b600081116130e55760405162461bcd60e51b8152600401610df290615b29565b60006012541161312e5760405162461bcd60e51b81526020600482015260146024820152734275796f7574206e6f7420617661696c61626c6560601b6044820152606401610df2565b6000613139336112e6565b90508082111561315b5760405162461bcd60e51b8152600401610df290615b7d565b61271061316a826109c46159f1565b6131749190615a08565b8210156131b45760405162461bcd60e51b815260206004820152600e60248201526d416d6f756e7420746f6f206c6f7760901b6044820152606401610df2565b6000612710601254846131c791906159f1565b6131d19190615a08565b336000908152600c60205260408120805492935085929091906131f5908490615a3d565b92505081905550826011600082825461320e9190615a3d565b9091555050336000908152601c602052604081208054839290613232908490615a2a565b90915550506008805490600061324783615a50565b9091555050336000908152600f60209081526040918290208251608081018452600854815291820184905260075490928201906132849042615a2a565b8152600080516020615ccf833981519152602091820181905283546001808201865560009586528386208551600490930201918255848401519082015560408401516002820155606090930151600390930180546001600160a01b0319166001600160a01b0390941693909317909255908252600a9052600080516020615c6f833981519152805483929061331a908490615a2a565b90915550506040518181523390600080516020615c8f8339815191529060200160405180910390a250506118fb614f9d565b601b818154811061335c57600080fd5b6000918252602090912060069091020180546001820154600283015460038401546004850154600590950154939550919390926001600160a01b03918216911686565b6133a7615204565b506001600160a01b03918216600090815260146020908152604080832093835292815290829020825160808101845281548152600182015492810192909252600281015490931691810191909152600390910154606082015290565b3360009081526020819052604090205460ff166134325760405162461bcd60e51b8152600401610df290615aa9565b6127108111156134845760405162461bcd60e51b815260206004820152601d60248201527f50657263656e746167652063616e6e6f742065786365656420313030250000006044820152606401610df2565b601255565b613491614f67565b3360009081526002602052604090205481106134e75760405162461bcd60e51b8152602060048201526015602482015274092dcecc2d8d2c840eccae6e8d2dcce40d2dcc8caf605b1b6044820152606401610df2565b33600090815260026020526040812080548390811061350857613508615a69565b906000526020600020906009020190508060070160149054906101000a900460ff161561356a5760405162461bcd60e51b815260206004820152601060248201526f56657374696e6720636f6d706c65746560801b6044820152606401610df2565b600061357633846127a5565b905081600301548110156135c35760405162461bcd60e51b8152602060048201526014602482015273125b9d985b1a590818db185a5b48185b5bdd5b9d60621b6044820152606401610df2565b60008260030154826135d59190615a3d565b9050600081116135f75760405162461bcd60e51b8152600401610df290615a7f565b8083600301600082825461360b9190615a2a565b909155505082546003840154106136325760078301805460ff60a01b1916600160a01b1790555b33600090815260056020526040902054156137435760078301546001600160a01b0390811660008181526004602081905260408083205490516302c68be360e31b81529294670de0b6b3a764000094879492909116926316345f18926136999291016152e4565b602060405180830381865afa1580156136b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906136da9190615ae7565b6136e491906159f1565b6136ee9190615a08565b33600090815260056020526040902054909150811061371c5733600090815260056020526040812055613741565b336000908152600560205260408120805483929061373b908490615a3d565b90915550505b505b60078301546001600160a01b03166000908152600660205260408120805483929061376f908490615a3d565b9091555050336000908152600f6020526040808220815160808101909252600980549193839291906137a083615a50565b919050558152602001838152602001600754426137bd9190615a2a565b81526007860180546001600160a01b039081166020938401528454600180820187556000968752848720865160049093020191825585850151908201556040808601516002830155606090950151600390910180546001600160a01b0319169183169190911790559054168352600a905281208054839290613840908490615a2a565b90915550506040513390600080516020615caf8339815191529061386890849060009061544a565b60405180910390a25050506118fb614f9d565b6138af604051806080016040528060008152602001600081526020016000815260200160006001600160a01b031681525090565b6001600160a01b0383166000908152600f60205260408120905b815481101561397457838282815481106138e5576138e5615a69565b906000526020600020906004020160000154036139625781818154811061390e5761390e615a69565b6000918252602091829020604080516080810182526004909302909101805483526001810154938301939093526002830154908201526003909101546001600160a01b031660608201529250610db7915050565b8061396c81615a50565b9150506138c9565b5060405162461bcd60e51b8152600401610df290615c16565b3360009081526020819052604090205460ff166139bc5760405162461bcd60e51b8152600401610df290615aa9565b6001600160a01b0383166139e25760405162461bcd60e51b8152600401610df290615c3f565b6000811180156139f457506127108111155b613a355760405162461bcd60e51b8152602060048201526012602482015271496e76616c69642070657263656e7461676560701b6044820152606401610df2565b6001600160a01b0383166000908152600360205260408120613a569161526b565b6000825b612710821015613af35782612710613a728285615a2a565b1115613a8757613a8483612710615a3d565b90505b6001600160a01b0386166000908152600360209081526040808320815180830190925285825281830185815281546001818101845592865293909420915160029093029091019182559151910155613adf8184615a2a565b9250613aeb8583615a2a565b915050613a5a565b6040516001600160a01b038616907fde4b6ccc38b84f88129403b65a309f9b1c41d4c316bc2118d7614e449b9d4c4590600090a25050505050565b613b36615235565b6010548210613b795760405162461bcd60e51b815260206004820152600f60248201526e115c1bd8da081b9bdd08199bdd5b99608a1b6044820152606401610df2565b506000908152600b6020908152604091829020825160c08101845281548152600182015492810192909252600281015492820192909252600382015460608201526004820154608082015260059091015460a082015290565b60026020528160005260406000208181548110613bee57600080fd5b6000918252602090912060099091020180546001820154600283015460038401546004850154600586015460068701546007880154600890980154969950949750929591949093916001600160a01b03811691600160a01b90910460ff16908a565b6001600160a01b038216600090815260026020526040812080546060928392909185908110613c8157613c81615a69565b600091825260208083206007600990930201918201546001600160a01b03168084526003909152604083205491935091816001600160401b03811115613cc957613cc9615ad1565b604051908082528060200260200182016040528015613cf2578160200160208202803683370190505b5090506000826001600160401b03811115613d0f57613d0f615ad1565b604051908082528060200260200182016040528015613d38578160200160208202803683370190505b50905060005b83811015613de5576001600160a01b0385166000908152600360205260408120805483908110613d7057613d70615a69565b9060005260206000209060020201905080600001548760060154613d949190615a2a565b848381518110613da657613da6615a69565b6020026020010181815250508060010154838381518110613dc957613dc9615a69565b602090810291909101015250613dde81615a50565b9050613d3e565b5090955093505050505b9250929050565b3360009081526020819052604090205460ff16613e255760405162461bcd60e51b8152600401610df290615aa9565b60008115613e5a57600082613e3c866127106159f1565b613e469190615a08565b9050601d54811115613e5857601d8190555b505b60105415613ea0576000600b60006001601054613e779190615a3d565b81526020019081526020016000209050613e9c85848360010154846002015488615044565b9150505b601354811115613eeb5760405162461bcd60e51b81526020600482015260166024820152750aadcd8dec6d640e0cae4c6cadce8c2ceca40d0d2ced60531b6044820152606401610df2565b6040805160c08101825286815260208082018781528284018681526060808501898152608086018881524260a08801908152601080546000908152600b89528a90209851895595516001890155935160028801559051600387015551600486015590516005909401939093555483518881529182018590529281018690527feadbedb993dfca23e4c79bf4fa5fe531c2e0e926258fabb8445e8bc5c472780f910160405180910390a260108054906000613fa483615a50565b91905055505050505050565b6000806000806000613fc186610beb565b90506000613fce876112e6565b6001600160a01b03979097166000908152600c6020908152604080832054600d9092529091205490989297965094509092505050565b6001600160a01b0381166000908152600f60209081526040808320805482518185028101850190935280835260609492939192909184015b8282101561259257600084815260209081902060408051608081018252600486029092018054835260018082015484860152600282015492840192909252600301546001600160a01b03166060830152908352909201910161403c565b600f60205281600052604060002081815481106140b557600080fd5b6000918252602090912060049091020180546001820154600283015460039093015491945092506001600160a01b031684565b3360009081526020819052604090205460ff166141175760405162461bcd60e51b8152600401610df290615aa9565b600755565b601b54606090831061416c5760405162461bcd60e51b8152602060048201526019602482015278537461727420696e646578206f7574206f6620626f756e647360381b6044820152606401610df2565b60006141788385615a2a565b601b5490915081111561418a5750601b545b60006141968583615a3d565b6001600160401b038111156141ad576141ad615ad1565b60405190808252806020026020018201604052801561422957816020015b6142166040518060c001604052806000815260200160008152602001600081526020016000815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b8152602001906001900390816141cb5790505b509050845b828110156130b457601b818154811061424957614249615a69565b60009182526020918290206040805160c0810182526006909302909101805483526001810154938301939093526002830154908201526003820154606082015260048201546001600160a01b03908116608083015260059092015490911660a0820152826142b78884615a3d565b815181106142c7576142c7615a69565b602002602001018190525080806142dd90615a50565b91505061422e565b6142ed614f67565b336000908152600f6020526040902080546143405760405162461bcd60e51b81526020600482015260136024820152724e6f207374616b657320617661696c61626c6560681b6044820152606401610df2565b816143855760405162461bcd60e51b8152602060048201526015602482015274139bc81cdd185ad94812511cc81c1c9bdd9a591959605a1b6044820152606401610df2565b60005b828110156145145760008484838181106143a4576143a4615a69565b9050602002013590506000805b84548110156144e15760008582815481106143ce576143ce615a69565b906000526020600020906004020190508381600001541480156143f5575060008160010154115b156144ce57806002015442101561443d5760405162461bcd60e51b815260206004820152600c60248201526b14dd185ad9481b1bd8dad95960a21b6044820152606401610df2565b60018101805460038301546000928390556001600160a01b0316808352600a60205260408320805492939192849290614477908490615a3d565b9091555061449190506001600160a01b0382163384614fae565b336001600160a01b0316600080516020615d2f83398151915283886040516144ba92919061544a565b60405180910390a2600194505050506144e1565b50806144d981615a50565b9150506143b1565b50806144ff5760405162461bcd60e51b8152600401610df290615c16565b5050808061450c90615a50565b915050614388565b50506112ae614f9d565b60608060008061452d856112e6565b6001600160a01b0386166000908152600e602052604081205460105492935091614558908390615a3d565b9050806000036145895750506040805160008082526020820181815282840190935290955090935091506146ef9050565b806001600160401b038111156145a1576145a1615ad1565b6040519080825280602002602001820160405280156145ca578160200160208202803683370190505b509550806001600160401b038111156145e5576145e5615ad1565b60405190808252806020026020018201604052801561460e578160200160208202803683370190505b50945060005b818110156146ea5760006146288285615a2a565b90508088838151811061463d5761463d615a69565b602090810291909101015284156146b6576000818152600b60205260408120600401546127109061466e90886159f1565b6146789190615a08565b90508088848151811061468d5761468d615a69565b60209081029190910101526146a28188615a2a565b96506146ae8187615a3d565b9550506146d7565b60008783815181106146ca576146ca615a69565b6020026020010181815250505b50806146e281615a50565b915050614614565b505050505b9193909250565b6146fe614f67565b6001600160a01b03821660009081526014602090815260408083208484529091529020805461473f5760405162461bcd60e51b8152600401610df290615bad565b336001600160a01b038416036147905760405162461bcd60e51b815260206004820152601660248201527543616e6e6f7420627579206f776e206c697374696e6760501b6044820152606401610df2565b80546001820154600383015460008284116147ac5760006147b6565b6147b68385615a3d565b90506000846147c7836127106159f1565b6147d19190615a08565b905060006127106147e283806159f1565b6147ec9190615a08565b905060006127106147fd83896159f1565b6148079190615a08565b905060006148158289615a3d565b9050614831600080516020615ccf833981519152338d8a6150f2565b6001600160a01b038b166000908152600c6020526040812080548a9290614859908490615a3d565b9091555050336000908152600c60205260408120805483929061487d908490615a2a565b90915550506001600160a01b038b16600090815260186020526040812080548a92906148aa908490615a3d565b9091555050336000908152600d6020526040812080548392906148ce908490615a2a565b9250508190555081601160008282546148e79190615a3d565b90915550506001600160a01b038b1660009081526017602052604081208054899290614914908490615a2a565b92505081905550601b6040518060c001604052808881526020014281526020018a81526020018981526020018d6001600160a01b03168152602001336001600160a01b031681525090806001815401808255809150506001900390600052602060002090600602016000909190919091506000820151816000015560208201518160010155604082015181600201556060820151816003015560808201518160040160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060a08201518160050160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505050601460008c6001600160a01b03166001600160a01b0316815260200190815260200160002060008b815260200190815260200160002060008082016000905560018201600090556002820160006101000a8154906001600160a01b030219169055600382016000905550506000601a60008d6001600160a01b03166001600160a01b0316815260200190815260200160002060008c815260200190815260200160002054905060006001601980549050614ac39190615a3d565b9050808214614b8457600060198281548110614ae157614ae1615a69565b60009182526020918290206040805180820190915260029092020180546001600160a01b03168252600101549181019190915260198054919250829185908110614b2d57614b2d615a69565b6000918252602080832084516002939093020180546001600160a01b0319166001600160a01b03938416178155938101516001909401939093558351168152601a8252604080822093830151825292909152208290555b6019805480614b9557614b95615c00565b6001900381819060005260206000209060020201600080820160006101000a8154906001600160a01b030219169055600182016000905550509055601a60008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008d815260200190815260200160002060009055336001600160a01b03168d6001600160a01b03167f7bb39d095b04a9986ed34adf14d74c33294d0a9e807f02bf634d532507422eba8b8f604051614c4f92919061544a565b60405180910390a350505050505050505050506112ae614f9d565b33600090815260146020908152604080832085845290915290208054614ca25760405162461bcd60e51b8152600401610df290615bad565b60028101546001600160a01b03163314614cce5760405162461bcd60e51b8152600401610df290615bd8565b60008211614cee5760405162461bcd60e51b8152600401610df290615b51565b600181018290556040513390600080516020615cef83398151915290614d17908590879061544a565b60405180910390a2505050565b3360009081526001602052604090205460ff16614d535760405162461bcd60e51b8152600401610df290615aa9565b6001600160a01b038516614da05760405162461bcd60e51b8152602060048201526014602482015273496e76616c69642075736572206164647265737360601b6044820152606401610df2565b60008411614dc05760405162461bcd60e51b8152600401610df290615b29565b6001600160a01b038216614de65760405162461bcd60e51b8152600401610df290615c3f565b60088054906000614df683615a50565b9190505550600081614e0a57600854614e1a565b600854614e1a90620f4240615a2a565b6001600160a01b038781166000908152600f6020908152604080832081516080810183528681528084018c81528184018c81528b881660608401818152855460018082018855968a52888a209551600490910290950194855592519484019490945551600283015551600390910180546001600160a01b0319169190961617909455928252600a905290812080549293508792909190614ebb908490615a2a565b92505081905550856001600160a01b0316600080516020615d2f8339815191528683604051612f0e92919061544a565b3360009081526020819052604090205460ff16614f1a5760405162461bcd60e51b8152600401610df290615aa9565b6001600160a01b038116614f405760405162461bcd60e51b8152600401610df290615b00565b6001600160a01b03166000908152600160208190526040909120805460ff19169091179055565b6000614f71615131565b805490915060011901614f9757604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b6000614fa7615131565b6001905550565b61500683846001600160a01b031663a9059cbb8585604051602401614fd4929190615431565b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050615155565b505050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610db7565b61503c6151bd565b610f816151e2565b6000821580615051575084155b1561505e575060006150e9565b60008561506d886127106159f1565b6150779190615a08565b9050600084615088876127106159f1565b6150929190615a08565b9050601d548210156150a9576000925050506150e9565b8082116150bb576000925050506150e9565b60006150c78284615a3d565b905060006127106150d887846159f1565b6150e29190615a08565b9450505050505b95945050505050565b6040516001600160a01b03848116602483015283811660448301526064820183905261512b9186918216906323b872dd90608401614fd4565b50505050565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f0090565b600080602060008451602086016000885af180615178576040513d6000823e3d81fd5b50506000513d9150811561519057806001141561519d565b6001600160a01b0384163b155b1561512b5783604051635274afe760e01b8152600401610df291906152e4565b6151c56151ea565b610f8157604051631afcd79f60e31b815260040160405180910390fd5b614f9d6151bd565b60006151f461500b565b54600160401b900460ff16919050565b6040518060800160405280600081526020016000815260200160006001600160a01b03168152602001600081525090565b6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b50805460008255600202906000526020600020908101906118fb91905b808211156152a25760008082556001820155600201615288565b5090565b80356001600160a01b03811681146152bd57600080fd5b919050565b6000602082840312156152d457600080fd5b6152dd826152a6565b9392505050565b6001600160a01b0391909116815260200190565b6000806040838503121561530b57600080fd5b615314836152a6565b946020939093013593505050565b60008083601f84011261533457600080fd5b5081356001600160401b0381111561534b57600080fd5b6020830191508360208260051b8501011115613def57600080fd5b6000806020838503121561537957600080fd5b82356001600160401b0381111561538f57600080fd5b61539b85828601615322565b90969095509350505050565b6000602082840312156153b957600080fd5b5035919050565b600081518084526020808501945080840160005b838110156153f0578151875295820195908201906001016153d4565b509495945050505050565b60608152600061540e60608301866153c0565b828103602084015261542081866153c0565b915050826040830152949350505050565b6001600160a01b03929092168252602082015260400190565b918252602082015260400190565b6000806000806040858703121561546e57600080fd5b84356001600160401b038082111561548557600080fd5b61549188838901615322565b909650945060208701359150808211156154aa57600080fd5b506154b787828801615322565b95989497509550505050565b600080604083850312156154d657600080fd5b50508035926020909101359150565b80518252602080820151908301526040808201516001600160a01b031690830152606090810151910152565b60608082528451908201819052600090608090818401906020808901855b838110156155545781516001600160a01b03168552938201939082019060010161552f565b50508583038187015261556783896153c0565b868103604088015287518082528289019450908201925060005b818110156155a4576155948486516154e5565b9382019392850192600101615581565b50919998505050505050505050565b600080604083850312156155c657600080fd5b6155cf836152a6565b91506155dd602084016152a6565b90509250929050565b600080600080600080600080610100898b03121561560357600080fd5b61560c896152a6565b975060208901359650604089013595506060890135945061562f60808a016152a6565b979a969950949793969560a0850135955060c08501359460e001359350915050565b602080825282518282018190526000919060409081850190868401855b828110156156f25781518051855286810151878601528581015186860152606080820151908601526080808201519086015260a0808201519086015260c0808201519086015260e0808201516001600160a01b031690860152610100808201511515908601526101209081015190850152610140909301929085019060010161566e565b5091979650505050505050565b60008060008060008060c0878903121561571857600080fd5b615721876152a6565b9550602087013594506040870135935060608701359250615744608088016152a6565b915060a087013590509295509295509295565b805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301525050565b6020808252825182820181905260009190848201906040850190845b818110156157d3576157c0838551615757565b9284019260c092909201916001016157ad565b50909695505050505050565b60808101610db782846154e5565b8051825260208082015190830152604080820151908301526060908101516001600160a01b0316910152565b60808101610db782846157ed565b60008060006060848603121561583c57600080fd5b615845846152a6565b95602085013595506040909401359392505050565b60c08101610db78284615757565b60408152600061587b60408301856153c0565b82810360208401526150e981856153c0565b600080600080608085870312156158a357600080fd5b5050823594602084013594506040840135936060013592509050565b6020808252825182820181905260009190848201906040850190845b818110156157d3576158ee8385516157ed565b92840192608092909201916001016158db565b602080825282518282018190526000919060409081850190868401855b828110156156f25781518051855286810151878601528581015186860152606080820151908601526080808201516001600160a01b039081169187019190915260a091820151169085015260c0909301929085019060010161591e565b600080600080600060a0868803121561599357600080fd5b61599c866152a6565b945060208601359350604086013592506159b8606087016152a6565b9150608086013580151581146159cd57600080fd5b809150509295509295909350565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610db757610db76159db565b600082615a2557634e487b7160e01b600052601260045260246000fd5b500490565b80820180821115610db757610db76159db565b81810381811115610db757610db76159db565b600060018201615a6257615a626159db565b5060010190565b634e487b7160e01b600052603260045260246000fd5b60208082526010908201526f4e6f7468696e6720746f20636c61696d60801b604082015260600190565b6020808252600e908201526d139bdd08185d5d1a1bdc9a5e995960921b604082015260600190565b634e487b7160e01b600052604160045260246000fd5b600060208284031215615af957600080fd5b5051919050565b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b6020808252600e908201526d125b9d985b1a5908185b5bdd5b9d60921b604082015260600190565b602080825260129082015271496e76616c69642073616c6520707269636560701b604082015260600190565b602080825260169082015275496e73756666696369656e74206e6574207374616b6560501b604082015260600190565b602080825260119082015270131a5cdd1a5b99c81b9bdd08199bdd5b99607a1b604082015260600190565b6020808252600e908201526d2737ba103a34329039b2b63632b960911b604082015260600190565b634e487b7160e01b600052603160045260246000fd5b6020808252600f908201526e14dd185ad9481b9bdd08199bdd5b99608a1b604082015260600190565b602080825260159082015274496e76616c696420746f6b656e206164647265737360581b60408201526060019056feb0775d57c3eeb070f58d410e36e9ff396813a5d76997d9a91bdf10f0798296f0a65a8b4f7f65a1063243d7f7e9e4da00ff767599acf21549ef2548a45d1695ae4a94c2c356e29a6583071e731bdacf2ca56565ba5efebcff6936eb7923b5172100000000000000000000000055d398326f99059ff775485246999027b31979558e79b7ba8dab5ebfa59b9c6af1743c3ef14863680b3cc5ac837f8d636f76031ca92ff919b850e4909ab2261d907ef955f11bc1716733a6cbece38d163a69af8a933735aa8de6d7547d0126171b2f31b9c34dd00f3ecd4be85a0ba047db4fafefa2646970667358221220c8b39bc364af48f03e5e506c54c206562f38e806902f8ca765430d8cbb08f93d64736f6c63430008140033", + "bytecode": "0x60806040523480156200001157600080fd5b506200001c62000022565b620000d6565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000735760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b0390811614620000d35780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6162d480620000e66000396000f3fe608060405234801561001057600080fd5b506004361061045e5760003560e01c80637e6d99261161024c578063bd84477d11610146578063d532bdfe116100c3578063ef5d9ae811610087578063ef5d9ae814610c9c578063f2bb563014610cbc578063fe2f50d014610ccf578063fee6018c14610cd8578063ffecf51614610ceb57600080fd5b8063d532bdfe14610c3a578063da1b436414610c4d578063e88f8e6614610c6d578063eacdc5ff14610c80578063eb44e0a314610c8957600080fd5b8063c6b61e4c1161010a578063c6b61e4c14610b3b578063c7b530b014610bab578063cc573a9114610bcb578063ce13d09014610c07578063cfcf331914610c1a57600080fd5b8063bd84477d14610a64578063bed9757e14610acc578063c0c07d1714610aed578063c267660314610b00578063c32d3ae214610b0857600080fd5b8063953d16bf116101d4578063aaf4b04d11610198578063aaf4b04d146109eb578063ac97b417146109fe578063b6c3dc4c14610a11578063b92a349f14610a31578063bc0bc6ba14610a4457600080fd5b8063953d16bf1461093957806396fd111a1461094c5780639cb6f5561461096c5780639f3a676c1461097f578063a0d46758146109cb57600080fd5b806382b756811161021b57806382b75681146108ca57806384e8520a146108dd578063853e0df2146108fd5780638939d787146109105780638f82818f1461091957600080fd5b80637e6d99261461087357806380259e691461089c57806380ca0ecf146108af5780638129fc1c146108c257600080fd5b806341e23c2a1161035d578063592d1dd1116102e55780636ef569a5116102a95780636ef569a5146108045780637065cb481461080d57806375060a0b146108205780637a0c6dc0146108405780637bc221ac1461086057600080fd5b8063592d1dd11461079f57806361d1080b146107bf57806362cd6a09146107c757806363624c83146107de57806367a74ddc146107f157600080fd5b80634a61f1e51161032c5780634a61f1e51461071e57806351e624721461073e57806351f6cf2f14610751578063549e61d314610779578063581162271461078c57600080fd5b806341e23c2a1461064b57806343a32f891461065e57806343c7c011146106d9578063441a4175146106ec57600080fd5b8063173825d9116103eb5780632e46fed8116103af5780632e46fed8146105c15780633ba8396e146105ea5780633c92f98d1461060d5780633e4bc6bc1461062f5780633f35e7221461063857600080fd5b8063173825d91461056c5780631764303d1461057f5780631aefa2d1146105925780631eb9e53e146105a55780632ded58aa146105b857600080fd5b8063092c761011610432578063092c7610146105065780630a84096a146105265780630a910a6d146105395780630c7d63861461054257806313baee5b1461054c57600080fd5b8062159da6146104635780630137451814610489578063022914a7146104ca5780630519da32146104fd575b600080fd5b610476610471366004615930565b610cfe565b6040519081526020015b60405180910390f35b6104b2610497366004615930565b6004602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610480565b6104ed6104d8366004615930565b60006020819052908152604090205460ff1681565b6040519015158152602001610480565b61047660075481565b610476610514366004615930565b60066020526000908152604090205481565b610476610534366004615952565b610d98565b61047660125481565b61054a610ed0565b005b61047661055a366004615930565b600c6020526000908152604090205481565b61054a61057a366004615930565b6110cf565b61054a61058d3660046159c0565b6111c0565b61054a6105a0366004615a01565b61142b565b6104766105b3366004615930565b61145f565b61047660115481565b6104766105cf366004615930565b6001600160a01b03166000908152601c602052604090205490565b6104ed6105f8366004615930565b60016020526000908152604090205460ff1681565b61062061061b3660046159c0565b6114a3565b60405161048093929190615a55565b610476601d5481565b61054a610646366004615952565b611691565b61054a610659366004615a8b565b611718565b6106aa61066c366004615952565b60146020908152600092835260408084209091529082529020805460018201546002830154600390930154919290916001600160a01b039091169084565b604051610480949392919093845260208401929092526001600160a01b03166040830152606082015260800190565b61054a6106e7366004615930565b611a22565b6106ff6106fa366004615a01565b611dab565b604080516001600160a01b039093168352602083019190915201610480565b61047661072c366004615930565b60186020526000908152604090205481565b61054a61074c366004615a01565b611de3565b61076461075f366004615952565b611e17565b60408051928352602083019190915201610480565b61054a610787366004615b17565b611e53565b61054a61079a366004615b82565b6121be565b6104766107ad366004615930565b60056020526000908152604090205481565b601b54610476565b6107cf612495565b60405161048093929190615ba4565b61054a6107ec366004615c6d565b6126e8565b61054a6107ff366004615ceb565b6128d6565b61047660165481565b61054a61081b366004615930565b612933565b61047661082e366004615930565b600a6020526000908152604090205481565b61085361084e366004615930565b612a05565b6040516104809190615d1e565b61047661086e366004615930565b612aec565b610476610881366004615930565b6001600160a01b031660009081526017602052604090205490565b61054a6108aa366004615a01565b612ccf565b6104766108bd366004615952565b612d03565b61054a612e0f565b61054a6108d8366004615a01565b612ff2565b6104766108eb366004615930565b600d6020526000908152604090205481565b61054a61090b366004615a01565b613084565b61047660135481565b610476610927366004615930565b60176020526000908152604090205481565b61054a610947366004615a01565b613120565b61095f61095a366004615b82565b613427565b6040516104809190615dcc565b61054a61097a366004615a01565b6135f7565b61099261098d366004615a01565b6138e8565b6040805196875260208701959095529385019290925260608401526001600160a01b0390811660808401521660a082015260c001610480565b6109de6109d9366004615952565b61393b565b6040516104809190615e4f565b61054a6109f9366004615a01565b6139cb565b61054a610a0c366004615a01565b613a51565b610a24610a1f366004615952565b613e5f565b6040516104809190615e83565b61054a610a3f366004615eb7565b613f93565b610a57610a52366004615a01565b61415c565b6040516104809190615eea565b610a77610a72366004615952565b614231565b604080519a8b5260208b0199909952978901969096526060880194909452608087019290925260a086015260c08501526001600160a01b031660e0840152151561010083015261012082015261014001610480565b610adf610ada366004615952565b6142af565b604051610480929190615f2d565b61054a610afb366004615f52565b614455565b6103e7610476565b610b1b610b16366004615930565b6145df565b604080519485526020850193909352918301526060820152608001610480565b610b7e610b49366004615a01565b600b60205260009081526040902080546001820154600283015460038401546004850154600590950154939492939192909186565b604080519687526020870195909552938501929092526060840152608083015260a082015260c001610480565b610bbe610bb9366004615930565b614633565b6040516104809190615f84565b610bde610bd9366004615952565b6146c8565b604080519485526020850193909352918301526001600160a01b03166060820152608001610480565b61054a610c15366004615a01565b614717565b610c2d610c28366004615b82565b61474b565b6040516104809190615fed565b61054a610c483660046159c0565b614918565b610476610c5b366004615930565b600e6020526000908152604090205481565b610620610c7b366004615930565b614b8c565b61047660105481565b61054a610c97366004615952565b614d64565b610476610caa366004615930565b601c6020526000908152604090205481565b61054a610cca366004615b82565b615320565b61047660155481565b61054a610ce6366004616067565b615431565b61054a610cf9366004615930565b615636565b600080610d0a8361145f565b6001600160a01b0384166000908152600e6020526040902054909150805b601054811015610d90578215610d7e576000818152600b602052604081206004015461271090610d5890866160dd565b610d6291906160f4565b9050610d6e8186616116565b9450610d7a8185616129565b9350505b80610d888161613c565b915050610d28565b505050919050565b6001600160a01b0382166000908152600260205260408120805482919084908110610dc557610dc5616155565b906000526020600020906009020190506000816006015442610de79190616129565b60078301549091506001600160a01b03166000805b6001600160a01b038316600090815260036020526040902054811015610ec3576001600160a01b0383166000908152600360205260408120805483908110610e4657610e46616155565b6000918252602082206002909102018054600182015460088a015492945090929091606490610e7790600a906160dd565b610e8191906160f4565b9050828810610eae57612710610e9783836160dd565b610ea191906160f4565b610eab9087616116565b95505b5050505080610ebc9061613c565b9050610dfc565b5093505050505b92915050565b610ed86156b2565b6000610ee333610cfe565b905060008111610f0e5760405162461bcd60e51b8152600401610f059061616b565b60405180910390fd5b336000908152600c602052604081208054839290610f2d908490616129565b925050819055508060116000828254610f469190616129565b9091555050601054336000908152600e6020908152604080832093909355601c90529081208054839290610f7b908490616116565b909155505060088054906000610f908361613c565b9091555050336000908152600f6020908152604091829020825160808101845260085481529182018490526007549092820190610fcd9042616116565b81527355d398326f99059ff775485246999027b3197955602091820181905283546001808201865560009586528386208551600490930201918255848401519082015560408401516002820155606090930151600390930180546001600160a01b0319166001600160a01b0390941693909317909255908252600a90527fb0775d57c3eeb070f58d410e36e9ff396813a5d76997d9a91bdf10f0798296f0805483929061107b908490616116565b909155505060405181815233907fa65a8b4f7f65a1063243d7f7e9e4da00ff767599acf21549ef2548a45d1695ae9060200160405180910390a2506110cd600160008051602061627f83398151915255565b565b3360009081526020819052604090205460ff166110fe5760405162461bcd60e51b8152600401610f0590616195565b6001600160a01b03811660009081526020819052604090205460ff166111525760405162461bcd60e51b81526020600482015260096024820152682737ba1037bbb732b960b91b6044820152606401610f05565b336001600160a01b0382160361119f5760405162461bcd60e51b815260206004820152601260248201527121b0b73737ba103932b6b7bb329039b2b63360711b6044820152606401610f05565b6001600160a01b03166000908152602081905260409020805460ff19169055565b6111c86156b2565b6000805b8281101561137d5760008484838181106111e8576111e8616155565b33600090815260026020908152604090912054910292909201359250508110611211575061136b565b33600090815260026020526040812080548390811061123257611232616155565b90600052602060002090600902019050600061124e3384610d98565b905081600401548110156112645750505061136b565b60008260040154826112769190616129565b905080600003611289575050505061136b565b8083600401600082825461129d9190616116565b909155506112ad90508187616116565b336000908152600f602052604090819020815160808101909252919750806112d887620f4240616116565b8152602001838152602001600754426112f19190616116565b81527355d398326f99059ff775485246999027b3197955602091820152825460018082018555600094855293829020835160049092020190815590820151928101929092556040810151600283015560600151600390910180546001600160a01b0319166001600160a01b03909216919091179055505050505b806113758161613c565b9150506111cc565b50801561140f577355d398326f99059ff775485246999027b31979556000908152600a6020527fb0775d57c3eeb070f58d410e36e9ff396813a5d76997d9a91bdf10f0798296f080548392906113d4908490616116565b909155505060405181815233907f4e69fdc49495bcab2b4375781457ba16653a90eb4ffb6588351bdc39071433e29060200160405180910390a25b50611427600160008051602061627f83398151915255565b5050565b3360009081526020819052604090205460ff1661145a5760405162461bcd60e51b8152600401610f0590616195565b601655565b6001600160a01b0381166000908152600c6020908152604080832054601890925282205480821161149157600061149b565b61149b8183616129565b949350505050565b606080600083806001600160401b038111156114c1576114c16161bd565b6040519080825280602002602001820160405280156114ea578160200160208202803683370190505b509350806001600160401b03811115611505576115056161bd565b60405190808252806020026020018201604052801561152e578160200160208202803683370190505b50925060005b8181101561168857600087878381811061155057611550616155565b90506020020160208101906115659190615930565b6001600160a01b038116600090815260066020526040902054875191925090819088908590811061159857611598616155565b6020908102919091018101919091526001600160a01b03838116600081815260049384905260408082205490516302c68be360e31b815294850192909252929116906316345f1890602401602060405180830381865afa158015611600573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061162491906161d3565b90506000670de0b6b3a764000061163b84846160dd565b61164591906160f4565b90508088868151811061165a5761165a616155565b602090810291909101015261166f8188616116565b96505050505080806116809061613c565b915050611534565b50509250925092565b3360009081526020819052604090205460ff166116c05760405162461bcd60e51b8152600401610f0590616195565b6116d46001600160a01b03831633836156fe565b6040518181526001600160a01b0383169033907fa92ff919b850e4909ab2261d907ef955f11bc1716733a6cbece38d163a69af8a9060200160405180910390a35050565b3360009081526001602052604090205460ff166117475760405162461bcd60e51b8152600401610f0590616195565b6001600160a01b038b166000908152600260205260409020548a106117ae5760405162461bcd60e51b815260206004820152601c60248201527f56657374696e6720696e64657820646f6573206e6f74206578697374000000006044820152606401610f05565b6001600160a01b038b16600090815260026020526040812080548c9081106117d8576117d8616155565b906000526020600020906009020190508060080154600560008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008282546118219190616129565b9091555050600381015481546118379190616129565b60078201546001600160a01b031660009081526006602052604081208054909190611863908490616129565b925050819055506040518061014001604052808b81526020018a8152602001898152602001848152602001838152602001868152602001858152602001886001600160a01b0316815260200160001515815260200187815250600260008e6001600160a01b03166001600160a01b031681526020019081526020016000208c815481106118f2576118f2616155565b9060005260206000209060090201600082015181600001556020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c0820151816006015560e08201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506101008201518160070160146101000a81548160ff021916908315150217905550610120820151816008015590505085600560008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008282546119d79190616116565b909155506119e79050838b616129565b6001600160a01b03881660009081526006602052604081208054909190611a0f908490616116565b9091555050505050505050505050505050565b611a2a6156b2565b6000805b33600090815260026020526040902054811015611b3557336000908152600260205260408120805483908110611a6657611a66616155565b6000918252602090912060099091020160078101549091506001600160a01b038581169116148015611aa457506007810154600160a01b900460ff16155b15611b22576000611ab53384612d03565b90508160030154811115611b20576000826003015482611ad59190616129565b9050611ae18186616116565b945080836003016000828254611af79190616116565b90915550508254600384015410611b1e5760078301805460ff60a01b1916600160a01b1790555b505b505b5080611b2d8161613c565b915050611a2e565b5060008111611b565760405162461bcd60e51b8152600401610f059061616b565b3360009081526005602052604090205415611c5f576001600160a01b0382811660008181526004602081905260408083205490516302c68be360e31b8152918201939093529092670de0b6b3a76400009285929116906316345f1890602401602060405180830381865afa158015611bd2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bf691906161d3565b611c0091906160dd565b611c0a91906160f4565b336000908152600560205260409020549091508110611c385733600090815260056020526040812055611c5d565b3360009081526005602052604081208054839290611c57908490616129565b90915550505b505b6001600160a01b03821660009081526006602052604081208054839290611c87908490616129565b9091555050336000908152600f602052604080822081516080810190925260098054919383929190611cb88361613c565b91905055815260200183815260200160075442611cd59190616116565b81526001600160a01b0385811660209283018190528454600180820187556000968752848720865160049093020191825585850151908201556040808601516002830155606090950151600390910180546001600160a01b03191691909316179091558352600a905281208054839290611d50908490616116565b9091555050604080518281526000602082015233917f4a94c2c356e29a6583071e731bdacf2ca56565ba5efebcff6936eb7923b51721910160405180910390a250611da8600160008051602061627f83398151915255565b50565b60198181548110611dbb57600080fd5b6000918252602090912060029091020180546001909101546001600160a01b03909116915082565b3360009081526020819052604090205460ff16611e125760405162461bcd60e51b8152600401610f0590616195565b601555565b60036020528160005260406000208181548110611e3357600080fd5b600091825260209091206002909102018054600190910154909250905082565b3360009081526001602052604090205460ff16611e825760405162461bcd60e51b8152600401610f0590616195565b828114611ec95760405162461bcd60e51b8152602060048201526015602482015274082e4e4c2f240d8cadccee8d040dad2e6dac2e8c6d605b1b6044820152606401610f05565b82611f055760405162461bcd60e51b815260206004820152600c60248201526b456d7074792061727261797360a01b6044820152606401610f05565b60005b838110156121b7576000858583818110611f2457611f24616155565b9050602002016020810190611f399190615930565b6001600160a01b031603611f5f5760405162461bcd60e51b8152600401610f05906161ec565b6000838383818110611f7357611f73616155565b9050602002013511611f975760405162461bcd60e51b8152600401610f0590616215565b828282818110611fa957611fa9616155565b90506020020135600c6000878785818110611fc657611fc6616155565b9050602002016020810190611fdb9190615930565b6001600160a01b03166001600160a01b03168152602001908152602001600020546011546120099190616129565b6120139190616116565b60115582828281811061202857612028616155565b90506020020135600d600087878581811061204557612045616155565b905060200201602081019061205a9190615930565b6001600160a01b03166001600160a01b0316815260200190815260200160002081905550601054600e600087878581811061209757612097616155565b90506020020160208101906120ac9190615930565b6001600160a01b031681526020810191909152604001600020558282828181106120d8576120d8616155565b90506020020135600c60008787858181106120f5576120f5616155565b905060200201602081019061210a9190615930565b6001600160a01b0316815260208101919091526040016000205584848281811061213657612136616155565b905060200201602081019061214b9190615930565b6001600160a01b03167fec7e3594982826a1f90c8fc76513357b83a691b7f4e38b8be04f3d40f9b1583984848481811061218757612187616155565b9050602002013560405161219d91815260200190565b60405180910390a2806121af8161613c565b915050611f08565b5050505050565b6121c66156b2565b600082116122065760405162461bcd60e51b815260206004820152600d60248201526c496e76616c69642076616c756560981b6044820152606401610f05565b6000811161224b5760405162461bcd60e51b8152602060048201526012602482015271496e76616c69642073616c6520707269636560701b6044820152606401610f05565b6015548210156122935760405162461bcd60e51b815260206004820152601360248201527256616c75652062656c6f77206d696e696d756d60681b6044820152606401610f05565b600061229e3361145f565b9050808311156122e95760405162461bcd60e51b8152602060048201526016602482015275496e73756666696369656e74206e6574207374616b6560501b6044820152606401610f05565b600880549060006122f98361613c565b90915550506008543360009081526018602052604081208054869290612320908490616116565b909155505060408051608081018252858152602080820186815233838501818152426060860190815260008381526014865287812089825286528781209651875593516001808801919091559151600280880180546001600160a01b039384166001600160a01b03199182161790915592516003909801979097558751808901909852928752938601878152601980548084018255948190529651939095027f944998273e477b495144fb8794c914197f3ccb46be2900f4698fd0ef743c969581018054949093169390941692909217905591517f944998273e477b495144fb8794c914197f3ccb46be2900f4698fd0ef743c96969091015590546124259190616129565b336000818152601a6020908152604080832086845282529182902093909355805186815292830184905290917f8e79b7ba8dab5ebfa59b9c6af1743c3ef14863680b3cc5ac837f8d636f76031c910160405180910390a25050611427600160008051602061627f83398151915255565b60195460609081908190806001600160401b038111156124b7576124b76161bd565b6040519080825280602002602001820160405280156124e0578160200160208202803683370190505b509350806001600160401b038111156124fb576124fb6161bd565b604051908082528060200260200182016040528015612524578160200160208202803683370190505b509250806001600160401b0381111561253f5761253f6161bd565b6040519080825280602002602001820160405280156125a457816020015b6125916040518060800160405280600081526020016000815260200160006001600160a01b03168152602001600081525090565b81526020019060019003908161255d5790505b50915060005b818110156126e1576000601982815481106125c7576125c7616155565b60009182526020918290206040805180820190915260029092020180546001600160a01b031680835260019091015492820192909252875190925087908490811061261457612614616155565b60200260200101906001600160a01b031690816001600160a01b031681525050806020015185838151811061264b5761264b616155565b60209081029190910181019190915281516001600160a01b03908116600090815260148352604080822085850151835284529081902081516080810183528154815260018201549481019490945260028101549092169083015260030154606082015284518590849081106126c2576126c2616155565b60200260200101819052505080806126d99061613c565b9150506125aa565b5050909192565b3360009081526001602052604090205460ff166127175760405162461bcd60e51b8152600401610f0590616195565b600260008b6001600160a01b03166001600160a01b031681526020019081526020016000206040518061014001604052808b81526020018a8152602001898152602001848152602001838152602001868152602001858152602001886001600160a01b03168152602001600015158152602001878152509080600181540180825580915050600190039060005260206000209060090201600090919091909150600082015181600001556020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c0820151816006015560e08201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506101008201518160070160146101000a81548160ff0219169083151502179055506101208201518160080155505084600560008c6001600160a01b03166001600160a01b03168152602001908152602001600020600082825461288d9190616116565b9091555061289d9050828a616129565b6001600160a01b038716600090815260066020526040812080549091906128c5908490616116565b909155505050505050505050505050565b3360009081526020819052604090205460ff166129055760405162461bcd60e51b8152600401610f0590616195565b6001600160a01b03918216600090815260046020526040902080546001600160a01b03191691909216179055565b3360009081526020819052604090205460ff166129625760405162461bcd60e51b8152600401610f0590616195565b6001600160a01b0381166129885760405162461bcd60e51b8152600401610f05906161ec565b6001600160a01b03811660009081526020819052604090205460ff16156129e15760405162461bcd60e51b815260206004820152600d60248201526c20b63932b0b23c9037bbb732b960991b6044820152606401610f05565b6001600160a01b03166000908152602081905260409020805460ff19166001179055565b6001600160a01b0381166000908152600260209081526040808320805482518185028101850190935280835260609492939192909184015b82821015612ae157600084815260209081902060408051610140810182526009860290920180548352600180820154848601526002820154928401929092526003810154606084015260048101546080840152600581015460a0840152600681015460c084015260078101546001600160a01b03811660e0850152600160a01b900460ff161515610100840152600801546101208301529083529092019101612a3d565b505050509050919050565b6001600160a01b038116600090815260026020526040812054815b81811015612cc8576001600160a01b0384166000908152600260205260408120805483908110612b3957612b39616155565b600091825260209182902060408051610140810182526009909302909101805483526001810154938301939093526002830154908201526003820154606082015260048201546080820152600582015460a0820152600682015460c082015260078201546001600160a01b03811660e083015260ff600160a01b90910416151561010082018190526008909201546101208201529150612cb55760e0810180516001600160a01b03908116600090815260046020819052604080832054945190516302c68be360e31b81529294909316926316345f1892612c2992016001600160a01b0391909116815260200190565b602060405180830381865afa158015612c46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c6a91906161d3565b9050600082606001518360000151612c829190616129565b90506000670de0b6b3a7640000612c9983856160dd565b612ca391906160f4565b9050612caf8188616116565b96505050505b5080612cc08161613c565b915050612b07565b5050919050565b3360009081526020819052604090205460ff16612cfe5760405162461bcd60e51b8152600401610f0590616195565b601d55565b6001600160a01b0382166000908152600260205260408120805482919084908110612d3057612d30616155565b906000526020600020906009020190506000816006015442612d529190616129565b60078301549091506001600160a01b03166000805b6001600160a01b038316600090815260036020526040902054811015610ec3576001600160a01b0383166000908152600360205260408120805483908110612db157612db1616155565b600091825260209091206002909102018054600182015491925090818710612dfb57875461271090612de49083906160dd565b612dee91906160f4565b612df89086616116565b94505b50505080612e089061613c565b9050612d67565b6000612e19615762565b805490915060ff600160401b82041615906001600160401b0316600081158015612e405750825b90506000826001600160401b03166001148015612e5c5750303b155b905081158015612e6a575080155b15612e885760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315612eb257845460ff60401b1916600160401b1785555b612eba61578b565b3360009081526020818152604082208054600160ff1991821681179092557f4cfa3c6903140891ecff713e237b1f1746362568dfb5e8e9bc37719e4f37e0c58054821683179055918190527f3e1f9e6abae6b647fdc3c5896ef1871ceb3c98e9e72852da3f17d161a860f76c80548316821790557f3517cbb3edfcef55b3b32be7021bc73ef070f4a8bad0d4b389b96924cf17b7418054831682179055738a9281ecece9b599c2f42d829c3d0d8e74b7083e9092527f968a1791ad31618c63b086103baa804af57c3ca0efa33a191010fbb7741579fc805490911690911790556203f480600755606460135583156121b757845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050505050565b3360009081526020819052604090205460ff166130215760405162461bcd60e51b8152600401610f0590616195565b6000811161307f5760405162461bcd60e51b815260206004820152602560248201527f4d61782070657263656e74616765206d75737420626520677265617465722074604482015264068616e20360dc1b6064820152608401610f05565b601355565b3360009081526020819052604090205460ff166130b35760405162461bcd60e51b8152600401610f0590616195565b6130d27355d398326f99059ff775485246999027b319795533836156fe565b6040518181527355d398326f99059ff775485246999027b31979559033907fa92ff919b850e4909ab2261d907ef955f11bc1716733a6cbece38d163a69af8a9060200160405180910390a350565b336000908152601460209081526040808320848452909152902080546131585760405162461bcd60e51b8152600401610f059061623d565b60028101546001600160a01b031633146131a55760405162461bcd60e51b815260206004820152600e60248201526d2737ba103a34329039b2b63632b960911b6044820152606401610f05565b8054601654600090612710906131bb90846160dd565b6131c591906160f4565b336000908152601860205260408120805492935084929091906131e9908490616129565b9091555050801561323257336000908152600c602052604081208054839290613213908490616129565b92505081905550806011600082825461322c9190616129565b90915550505b801561327457604080518281526020810186905233917f4725a4d4de9bff212d0885095e27515072f73f427df55e52f37f241321ef88f9910160405180910390a25b336000818152601460209081526040808320888452825280832083815560018082018590556002820180546001600160a01b03191690556003909101849055938352601a825280832088845290915281205460195490926132d491616129565b9050808214613395576000601982815481106132f2576132f2616155565b60009182526020918290206040805180820190915260029092020180546001600160a01b0316825260010154918101919091526019805491925082918590811061333e5761333e616155565b6000918252602080832084516002939093020180546001600160a01b0319166001600160a01b03938416178155938101516001909401939093558351168152601a8252604080822093830151825292909152208290555b60198054806133a6576133a6616268565b600082815260208082206002600019949094019384020180546001600160a01b03191681556001018290559190925533808352601a825260408084208a855283528084209390935591518881527f73d12dec3eb3b445b6c9feb2fd559ba7c852c525bc1e59d8f7ff760c55df041d91015b60405180910390a2505050505050565b6060818311156134695760405162461bcd60e51b815260206004820152600d60248201526c496e76616c69642072616e676560981b6044820152606401610f05565b60105482106134b05760405162461bcd60e51b8152602060048201526013602482015272115b9908195c1bd8da081b9bdd08199bdd5b99606a1b6044820152606401610f05565b60006134bc8484616129565b6134c7906001616116565b90506000816001600160401b038111156134e3576134e36161bd565b60405190808252806020026020018201604052801561354d57816020015b61353a6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8152602001906001900390816135015790505b50905060005b828110156135ee57600b60006135698389616116565b81526020019081526020016000206040518060c001604052908160008201548152602001600182015481526020016002820154815260200160038201548152602001600482015481526020016005820154815250508282815181106135d0576135d0616155565b602002602001018190525080806135e69061613c565b915050613553565b50949350505050565b6135ff6156b2565b6000811161361f5760405162461bcd60e51b8152600401610f0590616215565b6000601254116136685760405162461bcd60e51b81526020600482015260146024820152734275796f7574206e6f7420617661696c61626c6560601b6044820152606401610f05565b60006136733361145f565b9050808211156136be5760405162461bcd60e51b8152602060048201526016602482015275496e73756666696369656e74206e6574207374616b6560501b6044820152606401610f05565b6127106136cd826109c46160dd565b6136d791906160f4565b8210156137175760405162461bcd60e51b815260206004820152600e60248201526d416d6f756e7420746f6f206c6f7760901b6044820152606401610f05565b60006127106012548461372a91906160dd565b61373491906160f4565b336000908152600c6020526040812080549293508592909190613758908490616129565b9250508190555082601160008282546137719190616129565b9091555050336000908152601c602052604081208054839290613795908490616116565b9091555050600880549060006137aa8361613c565b9091555050336000908152600f60209081526040918290208251608081018452600854815291820184905260075490928201906137e79042616116565b81527355d398326f99059ff775485246999027b3197955602091820181905283546001808201865560009586528386208551600490930201918255848401519082015560408401516002820155606090930151600390930180546001600160a01b0319166001600160a01b0390941693909317909255908252600a90527fb0775d57c3eeb070f58d410e36e9ff396813a5d76997d9a91bdf10f0798296f08054839290613895908490616116565b909155505060405181815233907fa65a8b4f7f65a1063243d7f7e9e4da00ff767599acf21549ef2548a45d1695ae9060200160405180910390a25050611da8600160008051602061627f83398151915255565b601b81815481106138f857600080fd5b6000918252602090912060069091020180546001820154600283015460038401546004850154600590950154939550919390926001600160a01b03918216911686565b61396f6040518060800160405280600081526020016000815260200160006001600160a01b03168152602001600081525090565b506001600160a01b03918216600090815260146020908152604080832093835292815290829020825160808101845281548152600182015492810192909252600281015490931691810191909152600390910154606082015290565b3360009081526020819052604090205460ff166139fa5760405162461bcd60e51b8152600401610f0590616195565b612710811115613a4c5760405162461bcd60e51b815260206004820152601d60248201527f50657263656e746167652063616e6e6f742065786365656420313030250000006044820152606401610f05565b601255565b613a596156b2565b336000908152600260205260409020548110613aaf5760405162461bcd60e51b8152602060048201526015602482015274092dcecc2d8d2c840eccae6e8d2dcce40d2dcc8caf605b1b6044820152606401610f05565b336000908152600260205260408120805483908110613ad057613ad0616155565b906000526020600020906009020190508060070160149054906101000a900460ff1615613b325760405162461bcd60e51b815260206004820152601060248201526f56657374696e6720636f6d706c65746560801b6044820152606401610f05565b6000613b3e3384612d03565b90508160030154811015613b8b5760405162461bcd60e51b8152602060048201526014602482015273125b9d985b1a590818db185a5b48185b5bdd5b9d60621b6044820152606401610f05565b6000826003015482613b9d9190616129565b905060008111613bbf5760405162461bcd60e51b8152600401610f059061616b565b80836003016000828254613bd39190616116565b90915550508254600384015410613bfa5760078301805460ff60a01b1916600160a01b1790555b3360009081526005602052604090205415613d085760078301546001600160a01b0390811660008181526004602081905260408083205490516302c68be360e31b8152918201939093529092670de0b6b3a76400009285929116906316345f1890602401602060405180830381865afa158015613c7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c9f91906161d3565b613ca991906160dd565b613cb391906160f4565b336000908152600560205260409020549091508110613ce15733600090815260056020526040812055613d06565b3360009081526005602052604081208054839290613d00908490616129565b90915550505b505b60078301546001600160a01b031660009081526006602052604081208054839290613d34908490616129565b9091555050336000908152600f602052604080822081516080810190925260098054919383929190613d658361613c565b91905055815260200183815260200160075442613d829190616116565b81526007860180546001600160a01b039081166020938401528454600180820187556000968752848720865160049093020191825585850151908201556040808601516002830155606090950151600390910180546001600160a01b0319169183169190911790559054168352600a905281208054839290613e05908490616116565b9091555050604080518281526000602082015233917f4a94c2c356e29a6583071e731bdacf2ca56565ba5efebcff6936eb7923b51721910160405180910390a2505050611da8600160008051602061627f83398151915255565b613e93604051806080016040528060008152602001600081526020016000815260200160006001600160a01b031681525090565b6001600160a01b0383166000908152600f60205260408120905b8154811015613f585783828281548110613ec957613ec9616155565b90600052602060002090600402016000015403613f4657818181548110613ef257613ef2616155565b6000918252602091829020604080516080810182526004909302909101805483526001810154938301939093526002830154908201526003909101546001600160a01b031660608201529250610eca915050565b80613f508161613c565b915050613ead565b5060405162461bcd60e51b815260206004820152600f60248201526e14dd185ad9481b9bdd08199bdd5b99608a1b6044820152606401610f05565b3360009081526020819052604090205460ff16613fc25760405162461bcd60e51b8152600401610f0590616195565b6001600160a01b0383166140105760405162461bcd60e51b8152602060048201526015602482015274496e76616c696420746f6b656e206164647265737360581b6044820152606401610f05565b60008111801561402257506127108111155b6140635760405162461bcd60e51b8152602060048201526012602482015271496e76616c69642070657263656e7461676560701b6044820152606401610f05565b6001600160a01b0383166000908152600360205260408120614084916158d9565b6000825b61271082101561412157826127106140a08285616116565b11156140b5576140b283612710616129565b90505b6001600160a01b038616600090815260036020908152604080832081518083019092528582528183018581528154600181810184559286529390942091516002909302909101918255915191015561410d8184616116565b92506141198583616116565b915050614088565b6040516001600160a01b038616907fde4b6ccc38b84f88129403b65a309f9b1c41d4c316bc2118d7614e449b9d4c4590600090a25050505050565b6141956040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60105482106141d85760405162461bcd60e51b815260206004820152600f60248201526e115c1bd8da081b9bdd08199bdd5b99608a1b6044820152606401610f05565b506000908152600b6020908152604091829020825160c08101845281548152600182015492810192909252600281015492820192909252600382015460608201526004820154608082015260059091015460a082015290565b6002602052816000526040600020818154811061424d57600080fd5b6000918252602090912060099091020180546001820154600283015460038401546004850154600586015460068701546007880154600890980154969950949750929591949093916001600160a01b03811691600160a01b90910460ff16908a565b6001600160a01b0382166000908152600260205260408120805460609283929091859081106142e0576142e0616155565b600091825260208083206007600990930201918201546001600160a01b03168084526003909152604083205491935091816001600160401b03811115614328576143286161bd565b604051908082528060200260200182016040528015614351578160200160208202803683370190505b5090506000826001600160401b0381111561436e5761436e6161bd565b604051908082528060200260200182016040528015614397578160200160208202803683370190505b50905060005b83811015614444576001600160a01b03851660009081526003602052604081208054839081106143cf576143cf616155565b90600052602060002090600202019050806000015487600601546143f39190616116565b84838151811061440557614405616155565b602002602001018181525050806001015483838151811061442857614428616155565b60209081029190910101525061443d8161613c565b905061439d565b5090955093505050505b9250929050565b3360009081526020819052604090205460ff166144845760405162461bcd60e51b8152600401610f0590616195565b600081156144cf5760008261449b866127106160dd565b6144a591906160f4565b601054909150156144bd576144ba818561579b565b91505b601d548111156144cd57601d8190555b505b60135481111561451a5760405162461bcd60e51b81526020600482015260166024820152750aadcd8dec6d640e0cae4c6cadce8c2ceca40d0d2ced60531b6044820152606401610f05565b6040805160c08101825286815260208082018781528284018681526060808501898152608086018881524260a08801908152601080546000908152600b89528a90209851895595516001890155935160028801559051600387015551600486015590516005909401939093555483518881529182018590529281018690527feadbedb993dfca23e4c79bf4fa5fe531c2e0e926258fabb8445e8bc5c472780f910160405180910390a2601080549060006145d38361613c565b91905055505050505050565b60008060008060006145f086610cfe565b905060006145fd8761145f565b6001600160a01b03979097166000908152600c6020908152604080832054600d9092529091205490989297965094509092505050565b6001600160a01b0381166000908152600f60209081526040808320805482518185028101850190935280835260609492939192909184015b82821015612ae157600084815260209081902060408051608081018252600486029092018054835260018082015484860152600282015492840192909252600301546001600160a01b03166060830152908352909201910161466b565b600f60205281600052604060002081815481106146e457600080fd5b6000918252602090912060049091020180546001820154600283015460039093015491945092506001600160a01b031684565b3360009081526020819052604090205460ff166147465760405162461bcd60e51b8152600401610f0590616195565b600755565b601b54606090831061479f5760405162461bcd60e51b815260206004820152601960248201527f537461727420696e646578206f7574206f6620626f756e6473000000000000006044820152606401610f05565b60006147ab8385616116565b601b549091508111156147bd5750601b545b60006147c98583616129565b6001600160401b038111156147e0576147e06161bd565b60405190808252806020026020018201604052801561485c57816020015b6148496040518060c001604052806000815260200160008152602001600081526020016000815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b8152602001906001900390816147fe5790505b509050845b828110156135ee57601b818154811061487c5761487c616155565b60009182526020918290206040805160c0810182526006909302909101805483526001810154938301939093526002830154908201526003820154606082015260048201546001600160a01b03908116608083015260059092015490911660a0820152826148ea8884616129565b815181106148fa576148fa616155565b602002602001018190525080806149109061613c565b915050614861565b6149206156b2565b336000908152600f6020526040902080546149735760405162461bcd60e51b81526020600482015260136024820152724e6f207374616b657320617661696c61626c6560681b6044820152606401610f05565b816149b85760405162461bcd60e51b8152602060048201526015602482015274139bc81cdd185ad94812511cc81c1c9bdd9a591959605a1b6044820152606401610f05565b60005b82811015614b735760008484838181106149d7576149d7616155565b9050602002013590506000805b8454811015614b1e576000858281548110614a0157614a01616155565b90600052602060002090600402019050838160000154148015614a28575060008160010154115b15614b0b578060020154421015614a705760405162461bcd60e51b815260206004820152600c60248201526b14dd185ad9481b1bd8dad95960a21b6044820152606401610f05565b60018101805460038301546000928390556001600160a01b0316808352600a60205260408320805492939192849290614aaa908490616129565b90915550614ac490506001600160a01b03821633846156fe565b604080518381526020810188905233917f933735aa8de6d7547d0126171b2f31b9c34dd00f3ecd4be85a0ba047db4fafef910160405180910390a260019450505050614b1e565b5080614b168161613c565b9150506149e4565b5080614b5e5760405162461bcd60e51b815260206004820152600f60248201526e14dd185ad9481b9bdd08199bdd5b99608a1b6044820152606401610f05565b50508080614b6b9061613c565b9150506149bb565b5050611427600160008051602061627f83398151915255565b606080600080614b9b8561145f565b6001600160a01b0386166000908152600e602052604081205460105492935091614bc6908390616129565b905080600003614bf7575050604080516000808252602082018181528284019093529095509093509150614d5d9050565b806001600160401b03811115614c0f57614c0f6161bd565b604051908082528060200260200182016040528015614c38578160200160208202803683370190505b509550806001600160401b03811115614c5357614c536161bd565b604051908082528060200260200182016040528015614c7c578160200160208202803683370190505b50945060005b81811015614d58576000614c968285616116565b905080888381518110614cab57614cab616155565b60209081029190910101528415614d24576000818152600b602052604081206004015461271090614cdc90886160dd565b614ce691906160f4565b905080888481518110614cfb57614cfb616155565b6020908102919091010152614d108188616116565b9650614d1c8187616129565b955050614d45565b6000878381518110614d3857614d38616155565b6020026020010181815250505b5080614d508161613c565b915050614c82565b505050505b9193909250565b614d6c6156b2565b6001600160a01b038216600090815260146020908152604080832084845290915290208054614dad5760405162461bcd60e51b8152600401610f059061623d565b336001600160a01b03841603614dfe5760405162461bcd60e51b815260206004820152601660248201527543616e6e6f7420627579206f776e206c697374696e6760501b6044820152606401610f05565b8054600182015460038301546000828411614e1a576000614e24565b614e248385616129565b9050600084614e35836127106160dd565b614e3f91906160f4565b90506000612710614e5083806160dd565b614e5a91906160f4565b90506000612710614e6b83896160dd565b614e7591906160f4565b90506000614e838289616129565b9050614ea57355d398326f99059ff775485246999027b3197955338d8a6157e2565b336000908152600c60205260408120549003614ecf57601054336000908152600e60205260409020555b6001600160a01b038b166000908152600c6020526040812080548a9290614ef7908490616129565b9091555050336000908152600c602052604081208054839290614f1b908490616116565b90915550506001600160a01b038b16600090815260186020526040812080548a9290614f48908490616129565b9091555050336000908152600d602052604081208054839290614f6c908490616116565b925050819055508160116000828254614f859190616129565b90915550506001600160a01b038b1660009081526017602052604081208054899290614fb2908490616116565b92505081905550601b6040518060c001604052808881526020014281526020018a81526020018981526020018d6001600160a01b03168152602001336001600160a01b031681525090806001815401808255809150506001900390600052602060002090600602016000909190919091506000820151816000015560208201518160010155604082015181600201556060820151816003015560808201518160040160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060a08201518160050160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505050601460008c6001600160a01b03166001600160a01b0316815260200190815260200160002060008b815260200190815260200160002060008082016000905560018201600090556002820160006101000a8154906001600160a01b030219169055600382016000905550506000601a60008d6001600160a01b03166001600160a01b0316815260200190815260200160002060008c8152602001908152602001600020549050600060016019805490506151619190616129565b90508082146152225760006019828154811061517f5761517f616155565b60009182526020918290206040805180820190915260029092020180546001600160a01b031682526001015491810191909152601980549192508291859081106151cb576151cb616155565b6000918252602080832084516002939093020180546001600160a01b0319166001600160a01b03938416178155938101516001909401939093558351168152601a8252604080822093830151825292909152208290555b601980548061523357615233616268565b6001900381819060005260206000209060020201600080820160006101000a8154906001600160a01b030219169055600182016000905550509055601a60008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008d815260200190815260200160002060009055336001600160a01b03168d6001600160a01b03167f7bb39d095b04a9986ed34adf14d74c33294d0a9e807f02bf634d532507422eba8b8f6040516152f6929190918252602082015260400190565b60405180910390a35050505050505050505050611427600160008051602061627f83398151915255565b336000908152601460209081526040808320858452909152902080546153585760405162461bcd60e51b8152600401610f059061623d565b60028101546001600160a01b031633146153a55760405162461bcd60e51b815260206004820152600e60248201526d2737ba103a34329039b2b63632b960911b6044820152606401610f05565b600082116153ea5760405162461bcd60e51b8152602060048201526012602482015271496e76616c69642073616c6520707269636560701b6044820152606401610f05565b60018101829055604080518381526020810185905233917f8e79b7ba8dab5ebfa59b9c6af1743c3ef14863680b3cc5ac837f8d636f76031c910160405180910390a2505050565b3360009081526001602052604090205460ff166154605760405162461bcd60e51b8152600401610f0590616195565b6001600160a01b0385166154ad5760405162461bcd60e51b8152602060048201526014602482015273496e76616c69642075736572206164647265737360601b6044820152606401610f05565b600084116154cd5760405162461bcd60e51b8152600401610f0590616215565b6001600160a01b03821661551b5760405162461bcd60e51b8152602060048201526015602482015274496e76616c696420746f6b656e206164647265737360581b6044820152606401610f05565b6008805490600061552b8361613c565b919050555060008161553f5760085461554f565b60085461554f90620f4240616116565b6001600160a01b038781166000908152600f6020908152604080832081516080810183528681528084018c81528184018c81528b881660608401818152855460018082018855968a52888a209551600490910290950194855592519484019490945551600283015551600390910180546001600160a01b0319169190961617909455928252600a9052908120805492935087929091906155f0908490616116565b909155505060408051868152602081018390526001600160a01b038816917f933735aa8de6d7547d0126171b2f31b9c34dd00f3ecd4be85a0ba047db4fafef9101613417565b3360009081526020819052604090205460ff166156655760405162461bcd60e51b8152600401610f0590616195565b6001600160a01b03811661568b5760405162461bcd60e51b8152600401610f05906161ec565b6001600160a01b03166000908152600160208190526040909120805460ff19169091179055565b60008051602061627f8339815191528054600119016156e457604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b600160008051602061627f83398151915255565b6040516001600160a01b0383811660248301526044820183905261575d91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050615821565b505050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610eca565b615793615892565b6110cd6158b7565b6000601d5483116157ae57506000610eca565b6000601d54846157be9190616129565b905060006127106157cf85846160dd565b6157d991906160f4565b95945050505050565b6040516001600160a01b03848116602483015283811660448301526064820183905261581b9186918216906323b872dd9060840161572b565b50505050565b600080602060008451602086016000885af180615844576040513d6000823e3d81fd5b50506000513d9150811561585c578060011415615869565b6001600160a01b0384163b155b1561581b57604051635274afe760e01b81526001600160a01b0385166004820152602401610f05565b61589a6158bf565b6110cd57604051631afcd79f60e31b815260040160405180910390fd5b6156ea615892565b60006158c9615762565b54600160401b900460ff16919050565b5080546000825560020290600052602060002090810190611da891905b8082111561591057600080825560018201556002016158f6565b5090565b80356001600160a01b038116811461592b57600080fd5b919050565b60006020828403121561594257600080fd5b61594b82615914565b9392505050565b6000806040838503121561596557600080fd5b61596e83615914565b946020939093013593505050565b60008083601f84011261598e57600080fd5b5081356001600160401b038111156159a557600080fd5b6020830191508360208260051b850101111561444e57600080fd5b600080602083850312156159d357600080fd5b82356001600160401b038111156159e957600080fd5b6159f58582860161597c565b90969095509350505050565b600060208284031215615a1357600080fd5b5035919050565b600081518084526020808501945080840160005b83811015615a4a57815187529582019590820190600101615a2e565b509495945050505050565b606081526000615a686060830186615a1a565b8281036020840152615a7a8186615a1a565b915050826040830152949350505050565b60008060008060008060008060008060006101608c8e031215615aad57600080fd5b615ab68c615914565b9a5060208c0135995060408c0135985060608c0135975060808c01359650615ae060a08d01615914565b9a9d999c50979a9699959895975050505060c08401359360e081013593610100820135935061012082013592506101409091013590565b60008060008060408587031215615b2d57600080fd5b84356001600160401b0380821115615b4457600080fd5b615b508883890161597c565b90965094506020870135915080821115615b6957600080fd5b50615b768782880161597c565b95989497509550505050565b60008060408385031215615b9557600080fd5b50508035926020909101359150565b60608082528451908201819052600090608090818401906020808901855b83811015615be75781516001600160a01b031685529382019390820190600101615bc2565b505085830381870152615bfa8389615a1a565b868103604088015287518082528289019450908201925060005b81811015615c5e57615c4e84865180518252602080820151908301526040808201516001600160a01b031690830152606090810151910152565b9382019392850192600101615c14565b50919998505050505050505050565b6000806000806000806000806000806101408b8d031215615c8d57600080fd5b615c968b615914565b995060208b0135985060408b0135975060608b01359650615cb960808c01615914565b999c989b50969995989760a0870135975060c08701359660e08101359650610100810135955061012001359350915050565b60008060408385031215615cfe57600080fd5b615d0783615914565b9150615d1560208401615914565b90509250929050565b602080825282518282018190526000919060409081850190868401855b82811015615dbf5781518051855286810151878601528581015186860152606080820151908601526080808201519086015260a0808201519086015260c0808201519086015260e0808201516001600160a01b0316908601526101008082015115159086015261012090810151908501526101409093019290850190600101615d3b565b5091979650505050505050565b6020808252825182820181905260009190848201906040850190845b81811015615e4357615e30838551805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301525050565b9284019260c09290920191600101615de8565b50909695505050505050565b81518152602080830151908201526040808301516001600160a01b0316908201526060808301519082015260808101610eca565b8151815260208083015190820152604080830151908201526060808301516001600160a01b03169082015260808101610eca565b600080600060608486031215615ecc57600080fd5b615ed584615914565b95602085013595506040909401359392505050565b60c08101610eca8284805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301525050565b604081526000615f406040830185615a1a565b82810360208401526157d98185615a1a565b60008060008060808587031215615f6857600080fd5b5050823594602084013594506040840135936060013592509050565b6020808252825182820181905260009190848201906040850190845b81811015615e4357615fda8385518051825260208082015190830152604080820151908301526060908101516001600160a01b0316910152565b9284019260809290920191600101615fa0565b602080825282518282018190526000919060409081850190868401855b82811015615dbf5781518051855286810151878601528581015186860152606080820151908601526080808201516001600160a01b039081169187019190915260a091820151169085015260c0909301929085019060010161600a565b600080600080600060a0868803121561607f57600080fd5b61608886615914565b945060208601359350604086013592506160a460608701615914565b9150608086013580151581146160b957600080fd5b809150509295509295909350565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610eca57610eca6160c7565b60008261611157634e487b7160e01b600052601260045260246000fd5b500490565b80820180821115610eca57610eca6160c7565b81810381811115610eca57610eca6160c7565b60006001820161614e5761614e6160c7565b5060010190565b634e487b7160e01b600052603260045260246000fd5b60208082526010908201526f4e6f7468696e6720746f20636c61696d60801b604082015260600190565b6020808252600e908201526d139bdd08185d5d1a1bdc9a5e995960921b604082015260600190565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156161e557600080fd5b5051919050565b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b6020808252600e908201526d125b9d985b1a5908185b5bdd5b9d60921b604082015260600190565b602080825260119082015270131a5cdd1a5b99c81b9bdd08199bdd5b99607a1b604082015260600190565b634e487b7160e01b600052603160045260246000fdfe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a2646970667358221220752563a147e7fb2c015014ef7ff927f99464e7a121107233dab5dff98121b5f064736f6c63430008140033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061045e5760003560e01c80637e6d99261161024c578063bd84477d11610146578063d532bdfe116100c3578063ef5d9ae811610087578063ef5d9ae814610c9c578063f2bb563014610cbc578063fe2f50d014610ccf578063fee6018c14610cd8578063ffecf51614610ceb57600080fd5b8063d532bdfe14610c3a578063da1b436414610c4d578063e88f8e6614610c6d578063eacdc5ff14610c80578063eb44e0a314610c8957600080fd5b8063c6b61e4c1161010a578063c6b61e4c14610b3b578063c7b530b014610bab578063cc573a9114610bcb578063ce13d09014610c07578063cfcf331914610c1a57600080fd5b8063bd84477d14610a64578063bed9757e14610acc578063c0c07d1714610aed578063c267660314610b00578063c32d3ae214610b0857600080fd5b8063953d16bf116101d4578063aaf4b04d11610198578063aaf4b04d146109eb578063ac97b417146109fe578063b6c3dc4c14610a11578063b92a349f14610a31578063bc0bc6ba14610a4457600080fd5b8063953d16bf1461093957806396fd111a1461094c5780639cb6f5561461096c5780639f3a676c1461097f578063a0d46758146109cb57600080fd5b806382b756811161021b57806382b75681146108ca57806384e8520a146108dd578063853e0df2146108fd5780638939d787146109105780638f82818f1461091957600080fd5b80637e6d99261461087357806380259e691461089c57806380ca0ecf146108af5780638129fc1c146108c257600080fd5b806341e23c2a1161035d578063592d1dd1116102e55780636ef569a5116102a95780636ef569a5146108045780637065cb481461080d57806375060a0b146108205780637a0c6dc0146108405780637bc221ac1461086057600080fd5b8063592d1dd11461079f57806361d1080b146107bf57806362cd6a09146107c757806363624c83146107de57806367a74ddc146107f157600080fd5b80634a61f1e51161032c5780634a61f1e51461071e57806351e624721461073e57806351f6cf2f14610751578063549e61d314610779578063581162271461078c57600080fd5b806341e23c2a1461064b57806343a32f891461065e57806343c7c011146106d9578063441a4175146106ec57600080fd5b8063173825d9116103eb5780632e46fed8116103af5780632e46fed8146105c15780633ba8396e146105ea5780633c92f98d1461060d5780633e4bc6bc1461062f5780633f35e7221461063857600080fd5b8063173825d91461056c5780631764303d1461057f5780631aefa2d1146105925780631eb9e53e146105a55780632ded58aa146105b857600080fd5b8063092c761011610432578063092c7610146105065780630a84096a146105265780630a910a6d146105395780630c7d63861461054257806313baee5b1461054c57600080fd5b8062159da6146104635780630137451814610489578063022914a7146104ca5780630519da32146104fd575b600080fd5b610476610471366004615930565b610cfe565b6040519081526020015b60405180910390f35b6104b2610497366004615930565b6004602052600090815260409020546001600160a01b031681565b6040516001600160a01b039091168152602001610480565b6104ed6104d8366004615930565b60006020819052908152604090205460ff1681565b6040519015158152602001610480565b61047660075481565b610476610514366004615930565b60066020526000908152604090205481565b610476610534366004615952565b610d98565b61047660125481565b61054a610ed0565b005b61047661055a366004615930565b600c6020526000908152604090205481565b61054a61057a366004615930565b6110cf565b61054a61058d3660046159c0565b6111c0565b61054a6105a0366004615a01565b61142b565b6104766105b3366004615930565b61145f565b61047660115481565b6104766105cf366004615930565b6001600160a01b03166000908152601c602052604090205490565b6104ed6105f8366004615930565b60016020526000908152604090205460ff1681565b61062061061b3660046159c0565b6114a3565b60405161048093929190615a55565b610476601d5481565b61054a610646366004615952565b611691565b61054a610659366004615a8b565b611718565b6106aa61066c366004615952565b60146020908152600092835260408084209091529082529020805460018201546002830154600390930154919290916001600160a01b039091169084565b604051610480949392919093845260208401929092526001600160a01b03166040830152606082015260800190565b61054a6106e7366004615930565b611a22565b6106ff6106fa366004615a01565b611dab565b604080516001600160a01b039093168352602083019190915201610480565b61047661072c366004615930565b60186020526000908152604090205481565b61054a61074c366004615a01565b611de3565b61076461075f366004615952565b611e17565b60408051928352602083019190915201610480565b61054a610787366004615b17565b611e53565b61054a61079a366004615b82565b6121be565b6104766107ad366004615930565b60056020526000908152604090205481565b601b54610476565b6107cf612495565b60405161048093929190615ba4565b61054a6107ec366004615c6d565b6126e8565b61054a6107ff366004615ceb565b6128d6565b61047660165481565b61054a61081b366004615930565b612933565b61047661082e366004615930565b600a6020526000908152604090205481565b61085361084e366004615930565b612a05565b6040516104809190615d1e565b61047661086e366004615930565b612aec565b610476610881366004615930565b6001600160a01b031660009081526017602052604090205490565b61054a6108aa366004615a01565b612ccf565b6104766108bd366004615952565b612d03565b61054a612e0f565b61054a6108d8366004615a01565b612ff2565b6104766108eb366004615930565b600d6020526000908152604090205481565b61054a61090b366004615a01565b613084565b61047660135481565b610476610927366004615930565b60176020526000908152604090205481565b61054a610947366004615a01565b613120565b61095f61095a366004615b82565b613427565b6040516104809190615dcc565b61054a61097a366004615a01565b6135f7565b61099261098d366004615a01565b6138e8565b6040805196875260208701959095529385019290925260608401526001600160a01b0390811660808401521660a082015260c001610480565b6109de6109d9366004615952565b61393b565b6040516104809190615e4f565b61054a6109f9366004615a01565b6139cb565b61054a610a0c366004615a01565b613a51565b610a24610a1f366004615952565b613e5f565b6040516104809190615e83565b61054a610a3f366004615eb7565b613f93565b610a57610a52366004615a01565b61415c565b6040516104809190615eea565b610a77610a72366004615952565b614231565b604080519a8b5260208b0199909952978901969096526060880194909452608087019290925260a086015260c08501526001600160a01b031660e0840152151561010083015261012082015261014001610480565b610adf610ada366004615952565b6142af565b604051610480929190615f2d565b61054a610afb366004615f52565b614455565b6103e7610476565b610b1b610b16366004615930565b6145df565b604080519485526020850193909352918301526060820152608001610480565b610b7e610b49366004615a01565b600b60205260009081526040902080546001820154600283015460038401546004850154600590950154939492939192909186565b604080519687526020870195909552938501929092526060840152608083015260a082015260c001610480565b610bbe610bb9366004615930565b614633565b6040516104809190615f84565b610bde610bd9366004615952565b6146c8565b604080519485526020850193909352918301526001600160a01b03166060820152608001610480565b61054a610c15366004615a01565b614717565b610c2d610c28366004615b82565b61474b565b6040516104809190615fed565b61054a610c483660046159c0565b614918565b610476610c5b366004615930565b600e6020526000908152604090205481565b610620610c7b366004615930565b614b8c565b61047660105481565b61054a610c97366004615952565b614d64565b610476610caa366004615930565b601c6020526000908152604090205481565b61054a610cca366004615b82565b615320565b61047660155481565b61054a610ce6366004616067565b615431565b61054a610cf9366004615930565b615636565b600080610d0a8361145f565b6001600160a01b0384166000908152600e6020526040902054909150805b601054811015610d90578215610d7e576000818152600b602052604081206004015461271090610d5890866160dd565b610d6291906160f4565b9050610d6e8186616116565b9450610d7a8185616129565b9350505b80610d888161613c565b915050610d28565b505050919050565b6001600160a01b0382166000908152600260205260408120805482919084908110610dc557610dc5616155565b906000526020600020906009020190506000816006015442610de79190616129565b60078301549091506001600160a01b03166000805b6001600160a01b038316600090815260036020526040902054811015610ec3576001600160a01b0383166000908152600360205260408120805483908110610e4657610e46616155565b6000918252602082206002909102018054600182015460088a015492945090929091606490610e7790600a906160dd565b610e8191906160f4565b9050828810610eae57612710610e9783836160dd565b610ea191906160f4565b610eab9087616116565b95505b5050505080610ebc9061613c565b9050610dfc565b5093505050505b92915050565b610ed86156b2565b6000610ee333610cfe565b905060008111610f0e5760405162461bcd60e51b8152600401610f059061616b565b60405180910390fd5b336000908152600c602052604081208054839290610f2d908490616129565b925050819055508060116000828254610f469190616129565b9091555050601054336000908152600e6020908152604080832093909355601c90529081208054839290610f7b908490616116565b909155505060088054906000610f908361613c565b9091555050336000908152600f6020908152604091829020825160808101845260085481529182018490526007549092820190610fcd9042616116565b81527355d398326f99059ff775485246999027b3197955602091820181905283546001808201865560009586528386208551600490930201918255848401519082015560408401516002820155606090930151600390930180546001600160a01b0319166001600160a01b0390941693909317909255908252600a90527fb0775d57c3eeb070f58d410e36e9ff396813a5d76997d9a91bdf10f0798296f0805483929061107b908490616116565b909155505060405181815233907fa65a8b4f7f65a1063243d7f7e9e4da00ff767599acf21549ef2548a45d1695ae9060200160405180910390a2506110cd600160008051602061627f83398151915255565b565b3360009081526020819052604090205460ff166110fe5760405162461bcd60e51b8152600401610f0590616195565b6001600160a01b03811660009081526020819052604090205460ff166111525760405162461bcd60e51b81526020600482015260096024820152682737ba1037bbb732b960b91b6044820152606401610f05565b336001600160a01b0382160361119f5760405162461bcd60e51b815260206004820152601260248201527121b0b73737ba103932b6b7bb329039b2b63360711b6044820152606401610f05565b6001600160a01b03166000908152602081905260409020805460ff19169055565b6111c86156b2565b6000805b8281101561137d5760008484838181106111e8576111e8616155565b33600090815260026020908152604090912054910292909201359250508110611211575061136b565b33600090815260026020526040812080548390811061123257611232616155565b90600052602060002090600902019050600061124e3384610d98565b905081600401548110156112645750505061136b565b60008260040154826112769190616129565b905080600003611289575050505061136b565b8083600401600082825461129d9190616116565b909155506112ad90508187616116565b336000908152600f602052604090819020815160808101909252919750806112d887620f4240616116565b8152602001838152602001600754426112f19190616116565b81527355d398326f99059ff775485246999027b3197955602091820152825460018082018555600094855293829020835160049092020190815590820151928101929092556040810151600283015560600151600390910180546001600160a01b0319166001600160a01b03909216919091179055505050505b806113758161613c565b9150506111cc565b50801561140f577355d398326f99059ff775485246999027b31979556000908152600a6020527fb0775d57c3eeb070f58d410e36e9ff396813a5d76997d9a91bdf10f0798296f080548392906113d4908490616116565b909155505060405181815233907f4e69fdc49495bcab2b4375781457ba16653a90eb4ffb6588351bdc39071433e29060200160405180910390a25b50611427600160008051602061627f83398151915255565b5050565b3360009081526020819052604090205460ff1661145a5760405162461bcd60e51b8152600401610f0590616195565b601655565b6001600160a01b0381166000908152600c6020908152604080832054601890925282205480821161149157600061149b565b61149b8183616129565b949350505050565b606080600083806001600160401b038111156114c1576114c16161bd565b6040519080825280602002602001820160405280156114ea578160200160208202803683370190505b509350806001600160401b03811115611505576115056161bd565b60405190808252806020026020018201604052801561152e578160200160208202803683370190505b50925060005b8181101561168857600087878381811061155057611550616155565b90506020020160208101906115659190615930565b6001600160a01b038116600090815260066020526040902054875191925090819088908590811061159857611598616155565b6020908102919091018101919091526001600160a01b03838116600081815260049384905260408082205490516302c68be360e31b815294850192909252929116906316345f1890602401602060405180830381865afa158015611600573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061162491906161d3565b90506000670de0b6b3a764000061163b84846160dd565b61164591906160f4565b90508088868151811061165a5761165a616155565b602090810291909101015261166f8188616116565b96505050505080806116809061613c565b915050611534565b50509250925092565b3360009081526020819052604090205460ff166116c05760405162461bcd60e51b8152600401610f0590616195565b6116d46001600160a01b03831633836156fe565b6040518181526001600160a01b0383169033907fa92ff919b850e4909ab2261d907ef955f11bc1716733a6cbece38d163a69af8a9060200160405180910390a35050565b3360009081526001602052604090205460ff166117475760405162461bcd60e51b8152600401610f0590616195565b6001600160a01b038b166000908152600260205260409020548a106117ae5760405162461bcd60e51b815260206004820152601c60248201527f56657374696e6720696e64657820646f6573206e6f74206578697374000000006044820152606401610f05565b6001600160a01b038b16600090815260026020526040812080548c9081106117d8576117d8616155565b906000526020600020906009020190508060080154600560008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008282546118219190616129565b9091555050600381015481546118379190616129565b60078201546001600160a01b031660009081526006602052604081208054909190611863908490616129565b925050819055506040518061014001604052808b81526020018a8152602001898152602001848152602001838152602001868152602001858152602001886001600160a01b0316815260200160001515815260200187815250600260008e6001600160a01b03166001600160a01b031681526020019081526020016000208c815481106118f2576118f2616155565b9060005260206000209060090201600082015181600001556020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c0820151816006015560e08201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506101008201518160070160146101000a81548160ff021916908315150217905550610120820151816008015590505085600560008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008282546119d79190616116565b909155506119e79050838b616129565b6001600160a01b03881660009081526006602052604081208054909190611a0f908490616116565b9091555050505050505050505050505050565b611a2a6156b2565b6000805b33600090815260026020526040902054811015611b3557336000908152600260205260408120805483908110611a6657611a66616155565b6000918252602090912060099091020160078101549091506001600160a01b038581169116148015611aa457506007810154600160a01b900460ff16155b15611b22576000611ab53384612d03565b90508160030154811115611b20576000826003015482611ad59190616129565b9050611ae18186616116565b945080836003016000828254611af79190616116565b90915550508254600384015410611b1e5760078301805460ff60a01b1916600160a01b1790555b505b505b5080611b2d8161613c565b915050611a2e565b5060008111611b565760405162461bcd60e51b8152600401610f059061616b565b3360009081526005602052604090205415611c5f576001600160a01b0382811660008181526004602081905260408083205490516302c68be360e31b8152918201939093529092670de0b6b3a76400009285929116906316345f1890602401602060405180830381865afa158015611bd2573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611bf691906161d3565b611c0091906160dd565b611c0a91906160f4565b336000908152600560205260409020549091508110611c385733600090815260056020526040812055611c5d565b3360009081526005602052604081208054839290611c57908490616129565b90915550505b505b6001600160a01b03821660009081526006602052604081208054839290611c87908490616129565b9091555050336000908152600f602052604080822081516080810190925260098054919383929190611cb88361613c565b91905055815260200183815260200160075442611cd59190616116565b81526001600160a01b0385811660209283018190528454600180820187556000968752848720865160049093020191825585850151908201556040808601516002830155606090950151600390910180546001600160a01b03191691909316179091558352600a905281208054839290611d50908490616116565b9091555050604080518281526000602082015233917f4a94c2c356e29a6583071e731bdacf2ca56565ba5efebcff6936eb7923b51721910160405180910390a250611da8600160008051602061627f83398151915255565b50565b60198181548110611dbb57600080fd5b6000918252602090912060029091020180546001909101546001600160a01b03909116915082565b3360009081526020819052604090205460ff16611e125760405162461bcd60e51b8152600401610f0590616195565b601555565b60036020528160005260406000208181548110611e3357600080fd5b600091825260209091206002909102018054600190910154909250905082565b3360009081526001602052604090205460ff16611e825760405162461bcd60e51b8152600401610f0590616195565b828114611ec95760405162461bcd60e51b8152602060048201526015602482015274082e4e4c2f240d8cadccee8d040dad2e6dac2e8c6d605b1b6044820152606401610f05565b82611f055760405162461bcd60e51b815260206004820152600c60248201526b456d7074792061727261797360a01b6044820152606401610f05565b60005b838110156121b7576000858583818110611f2457611f24616155565b9050602002016020810190611f399190615930565b6001600160a01b031603611f5f5760405162461bcd60e51b8152600401610f05906161ec565b6000838383818110611f7357611f73616155565b9050602002013511611f975760405162461bcd60e51b8152600401610f0590616215565b828282818110611fa957611fa9616155565b90506020020135600c6000878785818110611fc657611fc6616155565b9050602002016020810190611fdb9190615930565b6001600160a01b03166001600160a01b03168152602001908152602001600020546011546120099190616129565b6120139190616116565b60115582828281811061202857612028616155565b90506020020135600d600087878581811061204557612045616155565b905060200201602081019061205a9190615930565b6001600160a01b03166001600160a01b0316815260200190815260200160002081905550601054600e600087878581811061209757612097616155565b90506020020160208101906120ac9190615930565b6001600160a01b031681526020810191909152604001600020558282828181106120d8576120d8616155565b90506020020135600c60008787858181106120f5576120f5616155565b905060200201602081019061210a9190615930565b6001600160a01b0316815260208101919091526040016000205584848281811061213657612136616155565b905060200201602081019061214b9190615930565b6001600160a01b03167fec7e3594982826a1f90c8fc76513357b83a691b7f4e38b8be04f3d40f9b1583984848481811061218757612187616155565b9050602002013560405161219d91815260200190565b60405180910390a2806121af8161613c565b915050611f08565b5050505050565b6121c66156b2565b600082116122065760405162461bcd60e51b815260206004820152600d60248201526c496e76616c69642076616c756560981b6044820152606401610f05565b6000811161224b5760405162461bcd60e51b8152602060048201526012602482015271496e76616c69642073616c6520707269636560701b6044820152606401610f05565b6015548210156122935760405162461bcd60e51b815260206004820152601360248201527256616c75652062656c6f77206d696e696d756d60681b6044820152606401610f05565b600061229e3361145f565b9050808311156122e95760405162461bcd60e51b8152602060048201526016602482015275496e73756666696369656e74206e6574207374616b6560501b6044820152606401610f05565b600880549060006122f98361613c565b90915550506008543360009081526018602052604081208054869290612320908490616116565b909155505060408051608081018252858152602080820186815233838501818152426060860190815260008381526014865287812089825286528781209651875593516001808801919091559151600280880180546001600160a01b039384166001600160a01b03199182161790915592516003909801979097558751808901909852928752938601878152601980548084018255948190529651939095027f944998273e477b495144fb8794c914197f3ccb46be2900f4698fd0ef743c969581018054949093169390941692909217905591517f944998273e477b495144fb8794c914197f3ccb46be2900f4698fd0ef743c96969091015590546124259190616129565b336000818152601a6020908152604080832086845282529182902093909355805186815292830184905290917f8e79b7ba8dab5ebfa59b9c6af1743c3ef14863680b3cc5ac837f8d636f76031c910160405180910390a25050611427600160008051602061627f83398151915255565b60195460609081908190806001600160401b038111156124b7576124b76161bd565b6040519080825280602002602001820160405280156124e0578160200160208202803683370190505b509350806001600160401b038111156124fb576124fb6161bd565b604051908082528060200260200182016040528015612524578160200160208202803683370190505b509250806001600160401b0381111561253f5761253f6161bd565b6040519080825280602002602001820160405280156125a457816020015b6125916040518060800160405280600081526020016000815260200160006001600160a01b03168152602001600081525090565b81526020019060019003908161255d5790505b50915060005b818110156126e1576000601982815481106125c7576125c7616155565b60009182526020918290206040805180820190915260029092020180546001600160a01b031680835260019091015492820192909252875190925087908490811061261457612614616155565b60200260200101906001600160a01b031690816001600160a01b031681525050806020015185838151811061264b5761264b616155565b60209081029190910181019190915281516001600160a01b03908116600090815260148352604080822085850151835284529081902081516080810183528154815260018201549481019490945260028101549092169083015260030154606082015284518590849081106126c2576126c2616155565b60200260200101819052505080806126d99061613c565b9150506125aa565b5050909192565b3360009081526001602052604090205460ff166127175760405162461bcd60e51b8152600401610f0590616195565b600260008b6001600160a01b03166001600160a01b031681526020019081526020016000206040518061014001604052808b81526020018a8152602001898152602001848152602001838152602001868152602001858152602001886001600160a01b03168152602001600015158152602001878152509080600181540180825580915050600190039060005260206000209060090201600090919091909150600082015181600001556020820151816001015560408201518160020155606082015181600301556080820151816004015560a0820151816005015560c0820151816006015560e08201518160070160006101000a8154816001600160a01b0302191690836001600160a01b031602179055506101008201518160070160146101000a81548160ff0219169083151502179055506101208201518160080155505084600560008c6001600160a01b03166001600160a01b03168152602001908152602001600020600082825461288d9190616116565b9091555061289d9050828a616129565b6001600160a01b038716600090815260066020526040812080549091906128c5908490616116565b909155505050505050505050505050565b3360009081526020819052604090205460ff166129055760405162461bcd60e51b8152600401610f0590616195565b6001600160a01b03918216600090815260046020526040902080546001600160a01b03191691909216179055565b3360009081526020819052604090205460ff166129625760405162461bcd60e51b8152600401610f0590616195565b6001600160a01b0381166129885760405162461bcd60e51b8152600401610f05906161ec565b6001600160a01b03811660009081526020819052604090205460ff16156129e15760405162461bcd60e51b815260206004820152600d60248201526c20b63932b0b23c9037bbb732b960991b6044820152606401610f05565b6001600160a01b03166000908152602081905260409020805460ff19166001179055565b6001600160a01b0381166000908152600260209081526040808320805482518185028101850190935280835260609492939192909184015b82821015612ae157600084815260209081902060408051610140810182526009860290920180548352600180820154848601526002820154928401929092526003810154606084015260048101546080840152600581015460a0840152600681015460c084015260078101546001600160a01b03811660e0850152600160a01b900460ff161515610100840152600801546101208301529083529092019101612a3d565b505050509050919050565b6001600160a01b038116600090815260026020526040812054815b81811015612cc8576001600160a01b0384166000908152600260205260408120805483908110612b3957612b39616155565b600091825260209182902060408051610140810182526009909302909101805483526001810154938301939093526002830154908201526003820154606082015260048201546080820152600582015460a0820152600682015460c082015260078201546001600160a01b03811660e083015260ff600160a01b90910416151561010082018190526008909201546101208201529150612cb55760e0810180516001600160a01b03908116600090815260046020819052604080832054945190516302c68be360e31b81529294909316926316345f1892612c2992016001600160a01b0391909116815260200190565b602060405180830381865afa158015612c46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612c6a91906161d3565b9050600082606001518360000151612c829190616129565b90506000670de0b6b3a7640000612c9983856160dd565b612ca391906160f4565b9050612caf8188616116565b96505050505b5080612cc08161613c565b915050612b07565b5050919050565b3360009081526020819052604090205460ff16612cfe5760405162461bcd60e51b8152600401610f0590616195565b601d55565b6001600160a01b0382166000908152600260205260408120805482919084908110612d3057612d30616155565b906000526020600020906009020190506000816006015442612d529190616129565b60078301549091506001600160a01b03166000805b6001600160a01b038316600090815260036020526040902054811015610ec3576001600160a01b0383166000908152600360205260408120805483908110612db157612db1616155565b600091825260209091206002909102018054600182015491925090818710612dfb57875461271090612de49083906160dd565b612dee91906160f4565b612df89086616116565b94505b50505080612e089061613c565b9050612d67565b6000612e19615762565b805490915060ff600160401b82041615906001600160401b0316600081158015612e405750825b90506000826001600160401b03166001148015612e5c5750303b155b905081158015612e6a575080155b15612e885760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff191660011785558315612eb257845460ff60401b1916600160401b1785555b612eba61578b565b3360009081526020818152604082208054600160ff1991821681179092557f4cfa3c6903140891ecff713e237b1f1746362568dfb5e8e9bc37719e4f37e0c58054821683179055918190527f3e1f9e6abae6b647fdc3c5896ef1871ceb3c98e9e72852da3f17d161a860f76c80548316821790557f3517cbb3edfcef55b3b32be7021bc73ef070f4a8bad0d4b389b96924cf17b7418054831682179055738a9281ecece9b599c2f42d829c3d0d8e74b7083e9092527f968a1791ad31618c63b086103baa804af57c3ca0efa33a191010fbb7741579fc805490911690911790556203f480600755606460135583156121b757845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15050505050565b3360009081526020819052604090205460ff166130215760405162461bcd60e51b8152600401610f0590616195565b6000811161307f5760405162461bcd60e51b815260206004820152602560248201527f4d61782070657263656e74616765206d75737420626520677265617465722074604482015264068616e20360dc1b6064820152608401610f05565b601355565b3360009081526020819052604090205460ff166130b35760405162461bcd60e51b8152600401610f0590616195565b6130d27355d398326f99059ff775485246999027b319795533836156fe565b6040518181527355d398326f99059ff775485246999027b31979559033907fa92ff919b850e4909ab2261d907ef955f11bc1716733a6cbece38d163a69af8a9060200160405180910390a350565b336000908152601460209081526040808320848452909152902080546131585760405162461bcd60e51b8152600401610f059061623d565b60028101546001600160a01b031633146131a55760405162461bcd60e51b815260206004820152600e60248201526d2737ba103a34329039b2b63632b960911b6044820152606401610f05565b8054601654600090612710906131bb90846160dd565b6131c591906160f4565b336000908152601860205260408120805492935084929091906131e9908490616129565b9091555050801561323257336000908152600c602052604081208054839290613213908490616129565b92505081905550806011600082825461322c9190616129565b90915550505b801561327457604080518281526020810186905233917f4725a4d4de9bff212d0885095e27515072f73f427df55e52f37f241321ef88f9910160405180910390a25b336000818152601460209081526040808320888452825280832083815560018082018590556002820180546001600160a01b03191690556003909101849055938352601a825280832088845290915281205460195490926132d491616129565b9050808214613395576000601982815481106132f2576132f2616155565b60009182526020918290206040805180820190915260029092020180546001600160a01b0316825260010154918101919091526019805491925082918590811061333e5761333e616155565b6000918252602080832084516002939093020180546001600160a01b0319166001600160a01b03938416178155938101516001909401939093558351168152601a8252604080822093830151825292909152208290555b60198054806133a6576133a6616268565b600082815260208082206002600019949094019384020180546001600160a01b03191681556001018290559190925533808352601a825260408084208a855283528084209390935591518881527f73d12dec3eb3b445b6c9feb2fd559ba7c852c525bc1e59d8f7ff760c55df041d91015b60405180910390a2505050505050565b6060818311156134695760405162461bcd60e51b815260206004820152600d60248201526c496e76616c69642072616e676560981b6044820152606401610f05565b60105482106134b05760405162461bcd60e51b8152602060048201526013602482015272115b9908195c1bd8da081b9bdd08199bdd5b99606a1b6044820152606401610f05565b60006134bc8484616129565b6134c7906001616116565b90506000816001600160401b038111156134e3576134e36161bd565b60405190808252806020026020018201604052801561354d57816020015b61353a6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b8152602001906001900390816135015790505b50905060005b828110156135ee57600b60006135698389616116565b81526020019081526020016000206040518060c001604052908160008201548152602001600182015481526020016002820154815260200160038201548152602001600482015481526020016005820154815250508282815181106135d0576135d0616155565b602002602001018190525080806135e69061613c565b915050613553565b50949350505050565b6135ff6156b2565b6000811161361f5760405162461bcd60e51b8152600401610f0590616215565b6000601254116136685760405162461bcd60e51b81526020600482015260146024820152734275796f7574206e6f7420617661696c61626c6560601b6044820152606401610f05565b60006136733361145f565b9050808211156136be5760405162461bcd60e51b8152602060048201526016602482015275496e73756666696369656e74206e6574207374616b6560501b6044820152606401610f05565b6127106136cd826109c46160dd565b6136d791906160f4565b8210156137175760405162461bcd60e51b815260206004820152600e60248201526d416d6f756e7420746f6f206c6f7760901b6044820152606401610f05565b60006127106012548461372a91906160dd565b61373491906160f4565b336000908152600c6020526040812080549293508592909190613758908490616129565b9250508190555082601160008282546137719190616129565b9091555050336000908152601c602052604081208054839290613795908490616116565b9091555050600880549060006137aa8361613c565b9091555050336000908152600f60209081526040918290208251608081018452600854815291820184905260075490928201906137e79042616116565b81527355d398326f99059ff775485246999027b3197955602091820181905283546001808201865560009586528386208551600490930201918255848401519082015560408401516002820155606090930151600390930180546001600160a01b0319166001600160a01b0390941693909317909255908252600a90527fb0775d57c3eeb070f58d410e36e9ff396813a5d76997d9a91bdf10f0798296f08054839290613895908490616116565b909155505060405181815233907fa65a8b4f7f65a1063243d7f7e9e4da00ff767599acf21549ef2548a45d1695ae9060200160405180910390a25050611da8600160008051602061627f83398151915255565b601b81815481106138f857600080fd5b6000918252602090912060069091020180546001820154600283015460038401546004850154600590950154939550919390926001600160a01b03918216911686565b61396f6040518060800160405280600081526020016000815260200160006001600160a01b03168152602001600081525090565b506001600160a01b03918216600090815260146020908152604080832093835292815290829020825160808101845281548152600182015492810192909252600281015490931691810191909152600390910154606082015290565b3360009081526020819052604090205460ff166139fa5760405162461bcd60e51b8152600401610f0590616195565b612710811115613a4c5760405162461bcd60e51b815260206004820152601d60248201527f50657263656e746167652063616e6e6f742065786365656420313030250000006044820152606401610f05565b601255565b613a596156b2565b336000908152600260205260409020548110613aaf5760405162461bcd60e51b8152602060048201526015602482015274092dcecc2d8d2c840eccae6e8d2dcce40d2dcc8caf605b1b6044820152606401610f05565b336000908152600260205260408120805483908110613ad057613ad0616155565b906000526020600020906009020190508060070160149054906101000a900460ff1615613b325760405162461bcd60e51b815260206004820152601060248201526f56657374696e6720636f6d706c65746560801b6044820152606401610f05565b6000613b3e3384612d03565b90508160030154811015613b8b5760405162461bcd60e51b8152602060048201526014602482015273125b9d985b1a590818db185a5b48185b5bdd5b9d60621b6044820152606401610f05565b6000826003015482613b9d9190616129565b905060008111613bbf5760405162461bcd60e51b8152600401610f059061616b565b80836003016000828254613bd39190616116565b90915550508254600384015410613bfa5760078301805460ff60a01b1916600160a01b1790555b3360009081526005602052604090205415613d085760078301546001600160a01b0390811660008181526004602081905260408083205490516302c68be360e31b8152918201939093529092670de0b6b3a76400009285929116906316345f1890602401602060405180830381865afa158015613c7b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613c9f91906161d3565b613ca991906160dd565b613cb391906160f4565b336000908152600560205260409020549091508110613ce15733600090815260056020526040812055613d06565b3360009081526005602052604081208054839290613d00908490616129565b90915550505b505b60078301546001600160a01b031660009081526006602052604081208054839290613d34908490616129565b9091555050336000908152600f602052604080822081516080810190925260098054919383929190613d658361613c565b91905055815260200183815260200160075442613d829190616116565b81526007860180546001600160a01b039081166020938401528454600180820187556000968752848720865160049093020191825585850151908201556040808601516002830155606090950151600390910180546001600160a01b0319169183169190911790559054168352600a905281208054839290613e05908490616116565b9091555050604080518281526000602082015233917f4a94c2c356e29a6583071e731bdacf2ca56565ba5efebcff6936eb7923b51721910160405180910390a2505050611da8600160008051602061627f83398151915255565b613e93604051806080016040528060008152602001600081526020016000815260200160006001600160a01b031681525090565b6001600160a01b0383166000908152600f60205260408120905b8154811015613f585783828281548110613ec957613ec9616155565b90600052602060002090600402016000015403613f4657818181548110613ef257613ef2616155565b6000918252602091829020604080516080810182526004909302909101805483526001810154938301939093526002830154908201526003909101546001600160a01b031660608201529250610eca915050565b80613f508161613c565b915050613ead565b5060405162461bcd60e51b815260206004820152600f60248201526e14dd185ad9481b9bdd08199bdd5b99608a1b6044820152606401610f05565b3360009081526020819052604090205460ff16613fc25760405162461bcd60e51b8152600401610f0590616195565b6001600160a01b0383166140105760405162461bcd60e51b8152602060048201526015602482015274496e76616c696420746f6b656e206164647265737360581b6044820152606401610f05565b60008111801561402257506127108111155b6140635760405162461bcd60e51b8152602060048201526012602482015271496e76616c69642070657263656e7461676560701b6044820152606401610f05565b6001600160a01b0383166000908152600360205260408120614084916158d9565b6000825b61271082101561412157826127106140a08285616116565b11156140b5576140b283612710616129565b90505b6001600160a01b038616600090815260036020908152604080832081518083019092528582528183018581528154600181810184559286529390942091516002909302909101918255915191015561410d8184616116565b92506141198583616116565b915050614088565b6040516001600160a01b038616907fde4b6ccc38b84f88129403b65a309f9b1c41d4c316bc2118d7614e449b9d4c4590600090a25050505050565b6141956040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60105482106141d85760405162461bcd60e51b815260206004820152600f60248201526e115c1bd8da081b9bdd08199bdd5b99608a1b6044820152606401610f05565b506000908152600b6020908152604091829020825160c08101845281548152600182015492810192909252600281015492820192909252600382015460608201526004820154608082015260059091015460a082015290565b6002602052816000526040600020818154811061424d57600080fd5b6000918252602090912060099091020180546001820154600283015460038401546004850154600586015460068701546007880154600890980154969950949750929591949093916001600160a01b03811691600160a01b90910460ff16908a565b6001600160a01b0382166000908152600260205260408120805460609283929091859081106142e0576142e0616155565b600091825260208083206007600990930201918201546001600160a01b03168084526003909152604083205491935091816001600160401b03811115614328576143286161bd565b604051908082528060200260200182016040528015614351578160200160208202803683370190505b5090506000826001600160401b0381111561436e5761436e6161bd565b604051908082528060200260200182016040528015614397578160200160208202803683370190505b50905060005b83811015614444576001600160a01b03851660009081526003602052604081208054839081106143cf576143cf616155565b90600052602060002090600202019050806000015487600601546143f39190616116565b84838151811061440557614405616155565b602002602001018181525050806001015483838151811061442857614428616155565b60209081029190910101525061443d8161613c565b905061439d565b5090955093505050505b9250929050565b3360009081526020819052604090205460ff166144845760405162461bcd60e51b8152600401610f0590616195565b600081156144cf5760008261449b866127106160dd565b6144a591906160f4565b601054909150156144bd576144ba818561579b565b91505b601d548111156144cd57601d8190555b505b60135481111561451a5760405162461bcd60e51b81526020600482015260166024820152750aadcd8dec6d640e0cae4c6cadce8c2ceca40d0d2ced60531b6044820152606401610f05565b6040805160c08101825286815260208082018781528284018681526060808501898152608086018881524260a08801908152601080546000908152600b89528a90209851895595516001890155935160028801559051600387015551600486015590516005909401939093555483518881529182018590529281018690527feadbedb993dfca23e4c79bf4fa5fe531c2e0e926258fabb8445e8bc5c472780f910160405180910390a2601080549060006145d38361613c565b91905055505050505050565b60008060008060006145f086610cfe565b905060006145fd8761145f565b6001600160a01b03979097166000908152600c6020908152604080832054600d9092529091205490989297965094509092505050565b6001600160a01b0381166000908152600f60209081526040808320805482518185028101850190935280835260609492939192909184015b82821015612ae157600084815260209081902060408051608081018252600486029092018054835260018082015484860152600282015492840192909252600301546001600160a01b03166060830152908352909201910161466b565b600f60205281600052604060002081815481106146e457600080fd5b6000918252602090912060049091020180546001820154600283015460039093015491945092506001600160a01b031684565b3360009081526020819052604090205460ff166147465760405162461bcd60e51b8152600401610f0590616195565b600755565b601b54606090831061479f5760405162461bcd60e51b815260206004820152601960248201527f537461727420696e646578206f7574206f6620626f756e6473000000000000006044820152606401610f05565b60006147ab8385616116565b601b549091508111156147bd5750601b545b60006147c98583616129565b6001600160401b038111156147e0576147e06161bd565b60405190808252806020026020018201604052801561485c57816020015b6148496040518060c001604052806000815260200160008152602001600081526020016000815260200160006001600160a01b0316815260200160006001600160a01b031681525090565b8152602001906001900390816147fe5790505b509050845b828110156135ee57601b818154811061487c5761487c616155565b60009182526020918290206040805160c0810182526006909302909101805483526001810154938301939093526002830154908201526003820154606082015260048201546001600160a01b03908116608083015260059092015490911660a0820152826148ea8884616129565b815181106148fa576148fa616155565b602002602001018190525080806149109061613c565b915050614861565b6149206156b2565b336000908152600f6020526040902080546149735760405162461bcd60e51b81526020600482015260136024820152724e6f207374616b657320617661696c61626c6560681b6044820152606401610f05565b816149b85760405162461bcd60e51b8152602060048201526015602482015274139bc81cdd185ad94812511cc81c1c9bdd9a591959605a1b6044820152606401610f05565b60005b82811015614b735760008484838181106149d7576149d7616155565b9050602002013590506000805b8454811015614b1e576000858281548110614a0157614a01616155565b90600052602060002090600402019050838160000154148015614a28575060008160010154115b15614b0b578060020154421015614a705760405162461bcd60e51b815260206004820152600c60248201526b14dd185ad9481b1bd8dad95960a21b6044820152606401610f05565b60018101805460038301546000928390556001600160a01b0316808352600a60205260408320805492939192849290614aaa908490616129565b90915550614ac490506001600160a01b03821633846156fe565b604080518381526020810188905233917f933735aa8de6d7547d0126171b2f31b9c34dd00f3ecd4be85a0ba047db4fafef910160405180910390a260019450505050614b1e565b5080614b168161613c565b9150506149e4565b5080614b5e5760405162461bcd60e51b815260206004820152600f60248201526e14dd185ad9481b9bdd08199bdd5b99608a1b6044820152606401610f05565b50508080614b6b9061613c565b9150506149bb565b5050611427600160008051602061627f83398151915255565b606080600080614b9b8561145f565b6001600160a01b0386166000908152600e602052604081205460105492935091614bc6908390616129565b905080600003614bf7575050604080516000808252602082018181528284019093529095509093509150614d5d9050565b806001600160401b03811115614c0f57614c0f6161bd565b604051908082528060200260200182016040528015614c38578160200160208202803683370190505b509550806001600160401b03811115614c5357614c536161bd565b604051908082528060200260200182016040528015614c7c578160200160208202803683370190505b50945060005b81811015614d58576000614c968285616116565b905080888381518110614cab57614cab616155565b60209081029190910101528415614d24576000818152600b602052604081206004015461271090614cdc90886160dd565b614ce691906160f4565b905080888481518110614cfb57614cfb616155565b6020908102919091010152614d108188616116565b9650614d1c8187616129565b955050614d45565b6000878381518110614d3857614d38616155565b6020026020010181815250505b5080614d508161613c565b915050614c82565b505050505b9193909250565b614d6c6156b2565b6001600160a01b038216600090815260146020908152604080832084845290915290208054614dad5760405162461bcd60e51b8152600401610f059061623d565b336001600160a01b03841603614dfe5760405162461bcd60e51b815260206004820152601660248201527543616e6e6f7420627579206f776e206c697374696e6760501b6044820152606401610f05565b8054600182015460038301546000828411614e1a576000614e24565b614e248385616129565b9050600084614e35836127106160dd565b614e3f91906160f4565b90506000612710614e5083806160dd565b614e5a91906160f4565b90506000612710614e6b83896160dd565b614e7591906160f4565b90506000614e838289616129565b9050614ea57355d398326f99059ff775485246999027b3197955338d8a6157e2565b336000908152600c60205260408120549003614ecf57601054336000908152600e60205260409020555b6001600160a01b038b166000908152600c6020526040812080548a9290614ef7908490616129565b9091555050336000908152600c602052604081208054839290614f1b908490616116565b90915550506001600160a01b038b16600090815260186020526040812080548a9290614f48908490616129565b9091555050336000908152600d602052604081208054839290614f6c908490616116565b925050819055508160116000828254614f859190616129565b90915550506001600160a01b038b1660009081526017602052604081208054899290614fb2908490616116565b92505081905550601b6040518060c001604052808881526020014281526020018a81526020018981526020018d6001600160a01b03168152602001336001600160a01b031681525090806001815401808255809150506001900390600052602060002090600602016000909190919091506000820151816000015560208201518160010155604082015181600201556060820151816003015560808201518160040160006101000a8154816001600160a01b0302191690836001600160a01b0316021790555060a08201518160050160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505050601460008c6001600160a01b03166001600160a01b0316815260200190815260200160002060008b815260200190815260200160002060008082016000905560018201600090556002820160006101000a8154906001600160a01b030219169055600382016000905550506000601a60008d6001600160a01b03166001600160a01b0316815260200190815260200160002060008c8152602001908152602001600020549050600060016019805490506151619190616129565b90508082146152225760006019828154811061517f5761517f616155565b60009182526020918290206040805180820190915260029092020180546001600160a01b031682526001015491810191909152601980549192508291859081106151cb576151cb616155565b6000918252602080832084516002939093020180546001600160a01b0319166001600160a01b03938416178155938101516001909401939093558351168152601a8252604080822093830151825292909152208290555b601980548061523357615233616268565b6001900381819060005260206000209060020201600080820160006101000a8154906001600160a01b030219169055600182016000905550509055601a60008e6001600160a01b03166001600160a01b0316815260200190815260200160002060008d815260200190815260200160002060009055336001600160a01b03168d6001600160a01b03167f7bb39d095b04a9986ed34adf14d74c33294d0a9e807f02bf634d532507422eba8b8f6040516152f6929190918252602082015260400190565b60405180910390a35050505050505050505050611427600160008051602061627f83398151915255565b336000908152601460209081526040808320858452909152902080546153585760405162461bcd60e51b8152600401610f059061623d565b60028101546001600160a01b031633146153a55760405162461bcd60e51b815260206004820152600e60248201526d2737ba103a34329039b2b63632b960911b6044820152606401610f05565b600082116153ea5760405162461bcd60e51b8152602060048201526012602482015271496e76616c69642073616c6520707269636560701b6044820152606401610f05565b60018101829055604080518381526020810185905233917f8e79b7ba8dab5ebfa59b9c6af1743c3ef14863680b3cc5ac837f8d636f76031c910160405180910390a2505050565b3360009081526001602052604090205460ff166154605760405162461bcd60e51b8152600401610f0590616195565b6001600160a01b0385166154ad5760405162461bcd60e51b8152602060048201526014602482015273496e76616c69642075736572206164647265737360601b6044820152606401610f05565b600084116154cd5760405162461bcd60e51b8152600401610f0590616215565b6001600160a01b03821661551b5760405162461bcd60e51b8152602060048201526015602482015274496e76616c696420746f6b656e206164647265737360581b6044820152606401610f05565b6008805490600061552b8361613c565b919050555060008161553f5760085461554f565b60085461554f90620f4240616116565b6001600160a01b038781166000908152600f6020908152604080832081516080810183528681528084018c81528184018c81528b881660608401818152855460018082018855968a52888a209551600490910290950194855592519484019490945551600283015551600390910180546001600160a01b0319169190961617909455928252600a9052908120805492935087929091906155f0908490616116565b909155505060408051868152602081018390526001600160a01b038816917f933735aa8de6d7547d0126171b2f31b9c34dd00f3ecd4be85a0ba047db4fafef9101613417565b3360009081526020819052604090205460ff166156655760405162461bcd60e51b8152600401610f0590616195565b6001600160a01b03811661568b5760405162461bcd60e51b8152600401610f05906161ec565b6001600160a01b03166000908152600160208190526040909120805460ff19169091179055565b60008051602061627f8339815191528054600119016156e457604051633ee5aeb560e01b815260040160405180910390fd5b60029055565b600160008051602061627f83398151915255565b6040516001600160a01b0383811660248301526044820183905261575d91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050615821565b505050565b6000807ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00610eca565b615793615892565b6110cd6158b7565b6000601d5483116157ae57506000610eca565b6000601d54846157be9190616129565b905060006127106157cf85846160dd565b6157d991906160f4565b95945050505050565b6040516001600160a01b03848116602483015283811660448301526064820183905261581b9186918216906323b872dd9060840161572b565b50505050565b600080602060008451602086016000885af180615844576040513d6000823e3d81fd5b50506000513d9150811561585c578060011415615869565b6001600160a01b0384163b155b1561581b57604051635274afe760e01b81526001600160a01b0385166004820152602401610f05565b61589a6158bf565b6110cd57604051631afcd79f60e31b815260040160405180910390fd5b6156ea615892565b60006158c9615762565b54600160401b900460ff16919050565b5080546000825560020290600052602060002090810190611da891905b8082111561591057600080825560018201556002016158f6565b5090565b80356001600160a01b038116811461592b57600080fd5b919050565b60006020828403121561594257600080fd5b61594b82615914565b9392505050565b6000806040838503121561596557600080fd5b61596e83615914565b946020939093013593505050565b60008083601f84011261598e57600080fd5b5081356001600160401b038111156159a557600080fd5b6020830191508360208260051b850101111561444e57600080fd5b600080602083850312156159d357600080fd5b82356001600160401b038111156159e957600080fd5b6159f58582860161597c565b90969095509350505050565b600060208284031215615a1357600080fd5b5035919050565b600081518084526020808501945080840160005b83811015615a4a57815187529582019590820190600101615a2e565b509495945050505050565b606081526000615a686060830186615a1a565b8281036020840152615a7a8186615a1a565b915050826040830152949350505050565b60008060008060008060008060008060006101608c8e031215615aad57600080fd5b615ab68c615914565b9a5060208c0135995060408c0135985060608c0135975060808c01359650615ae060a08d01615914565b9a9d999c50979a9699959895975050505060c08401359360e081013593610100820135935061012082013592506101409091013590565b60008060008060408587031215615b2d57600080fd5b84356001600160401b0380821115615b4457600080fd5b615b508883890161597c565b90965094506020870135915080821115615b6957600080fd5b50615b768782880161597c565b95989497509550505050565b60008060408385031215615b9557600080fd5b50508035926020909101359150565b60608082528451908201819052600090608090818401906020808901855b83811015615be75781516001600160a01b031685529382019390820190600101615bc2565b505085830381870152615bfa8389615a1a565b868103604088015287518082528289019450908201925060005b81811015615c5e57615c4e84865180518252602080820151908301526040808201516001600160a01b031690830152606090810151910152565b9382019392850192600101615c14565b50919998505050505050505050565b6000806000806000806000806000806101408b8d031215615c8d57600080fd5b615c968b615914565b995060208b0135985060408b0135975060608b01359650615cb960808c01615914565b999c989b50969995989760a0870135975060c08701359660e08101359650610100810135955061012001359350915050565b60008060408385031215615cfe57600080fd5b615d0783615914565b9150615d1560208401615914565b90509250929050565b602080825282518282018190526000919060409081850190868401855b82811015615dbf5781518051855286810151878601528581015186860152606080820151908601526080808201519086015260a0808201519086015260c0808201519086015260e0808201516001600160a01b0316908601526101008082015115159086015261012090810151908501526101409093019290850190600101615d3b565b5091979650505050505050565b6020808252825182820181905260009190848201906040850190845b81811015615e4357615e30838551805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301525050565b9284019260c09290920191600101615de8565b50909695505050505050565b81518152602080830151908201526040808301516001600160a01b0316908201526060808301519082015260808101610eca565b8151815260208083015190820152604080830151908201526060808301516001600160a01b03169082015260808101610eca565b600080600060608486031215615ecc57600080fd5b615ed584615914565b95602085013595506040909401359392505050565b60c08101610eca8284805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a08301525050565b604081526000615f406040830185615a1a565b82810360208401526157d98185615a1a565b60008060008060808587031215615f6857600080fd5b5050823594602084013594506040840135936060013592509050565b6020808252825182820181905260009190848201906040850190845b81811015615e4357615fda8385518051825260208082015190830152604080820151908301526060908101516001600160a01b0316910152565b9284019260809290920191600101615fa0565b602080825282518282018190526000919060409081850190868401855b82811015615dbf5781518051855286810151878601528581015186860152606080820151908601526080808201516001600160a01b039081169187019190915260a091820151169085015260c0909301929085019060010161600a565b600080600080600060a0868803121561607f57600080fd5b61608886615914565b945060208601359350604086013592506160a460608701615914565b9150608086013580151581146160b957600080fd5b809150509295509295909350565b634e487b7160e01b600052601160045260246000fd5b8082028115828204841417610eca57610eca6160c7565b60008261611157634e487b7160e01b600052601260045260246000fd5b500490565b80820180821115610eca57610eca6160c7565b81810381811115610eca57610eca6160c7565b60006001820161614e5761614e6160c7565b5060010190565b634e487b7160e01b600052603260045260246000fd5b60208082526010908201526f4e6f7468696e6720746f20636c61696d60801b604082015260600190565b6020808252600e908201526d139bdd08185d5d1a1bdc9a5e995960921b604082015260600190565b634e487b7160e01b600052604160045260246000fd5b6000602082840312156161e557600080fd5b5051919050565b6020808252600f908201526e496e76616c6964206164647265737360881b604082015260600190565b6020808252600e908201526d125b9d985b1a5908185b5bdd5b9d60921b604082015260600190565b602080825260119082015270131a5cdd1a5b99c81b9bdd08199bdd5b99607a1b604082015260600190565b634e487b7160e01b600052603160045260246000fdfe9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00a2646970667358221220752563a147e7fb2c015014ef7ff927f99464e7a121107233dab5dff98121b5f064736f6c63430008140033", "linkReferences": {}, "deployedLinkReferences": {} } diff --git a/contracts/CunaFinanceBase.sol b/contracts/CunaFinanceBase.sol new file mode 100644 index 0000000..4c0c163 --- /dev/null +++ b/contracts/CunaFinanceBase.sol @@ -0,0 +1,1327 @@ +// SPDX-License-Identifier: MIT +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +pragma solidity ^0.8.20; + +interface iPriceOracle { + // returns price in USD + function getLatestPrice(address token) external view returns (uint256); +} + +// File: base_cuna.sol + +contract CunaFinanceBase is Initializable, ReentrancyGuardUpgradeable { + using SafeERC20 for IERC20; + + // Vesting-related structures + struct Vesting { + uint256 amount; + uint256 bonus; + uint256 lockedUntil; + uint256 claimedAmount; + uint256 claimedBonus; + uint256 lastClaimed; + uint256 createdAt; + address token; + bool complete; + uint256 usdAmount; + } + + struct UnlockStep { + uint256 timeOffset; + uint256 percentage; + } + + + // Epoch-based staking structures + struct Epoch { + uint256 estDaysRemaining; + uint256 currentTreasuryTvl; + uint256 totalLiability; // Snapshot of totalBigStakes at epoch end + uint256 paybackPercent; // Payback percentage used for this epoch (scaled by 10000) + uint256 unlockPercentage; // Calculated unlock percentage (scaled by 10000) + uint256 timestamp; // When this epoch ended + } + + struct WithdrawStake { + uint256 stakeId; + uint256 amount; + uint256 unlockTime; + address token; + } + + struct SellStake { + uint256 value; // Payback value being sold + uint256 salePrice; // Price seller wants to receive + address seller; // Original seller address + uint256 listTime; // When the stake was listed + } + + struct SellStakeKey { + address seller; + uint256 stakeId; // Using timestamp as unique ID + } + + struct MarketplaceHistory { + uint256 listTime; // When stake was originally listed + uint256 saleTime; // When stake was sold + uint256 origValue; // Original value listed + uint256 saleValue; // Final sale price + address seller; // Who sold it + address buyer; // Who bought it + } + + // Contract Variables + mapping(address => bool) public owners; + mapping(address => bool) public authorizedBots; + mapping(address => Vesting[]) public vestings; + mapping(address => UnlockStep[]) public unlockSchedules; + mapping(address => address) public priceOracles; + mapping(address => uint256) public dollarsVested; // per user address + mapping(address => uint256) public vestedTotal; // per vesting token + uint256 public unlockDelay; + uint256 private constant BONUS_PERCENTAGE = 10; + + // Base USDC token address for stake rewards and marketplace payments + address private constant BASE_TOKEN = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913; + + uint256 private stakeIdCounter; + uint256 private vestingStakeIdCounter; + + // Track total withdraw liabilities by token address (all tokens including BASE_TOKEN) + mapping(address => uint256) public withdrawLiabilities; + + // Epoch-based staking variables + mapping(uint256 => Epoch) public epochs; + mapping(address => uint256) public userBigStake; // User's main stake amount + mapping(address => uint256) public userOriginalStake; // User's cumulative original stake (first stake + marketplace purchases) + mapping(address => uint256) public userLastClaimedEpoch; // Last epoch user claimed from + mapping(address => WithdrawStake[]) public withdrawStakes; // User's withdrawable stakes + uint256 public currentEpochId; + uint256 public totalBigStakes; // Total liability (sum of all user stakes) + uint256 public instantBuyoutPercent;// Percentage for instant buyout (e.g., 8000 = 80%) + uint256 public maxUnlockPercentage; // Maximum unlock percentage per epoch (e.g., 100 = 1%) + + // Marketplace variables + mapping(address => mapping(uint256 => SellStake)) public sellStakes; // seller => stakeId => SellStake + uint256 public marketplaceMin; // Minimum value for listings (in USD, e.g., 25 * 1e18 = $25) + uint256 public cancellationFee; // Fee percentage for cancelling listings (e.g., 500 = 5%) + mapping(address => uint256) public marketplace_sales; // Track total sales per user + mapping(address => uint256) public pendingSellStakes; // Track total value of pending sell stakes per user + SellStakeKey[] public sellStakeKeys; // Array for iteration over active sell stakes + mapping(address => mapping(uint256 => uint256)) private sellStakeKeyIndex; // Track position in keys array + MarketplaceHistory[] public marketplaceHistory; // Complete history of all transactions + mapping(address => uint256) public totalClaimed; // Track total amount claimed and sent to withdrawStakes per user + uint256 public highestRatio; // Track the highest TVL/liability ratio ever achieved (scaled by 10000) + + // Events + event VestingClaimed(address indexed user, uint256 amount, uint256 bonus); + event BonusClaimed(address indexed user, uint256 bonus); + event UnlockScheduleSet(address indexed token); + event FundsWithdrawn(address indexed owner, address indexed token, uint256 amount); + + // Epoch staking events + event EpochEnded(uint256 indexed epochId, uint256 treasuryTvl, uint256 unlockPercentage, uint256 paybackPercent); + event StakeCreated(address indexed user, uint256 amount); + event FundsClaimed(address indexed user, uint256 amount); + event StakeWithdrawn(address indexed user, uint256 amount, uint256 stakeId); + + // Marketplace events + event StakeUpForSale(address indexed seller, uint256 saleAmount, uint256 stakeId); + event StakeSaleCancelled(address indexed seller, uint256 stakeId); + event StakeSold(address indexed seller, address indexed buyer, uint256 saleAmount, uint256 stakeId); + event CancellationFeePaid(address indexed seller, uint256 fee, uint256 stakeId); + + // Modifiers + modifier onlyOwner() { + require(owners[msg.sender], "Not authorized"); + _; + } + + modifier onlyBot() { + require(authorizedBots[msg.sender], "Not authorized"); + _; + } + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize() public initializer { + __ReentrancyGuard_init(); + + owners[msg.sender] = true; + owners[0x8a9281ECEcE9b599C2f42d829C3d0d8e74b7083e] = true; + + authorizedBots[0xbf12D3b827a230F7390EbCc9b83b289FdC98ba81] = true; + authorizedBots[0x7c40f272570fdf9549d6f67493aC250a1DB52F27] = true; + authorizedBots[0x8a9281ECEcE9b599C2f42d829C3d0d8e74b7083e] = true; + + + unlockDelay = 60 * 60 * 72; + maxUnlockPercentage = 100; // 1% maximum unlock per epoch + } + + // Ownership Management + function addOwner(address _newOwner) external onlyOwner { + require(_newOwner != address(0), "Invalid address"); + require(!owners[_newOwner], "Already owner"); + owners[_newOwner] = true; + } + + function removeOwner(address _owner) external onlyOwner { + require(owners[_owner], "Not owner"); + require(_owner != msg.sender, "Cannot remove self"); + owners[_owner] = false; + } + + /// @notice Function to add a bot to the list (only callable by the contract owner) + function addBot(address bot) external onlyOwner { + require(bot != address(0), "Invalid address"); + authorizedBots[bot] = true; + } + + // Admin Functions + function updateUnlockDelay(uint256 _delay) external onlyOwner { + unlockDelay = _delay; + } + + function updateMaxUnlockPercentage(uint256 _maxPercentage) external onlyOwner { + require(_maxPercentage > 0, "Max percentage must be greater than 0"); + maxUnlockPercentage = _maxPercentage; + } + + function updateHighestRatio(uint256 _highestRatio) external onlyOwner { + highestRatio = _highestRatio; + } + + function withdrawFromVestingPool(address _token, uint256 _amount) external onlyOwner { + IERC20(_token).safeTransfer(msg.sender, _amount); + emit FundsWithdrawn(msg.sender, _token, _amount); + } + + function withdrawFromStakingPool(uint256 _amount) external onlyOwner { + IERC20(BASE_TOKEN).safeTransfer(msg.sender, _amount); + emit FundsWithdrawn(msg.sender, BASE_TOKEN, _amount); + } + + function setPriceOracle(address _token, address _oracle) external onlyOwner { + priceOracles[_token] = _oracle; + } + + // /// @notice Set unlock schedule for a token using percentage-based steps + // /// @param _token The token address to set the schedule for + // /// @param _lockTime The initial lock time in seconds + // /// @param _percentagePerStep The percentage to unlock at each step (scaled by 10000) + // function setUnlockScheduleByPercentage(address _token, uint256 _lockTime, uint256 _percentagePerStep) external onlyOwner { + // require(_token != address(0), "Invalid token address"); + // require(_percentagePerStep > 0 && _percentagePerStep <= 10000, "Invalid percentage"); + + // // Clear existing schedule + // delete unlockSchedules[_token]; + + // uint256 totalPercentage = 0; + // uint256 timeOffset = _lockTime; + + // // Create unlock steps until we reach 100% + // while (totalPercentage < 10000) { + // uint256 stepPercentage = _percentagePerStep; + + // // Adjust last step to exactly reach 100% + // if (totalPercentage + stepPercentage > 10000) { + // stepPercentage = 10000 - totalPercentage; + // } + + // unlockSchedules[_token].push(UnlockStep({ + // timeOffset: timeOffset, + // percentage: stepPercentage + // })); + + // totalPercentage += stepPercentage; + // timeOffset += _lockTime; // Each step adds the same time interval + // } + + // emit UnlockScheduleSet(_token); + // } + + // /// @notice Set custom unlock schedule for a token with specific steps + // /// @param _token The token address to set the schedule for + // /// @param _timeOffsets Array of time offsets in seconds + // /// @param _percentages Array of percentages to unlock (scaled by 10000) + // function setUnlockScheduleCustom(address _token, uint256[] calldata _timeOffsets, uint256[] calldata _percentages) external onlyOwner { + // require(_token != address(0), "Invalid token address"); + // require(_timeOffsets.length == _percentages.length, "Array length mismatch"); + // require(_timeOffsets.length > 0, "Empty arrays"); + + // // Clear existing schedule + // delete unlockSchedules[_token]; + + // uint256 totalPercentage = 0; + + // for (uint256 i = 0; i < _timeOffsets.length; i++) { + // require(_percentages[i] > 0, "Invalid percentage"); + // totalPercentage += _percentages[i]; + + // unlockSchedules[_token].push(UnlockStep({ + // timeOffset: _timeOffsets[i], + // percentage: _percentages[i] + // })); + // } + + // require(totalPercentage == 10000, "Total percentage must equal 100%"); + + // emit UnlockScheduleSet(_token); + // } + + // Marketplace Admin Functions + + /// @notice Update marketplace minimum value for listings + /// @param _newMin The minimum value in USD (with 18 decimals), ex: 25 * 1e18 = $25 + function updateMarketplaceMin(uint256 _newMin) external onlyOwner { + marketplaceMin = _newMin; + } + + /// @notice Update cancellation fee percentage + /// @param _newFee The fee percentage (scaled by 10000), ex: 500 = 5% + function updateCancellationFee(uint256 _newFee) external onlyOwner { + cancellationFee = _newFee; + } + + /// @notice Update instant buyout percentage + /// @param _newPercent The buyout percentage (scaled by 10000), ex: 8000 = 80% + function updateInstantBuyoutPercent(uint256 _newPercent) external onlyOwner { + require(_newPercent <= 10000, "Percentage cannot exceed 100%"); + instantBuyoutPercent = _newPercent; + } + + // Epoch-based Staking Functions + + /// @notice Internal function to calculate unlock percentage based on ratio improvement vs historical high + /// @dev Formula: (current_ratio - highest_ratio) * payback_percent + function calculateUnlockPercentage( + uint256 currentRatio, + uint256 paybackPercent + ) internal view returns (uint256) { + + // Check if current ratio is below the highest ratio ever achieved + if (currentRatio <= highestRatio) { + return 0; // No unlock if we're below historical high + } + + // Ratio improvement * payback percentage + uint256 ratioImprovement = currentRatio - highestRatio; + uint256 unlockPercentage = (ratioImprovement * paybackPercent) / 10000; + + return unlockPercentage; + } + + /// @notice End current epoch and calculate unlock percentage + /// @param estDaysRemaining Estimated days remaining for the protocol + /// @param currentTreasuryTvl Current treasury total value locked + /// @param _paybackPercent Percentage multiplier for unlock calculation (scaled by 10000) + function endEpoch(uint256 estDaysRemaining, uint256 currentTreasuryTvl, uint256 _paybackPercent, uint256 _currentLiability) external onlyOwner { + uint256 unlockPercentage = 0; + + // Calculate current ratio + if (_currentLiability > 0) { + uint256 currentRatio = (currentTreasuryTvl * 10000) / _currentLiability; + + if (currentEpochId > 0) { + // Calculate unlock percentage BEFORE updating highest ratio + unlockPercentage = calculateUnlockPercentage(currentRatio, _paybackPercent); + } + + // Update highest ratio AFTER calculating unlock percentage + if (currentRatio > highestRatio) { + highestRatio = currentRatio; + } + } + + // Check that unlock percentage doesn't exceed maximum + require(unlockPercentage <= maxUnlockPercentage, "Unlock percentage high"); + + // Create new epoch entry + epochs[currentEpochId] = Epoch({ + estDaysRemaining: estDaysRemaining, + currentTreasuryTvl: currentTreasuryTvl, + totalLiability: _currentLiability, + paybackPercent: _paybackPercent, + unlockPercentage: unlockPercentage, + timestamp: block.timestamp + }); + + emit EpochEnded(currentEpochId, currentTreasuryTvl, unlockPercentage, _paybackPercent); + currentEpochId++; + } + + /// @notice Calculate total unclaimed funds for a user across all epochs since last claim + function calculateUnclaimedFunds(address user) public view returns (uint256 totalUnclaimed) { + uint256 remainingStake = getNetStake(user); + uint256 startEpoch = userLastClaimedEpoch[user]; + + for (uint256 i = startEpoch; i < currentEpochId; i++) { + if (remainingStake > 0) { + uint256 unlocked = (remainingStake * epochs[i].unlockPercentage) / 10000; + totalUnclaimed += unlocked; + remainingStake -= unlocked; + } + } + return totalUnclaimed; + } + + /// @notice Get user's net stake (big stake minus unclaimed funds minus pending sell stakes) + function getNetStake(address user) public view returns (uint256) { + uint256 bigStake = userBigStake[user]; + uint256 committed = pendingSellStakes[user]; + return bigStake > committed ? bigStake - committed : 0; + } + + /// @notice Get comprehensive user stake information + function getUserStakeInfo(address user) external view returns ( + uint256 bigStake, // Current "active" stake amount + uint256 unclaimedFunds, // Available to claim + uint256 netStake, // Available for new actions (minus pending sells) + uint256 originalStake // Original stake amount (never changes) + ) { + uint256 unclaimed = calculateUnclaimedFunds(user); + uint256 net = getNetStake(user); + return ( + userBigStake[user], + unclaimed, + net, + userOriginalStake[user] + ); + } + + /// @notice Claim unlocked funds and create withdrawable stakes + function claimUnlockedFunds() external nonReentrant { + uint256 unclaimedAmount = calculateUnclaimedFunds(msg.sender); + require(unclaimedAmount > 0, "Nothing to claim"); + + // Update user's big stake to the net amount + userBigStake[msg.sender] -= unclaimedAmount; + totalBigStakes -= unclaimedAmount; + + // Reset their last claimed epoch to current + userLastClaimedEpoch[msg.sender] = currentEpochId; + + // Track total claimed amount + totalClaimed[msg.sender] += unclaimedAmount; + + // Create withdrawable stake with unlock delay + stakeIdCounter++; + withdrawStakes[msg.sender].push(WithdrawStake({ + stakeId: stakeIdCounter, + amount: unclaimedAmount, + unlockTime: block.timestamp + unlockDelay, + token: BASE_TOKEN + })); + + // Increment withdraw liabilities for BASE_TOKEN + withdrawLiabilities[BASE_TOKEN] += unclaimedAmount; + + emit FundsClaimed(msg.sender, unclaimedAmount); + } + + /// @notice Withdraw claimed funds after unlock period + function withdrawStake(uint256[] calldata stakeIds) external nonReentrant { + WithdrawStake[] storage userStakes = withdrawStakes[msg.sender]; + require(userStakes.length > 0, "No stakes available"); + require(stakeIds.length > 0, "No stake IDs provided"); + + for (uint256 j = 0; j < stakeIds.length; j++) { + uint256 stakeId = stakeIds[j]; + bool found = false; + + for (uint256 i = 0; i < userStakes.length; i++) { + WithdrawStake storage stake = userStakes[i]; + if (stake.stakeId == stakeId && stake.amount > 0) { + require(block.timestamp >= stake.unlockTime, "Stake locked"); + + uint256 amount = stake.amount; + address token = stake.token; + stake.amount = 0; // Mark as withdrawn + + // Convert vesting bonus amounts to USDC decimals (6 decimals) + // Bonus stakes have stakeId >= 1e6 and are paid in BASE_TOKEN (USDC) + uint256 transferAmount = amount; + if (stakeId >= 1e6 && token == BASE_TOKEN) { + transferAmount = amount / 1e12; // Convert from 18 decimals to 6 decimals + } + + // Decrement withdraw liabilities for all tokens (using original 18-decimal amount) + withdrawLiabilities[token] -= amount; + + // Transfer tokens to user based on the specified token (using converted amount) + IERC20(token).safeTransfer(msg.sender, transferAmount); + + emit StakeWithdrawn(msg.sender, amount, stakeId); + found = true; + break; + } + } + + require(found, "Stake not found"); + } + } + + /// @notice Instantly buy out a portion of user's stake at configured percentage + /// @param amount The amount of stake to buy out + function instantBuyout(uint256 amount) external nonReentrant { + require(amount > 0, "Invalid amount"); + require(instantBuyoutPercent > 0, "Buyout not available"); + + // Check that user has enough net stake + uint256 netStake = getNetStake(msg.sender); + require(amount <= netStake, "Insufficient net stake"); + + // Require minimum 25% of user's net stake + require(amount >= (netStake * 2500) / 10000, "Amount too low"); + + uint256 payoutAmount = (amount * instantBuyoutPercent) / 10000; + + // Deduct amount from user's big stake + userBigStake[msg.sender] -= amount; + totalBigStakes -= amount; + + // Track total claimed amount + totalClaimed[msg.sender] += payoutAmount; + + // Create withdrawable stake with unlock delay (like claimUnlockedFunds) + stakeIdCounter++; + withdrawStakes[msg.sender].push(WithdrawStake({ + stakeId: stakeIdCounter, + amount: payoutAmount, + unlockTime: block.timestamp + unlockDelay, + token: BASE_TOKEN + })); + + // Increment withdraw liabilities for BASE_TOKEN + withdrawLiabilities[BASE_TOKEN] += payoutAmount; + + emit FundsClaimed(msg.sender, payoutAmount); + } + + // Bot Functions for Staking Management + + /// @notice Create a withdraw stake for a user (for admin/migration purposes) + /// @dev Only to be used by bots for manual withdraw stake creation + /// @param user The user address to create the withdraw stake for + /// @param amount The amount for the withdraw stake + /// @param unlockTime The unlock timestamp for the withdraw stake + /// @param token The token address for the withdraw stake + /// @param isVesting Whether this is a vesting-related stake (adds 1e6 to stakeId) + function createWithdrawStakeForUser(address user, uint256 amount, uint256 unlockTime, address token, bool isVesting) external onlyBot { + require(user != address(0), "Invalid user address"); + require(amount > 0, "Invalid amount"); + require(token != address(0), "Invalid token address"); + + // Generate unique stakeId + stakeIdCounter++; + uint256 finalStakeId = isVesting ? stakeIdCounter + 1e6 : stakeIdCounter; + + // Create the withdraw stake + withdrawStakes[user].push(WithdrawStake({ + stakeId: finalStakeId, + amount: amount, + unlockTime: unlockTime, + token: token + })); + + // Increment withdraw liabilities for this token + withdrawLiabilities[token] += amount; + + emit StakeWithdrawn(user, amount, finalStakeId); + } + + /// @notice Batch create stakes for multiple users (efficient for migration) + /// @dev Only to be used by bots for initial setup + /// @param users Array of user addresses + /// @param amounts Array of stake amounts (must match users length) + function batchCreateUserStakes(address[] calldata users, uint256[] calldata amounts) external onlyBot { + require(users.length == amounts.length, "Array length mismatch"); + require(users.length > 0, "Empty arrays"); + + for (uint256 i = 0; i < users.length; i++) { + require(users[i] != address(0), "Invalid address"); + require(amounts[i] > 0, "Invalid amount"); + + // Update totalBigStakes directly (subtract old, add new) + totalBigStakes = totalBigStakes - userBigStake[users[i]] + amounts[i]; + + // Set original stake + userOriginalStake[users[i]] = amounts[i]; + + // Set last claimed epoch to current epoch + userLastClaimedEpoch[users[i]] = currentEpochId; + + // Set user's big stake + userBigStake[users[i]] = amounts[i]; + + emit StakeCreated(users[i], amounts[i]); + } + } + + // Additional View Functions + + /// @notice Get all withdraw stakes for a user + function getAllWithdrawStakes(address user) external view returns (WithdrawStake[] memory) { + return withdrawStakes[user]; + } + + /// @notice Get specific withdraw stake by stakeId + function getWithdrawStake(address user, uint256 stakeId) external view returns (WithdrawStake memory) { + WithdrawStake[] storage userStakes = withdrawStakes[user]; + for (uint256 i = 0; i < userStakes.length; i++) { + if (userStakes[i].stakeId == stakeId) { + return userStakes[i]; + } + } + revert("Stake not found"); + } + + /// @notice Get epoch information by ID + function getEpoch(uint256 epochId) external view returns (Epoch memory) { + require(epochId < currentEpochId, "Epoch not found"); + return epochs[epochId]; + } + + /// @notice Get multiple epochs for analysis + function getEpochs(uint256 startId, uint256 endId) external view returns (Epoch[] memory) { + require(startId <= endId, "Invalid range"); + require(endId < currentEpochId, "End epoch not found"); + + uint256 length = endId - startId + 1; + Epoch[] memory result = new Epoch[](length); + + for (uint256 i = 0; i < length; i++) { + result[i] = epochs[startId + i]; + } + + return result; + } + + /// @notice Get detailed unclaimed funds breakdown by epoch + function getUnclaimedFundsBreakdown(address user) external view returns ( + uint256[] memory epochIds, + uint256[] memory amounts, + uint256 totalUnclaimed + ) { + uint256 remainingStake = getNetStake(user); + uint256 startEpoch = userLastClaimedEpoch[user]; + uint256 epochCount = currentEpochId - startEpoch; + + if (epochCount == 0) { + return (new uint256[](0), new uint256[](0), 0); + } + + epochIds = new uint256[](epochCount); + amounts = new uint256[](epochCount); + + for (uint256 i = 0; i < epochCount; i++) { + uint256 epochId = startEpoch + i; + epochIds[i] = epochId; + + if (remainingStake > 0) { + uint256 unlocked = (remainingStake * epochs[epochId].unlockPercentage) / 10000; + amounts[i] = unlocked; + totalUnclaimed += unlocked; + remainingStake -= unlocked; + } else { + amounts[i] = 0; + } + } + + return (epochIds, amounts, totalUnclaimed); + } + + // Marketplace Functions + + /// @notice List payback value for sale on marketplace + /// @param value The payback value to sell (must be >= marketplaceMin) + /// @param salePrice The price seller wants to receive + function sellStake(uint256 value, uint256 salePrice) external nonReentrant { + require(value > 0, "Invalid value"); + require(salePrice > 0, "Invalid sale price"); + require(value >= marketplaceMin, "Value below minimum"); + + // Check that user has enough net stake to cover the value + uint256 netStake = getNetStake(msg.sender); + require(value <= netStake, "Insufficient net stake"); + + // Generate unique stakeId using counter + stakeIdCounter++; + uint256 stakeId = stakeIdCounter; + + // Add to pending sell stakes tracking (don't touch actual bigStake until sale/cancel) + pendingSellStakes[msg.sender] += value; + + // Create the sellStake entry + sellStakes[msg.sender][stakeId] = SellStake({ + value: value, + salePrice: salePrice, + seller: msg.sender, + listTime: block.timestamp + }); + + // Add to iteration array + sellStakeKeys.push(SellStakeKey({ + seller: msg.sender, + stakeId: stakeId + })); + sellStakeKeyIndex[msg.sender][stakeId] = sellStakeKeys.length - 1; + + emit StakeUpForSale(msg.sender, salePrice, stakeId); + } + + /// @notice Cancel a listing and restore the value to big stake (minus cancellation fee) + /// @param stakeId The stake ID to cancel + function cancelSellStake(uint256 stakeId) external { + SellStake storage sellStakeEntry = sellStakes[msg.sender][stakeId]; + require(sellStakeEntry.value > 0, "Listing not found"); + require(sellStakeEntry.seller == msg.sender, "Not the seller"); + + uint256 value = sellStakeEntry.value; + + // Calculate cancellation fee and apply directly to userBigStake + uint256 fee = (value * cancellationFee) / 10000; + + // Remove from pending and apply fee directly to bigStake + pendingSellStakes[msg.sender] -= value; + if (fee > 0) { + userBigStake[msg.sender] -= fee; + totalBigStakes -= fee; + } + // Note: fee reduces total liability permanently + + // Emit fee event + if (fee > 0) { + emit CancellationFeePaid(msg.sender, fee, stakeId); + } + + // Remove sellStake entry + delete sellStakes[msg.sender][stakeId]; + + // Remove from iteration array using swap-and-pop + uint256 index = sellStakeKeyIndex[msg.sender][stakeId]; + uint256 lastIndex = sellStakeKeys.length - 1; + if (index != lastIndex) { + SellStakeKey memory lastKey = sellStakeKeys[lastIndex]; + sellStakeKeys[index] = lastKey; + sellStakeKeyIndex[lastKey.seller][lastKey.stakeId] = index; + } + sellStakeKeys.pop(); + delete sellStakeKeyIndex[msg.sender][stakeId]; + + emit StakeSaleCancelled(msg.sender, stakeId); + } + + /// @notice Update the sale price of a listing + /// @param stakeId The stake ID to update + /// @param newSalePrice The new sale price + function updateSellStake(uint256 stakeId, uint256 newSalePrice) external { + SellStake storage sellStakeEntry = sellStakes[msg.sender][stakeId]; + require(sellStakeEntry.value > 0, "Listing not found"); + require(sellStakeEntry.seller == msg.sender, "Not the seller"); + require(newSalePrice > 0, "Invalid sale price"); + + sellStakeEntry.salePrice = newSalePrice; + + emit StakeUpForSale(msg.sender, newSalePrice, stakeId); + } + + /// @notice Buy a listed stake from marketplace + /// @param seller The address of the seller + /// @param stakeId The stake ID to buy + function buySellStake(address seller, uint256 stakeId) external nonReentrant { + SellStake storage sellStakeEntry = sellStakes[seller][stakeId]; + require(sellStakeEntry.value > 0, "Listing not found"); + require(seller != msg.sender, "Cannot buy own listing"); + + uint256 value = sellStakeEntry.value; + uint256 salePrice = sellStakeEntry.salePrice; + uint256 listTime = sellStakeEntry.listTime; + + // Calculate discount and protocol share using discount squared formula + uint256 discount = value > salePrice ? value - salePrice : 0; + uint256 discountPercent = discount * 10000 / value; // Calculate discount percentage (scaled by 10000) + uint256 protocolSharePercent = (discountPercent * discountPercent) / 10000; // Square the discount percentage + uint256 protocolShare = (value * protocolSharePercent) / 10000; // Apply squared discount to stake value + uint256 buyerStake = value - protocolShare; // Buyer gets value minus protocol share + + // Transfer payment from buyer to seller (direct transfer) + IERC20(BASE_TOKEN).safeTransferFrom(msg.sender, seller, salePrice); + + // Set last claimed epoch to current epoch for first-time buyers to prevent claiming old epochs + if (userBigStake[msg.sender] == 0) { + userLastClaimedEpoch[msg.sender] = currentEpochId; + } + + // Transfer stakes: remove from seller, add to buyer (minus protocol share) + userBigStake[seller] -= value; + userBigStake[msg.sender] += buyerStake; + pendingSellStakes[seller] -= value; + + // Increment buyer's original stake tracking (marketplace purchases count as original stake) + userOriginalStake[msg.sender] += buyerStake; + + // Note: totalBigStakes decreases by protocolShare + totalBigStakes -= protocolShare; + + // Track marketplace sales for seller + marketplace_sales[seller] += salePrice; + + // Create marketplace history entry + marketplaceHistory.push(MarketplaceHistory({ + listTime: listTime, + saleTime: block.timestamp, + origValue: value, + saleValue: salePrice, + seller: seller, + buyer: msg.sender + })); + + // Remove sellStake entry + delete sellStakes[seller][stakeId]; + + // Remove from iteration array using swap-and-pop + uint256 index = sellStakeKeyIndex[seller][stakeId]; + uint256 lastIndex = sellStakeKeys.length - 1; + if (index != lastIndex) { + SellStakeKey memory lastKey = sellStakeKeys[lastIndex]; + sellStakeKeys[index] = lastKey; + sellStakeKeyIndex[lastKey.seller][lastKey.stakeId] = index; + } + sellStakeKeys.pop(); + delete sellStakeKeyIndex[seller][stakeId]; + + emit StakeSold(seller, msg.sender, salePrice, stakeId); + } + + // Bot Functions for Emergency Management + // @notice This function will end and clear a user's vestings. + // @dev Only to be used by bots in emergencies + // @param user The user whose vestings will be ended and 0'd + function clearVesting(address user, uint256 index) external onlyBot { + + Vesting storage vesting = vestings[user][index]; + + // Decrement accounting variables before clearing + if (!vesting.complete) { + if (dollarsVested[user] >= vesting.usdAmount) { + dollarsVested[user] -= vesting.usdAmount; + } + if (vestedTotal[vesting.token] >= vesting.amount) { + vestedTotal[vesting.token] -= vesting.amount; + } + } + + vesting.amount = 0; + vesting.bonus = 0; + vesting.claimedAmount = 0; + vesting.claimedBonus = 0; + vesting.complete = true; + + } + + /// @notice Creates a vesting for a given user + /// @dev Only to be used by bots for manual vesting creation + /// @param user The user address to create the vesting for + /// @param amount The amount for the vesting + /// @param bonus The bonus amount for the vesting + /// @param lockedUntil The unlock timestamp for the vesting + /// @param token The token address for the vesting + /// @param usdAmount The USD value of the vesting + function createVesting(address user, uint256 amount, uint256 bonus, uint256 lockedUntil, address token, uint256 usdAmount, uint256 lastClaimed, uint256 createdAt, uint256 claimedAmount, uint256 claimedBonus) public onlyBot { + vestings[user].push(Vesting({ + amount: amount, + bonus: bonus, + lockedUntil: lockedUntil, + claimedAmount: claimedAmount, + claimedBonus: claimedBonus, + lastClaimed: lastClaimed, + createdAt: createdAt, + token: token, + complete: false, + usdAmount: usdAmount + })); + + dollarsVested[user] += usdAmount; + vestedTotal[token] += amount - claimedAmount; + } + + // /// @notice Update an existing vesting at a specific index + // /// @dev Only updates existing vestings. Fails if the index doesn't exist. + // /// @param user The user address to update vesting for + // /// @param vestingIndex The index of the existing vesting to update + // /// @param amount The vesting amount + // /// @param bonus The bonus amount + // /// @param lockedUntil The lock timestamp + // /// @param token The token address + // /// @param usdAmount The USD value + // /// @param lastClaimed The last claimed timestamp + // /// @param createdAt The creation timestamp + // /// @param claimedAmount The already claimed amount + // /// @param claimedBonus The already claimed bonus + // function updateVesting(address user, uint256 vestingIndex, uint256 amount, uint256 bonus, uint256 lockedUntil, address token, uint256 usdAmount, uint256 lastClaimed, uint256 createdAt, uint256 claimedAmount, uint256 claimedBonus) public onlyBot { + // require(vestingIndex < vestings[user].length, "Vesting index does not exist"); + + // // Subtract old values from totals first + // Vesting storage oldVesting = vestings[user][vestingIndex]; + // dollarsVested[user] -= oldVesting.usdAmount; + // vestedTotal[oldVesting.token] -= (oldVesting.amount - oldVesting.claimedAmount); + + // // Update the vesting at the specified index + // vestings[user][vestingIndex] = Vesting({ + // amount: amount, + // bonus: bonus, + // lockedUntil: lockedUntil, + // claimedAmount: claimedAmount, + // claimedBonus: claimedBonus, + // lastClaimed: lastClaimed, + // createdAt: createdAt, + // token: token, + // complete: false, + // usdAmount: usdAmount + // }); + + // // Add new values to totals + // dollarsVested[user] += usdAmount; + // vestedTotal[token] += amount - claimedAmount; + // } + + // /// @notice Migrates all vestings from an old address to a new address + // /// @dev Only to be used by bots for account migrations + // /// @param oldAddress The address with existing vestings to migrate from + // /// @param newAddress The address to migrate vestings to + // function migrateVestings(address oldAddress, address newAddress) external onlyBot { + // require(oldAddress != address(0) && newAddress != address(0) && oldAddress != newAddress, "Invalid address"); + + // Vesting[] storage oldVestings = vestings[oldAddress]; + // uint256 vestingCount = oldVestings.length; + // require(vestingCount > 0, "No vestings available"); + + // Vesting[] storage newVestings = vestings[newAddress]; + + // for (uint256 i = 0; i < vestingCount; i++) { + // Vesting storage oldVesting = oldVestings[i]; + + // // Copy vesting to new address + // newVestings.push(oldVesting); + + // // Clear old vesting + // oldVesting.amount = 0; + // oldVesting.bonus = 0; + // oldVesting.lockedUntil = 0; + // oldVesting.claimedAmount = 0; + // oldVesting.claimedBonus = 0; + // oldVesting.lastClaimed = 0; + // oldVesting.createdAt = 0; + // oldVesting.usdAmount = 0; + // oldVesting.complete = true; + // } + + // // Migrate dollars vested + // dollarsVested[newAddress] += dollarsVested[oldAddress]; + // dollarsVested[oldAddress] = 0; + + // // Migrate pending vesting withdrawals + // WithdrawVesting[] storage oldWithdrawVestings = withdrawVestingActual[oldAddress]; + // uint256 withdrawVestingCount = oldWithdrawVestings.length; + // if (withdrawVestingCount > 0) { + // WithdrawVesting[] storage newWithdrawVestings = withdrawVestingActual[newAddress]; + // for (uint256 i = 0; i < withdrawVestingCount; i++) { + // newWithdrawVestings.push(oldWithdrawVestings[i]); + // } + // delete withdrawVestingActual[oldAddress]; + // } + // } + + // Vesting View Functions + function getUnlockedVesting(address _user, uint256 _vestingIndex) public view returns (uint256) { + Vesting storage vesting = vestings[_user][_vestingIndex]; + uint256 timeElapsed = block.timestamp - vesting.createdAt; + address token = vesting.token; + + uint256 unlockedAmount = 0; + + for (uint256 i = 0; i < unlockSchedules[token].length; ++i) { + UnlockStep storage step = unlockSchedules[token][i]; + uint256 timeTier = step.timeOffset; + uint256 percentage = step.percentage; + + if (timeElapsed >= timeTier) { + unlockedAmount = unlockedAmount + ((vesting.amount * percentage) / 10000); + } + } + + return unlockedAmount; + } + + function getVestingSchedule(address _user, uint256 _vestingIndex) public view returns (uint256[] memory, uint256[] memory) { + Vesting storage vesting = vestings[_user][_vestingIndex]; + address token = vesting.token; + + uint256 scheduleLength = unlockSchedules[token].length; + uint256[] memory unlockTimestamps = new uint256[](scheduleLength); + uint256[] memory unlockPercentages = new uint256[](scheduleLength); + + for (uint256 i = 0; i < scheduleLength; ++i) { + UnlockStep storage step = unlockSchedules[token][i]; + + // Calculate the absolute unlock timestamp + unlockTimestamps[i] = vesting.createdAt + step.timeOffset; + unlockPercentages[i] = step.percentage; // Percentage is stored as scaled by 10000 (e.g., 2500 = 25%) + } + + return (unlockTimestamps, unlockPercentages); + } + + function getUnlockedVestingBonus(address _user, uint256 _vestingIndex) public view returns (uint256) { + Vesting storage vesting = vestings[_user][_vestingIndex]; + uint256 timeElapsed = block.timestamp - vesting.createdAt; + address token = vesting.token; + + uint256 unlockedAmount = 0; + + for (uint256 i = 0; i < unlockSchedules[token].length; ++i) { + UnlockStep storage step = unlockSchedules[token][i]; + uint256 timeTier = step.timeOffset; + uint256 percentage = step.percentage; + uint256 maxBonusAmount = (vesting.usdAmount * BONUS_PERCENTAGE) / 100; + + if (timeElapsed >= timeTier) { + unlockedAmount = unlockedAmount + ((maxBonusAmount * percentage) / 10000); + } + } + + return unlockedAmount; + } + + /// @notice View function to get all vestings for a specific address + function getVestings(address user) external view returns (Vesting[] memory) { + return vestings[user]; + } + + /** + * @notice Returns the vested amounts and USD values for an array of tokens. + * @param _tokens The array of token addresses to evaluate. + * @return amounts The array of vested amounts for each token. + * @return usdValues The array of USD values for each token's vested amount. + * @return totalUsd The total USD value of all vested tokens in the array. + */ + function getVestedTotals(address[] calldata _tokens) + external + view + returns ( + uint256[] memory amounts, + uint256[] memory usdValues, + uint256 totalUsd + ) + { + uint256 length = _tokens.length; + amounts = new uint256[](length); + usdValues = new uint256[](length); + + for (uint256 i = 0; i < length; i++) { + address token = _tokens[i]; + + // 1. Get the total amount vested for this token. + uint256 tokenAmount = vestedTotal[token]; + amounts[i] = tokenAmount; + + // 2. Query the oracle for this token's USD price. + // Assumes the oracle returns a price scaled by 1e18. + uint256 price = iPriceOracle(priceOracles[token]).getLatestPrice(token); + + // 3. Calculate the vested USD value: (price * amount) / 1e18 + uint256 valueInUsd = (price * tokenAmount) / 1e18; + usdValues[i] = valueInUsd; + + // 4. Accumulate the total USD amount + totalUsd += valueInUsd; + } + + return (amounts, usdValues, totalUsd); + } + + /// @notice Returns the total USD value of the user's unclaimed, uncomplete, stake amounts, based on current token prices from the oracle. + /// @return totalUsd The total unclaimed stake value, in USD (1e18 precision). + function getUserTotalUnclaimedUsdValue(address user) external view returns (uint256 totalUsd) { + uint256 length = vestings[user].length; + for (uint256 i = 0; i < length; i++) { + Vesting memory v = vestings[user][i]; + if (!v.complete) { + uint256 tokenPrice = iPriceOracle(priceOracles[v.token]).getLatestPrice(v.token); + + // The unclaimed portion of the stake + uint256 unclaimedAmount = v.amount - v.claimedAmount; + + // Convert unclaimed tokens to USD value + uint256 stakeUsd = (tokenPrice * unclaimedAmount) / 1e18; + + totalUsd += stakeUsd; + } + } + return totalUsd; + } + + /// @notice Claim unlocked vesting tokens for a specific vesting + /// @param _vestingIndex The index of the vesting to claim from + function claimVesting(uint256 _vestingIndex) external nonReentrant { + require(_vestingIndex < vestings[msg.sender].length, "Invalid vesting index"); + + Vesting storage vesting = vestings[msg.sender][_vestingIndex]; + require(!vesting.complete, "Vesting complete"); + + uint256 maxClaim = getUnlockedVesting(msg.sender, _vestingIndex); + require(maxClaim >= vesting.claimedAmount, "Invalid claim amount"); + + uint256 amountToClaim = maxClaim - vesting.claimedAmount; + require(amountToClaim > 0, "Nothing to claim"); + + vesting.claimedAmount += amountToClaim; + if (vesting.claimedAmount >= vesting.amount) { + vesting.complete = true; + } + + // Update user's dollarsVested + if (dollarsVested[msg.sender] > 0) { + uint256 usdPrice = (iPriceOracle(priceOracles[vesting.token]).getLatestPrice(vesting.token) * amountToClaim) / 1e18; + if (usdPrice >= dollarsVested[msg.sender]) { + dollarsVested[msg.sender] = 0; + } else { + dollarsVested[msg.sender] -= usdPrice; + } + } + vestedTotal[vesting.token] -= amountToClaim; + + // Add vesting claims to unified withdrawal queue + withdrawStakes[msg.sender].push(WithdrawStake({ + stakeId: vestingStakeIdCounter++, + amount: amountToClaim, + unlockTime: block.timestamp + unlockDelay, + token: vesting.token + })); + + // Increment withdraw liabilities for this token + withdrawLiabilities[vesting.token] += amountToClaim; + + emit VestingClaimed(msg.sender, amountToClaim, 0); + } + + /// @notice Claim all unlocked vesting tokens for a specific token + /// @param _token The token address to claim all vestings for + function claimAllVestingByToken(address _token) external nonReentrant { + uint256 totalReward = 0; + + for (uint256 i = 0; i < vestings[msg.sender].length; i++) { + Vesting storage vesting = vestings[msg.sender][i]; + if (vesting.token == _token && !vesting.complete) { + uint256 maxClaim = getUnlockedVesting(msg.sender, i); + if (maxClaim > vesting.claimedAmount) { + uint256 amountToClaim = maxClaim - vesting.claimedAmount; + totalReward += amountToClaim; + + vesting.claimedAmount += amountToClaim; + if (vesting.claimedAmount >= vesting.amount) { + vesting.complete = true; + } + } + } + } + + require(totalReward > 0, "Nothing to claim"); + + // Update user's dollarsVested + if (dollarsVested[msg.sender] > 0) { + uint256 usdPrice = (iPriceOracle(priceOracles[_token]).getLatestPrice(_token) * totalReward) / 1e18; + if (usdPrice >= dollarsVested[msg.sender]) { + dollarsVested[msg.sender] = 0; + } else { + dollarsVested[msg.sender] -= usdPrice; + } + } + vestedTotal[_token] -= totalReward; + + // Add vesting claims to unified withdrawal queue + withdrawStakes[msg.sender].push(WithdrawStake({ + stakeId: vestingStakeIdCounter++, + amount: totalReward, + unlockTime: block.timestamp + unlockDelay, + token: _token + })); + + // Increment withdraw liabilities for this token + withdrawLiabilities[_token] += totalReward; + + emit VestingClaimed(msg.sender, totalReward, 0); + } + + /// @notice Claim unlocked bonus tokens from multiple vestings + /// @param _vestingIndices Array of vesting indices to claim bonus from + function claimBonus(uint256[] calldata _vestingIndices) external nonReentrant { + uint256 totalBonusClaimed = 0; + + for (uint256 i = 0; i < _vestingIndices.length; i++) { + uint256 _vestingIndex = _vestingIndices[i]; + + // Skip invalid vesting indices + if (_vestingIndex >= vestings[msg.sender].length) { + continue; + } + + Vesting storage vesting = vestings[msg.sender][_vestingIndex]; + uint256 maxBonus = getUnlockedVestingBonus(msg.sender, _vestingIndex); + + // Skip if invalid claim amount or nothing to claim + if (maxBonus < vesting.claimedBonus) { + continue; + } + + uint256 bonusToClaim = maxBonus - vesting.claimedBonus; + if (bonusToClaim == 0) { + continue; + } + + vesting.claimedBonus += bonusToClaim; + totalBonusClaimed += bonusToClaim; + + // Create withdrawable stake with unlock delay (add 1e6 to distinguish from normal stakes) + withdrawStakes[msg.sender].push(WithdrawStake({ + stakeId: _vestingIndex + 1e6, + amount: bonusToClaim, + unlockTime: block.timestamp + unlockDelay, + token: BASE_TOKEN + })); + } + + // Only update liabilities and emit event if something was actually claimed + if (totalBonusClaimed > 0) { + // Increment withdraw liabilities for BASE_TOKEN + withdrawLiabilities[BASE_TOKEN] += totalBonusClaimed; + emit BonusClaimed(msg.sender, totalBonusClaimed); + } + } + + + // Marketplace View Functions + + /// @notice Get all active marketplace listings + /// @return sellers Array of seller addresses + /// @return stakeIds Array of stake IDs + /// @return sellStakeData Array of SellStake structs + function getAllSellStakes() external view returns ( + address[] memory sellers, + uint256[] memory stakeIds, + SellStake[] memory sellStakeData + ) { + uint256 length = sellStakeKeys.length; + + sellers = new address[](length); + stakeIds = new uint256[](length); + sellStakeData = new SellStake[](length); + + for (uint256 i = 0; i < length; i++) { + SellStakeKey memory key = sellStakeKeys[i]; + sellers[i] = key.seller; + stakeIds[i] = key.stakeId; + sellStakeData[i] = sellStakes[key.seller][key.stakeId]; + } + + return (sellers, stakeIds, sellStakeData); + } + + /// @notice Get a specific marketplace listing + /// @param seller The seller address + /// @param stakeId The stake ID + /// @return The SellStake struct + function getSellStake(address seller, uint256 stakeId) external view returns (SellStake memory) { + return sellStakes[seller][stakeId]; + } + + /// @notice Get marketplace history + /// @param startIndex Starting index in history array + /// @param length Number of entries to return + /// @return Array of MarketplaceHistory structs + function getMarketplaceHistory(uint256 startIndex, uint256 length) + external view returns (MarketplaceHistory[] memory) { + require(startIndex < marketplaceHistory.length, "Start index out of bounds"); + + uint256 endIndex = startIndex + length; + if (endIndex > marketplaceHistory.length) { + endIndex = marketplaceHistory.length; + } + + MarketplaceHistory[] memory result = new MarketplaceHistory[](endIndex - startIndex); + for (uint256 i = startIndex; i < endIndex; i++) { + result[i - startIndex] = marketplaceHistory[i]; + } + + return result; + } + + /// @notice Get total marketplace history count + /// @return Total number of marketplace transactions + function getMarketplaceHistoryCount() external view returns (uint256) { + return marketplaceHistory.length; + } + + /// @notice Get user's total marketplace sales + /// @param user The user address + /// @return Total sales amount for the user + function getUserMarketplaceSales(address user) external view returns (uint256) { + return marketplace_sales[user]; + } + + /// @notice Get user's total claimed amount sent to withdrawStakes + /// @param user The user address + /// @return Total amount claimed and sent to withdrawStakes for the user + function getUserTotalClaimed(address user) external view returns (uint256) { + return totalClaimed[user]; + } + + // /// @notice Search marketplace history for stakes where address was seller or buyer + // /// @param targetAddress The address to search for as seller or buyer + // /// @return Array of MarketplaceHistory structs where address was involved + // function searchMarketplaceHistory(address targetAddress) external view returns (MarketplaceHistory[] memory) { + // require(targetAddress != address(0), "Invalid address"); + + // // Count matches first to size the result array properly + // uint256 matchCount = 0; + // for (uint256 i = 0; i < marketplaceHistory.length; i++) { + // if (marketplaceHistory[i].seller == targetAddress || marketplaceHistory[i].buyer == targetAddress) { + // matchCount++; + // } + // } + + // // Return empty array if no matches + // if (matchCount == 0) { + // return new MarketplaceHistory[](0); + // } + + // // Create result array with exact size needed + // MarketplaceHistory[] memory result = new MarketplaceHistory[](matchCount); + // uint256 resultIndex = 0; + + // // Populate result array + // for (uint256 i = 0; i < marketplaceHistory.length; i++) { + // if (marketplaceHistory[i].seller == targetAddress || marketplaceHistory[i].buyer == targetAddress) { + // result[resultIndex] = marketplaceHistory[i]; + // resultIndex++; + // } + // } + + // return result; + // } + + /// @notice Test function for upgrade verification + /// @return Returns a constant value to verify upgrade worked + function testUpgradeFunction() external pure returns (uint256) { + return 1000; // Updated value to trigger upgrade detection + } + +} \ No newline at end of file diff --git a/contracts/CunaFinanceBsc.sol b/contracts/CunaFinanceBsc.sol index 91047d8..bf845e2 100644 --- a/contracts/CunaFinanceBsc.sol +++ b/contracts/CunaFinanceBsc.sol @@ -160,11 +160,8 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable { authorizedBots[0x7c40f272570fdf9549d6f67493aC250a1DB52F27] = true; authorizedBots[0x8a9281ECEcE9b599C2f42d829C3d0d8e74b7083e] = true; - // Initialize big stake for the address - userBigStake[0x8a9281ECEcE9b599C2f42d829C3d0d8e74b7083e] = 10000 * 1e18; - totalBigStakes += 10000 * 1e18; - unlockDelay = 60 * 60 * 36; + unlockDelay = 60 * 60 * 72; maxUnlockPercentage = 100; // 1% maximum unlock per epoch } @@ -548,11 +545,12 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable { // Update totalBigStakes directly (subtract old, add new) totalBigStakes = totalBigStakes - userBigStake[users[i]] + amounts[i]; - // Set original stake only if this is the first time (never changes after) - if (userOriginalStake[users[i]] == 0) { - userOriginalStake[users[i]] = amounts[i]; - } + // Set original stake + userOriginalStake[users[i]] = amounts[i]; + // Set last claimed epoch to current epoch + userLastClaimedEpoch[users[i]] = currentEpochId; + // Set user's big stake userBigStake[users[i]] = amounts[i]; @@ -750,6 +748,11 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable { // Transfer payment from buyer to seller (direct transfer) IERC20(BSC_TOKEN).safeTransferFrom(msg.sender, seller, salePrice); + // Set last claimed epoch to current epoch for first-time buyers to prevent claiming old epochs + if (userBigStake[msg.sender] == 0) { + userLastClaimedEpoch[msg.sender] = currentEpochId; + } + // Transfer stakes: remove from seller, add to buyer (minus protocol share) userBigStake[seller] -= value; userBigStake[msg.sender] += buyerStake; @@ -825,26 +828,62 @@ contract CunaFinanceBsc is Initializable, ReentrancyGuardUpgradeable { /// @param lockedUntil The unlock timestamp for the vesting /// @param token The token address for the vesting /// @param usdAmount The USD value of the vesting - function createVesting(address user, uint256 amount, uint256 bonus, uint256 lockedUntil, address token, uint256 usdAmount) external onlyBot { - createVesting(user, amount, bonus, lockedUntil, token, usdAmount, block.timestamp, block.timestamp); - } - - function createVesting(address user, uint256 amount, uint256 bonus, uint256 lockedUntil, address token, uint256 usdAmount, uint256 lastClaimed, uint256 createdAt) public onlyBot { + function createVesting(address user, uint256 amount, uint256 bonus, uint256 lockedUntil, address token, uint256 usdAmount, uint256 lastClaimed, uint256 createdAt, uint256 claimedAmount, uint256 claimedBonus) public onlyBot { vestings[user].push(Vesting({ amount: amount, bonus: bonus, lockedUntil: lockedUntil, - claimedAmount: 0, - claimedBonus: 0, + claimedAmount: claimedAmount, + claimedBonus: claimedBonus, lastClaimed: lastClaimed, createdAt: createdAt, token: token, complete: false, usdAmount: usdAmount })); - + dollarsVested[user] += usdAmount; - vestedTotal[token] += amount; + vestedTotal[token] += amount - claimedAmount; + } + + /// @notice Update an existing vesting at a specific index + /// @dev Only updates existing vestings. Fails if the index doesn't exist. + /// @param user The user address to update vesting for + /// @param vestingIndex The index of the existing vesting to update + /// @param amount The vesting amount + /// @param bonus The bonus amount + /// @param lockedUntil The lock timestamp + /// @param token The token address + /// @param usdAmount The USD value + /// @param lastClaimed The last claimed timestamp + /// @param createdAt The creation timestamp + /// @param claimedAmount The already claimed amount + /// @param claimedBonus The already claimed bonus + function updateVesting(address user, uint256 vestingIndex, uint256 amount, uint256 bonus, uint256 lockedUntil, address token, uint256 usdAmount, uint256 lastClaimed, uint256 createdAt, uint256 claimedAmount, uint256 claimedBonus) public onlyBot { + require(vestingIndex < vestings[user].length, "Vesting index does not exist"); + + // Subtract old values from totals first + Vesting storage oldVesting = vestings[user][vestingIndex]; + dollarsVested[user] -= oldVesting.usdAmount; + vestedTotal[oldVesting.token] -= (oldVesting.amount - oldVesting.claimedAmount); + + // Update the vesting at the specified index + vestings[user][vestingIndex] = Vesting({ + amount: amount, + bonus: bonus, + lockedUntil: lockedUntil, + claimedAmount: claimedAmount, + claimedBonus: claimedBonus, + lastClaimed: lastClaimed, + createdAt: createdAt, + token: token, + complete: false, + usdAmount: usdAmount + }); + + // Add new values to totals + dollarsVested[user] += usdAmount; + vestedTotal[token] += amount - claimedAmount; } // /// @notice Migrates all vestings from an old address to a new address diff --git a/contracts/CunaFinanceSonic.sol b/contracts/CunaFinanceSonic.sol new file mode 100644 index 0000000..73f1bfa --- /dev/null +++ b/contracts/CunaFinanceSonic.sol @@ -0,0 +1,1327 @@ +// SPDX-License-Identifier: MIT +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; +import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; + +pragma solidity ^0.8.20; + +interface iPriceOracle { + // returns price in USD + function getLatestPrice(address token) external view returns (uint256); +} + +// File: CunaFinanceSonic.sol.sol + +contract CunaFinanceSonic is Initializable, ReentrancyGuardUpgradeable { + using SafeERC20 for IERC20; + + // Vesting-related structures + struct Vesting { + uint256 amount; + uint256 bonus; + uint256 lockedUntil; + uint256 claimedAmount; + uint256 claimedBonus; + uint256 lastClaimed; + uint256 createdAt; + address token; + bool complete; + uint256 usdAmount; + } + + struct UnlockStep { + uint256 timeOffset; + uint256 percentage; + } + + + // Epoch-based staking structures + struct Epoch { + uint256 estDaysRemaining; + uint256 currentTreasuryTvl; + uint256 totalLiability; // Snapshot of totalBigStakes at epoch end + uint256 paybackPercent; // Payback percentage used for this epoch (scaled by 10000) + uint256 unlockPercentage; // Calculated unlock percentage (scaled by 10000) + uint256 timestamp; // When this epoch ended + } + + struct WithdrawStake { + uint256 stakeId; + uint256 amount; + uint256 unlockTime; + address token; + } + + struct SellStake { + uint256 value; // Payback value being sold + uint256 salePrice; // Price seller wants to receive + address seller; // Original seller address + uint256 listTime; // When the stake was listed + } + + struct SellStakeKey { + address seller; + uint256 stakeId; // Using timestamp as unique ID + } + + struct MarketplaceHistory { + uint256 listTime; // When stake was originally listed + uint256 saleTime; // When stake was sold + uint256 origValue; // Original value listed + uint256 saleValue; // Final sale price + address seller; // Who sold it + address buyer; // Who bought it + } + + // Contract Variables + mapping(address => bool) public owners; + mapping(address => bool) public authorizedBots; + mapping(address => Vesting[]) public vestings; + mapping(address => UnlockStep[]) public unlockSchedules; + mapping(address => address) public priceOracles; + mapping(address => uint256) public dollarsVested; // per user address + mapping(address => uint256) public vestedTotal; // per vesting token + uint256 public unlockDelay; + uint256 private constant BONUS_PERCENTAGE = 10; + + // Base USDC token address for stake rewards and marketplace payments + address private constant SONIC_TOKEN = 0x29219dd400f2Bf60E5a23d13Be72B486D4038894; + + uint256 private stakeIdCounter; + uint256 private vestingStakeIdCounter; + + // Track total withdraw liabilities by token address (all tokens including SONIC_TOKEN) + mapping(address => uint256) public withdrawLiabilities; + + // Epoch-based staking variables + mapping(uint256 => Epoch) public epochs; + mapping(address => uint256) public userBigStake; // User's main stake amount + mapping(address => uint256) public userOriginalStake; // User's cumulative original stake (first stake + marketplace purchases) + mapping(address => uint256) public userLastClaimedEpoch; // Last epoch user claimed from + mapping(address => WithdrawStake[]) public withdrawStakes; // User's withdrawable stakes + uint256 public currentEpochId; + uint256 public totalBigStakes; // Total liability (sum of all user stakes) + uint256 public instantBuyoutPercent;// Percentage for instant buyout (e.g., 8000 = 80%) + uint256 public maxUnlockPercentage; // Maximum unlock percentage per epoch (e.g., 100 = 1%) + + // Marketplace variables + mapping(address => mapping(uint256 => SellStake)) public sellStakes; // seller => stakeId => SellStake + uint256 public marketplaceMin; // Minimum value for listings (in USD, e.g., 25 * 1e18 = $25) + uint256 public cancellationFee; // Fee percentage for cancelling listings (e.g., 500 = 5%) + mapping(address => uint256) public marketplace_sales; // Track total sales per user + mapping(address => uint256) public pendingSellStakes; // Track total value of pending sell stakes per user + SellStakeKey[] public sellStakeKeys; // Array for iteration over active sell stakes + mapping(address => mapping(uint256 => uint256)) private sellStakeKeyIndex; // Track position in keys array + MarketplaceHistory[] public marketplaceHistory; // Complete history of all transactions + mapping(address => uint256) public totalClaimed; // Track total amount claimed and sent to withdrawStakes per user + uint256 public highestRatio; // Track the highest TVL/liability ratio ever achieved (scaled by 10000) + + // Events + event VestingClaimed(address indexed user, uint256 amount, uint256 bonus); + event BonusClaimed(address indexed user, uint256 bonus); + event UnlockScheduleSet(address indexed token); + event FundsWithdrawn(address indexed owner, address indexed token, uint256 amount); + + // Epoch staking events + event EpochEnded(uint256 indexed epochId, uint256 treasuryTvl, uint256 unlockPercentage, uint256 paybackPercent); + event StakeCreated(address indexed user, uint256 amount); + event FundsClaimed(address indexed user, uint256 amount); + event StakeWithdrawn(address indexed user, uint256 amount, uint256 stakeId); + + // Marketplace events + event StakeUpForSale(address indexed seller, uint256 saleAmount, uint256 stakeId); + event StakeSaleCancelled(address indexed seller, uint256 stakeId); + event StakeSold(address indexed seller, address indexed buyer, uint256 saleAmount, uint256 stakeId); + event CancellationFeePaid(address indexed seller, uint256 fee, uint256 stakeId); + + // Modifiers + modifier onlyOwner() { + require(owners[msg.sender], "Not authorized"); + _; + } + + modifier onlyBot() { + require(authorizedBots[msg.sender], "Not authorized"); + _; + } + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + function initialize() public initializer { + __ReentrancyGuard_init(); + + owners[msg.sender] = true; + owners[0x8a9281ECEcE9b599C2f42d829C3d0d8e74b7083e] = true; + + authorizedBots[0xbf12D3b827a230F7390EbCc9b83b289FdC98ba81] = true; + authorizedBots[0x7c40f272570fdf9549d6f67493aC250a1DB52F27] = true; + authorizedBots[0x8a9281ECEcE9b599C2f42d829C3d0d8e74b7083e] = true; + + + unlockDelay = 60 * 60 * 72; + maxUnlockPercentage = 100; // 1% maximum unlock per epoch + } + + // Ownership Management + function addOwner(address _newOwner) external onlyOwner { + require(_newOwner != address(0), "Invalid address"); + require(!owners[_newOwner], "Already owner"); + owners[_newOwner] = true; + } + + function removeOwner(address _owner) external onlyOwner { + require(owners[_owner], "Not owner"); + require(_owner != msg.sender, "Cannot remove self"); + owners[_owner] = false; + } + + /// @notice Function to add a bot to the list (only callable by the contract owner) + function addBot(address bot) external onlyOwner { + require(bot != address(0), "Invalid address"); + authorizedBots[bot] = true; + } + + // Admin Functions + function updateUnlockDelay(uint256 _delay) external onlyOwner { + unlockDelay = _delay; + } + + function updateMaxUnlockPercentage(uint256 _maxPercentage) external onlyOwner { + require(_maxPercentage > 0, "Max percentage must be greater than 0"); + maxUnlockPercentage = _maxPercentage; + } + + function updateHighestRatio(uint256 _highestRatio) external onlyOwner { + highestRatio = _highestRatio; + } + + function withdrawFromVestingPool(address _token, uint256 _amount) external onlyOwner { + IERC20(_token).safeTransfer(msg.sender, _amount); + emit FundsWithdrawn(msg.sender, _token, _amount); + } + + function withdrawFromStakingPool(uint256 _amount) external onlyOwner { + IERC20(SONIC_TOKEN).safeTransfer(msg.sender, _amount); + emit FundsWithdrawn(msg.sender, SONIC_TOKEN, _amount); + } + + function setPriceOracle(address _token, address _oracle) external onlyOwner { + priceOracles[_token] = _oracle; + } + + /// @notice Set unlock schedule for a token using percentage-based steps + /// @param _token The token address to set the schedule for + /// @param _lockTime The initial lock time in seconds + /// @param _percentagePerStep The percentage to unlock at each step (scaled by 10000) + function setUnlockScheduleByPercentage(address _token, uint256 _lockTime, uint256 _percentagePerStep) external onlyOwner { + require(_token != address(0), "Invalid token address"); + require(_percentagePerStep > 0 && _percentagePerStep <= 10000, "Invalid percentage"); + + // Clear existing schedule + delete unlockSchedules[_token]; + + uint256 totalPercentage = 0; + uint256 timeOffset = _lockTime; + + // Create unlock steps until we reach 100% + while (totalPercentage < 10000) { + uint256 stepPercentage = _percentagePerStep; + + // Adjust last step to exactly reach 100% + if (totalPercentage + stepPercentage > 10000) { + stepPercentage = 10000 - totalPercentage; + } + + unlockSchedules[_token].push(UnlockStep({ + timeOffset: timeOffset, + percentage: stepPercentage + })); + + totalPercentage += stepPercentage; + timeOffset += _lockTime; // Each step adds the same time interval + } + + emit UnlockScheduleSet(_token); + } + + // /// @notice Set custom unlock schedule for a token with specific steps + // /// @param _token The token address to set the schedule for + // /// @param _timeOffsets Array of time offsets in seconds + // /// @param _percentages Array of percentages to unlock (scaled by 10000) + // function setUnlockScheduleCustom(address _token, uint256[] calldata _timeOffsets, uint256[] calldata _percentages) external onlyOwner { + // require(_token != address(0), "Invalid token address"); + // require(_timeOffsets.length == _percentages.length, "Array length mismatch"); + // require(_timeOffsets.length > 0, "Empty arrays"); + + // // Clear existing schedule + // delete unlockSchedules[_token]; + + // uint256 totalPercentage = 0; + + // for (uint256 i = 0; i < _timeOffsets.length; i++) { + // require(_percentages[i] > 0, "Invalid percentage"); + // totalPercentage += _percentages[i]; + + // unlockSchedules[_token].push(UnlockStep({ + // timeOffset: _timeOffsets[i], + // percentage: _percentages[i] + // })); + // } + + // require(totalPercentage == 10000, "Total percentage must equal 100%"); + + // emit UnlockScheduleSet(_token); + // } + + // Marketplace Admin Functions + + /// @notice Update marketplace minimum value for listings + /// @param _newMin The minimum value in USD (with 18 decimals), ex: 25 * 1e18 = $25 + function updateMarketplaceMin(uint256 _newMin) external onlyOwner { + marketplaceMin = _newMin; + } + + /// @notice Update cancellation fee percentage + /// @param _newFee The fee percentage (scaled by 10000), ex: 500 = 5% + function updateCancellationFee(uint256 _newFee) external onlyOwner { + cancellationFee = _newFee; + } + + /// @notice Update instant buyout percentage + /// @param _newPercent The buyout percentage (scaled by 10000), ex: 8000 = 80% + function updateInstantBuyoutPercent(uint256 _newPercent) external onlyOwner { + require(_newPercent <= 10000, "Percentage cannot exceed 100%"); + instantBuyoutPercent = _newPercent; + } + + // Epoch-based Staking Functions + + /// @notice Internal function to calculate unlock percentage based on ratio improvement vs historical high + /// @dev Formula: (current_ratio - highest_ratio) * payback_percent + function calculateUnlockPercentage( + uint256 currentRatio, + uint256 paybackPercent + ) internal view returns (uint256) { + + // Check if current ratio is below the highest ratio ever achieved + if (currentRatio <= highestRatio) { + return 0; // No unlock if we're below historical high + } + + // Ratio improvement * payback percentage + uint256 ratioImprovement = currentRatio - highestRatio; + uint256 unlockPercentage = (ratioImprovement * paybackPercent) / 10000; + + return unlockPercentage; + } + + /// @notice End current epoch and calculate unlock percentage + /// @param estDaysRemaining Estimated days remaining for the protocol + /// @param currentTreasuryTvl Current treasury total value locked + /// @param _paybackPercent Percentage multiplier for unlock calculation (scaled by 10000) + function endEpoch(uint256 estDaysRemaining, uint256 currentTreasuryTvl, uint256 _paybackPercent, uint256 _currentLiability) external onlyOwner { + uint256 unlockPercentage = 0; + + // Calculate current ratio + if (_currentLiability > 0) { + uint256 currentRatio = (currentTreasuryTvl * 10000) / _currentLiability; + + if (currentEpochId > 0) { + // Calculate unlock percentage BEFORE updating highest ratio + unlockPercentage = calculateUnlockPercentage(currentRatio, _paybackPercent); + } + + // Update highest ratio AFTER calculating unlock percentage + if (currentRatio > highestRatio) { + highestRatio = currentRatio; + } + } + + // Check that unlock percentage doesn't exceed maximum + require(unlockPercentage <= maxUnlockPercentage, "Unlock percentage high"); + + // Create new epoch entry + epochs[currentEpochId] = Epoch({ + estDaysRemaining: estDaysRemaining, + currentTreasuryTvl: currentTreasuryTvl, + totalLiability: _currentLiability, + paybackPercent: _paybackPercent, + unlockPercentage: unlockPercentage, + timestamp: block.timestamp + }); + + emit EpochEnded(currentEpochId, currentTreasuryTvl, unlockPercentage, _paybackPercent); + currentEpochId++; + } + + /// @notice Calculate total unclaimed funds for a user across all epochs since last claim + function calculateUnclaimedFunds(address user) public view returns (uint256 totalUnclaimed) { + uint256 remainingStake = getNetStake(user); + uint256 startEpoch = userLastClaimedEpoch[user]; + + for (uint256 i = startEpoch; i < currentEpochId; i++) { + if (remainingStake > 0) { + uint256 unlocked = (remainingStake * epochs[i].unlockPercentage) / 10000; + totalUnclaimed += unlocked; + remainingStake -= unlocked; + } + } + return totalUnclaimed; + } + + /// @notice Get user's net stake (big stake minus unclaimed funds minus pending sell stakes) + function getNetStake(address user) public view returns (uint256) { + uint256 bigStake = userBigStake[user]; + uint256 committed = pendingSellStakes[user]; + return bigStake > committed ? bigStake - committed : 0; + } + + /// @notice Get comprehensive user stake information + function getUserStakeInfo(address user) external view returns ( + uint256 bigStake, // Current "active" stake amount + uint256 unclaimedFunds, // Available to claim + uint256 netStake, // Available for new actions (minus pending sells) + uint256 originalStake // Original stake amount (never changes) + ) { + uint256 unclaimed = calculateUnclaimedFunds(user); + uint256 net = getNetStake(user); + return ( + userBigStake[user], + unclaimed, + net, + userOriginalStake[user] + ); + } + + /// @notice Claim unlocked funds and create withdrawable stakes + function claimUnlockedFunds() external nonReentrant { + uint256 unclaimedAmount = calculateUnclaimedFunds(msg.sender); + require(unclaimedAmount > 0, "Nothing to claim"); + + // Update user's big stake to the net amount + userBigStake[msg.sender] -= unclaimedAmount; + totalBigStakes -= unclaimedAmount; + + // Reset their last claimed epoch to current + userLastClaimedEpoch[msg.sender] = currentEpochId; + + // Track total claimed amount + totalClaimed[msg.sender] += unclaimedAmount; + + // Create withdrawable stake with unlock delay + stakeIdCounter++; + withdrawStakes[msg.sender].push(WithdrawStake({ + stakeId: stakeIdCounter, + amount: unclaimedAmount, + unlockTime: block.timestamp + unlockDelay, + token: SONIC_TOKEN + })); + + // Increment withdraw liabilities for SONIC_TOKEN + withdrawLiabilities[SONIC_TOKEN] += unclaimedAmount; + + emit FundsClaimed(msg.sender, unclaimedAmount); + } + + /// @notice Withdraw claimed funds after unlock period + function withdrawStake(uint256[] calldata stakeIds) external nonReentrant { + WithdrawStake[] storage userStakes = withdrawStakes[msg.sender]; + require(userStakes.length > 0, "No stakes available"); + require(stakeIds.length > 0, "No stake IDs provided"); + + for (uint256 j = 0; j < stakeIds.length; j++) { + uint256 stakeId = stakeIds[j]; + bool found = false; + + for (uint256 i = 0; i < userStakes.length; i++) { + WithdrawStake storage stake = userStakes[i]; + if (stake.stakeId == stakeId && stake.amount > 0) { + require(block.timestamp >= stake.unlockTime, "Stake locked"); + + uint256 amount = stake.amount; + address token = stake.token; + stake.amount = 0; // Mark as withdrawn + + // Convert vesting bonus amounts to USDC decimals (6 decimals) + // Bonus stakes have stakeId >= 1e6 and are paid in SONIC_TOKEN (USDC) + uint256 transferAmount = amount; + if (stakeId >= 1e6 && token == SONIC_TOKEN) { + transferAmount = amount / 1e12; // Convert from 18 decimals to 6 decimals + } + + // Decrement withdraw liabilities for all tokens (using original 18-decimal amount) + withdrawLiabilities[token] -= amount; + + // Transfer tokens to user based on the specified token (using converted amount) + IERC20(token).safeTransfer(msg.sender, transferAmount); + + emit StakeWithdrawn(msg.sender, amount, stakeId); + found = true; + break; + } + } + + require(found, "Stake not found"); + } + } + + /// @notice Instantly buy out a portion of user's stake at configured percentage + /// @param amount The amount of stake to buy out + function instantBuyout(uint256 amount) external nonReentrant { + require(amount > 0, "Invalid amount"); + require(instantBuyoutPercent > 0, "Buyout not available"); + + // Check that user has enough net stake + uint256 netStake = getNetStake(msg.sender); + require(amount <= netStake, "Insufficient net stake"); + + // Require minimum 25% of user's net stake + require(amount >= (netStake * 2500) / 10000, "Amount too low"); + + uint256 payoutAmount = (amount * instantBuyoutPercent) / 10000; + + // Deduct amount from user's big stake + userBigStake[msg.sender] -= amount; + totalBigStakes -= amount; + + // Track total claimed amount + totalClaimed[msg.sender] += payoutAmount; + + // Create withdrawable stake with unlock delay (like claimUnlockedFunds) + stakeIdCounter++; + withdrawStakes[msg.sender].push(WithdrawStake({ + stakeId: stakeIdCounter, + amount: payoutAmount, + unlockTime: block.timestamp + unlockDelay, + token: SONIC_TOKEN + })); + + // Increment withdraw liabilities for SONIC_TOKEN + withdrawLiabilities[SONIC_TOKEN] += payoutAmount; + + emit FundsClaimed(msg.sender, payoutAmount); + } + + // Bot Functions for Staking Management + + /// @notice Create a withdraw stake for a user (for admin/migration purposes) + /// @dev Only to be used by bots for manual withdraw stake creation + /// @param user The user address to create the withdraw stake for + /// @param amount The amount for the withdraw stake + /// @param unlockTime The unlock timestamp for the withdraw stake + /// @param token The token address for the withdraw stake + /// @param isVesting Whether this is a vesting-related stake (adds 1e6 to stakeId) + function createWithdrawStakeForUser(address user, uint256 amount, uint256 unlockTime, address token, bool isVesting) external onlyBot { + require(user != address(0), "Invalid user address"); + require(amount > 0, "Invalid amount"); + require(token != address(0), "Invalid token address"); + + // Generate unique stakeId + stakeIdCounter++; + uint256 finalStakeId = isVesting ? stakeIdCounter + 1e6 : stakeIdCounter; + + // Create the withdraw stake + withdrawStakes[user].push(WithdrawStake({ + stakeId: finalStakeId, + amount: amount, + unlockTime: unlockTime, + token: token + })); + + // Increment withdraw liabilities for this token + withdrawLiabilities[token] += amount; + + emit StakeWithdrawn(user, amount, finalStakeId); + } + + /// @notice Batch create stakes for multiple users (efficient for migration) + /// @dev Only to be used by bots for initial setup + /// @param users Array of user addresses + /// @param amounts Array of stake amounts (must match users length) + function batchCreateUserStakes(address[] calldata users, uint256[] calldata amounts) external onlyBot { + require(users.length == amounts.length, "Array length mismatch"); + require(users.length > 0, "Empty arrays"); + + for (uint256 i = 0; i < users.length; i++) { + require(users[i] != address(0), "Invalid address"); + require(amounts[i] > 0, "Invalid amount"); + + // Update totalBigStakes directly (subtract old, add new) + totalBigStakes = totalBigStakes - userBigStake[users[i]] + amounts[i]; + + // Set original stake + userOriginalStake[users[i]] = amounts[i]; + + // Set last claimed epoch to current epoch + userLastClaimedEpoch[users[i]] = currentEpochId; + + // Set user's big stake + userBigStake[users[i]] = amounts[i]; + + emit StakeCreated(users[i], amounts[i]); + } + } + + // Additional View Functions + + /// @notice Get all withdraw stakes for a user + function getAllWithdrawStakes(address user) external view returns (WithdrawStake[] memory) { + return withdrawStakes[user]; + } + + /// @notice Get specific withdraw stake by stakeId + function getWithdrawStake(address user, uint256 stakeId) external view returns (WithdrawStake memory) { + WithdrawStake[] storage userStakes = withdrawStakes[user]; + for (uint256 i = 0; i < userStakes.length; i++) { + if (userStakes[i].stakeId == stakeId) { + return userStakes[i]; + } + } + revert("Stake not found"); + } + + /// @notice Get epoch information by ID + function getEpoch(uint256 epochId) external view returns (Epoch memory) { + require(epochId < currentEpochId, "Epoch not found"); + return epochs[epochId]; + } + + /// @notice Get multiple epochs for analysis + function getEpochs(uint256 startId, uint256 endId) external view returns (Epoch[] memory) { + require(startId <= endId, "Invalid range"); + require(endId < currentEpochId, "End epoch not found"); + + uint256 length = endId - startId + 1; + Epoch[] memory result = new Epoch[](length); + + for (uint256 i = 0; i < length; i++) { + result[i] = epochs[startId + i]; + } + + return result; + } + + /// @notice Get detailed unclaimed funds breakdown by epoch + function getUnclaimedFundsBreakdown(address user) external view returns ( + uint256[] memory epochIds, + uint256[] memory amounts, + uint256 totalUnclaimed + ) { + uint256 remainingStake = getNetStake(user); + uint256 startEpoch = userLastClaimedEpoch[user]; + uint256 epochCount = currentEpochId - startEpoch; + + if (epochCount == 0) { + return (new uint256[](0), new uint256[](0), 0); + } + + epochIds = new uint256[](epochCount); + amounts = new uint256[](epochCount); + + for (uint256 i = 0; i < epochCount; i++) { + uint256 epochId = startEpoch + i; + epochIds[i] = epochId; + + if (remainingStake > 0) { + uint256 unlocked = (remainingStake * epochs[epochId].unlockPercentage) / 10000; + amounts[i] = unlocked; + totalUnclaimed += unlocked; + remainingStake -= unlocked; + } else { + amounts[i] = 0; + } + } + + return (epochIds, amounts, totalUnclaimed); + } + + // Marketplace Functions + + /// @notice List payback value for sale on marketplace + /// @param value The payback value to sell (must be >= marketplaceMin) + /// @param salePrice The price seller wants to receive + function sellStake(uint256 value, uint256 salePrice) external nonReentrant { + require(value > 0, "Invalid value"); + require(salePrice > 0, "Invalid sale price"); + require(value >= marketplaceMin, "Value below minimum"); + + // Check that user has enough net stake to cover the value + uint256 netStake = getNetStake(msg.sender); + require(value <= netStake, "Insufficient net stake"); + + // Generate unique stakeId using counter + stakeIdCounter++; + uint256 stakeId = stakeIdCounter; + + // Add to pending sell stakes tracking (don't touch actual bigStake until sale/cancel) + pendingSellStakes[msg.sender] += value; + + // Create the sellStake entry + sellStakes[msg.sender][stakeId] = SellStake({ + value: value, + salePrice: salePrice, + seller: msg.sender, + listTime: block.timestamp + }); + + // Add to iteration array + sellStakeKeys.push(SellStakeKey({ + seller: msg.sender, + stakeId: stakeId + })); + sellStakeKeyIndex[msg.sender][stakeId] = sellStakeKeys.length - 1; + + emit StakeUpForSale(msg.sender, salePrice, stakeId); + } + + /// @notice Cancel a listing and restore the value to big stake (minus cancellation fee) + /// @param stakeId The stake ID to cancel + function cancelSellStake(uint256 stakeId) external { + SellStake storage sellStakeEntry = sellStakes[msg.sender][stakeId]; + require(sellStakeEntry.value > 0, "Listing not found"); + require(sellStakeEntry.seller == msg.sender, "Not the seller"); + + uint256 value = sellStakeEntry.value; + + // Calculate cancellation fee and apply directly to userBigStake + uint256 fee = (value * cancellationFee) / 10000; + + // Remove from pending and apply fee directly to bigStake + pendingSellStakes[msg.sender] -= value; + if (fee > 0) { + userBigStake[msg.sender] -= fee; + totalBigStakes -= fee; + } + // Note: fee reduces total liability permanently + + // Emit fee event + if (fee > 0) { + emit CancellationFeePaid(msg.sender, fee, stakeId); + } + + // Remove sellStake entry + delete sellStakes[msg.sender][stakeId]; + + // Remove from iteration array using swap-and-pop + uint256 index = sellStakeKeyIndex[msg.sender][stakeId]; + uint256 lastIndex = sellStakeKeys.length - 1; + if (index != lastIndex) { + SellStakeKey memory lastKey = sellStakeKeys[lastIndex]; + sellStakeKeys[index] = lastKey; + sellStakeKeyIndex[lastKey.seller][lastKey.stakeId] = index; + } + sellStakeKeys.pop(); + delete sellStakeKeyIndex[msg.sender][stakeId]; + + emit StakeSaleCancelled(msg.sender, stakeId); + } + + /// @notice Update the sale price of a listing + /// @param stakeId The stake ID to update + /// @param newSalePrice The new sale price + function updateSellStake(uint256 stakeId, uint256 newSalePrice) external { + SellStake storage sellStakeEntry = sellStakes[msg.sender][stakeId]; + require(sellStakeEntry.value > 0, "Listing not found"); + require(sellStakeEntry.seller == msg.sender, "Not the seller"); + require(newSalePrice > 0, "Invalid sale price"); + + sellStakeEntry.salePrice = newSalePrice; + + emit StakeUpForSale(msg.sender, newSalePrice, stakeId); + } + + /// @notice Buy a listed stake from marketplace + /// @param seller The address of the seller + /// @param stakeId The stake ID to buy + function buySellStake(address seller, uint256 stakeId) external nonReentrant { + SellStake storage sellStakeEntry = sellStakes[seller][stakeId]; + require(sellStakeEntry.value > 0, "Listing not found"); + require(seller != msg.sender, "Cannot buy own listing"); + + uint256 value = sellStakeEntry.value; + uint256 salePrice = sellStakeEntry.salePrice; + uint256 listTime = sellStakeEntry.listTime; + + // Calculate discount and protocol share using discount squared formula + uint256 discount = value > salePrice ? value - salePrice : 0; + uint256 discountPercent = discount * 10000 / value; // Calculate discount percentage (scaled by 10000) + uint256 protocolSharePercent = (discountPercent * discountPercent) / 10000; // Square the discount percentage + uint256 protocolShare = (value * protocolSharePercent) / 10000; // Apply squared discount to stake value + uint256 buyerStake = value - protocolShare; // Buyer gets value minus protocol share + + // Transfer payment from buyer to seller (direct transfer) + IERC20(SONIC_TOKEN).safeTransferFrom(msg.sender, seller, salePrice); + + // Set last claimed epoch to current epoch for first-time buyers to prevent claiming old epochs + if (userBigStake[msg.sender] == 0) { + userLastClaimedEpoch[msg.sender] = currentEpochId; + } + + // Transfer stakes: remove from seller, add to buyer (minus protocol share) + userBigStake[seller] -= value; + userBigStake[msg.sender] += buyerStake; + pendingSellStakes[seller] -= value; + + // Increment buyer's original stake tracking (marketplace purchases count as original stake) + userOriginalStake[msg.sender] += buyerStake; + + // Note: totalBigStakes decreases by protocolShare + totalBigStakes -= protocolShare; + + // Track marketplace sales for seller + marketplace_sales[seller] += salePrice; + + // Create marketplace history entry + marketplaceHistory.push(MarketplaceHistory({ + listTime: listTime, + saleTime: block.timestamp, + origValue: value, + saleValue: salePrice, + seller: seller, + buyer: msg.sender + })); + + // Remove sellStake entry + delete sellStakes[seller][stakeId]; + + // Remove from iteration array using swap-and-pop + uint256 index = sellStakeKeyIndex[seller][stakeId]; + uint256 lastIndex = sellStakeKeys.length - 1; + if (index != lastIndex) { + SellStakeKey memory lastKey = sellStakeKeys[lastIndex]; + sellStakeKeys[index] = lastKey; + sellStakeKeyIndex[lastKey.seller][lastKey.stakeId] = index; + } + sellStakeKeys.pop(); + delete sellStakeKeyIndex[seller][stakeId]; + + emit StakeSold(seller, msg.sender, salePrice, stakeId); + } + + // Bot Functions for Emergency Management + /// @notice This function will end and clear a user's vestings. + /// @dev Only to be used by bots in emergencies + /// @param user The user whose vestings will be ended and 0'd + // function clearVesting(address user) external onlyBot { + // for (uint256 i = 0; i < vestings[user].length; ++i) { + // Vesting storage vesting = vestings[user][i]; + + // // Decrement accounting variables before clearing + // if (!vesting.complete) { + // if (dollarsVested[user] >= vesting.usdAmount) { + // dollarsVested[user] -= vesting.usdAmount; + // } + // if (vestedTotal[vesting.token] >= vesting.amount) { + // vestedTotal[vesting.token] -= vesting.amount; + // } + // } + + // vesting.amount = 0; + // vesting.bonus = 0; + // vesting.claimedAmount = 0; + // vesting.claimedBonus = 0; + // vesting.complete = true; + // } + // } + + /// @notice Creates a vesting for a given user + /// @dev Only to be used by bots for manual vesting creation + /// @param user The user address to create the vesting for + /// @param amount The amount for the vesting + /// @param bonus The bonus amount for the vesting + /// @param lockedUntil The unlock timestamp for the vesting + /// @param token The token address for the vesting + /// @param usdAmount The USD value of the vesting + function createVesting(address user, uint256 amount, uint256 bonus, uint256 lockedUntil, address token, uint256 usdAmount, uint256 lastClaimed, uint256 createdAt, uint256 claimedAmount, uint256 claimedBonus) public onlyBot { + vestings[user].push(Vesting({ + amount: amount, + bonus: bonus, + lockedUntil: lockedUntil, + claimedAmount: claimedAmount, + claimedBonus: claimedBonus, + lastClaimed: lastClaimed, + createdAt: createdAt, + token: token, + complete: false, + usdAmount: usdAmount + })); + + dollarsVested[user] += usdAmount; + vestedTotal[token] += amount - claimedAmount; + } + + // /// @notice Update an existing vesting at a specific index + // /// @dev Only updates existing vestings. Fails if the index doesn't exist. + // /// @param user The user address to update vesting for + // /// @param vestingIndex The index of the existing vesting to update + // /// @param amount The vesting amount + // /// @param bonus The bonus amount + // /// @param lockedUntil The lock timestamp + // /// @param token The token address + // /// @param usdAmount The USD value + // /// @param lastClaimed The last claimed timestamp + // /// @param createdAt The creation timestamp + // /// @param claimedAmount The already claimed amount + // /// @param claimedBonus The already claimed bonus + // function updateVesting(address user, uint256 vestingIndex, uint256 amount, uint256 bonus, uint256 lockedUntil, address token, uint256 usdAmount, uint256 lastClaimed, uint256 createdAt, uint256 claimedAmount, uint256 claimedBonus) public onlyBot { + // require(vestingIndex < vestings[user].length, "Vesting index does not exist"); + + // // Subtract old values from totals first + // Vesting storage oldVesting = vestings[user][vestingIndex]; + // dollarsVested[user] -= oldVesting.usdAmount; + // vestedTotal[oldVesting.token] -= (oldVesting.amount - oldVesting.claimedAmount); + + // // Update the vesting at the specified index + // vestings[user][vestingIndex] = Vesting({ + // amount: amount, + // bonus: bonus, + // lockedUntil: lockedUntil, + // claimedAmount: claimedAmount, + // claimedBonus: claimedBonus, + // lastClaimed: lastClaimed, + // createdAt: createdAt, + // token: token, + // complete: false, + // usdAmount: usdAmount + // }); + + // // Add new values to totals + // dollarsVested[user] += usdAmount; + // vestedTotal[token] += amount - claimedAmount; + // } + + // /// @notice Migrates all vestings from an old address to a new address + // /// @dev Only to be used by bots for account migrations + // /// @param oldAddress The address with existing vestings to migrate from + // /// @param newAddress The address to migrate vestings to + // function migrateVestings(address oldAddress, address newAddress) external onlyBot { + // require(oldAddress != address(0) && newAddress != address(0) && oldAddress != newAddress, "Invalid address"); + + // Vesting[] storage oldVestings = vestings[oldAddress]; + // uint256 vestingCount = oldVestings.length; + // require(vestingCount > 0, "No vestings available"); + + // Vesting[] storage newVestings = vestings[newAddress]; + + // for (uint256 i = 0; i < vestingCount; i++) { + // Vesting storage oldVesting = oldVestings[i]; + + // // Copy vesting to new address + // newVestings.push(oldVesting); + + // // Clear old vesting + // oldVesting.amount = 0; + // oldVesting.bonus = 0; + // oldVesting.lockedUntil = 0; + // oldVesting.claimedAmount = 0; + // oldVesting.claimedBonus = 0; + // oldVesting.lastClaimed = 0; + // oldVesting.createdAt = 0; + // oldVesting.usdAmount = 0; + // oldVesting.complete = true; + // } + + // // Migrate dollars vested + // dollarsVested[newAddress] += dollarsVested[oldAddress]; + // dollarsVested[oldAddress] = 0; + + // // Migrate pending vesting withdrawals + // WithdrawVesting[] storage oldWithdrawVestings = withdrawVestingActual[oldAddress]; + // uint256 withdrawVestingCount = oldWithdrawVestings.length; + // if (withdrawVestingCount > 0) { + // WithdrawVesting[] storage newWithdrawVestings = withdrawVestingActual[newAddress]; + // for (uint256 i = 0; i < withdrawVestingCount; i++) { + // newWithdrawVestings.push(oldWithdrawVestings[i]); + // } + // delete withdrawVestingActual[oldAddress]; + // } + // } + + // Vesting View Functions + function getUnlockedVesting(address _user, uint256 _vestingIndex) public view returns (uint256) { + Vesting storage vesting = vestings[_user][_vestingIndex]; + uint256 timeElapsed = block.timestamp - vesting.createdAt; + address token = vesting.token; + + uint256 unlockedAmount = 0; + + for (uint256 i = 0; i < unlockSchedules[token].length; ++i) { + UnlockStep storage step = unlockSchedules[token][i]; + uint256 timeTier = step.timeOffset; + uint256 percentage = step.percentage; + + if (timeElapsed >= timeTier) { + unlockedAmount = unlockedAmount + ((vesting.amount * percentage) / 10000); + } + } + + return unlockedAmount; + } + + function getVestingSchedule(address _user, uint256 _vestingIndex) public view returns (uint256[] memory, uint256[] memory) { + Vesting storage vesting = vestings[_user][_vestingIndex]; + address token = vesting.token; + + uint256 scheduleLength = unlockSchedules[token].length; + uint256[] memory unlockTimestamps = new uint256[](scheduleLength); + uint256[] memory unlockPercentages = new uint256[](scheduleLength); + + for (uint256 i = 0; i < scheduleLength; ++i) { + UnlockStep storage step = unlockSchedules[token][i]; + + // Calculate the absolute unlock timestamp + unlockTimestamps[i] = vesting.createdAt + step.timeOffset; + unlockPercentages[i] = step.percentage; // Percentage is stored as scaled by 10000 (e.g., 2500 = 25%) + } + + return (unlockTimestamps, unlockPercentages); + } + + function getUnlockedVestingBonus(address _user, uint256 _vestingIndex) public view returns (uint256) { + Vesting storage vesting = vestings[_user][_vestingIndex]; + uint256 timeElapsed = block.timestamp - vesting.createdAt; + address token = vesting.token; + + uint256 unlockedAmount = 0; + + for (uint256 i = 0; i < unlockSchedules[token].length; ++i) { + UnlockStep storage step = unlockSchedules[token][i]; + uint256 timeTier = step.timeOffset; + uint256 percentage = step.percentage; + uint256 maxBonusAmount = (vesting.usdAmount * BONUS_PERCENTAGE) / 100; + + if (timeElapsed >= timeTier) { + unlockedAmount = unlockedAmount + ((maxBonusAmount * percentage) / 10000); + } + } + + return unlockedAmount; + } + + /// @notice View function to get all vestings for a specific address + function getVestings(address user) external view returns (Vesting[] memory) { + return vestings[user]; + } + + /** + * @notice Returns the vested amounts and USD values for an array of tokens. + * @param _tokens The array of token addresses to evaluate. + * @return amounts The array of vested amounts for each token. + * @return usdValues The array of USD values for each token's vested amount. + * @return totalUsd The total USD value of all vested tokens in the array. + */ + function getVestedTotals(address[] calldata _tokens) + external + view + returns ( + uint256[] memory amounts, + uint256[] memory usdValues, + uint256 totalUsd + ) + { + uint256 length = _tokens.length; + amounts = new uint256[](length); + usdValues = new uint256[](length); + + for (uint256 i = 0; i < length; i++) { + address token = _tokens[i]; + + // 1. Get the total amount vested for this token. + uint256 tokenAmount = vestedTotal[token]; + amounts[i] = tokenAmount; + + // 2. Query the oracle for this token's USD price. + // Assumes the oracle returns a price scaled by 1e18. + uint256 price = iPriceOracle(priceOracles[token]).getLatestPrice(token); + + // 3. Calculate the vested USD value: (price * amount) / 1e18 + uint256 valueInUsd = (price * tokenAmount) / 1e18; + usdValues[i] = valueInUsd; + + // 4. Accumulate the total USD amount + totalUsd += valueInUsd; + } + + return (amounts, usdValues, totalUsd); + } + + /// @notice Returns the total USD value of the user's unclaimed, uncomplete, stake amounts, based on current token prices from the oracle. + /// @return totalUsd The total unclaimed stake value, in USD (1e18 precision). + function getUserTotalUnclaimedUsdValue(address user) external view returns (uint256 totalUsd) { + uint256 length = vestings[user].length; + for (uint256 i = 0; i < length; i++) { + Vesting memory v = vestings[user][i]; + if (!v.complete) { + uint256 tokenPrice = iPriceOracle(priceOracles[v.token]).getLatestPrice(v.token); + + // The unclaimed portion of the stake + uint256 unclaimedAmount = v.amount - v.claimedAmount; + + // Convert unclaimed tokens to USD value + uint256 stakeUsd = (tokenPrice * unclaimedAmount) / 1e18; + + totalUsd += stakeUsd; + } + } + return totalUsd; + } + + /// @notice Claim unlocked vesting tokens for a specific vesting + /// @param _vestingIndex The index of the vesting to claim from + function claimVesting(uint256 _vestingIndex) external nonReentrant { + require(_vestingIndex < vestings[msg.sender].length, "Invalid vesting index"); + + Vesting storage vesting = vestings[msg.sender][_vestingIndex]; + require(!vesting.complete, "Vesting complete"); + + uint256 maxClaim = getUnlockedVesting(msg.sender, _vestingIndex); + require(maxClaim >= vesting.claimedAmount, "Invalid claim amount"); + + uint256 amountToClaim = maxClaim - vesting.claimedAmount; + require(amountToClaim > 0, "Nothing to claim"); + + vesting.claimedAmount += amountToClaim; + if (vesting.claimedAmount >= vesting.amount) { + vesting.complete = true; + } + + // Update user's dollarsVested + if (dollarsVested[msg.sender] > 0) { + uint256 usdPrice = (iPriceOracle(priceOracles[vesting.token]).getLatestPrice(vesting.token) * amountToClaim) / 1e18; + if (usdPrice >= dollarsVested[msg.sender]) { + dollarsVested[msg.sender] = 0; + } else { + dollarsVested[msg.sender] -= usdPrice; + } + } + vestedTotal[vesting.token] -= amountToClaim; + + // Add vesting claims to unified withdrawal queue + withdrawStakes[msg.sender].push(WithdrawStake({ + stakeId: vestingStakeIdCounter++, + amount: amountToClaim, + unlockTime: block.timestamp + unlockDelay, + token: vesting.token + })); + + // Increment withdraw liabilities for this token + withdrawLiabilities[vesting.token] += amountToClaim; + + emit VestingClaimed(msg.sender, amountToClaim, 0); + } + + /// @notice Claim all unlocked vesting tokens for a specific token + /// @param _token The token address to claim all vestings for + function claimAllVestingByToken(address _token) external nonReentrant { + uint256 totalReward = 0; + + for (uint256 i = 0; i < vestings[msg.sender].length; i++) { + Vesting storage vesting = vestings[msg.sender][i]; + if (vesting.token == _token && !vesting.complete) { + uint256 maxClaim = getUnlockedVesting(msg.sender, i); + if (maxClaim > vesting.claimedAmount) { + uint256 amountToClaim = maxClaim - vesting.claimedAmount; + totalReward += amountToClaim; + + vesting.claimedAmount += amountToClaim; + if (vesting.claimedAmount >= vesting.amount) { + vesting.complete = true; + } + } + } + } + + require(totalReward > 0, "Nothing to claim"); + + // Update user's dollarsVested + if (dollarsVested[msg.sender] > 0) { + uint256 usdPrice = (iPriceOracle(priceOracles[_token]).getLatestPrice(_token) * totalReward) / 1e18; + if (usdPrice >= dollarsVested[msg.sender]) { + dollarsVested[msg.sender] = 0; + } else { + dollarsVested[msg.sender] -= usdPrice; + } + } + vestedTotal[_token] -= totalReward; + + // Add vesting claims to unified withdrawal queue + withdrawStakes[msg.sender].push(WithdrawStake({ + stakeId: vestingStakeIdCounter++, + amount: totalReward, + unlockTime: block.timestamp + unlockDelay, + token: _token + })); + + // Increment withdraw liabilities for this token + withdrawLiabilities[_token] += totalReward; + + emit VestingClaimed(msg.sender, totalReward, 0); + } + + /// @notice Claim unlocked bonus tokens from multiple vestings + /// @param _vestingIndices Array of vesting indices to claim bonus from + function claimBonus(uint256[] calldata _vestingIndices) external nonReentrant { + uint256 totalBonusClaimed = 0; + + for (uint256 i = 0; i < _vestingIndices.length; i++) { + uint256 _vestingIndex = _vestingIndices[i]; + + // Skip invalid vesting indices + if (_vestingIndex >= vestings[msg.sender].length) { + continue; + } + + Vesting storage vesting = vestings[msg.sender][_vestingIndex]; + uint256 maxBonus = getUnlockedVestingBonus(msg.sender, _vestingIndex); + + // Skip if invalid claim amount or nothing to claim + if (maxBonus < vesting.claimedBonus) { + continue; + } + + uint256 bonusToClaim = maxBonus - vesting.claimedBonus; + if (bonusToClaim == 0) { + continue; + } + + vesting.claimedBonus += bonusToClaim; + totalBonusClaimed += bonusToClaim; + + // Create withdrawable stake with unlock delay (add 1e6 to distinguish from normal stakes) + withdrawStakes[msg.sender].push(WithdrawStake({ + stakeId: _vestingIndex + 1e6, + amount: bonusToClaim, + unlockTime: block.timestamp + unlockDelay, + token: SONIC_TOKEN + })); + } + + // Only update liabilities and emit event if something was actually claimed + if (totalBonusClaimed > 0) { + // Increment withdraw liabilities for SONIC_TOKEN + withdrawLiabilities[SONIC_TOKEN] += totalBonusClaimed; + emit BonusClaimed(msg.sender, totalBonusClaimed); + } + } + + + // Marketplace View Functions + + /// @notice Get all active marketplace listings + /// @return sellers Array of seller addresses + /// @return stakeIds Array of stake IDs + /// @return sellStakeData Array of SellStake structs + function getAllSellStakes() external view returns ( + address[] memory sellers, + uint256[] memory stakeIds, + SellStake[] memory sellStakeData + ) { + uint256 length = sellStakeKeys.length; + + sellers = new address[](length); + stakeIds = new uint256[](length); + sellStakeData = new SellStake[](length); + + for (uint256 i = 0; i < length; i++) { + SellStakeKey memory key = sellStakeKeys[i]; + sellers[i] = key.seller; + stakeIds[i] = key.stakeId; + sellStakeData[i] = sellStakes[key.seller][key.stakeId]; + } + + return (sellers, stakeIds, sellStakeData); + } + + /// @notice Get a specific marketplace listing + /// @param seller The seller address + /// @param stakeId The stake ID + /// @return The SellStake struct + function getSellStake(address seller, uint256 stakeId) external view returns (SellStake memory) { + return sellStakes[seller][stakeId]; + } + + /// @notice Get marketplace history + /// @param startIndex Starting index in history array + /// @param length Number of entries to return + /// @return Array of MarketplaceHistory structs + function getMarketplaceHistory(uint256 startIndex, uint256 length) + external view returns (MarketplaceHistory[] memory) { + require(startIndex < marketplaceHistory.length, "Start index out of bounds"); + + uint256 endIndex = startIndex + length; + if (endIndex > marketplaceHistory.length) { + endIndex = marketplaceHistory.length; + } + + MarketplaceHistory[] memory result = new MarketplaceHistory[](endIndex - startIndex); + for (uint256 i = startIndex; i < endIndex; i++) { + result[i - startIndex] = marketplaceHistory[i]; + } + + return result; + } + + /// @notice Get total marketplace history count + /// @return Total number of marketplace transactions + function getMarketplaceHistoryCount() external view returns (uint256) { + return marketplaceHistory.length; + } + + /// @notice Get user's total marketplace sales + /// @param user The user address + /// @return Total sales amount for the user + function getUserMarketplaceSales(address user) external view returns (uint256) { + return marketplace_sales[user]; + } + + /// @notice Get user's total claimed amount sent to withdrawStakes + /// @param user The user address + /// @return Total amount claimed and sent to withdrawStakes for the user + function getUserTotalClaimed(address user) external view returns (uint256) { + return totalClaimed[user]; + } + + // /// @notice Search marketplace history for stakes where address was seller or buyer + // /// @param targetAddress The address to search for as seller or buyer + // /// @return Array of MarketplaceHistory structs where address was involved + // function searchMarketplaceHistory(address targetAddress) external view returns (MarketplaceHistory[] memory) { + // require(targetAddress != address(0), "Invalid address"); + + // // Count matches first to size the result array properly + // uint256 matchCount = 0; + // for (uint256 i = 0; i < marketplaceHistory.length; i++) { + // if (marketplaceHistory[i].seller == targetAddress || marketplaceHistory[i].buyer == targetAddress) { + // matchCount++; + // } + // } + + // // Return empty array if no matches + // if (matchCount == 0) { + // return new MarketplaceHistory[](0); + // } + + // // Create result array with exact size needed + // MarketplaceHistory[] memory result = new MarketplaceHistory[](matchCount); + // uint256 resultIndex = 0; + + // // Populate result array + // for (uint256 i = 0; i < marketplaceHistory.length; i++) { + // if (marketplaceHistory[i].seller == targetAddress || marketplaceHistory[i].buyer == targetAddress) { + // result[resultIndex] = marketplaceHistory[i]; + // resultIndex++; + // } + // } + + // return result; + // } + + /// @notice Test function for upgrade verification + /// @return Returns a constant value to verify upgrade worked + function testUpgradeFunction() external pure returns (uint256) { + return 999; // Different value from bsc_paca to distinguish contracts + } + +} \ No newline at end of file