erc20-demurrage-token

ERC20 token with redistributed continual demurrage
Log | Files | Refs | README

commit 5c85a8abba8f58cd3ddcbfe3d562482efc956bb7
parent e6eef4880876602e836f003598249513892ab7c3
Author: lash <dev@holbrook.no>
Date:   Fri, 10 Feb 2023 16:17:06 +0000

Fix period test to recognize actual sink address

Diffstat:
Mpython/CHANGELOG | 6++++++
Mpython/erc20_demurrage_token/data/DemurrageTokenSingleNocap.bin | 4++--
Mpython/run_tests.sh | 51+++++++++------------------------------------------
Dpython/tests/test_demurrage_ext.py | 41-----------------------------------------
Mpython/tests/test_period.py | 4++--
Dpython/tests/test_redistribution.py | 328-------------------------------------------------------------------------------
Dsolidity/DemurrageTokenMultiCap.sol | 633-------------------------------------------------------------------------------
Dsolidity/DemurrageTokenMultiNocap.sol | 614-------------------------------------------------------------------------------
Dsolidity/DemurrageTokenSingleCap.sol | 517-------------------------------------------------------------------------------
Msolidity/DemurrageTokenSingleNocap.sol | 3+--
Msolidity/Makefile | 19+------------------
11 files changed, 21 insertions(+), 2199 deletions(-)

diff --git a/python/CHANGELOG b/python/CHANGELOG @@ -1,3 +1,9 @@ +- 0.3.0 + * Smart contracts use abdk math libraries, all exponential operations are static gas cost + * Add expiry features, after which balances are frozen and no more transfers or demurrage will occur + * Add sealable features for supply, sink address, expiry and minters (when sealed cannot be changed) + * Deployer script now takes demurrage amount as ppm instead of literal growth fraction + * Retire old multi and cap contracts - 0.1.1 * Settable demurrage steps for apply demurrage cli tool - 0.1.0 diff --git a/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap.bin b/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap.bin @@ -1 +1 @@ -60c06040523480156200001157600080fd5b506040516200610038038062006100833981810160405281019062000037919062000909565b6801000000000000000083600f0b126200005057600080fd5b6200005a6200061e565b33600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600e6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550866006908162000126919062000c2a565b50856007908162000138919062000c2a565b508460ff166008819055504260038190555060035460808181525050603c8362000163919062000d40565b60a081815250506200018a60016200034f6401000000000262002d48176401000000009004565b600260006101000a8154816fffffffffffffffffffffffffffffffff0219169083600f0b6fffffffffffffffffffffffffffffffff160217905550620001e484620003766401000000000262002d6e176401000000009004565b600d60006101000a8154816fffffffffffffffffffffffffffffffff0219169083600f0b6fffffffffffffffffffffffffffffffff1602179055506200024e6000600260009054906101000a9004600f0b60006001620003ca640100000000026401000000009004565b90506000819080600181540180825580915050600190039060005260206000200160009091909190915060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548168ffffffffffffffffff021916908368ffffffffffffffffff160217905550604082015181600001600d6101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505081601060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050505050505062000d8b565b6000677fffffffffffffff8211156200036757600080fd5b6040829060020a029050919050565b60008082600f0b136200038857600080fd5b60806fb17217f7d1cf79abc9e3b39803f2f6af620003b5846200044e640100000000026401000000009004565b600f0b02908060020a82049150509050919050565b620003d46200061e565b620003de6200061e565b82816000019063ffffffff16908163ffffffff168152505083816020019068ffffffffffffffffff16908168ffffffffffffffffff168152505067ffffffffffffffff8516816040019067ffffffffffffffff16908167ffffffffffffffff168152505080915050949350505050565b60008082600f0b136200046057600080fd5b60008083600f0b90506801000000000000000081126200049c576040819060008212600003808260020a82851804189250505090506040820191505b6401000000008112620004cb576020819060008212600003808260020a82851804189250505090506020820191505b620100008112620004f8576010819060008212600003808260020a82851804189250505090506010820191505b610100811262000524576008819060008212600003808260020a82851804189250505090506008820191505b601081126200054f576004819060008212600003808260020a82851804189250505090506004820191505b600481126200057a576002819060008212600003808260020a82851804189250505090506002820191505b600281126200058a576001820191505b600060408084039060020a029050600083607f0386600f0b9060020a029050600067800000000000000090505b600081131562000611578182029150600060ff83908060020a8204915050905080607f0183908060020a8204915050925080820284019350506001819060008212600003808260020a8285180418925050509050620005b7565b5081945050505050919050565b6040518060600160405280600063ffffffff168152602001600068ffffffffffffffffff168152602001600067ffffffffffffffff1681525090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620006c38262000678565b810181811067ffffffffffffffff82111715620006e557620006e462000689565b5b80604052505050565b6000620006fa6200065a565b9050620007088282620006b8565b919050565b600067ffffffffffffffff8211156200072b576200072a62000689565b5b620007368262000678565b9050602081019050919050565b60005b838110156200076357808201518184015260208101905062000746565b60008484015250505050565b60006200078662000780846200070d565b620006ee565b905082815260208101848484011115620007a557620007a462000673565b5b620007b284828562000743565b509392505050565b600082601f830112620007d257620007d16200066e565b5b8151620007e48482602086016200076f565b91505092915050565b600060ff82169050919050565b6200080581620007ed565b81146200081157600080fd5b50565b6000815190506200082581620007fa565b92915050565b600081600f0b9050919050565b62000843816200082b565b81146200084f57600080fd5b50565b600081519050620008638162000838565b92915050565b6000819050919050565b6200087e8162000869565b81146200088a57600080fd5b50565b6000815190506200089e8162000873565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620008d182620008a4565b9050919050565b620008e381620008c4565b8114620008ef57600080fd5b50565b6000815190506200090381620008d8565b92915050565b60008060008060008060c0878903121562000929576200092862000664565b5b600087015167ffffffffffffffff8111156200094a576200094962000669565b5b6200095889828a01620007ba565b965050602087015167ffffffffffffffff8111156200097c576200097b62000669565b5b6200098a89828a01620007ba565b95505060406200099d89828a0162000814565b9450506060620009b089828a0162000852565b9350506080620009c389828a016200088d565b92505060a0620009d689828a01620008f2565b9150509295509295509295565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168062000a3657607f821691505b60208210810362000a4c5762000a4b620009ee565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b60008160020a8302905092915050565b60006008830262000ab97fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000a77565b62000ac5868362000a77565b95508019841693508086168417925050509392505050565b6000819050919050565b600062000b0862000b0262000afc8462000869565b62000add565b62000869565b9050919050565b6000819050919050565b62000b248362000ae7565b62000b3c62000b338262000b0f565b84845462000a87565b825550505050565b600090565b62000b5362000b44565b62000b6081848462000b19565b505050565b5b8181101562000b885762000b7c60008262000b49565b60018101905062000b66565b5050565b601f82111562000bd75762000ba18162000a52565b62000bac8462000a67565b8101602085101562000bbc578190505b62000bd462000bcb8562000a67565b83018262000b65565b50505b505050565b60008160020a8304905092915050565b600062000bff6000198460080262000bdc565b1980831691505092915050565b600062000c1a838362000bec565b9150826002028217905092915050565b62000c3582620009e3565b67ffffffffffffffff81111562000c515762000c5062000689565b5b62000c5d825462000a1d565b62000c6a82828562000b8c565b600060209050601f83116001811462000ca2576000841562000c8d578287015190505b62000c99858262000c0c565b86555062000d09565b601f19841662000cb28662000a52565b60005b8281101562000cdc5784890151825560018201915060208501945060208101905062000cb5565b8683101562000cfc578489015162000cf8601f89168262000bec565b8355505b6001600288020188555050505b505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600062000d4d8262000869565b915062000d5a8362000869565b925082820262000d6a8162000869565b9150828204841483151762000d845762000d8362000d11565b5b5092915050565b60805160a05161531e62000de260003960008181611aec015281816122d5015281816128d701528181612b0a0152612c1e015260008181611b1801528181612b3601528181612c3f0152612c84015261531e6000f3fe608060405234801561001057600080fd5b50600436106103a0576000357c0100000000000000000000000000000000000000000000000000000000900480636a2d094e116101fb578063983b2d561161012c578063d5abeb01116100ca578063e69571af11610099578063e69571af14610ba9578063e809529514610bd9578063eda4e6d614610bf7578063f2fde38b14610c15576103a0565b8063d5abeb0114610b21578063d7a52fa914610b3f578063d89135cd14610b5b578063e54063a614610b79576103a0565b8063b1cb0db311610106578063b1cb0db314610a97578063b470aade14610ab5578063c0ab707714610ad3578063d340ef8a14610b03576103a0565b8063983b2d5614610a07578063a457c2d714610a37578063a9059cbb14610a67576103a0565b806379ba5097116101995780638da5cb5b116101735780638da5cb5b146109915780638f0b2d5d146109af5780638f1df6bc146109cb57806395d89b41146109e9576103a0565b806379ba50971461092557806384dde4af1461094357806386fe212d14610961576103a0565b8063731f237c116101d5578063731f237c1461089b57806373f42561146108b95780637445e33a146108d757806374bedb9514610907576103a0565b80636a2d094e1461081f5780636f8b44b01461084f57806370a082311461086b576103a0565b80633092afd5116102d5578063449a52f81161027357806351b4541c1161024d57806351b4541c1461078357806358b2833b146107b35780635f408c04146107d15780636787a9be146107ef576103a0565b8063449a52f8146106f357806347a50517146107235780634abfbba214610753576103a0565b806331a5995d116102af57806331a5995d14610657578063395093511461067557806340452d91146106a557806342966c68146106d7576103a0565b80633092afd5146105d95780633133348714610609578063313ce56714610639576103a0565b80631989c6a81161034257806323a859441161031c57806323a859441461051957806323b872dd146105495780632a7aec09146105795780632c1758c1146105a9576103a0565b80631989c6a8146104ad578063213d1e1e146104cb578063229ba197146104fb576103a0565b806309f28f3c1161037e57806309f28f3c1461042357806311c566151461044157806318160ddd1461047157806318cbbcfc1461048f576103a0565b806301ffc9a7146103a557806306fdde03146103d5578063095ea7b3146103f3575b600080fd5b6103bf60048036038101906103ba91906146d2565b610c45565b6040516103cc919061471a565b60405180910390f35b6103dd610deb565b6040516103ea91906147c5565b60405180910390f35b61040d6004803603810190610408919061487b565b610e79565b60405161041a919061471a565b60405180910390f35b61042b6110b5565b60405161043891906148d7565b60405180910390f35b61045b6004803603810190610456919061491e565b6110c8565b604051610468919061496d565b60405180910390f35b610479611118565b604051610486919061496d565b60405180910390f35b61049761112f565b6040516104a4919061496d565b60405180910390f35b6104b5611134565b6040516104c2919061496d565b60405180910390f35b6104e560048036038101906104e09190614b29565b611140565b6040516104f2919061496d565b60405180910390f35b610503611159565b60405161051091906148d7565b60405180910390f35b610533600480360381019061052e9190614b29565b61116c565b604051610540919061471a565b60405180910390f35b610563600480360381019061055e9190614b56565b6111d4565b604051610570919061471a565b60405180910390f35b610593600480360381019061058e9190614b29565b6113f2565b6040516105a091906148d7565b60405180910390f35b6105c360048036038101906105be9190614ba9565b61142a565b6040516105d0919061471a565b60405180910390f35b6105f360048036038101906105ee9190614bd6565b61145d565b604051610600919061471a565b60405180910390f35b610623600480360381019061061e9190614c03565b611567565b604051610630919061496d565b60405180910390f35b6106416115d3565b60405161064e919061496d565b60405180910390f35b61065f6115d9565b60405161066c919061496d565b60405180910390f35b61068f600480360381019061068a919061487b565b6115df565b60405161069c919061471a565b60405180910390f35b6106bf60048036038101906106ba9190614ba9565b611775565b6040516106ce93929190614c70565b60405180910390f35b6106f160048036038101906106ec9190614ba9565b6117e4565b005b61070d6004803603810190610708919061487b565b611920565b60405161071a919061471a565b60405180910390f35b61073d60048036038101906107389190614bd6565b611a9f565b60405161074a919061496d565b60405180910390f35b61076d60048036038101906107689190614ba9565b611ae8565b60405161077a919061496d565b60405180910390f35b61079d60048036038101906107989190614ba9565b611b48565b6040516107aa919061496d565b60405180910390f35b6107bb611b69565b6040516107c8919061496d565b60405180910390f35b6107d9611b6f565b6040516107e69190614cc3565b60405180910390f35b61080960048036038101906108049190614ba9565b611c3c565b604051610816919061496d565b60405180910390f35b61083960048036038101906108349190614ba9565b611c7d565b604051610846919061496d565b60405180910390f35b61086960048036038101906108649190614ba9565b611c9e565b005b61088560048036038101906108809190614bd6565b611d61565b604051610892919061496d565b60405180910390f35b6108a3611dcb565b6040516108b0919061496d565b60405180910390f35b6108c1611ddc565b6040516108ce919061496d565b60405180910390f35b6108f160048036038101906108ec9190614b29565b611de2565b6040516108fe919061496d565b60405180910390f35b61090f611e10565b60405161091c919061496d565b60405180910390f35b61092d611e16565b60405161093a919061471a565b60405180910390f35b61094b611fbd565b6040516109589190614ced565b60405180910390f35b61097b60048036038101906109769190614ba9565b611fe3565b604051610988919061496d565b60405180910390f35b6109996120c2565b6040516109a69190614ced565b60405180910390f35b6109c960048036038101906109c49190614bd6565b6120e8565b005b6109d361219d565b6040516109e0919061471a565b60405180910390f35b6109f161244d565b6040516109fe91906147c5565b60405180910390f35b610a216004803603810190610a1c9190614bd6565b6124db565b604051610a2e919061471a565b60405180910390f35b610a516004803603810190610a4c919061487b565b6125af565b604051610a5e919061471a565b60405180910390f35b610a816004803603810190610a7c919061487b565b6127ce565b604051610a8e919061471a565b60405180910390f35b610a9f6128cf565b604051610aac919061496d565b60405180910390f35b610abd6128d5565b604051610aca919061496d565b60405180910390f35b610aed6004803603810190610ae89190614ba9565b6128f9565b604051610afa919061496d565b60405180910390f35b610b0b612a6f565b604051610b18919061496d565b60405180910390f35b610b29612a75565b604051610b36919061496d565b60405180910390f35b610b596004803603810190610b549190614ba9565b612a7b565b005b610b63612b7a565b604051610b70919061496d565b60405180910390f35b610b936004803603810190610b8e9190614b29565b612b84565b604051610ba0919061496d565b60405180910390f35b610bc36004803603810190610bbe9190614d08565b612b98565b604051610bd09190614dde565b60405180910390f35b610be1612c18565b604051610bee9190614e24565b60405180910390f35b610bff612c82565b604051610c0c919061496d565b60405180910390f35b610c2f6004803603810190610c2a9190614bd6565b612ca6565b604051610c3c919061471a565b60405180910390f35b600063c6bb4b707c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603610c995760019050610de6565b63449a52f87c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603610ceb5760019050610de6565b6301ffc9a77c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603610d3d5760019050610de6565b639493f8b27c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603610d8f5760019050610de6565b6337a47be47c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603610de15760019050610de6565b600090505b919050565b60068054610df890614e6e565b80601f0160208091040260200160405190810160405280929190818152602001828054610e2490614e6e565b8015610e715780601f10610e4657610100808354040283529160200191610e71565b820191906000526020600020905b815481529060010190602001808311610e5457829003601f168201915b505050505081565b6000806000610e86611b6f565b905060028160ff1603610e9e576000925050506110af565b60008160ff161115610ee5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610edc90614eeb565b60405180910390fd5b6000600f60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541115610fae5760008414610fad576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610fa490614f57565b60405180910390fd5b5b610fb661219d565b50610fc084611c3c565b915081600f60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925866040516110a0919061496d565b60405180910390a36001925050505b92915050565b600260009054906101000a9004600f0b81565b6000806110ef6110d785612d48565b6110ea6110e46001612d48565b86612db0565b612e17565b90506110fa81612e96565b67ffffffffffffffff168461110f9190614fa6565b91505092915050565b6000600c5460095461112a9190614fa6565b905090565b600f81565b60008080549050905090565b6000816020015168ffffffffffffffffff169050919050565b600d60009054906101000a9004600f0b81565b600080826000015163ffffffff16111561118957600090506111cf565b6000826020015168ffffffffffffffffff1611156111aa57600090506111cf565b6000826040015167ffffffffffffffff1611156111ca57600090506111cf565b600190505b919050565b6000806000806111e2611b6f565b905060028160ff16036111fb57600093505050506113eb565b60008160ff161115611242576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161123990614eeb565b60405180910390fd5b61124a61219d565b5061125485611c3c565b925082600f60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156112df57600080fd5b82600f60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461136b9190614fa6565b9250508190555061137d878785612ecb565b91508573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516113dc919061496d565b60405180910390a38193505050505b9392505050565b60008067ffffffffffffffff836040015160070b169050600081600f0b036114215761141e6001612d48565b90505b80915050919050565b6000600f821061143957600080fd5b6000820361144e57600f601454149050611458565b8160145483161490505b919050565b600061146c600160ff1661142a565b1561147657600080fd5b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806114fd57503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b61150657600080fd5b6000600e60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060019050919050565b60008060008061157686612d48565b925061158185612d48565b915061159c600d60009054906101000a9004600f0b83612e17565b90506115a781612ef0565b90506115b38382612e17565b90506115be81612e96565b67ffffffffffffffff16935050505092915050565b60085481565b60145481565b6000806115ea61219d565b506115f483611c3c565b905080600f60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546116829190614fda565b925050819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600f60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054604051611762919061496d565b60405180910390a3600191505092915050565b6000818154811061178557600080fd5b906000526020600020016000915090508060000160009054906101000a900463ffffffff16908060000160049054906101000a900468ffffffffffffffffff169080600001600d9054906101000a900467ffffffffffffffff16905083565b60006117ee611b6f565b60ff16146117fb57600080fd5b600e60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1661185157600080fd5b600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205481111561189d57600080fd5b60006118a882611c3c565b90506118b43382612f7d565b5081600c60008282546118c79190614fda565b925050819055503373ffffffffffffffffffffffffffffffffffffffff167fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca583604051611914919061496d565b60405180910390a25050565b600080600061192d611b6f565b60ff161461193a57600080fd5b600e60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff166119c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119bd9061505a565b60405180910390fd5b6119ce61219d565b50600060135411156119f757601354836009546119eb9190614fda565b11156119f657600080fd5b5b8260096000828254611a099190614fda565b92505081905550611a1983611c3c565b9050611a258482613084565b508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fab8530f87dc9b59234c4623bf917212bb2536d647574c8e7e5da92c2ede0c9f885604051611a83919061496d565b60405180910390a3611a93613148565b50600191505092915050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60007f000000000000000000000000000000000000000000000000000000000000000082611b16919061507a565b7f0000000000000000000000000000000000000000000000000000000000000000611b419190614fda565b9050919050565b6000603c8242611b589190614fa6565b611b6291906150eb565b9050919050565b60035481565b6000601260009054906101000a900460ff1615611b8f5760019050611c39565b600060115403611ba25760009050611c39565b6011544210611c3457611bcf603c600354611bbd91906150eb565b601154611bca9190614fa6565b6128f9565b506001601260006101000a81548160ff0219169083151502179055507ff80dbaea4785589e52984ca36a31de106adc77759539a5c7d92883bf49692fe942604051611c1a919061496d565b60405180910390a1611c2a61219d565b5060029050611c39565b600090505b90565b600080611c60611c4b84612d48565b600260009054906101000a9004600f0b61330c565b9050611c6b81612e96565b67ffffffffffffffff16915050919050565b6000603c8242611c8d9190614fa6565b611c9791906150eb565b9050919050565b611cab600860ff1661142a565b15611cb557600080fd5b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611d0f57600080fd5b6009548111611d1d57600080fd5b6013547f9722adea12ab7ef86fc45b88f0e0b567639e8dddaae60261e08c03d747fbbfe682604051611d4f919061496d565b60405180910390a28060138190555050565b6000806000806000611d7a611d7587611a9f565b612d48565b9350611d87600354611b48565b9150611da284600260009054906101000a9004600f0b612e17565b9250611dc0611db084612e96565b67ffffffffffffffff1683611567565b945050505050919050565b6000611dd760006128f9565b905090565b600c5481565b6000806000611df084611140565b9150611dfb846113f2565b9050611e0782826110c8565b92505050919050565b600b5481565b600080600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611e7357600080fd5b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35090565b601060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b600060108210612028576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161201f90615168565b60405180910390fd5b600060145483161461206f576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612066906151d4565b60405180910390fd5b816014600082825417925050819055507fdd5e7cad9599c1dcaa8d5adcd88f157fc5fada62511c0d3edb1bfd3b778acc416014546040516120b0919061496d565b60405180910390a16014549050919050565b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6120f5600260ff1661142a565b156120ff57600080fd5b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461215957600080fd5b80601060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60006121a761462f565b6121af61462f565b6121b761462f565b60008060008060006121c7611dcb565b506121d0613398565b97506121db8861116c565b156121f15760009850505050505050505061244a565b6000600a5481548110612207576122066151f4565b5b906000526020600020016040518060600160405290816000820160009054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160049054906101000a900468ffffffffffffffffff1668ffffffffffffffffff1668ffffffffffffffffff16815260200160008201600d9054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff168152505095506122b488612b84565b94506001856122c39190614fda565b90506122ce866113f2565b9350603c857f00000000000000000000000000000000000000000000000000000000000000006122fe919061507a565b61230891906150eb565b915061233361232e600d60009054906101000a9004600f0b61232985612d48565b612e17565b612ef0565b9250612349600084612343611118565b84612b98565b96506000879080600181540180825580915050600190039060005260206000200160009091909190915060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548168ffffffffffffffffff021916908368ffffffffffffffffff160217905550604082015181600001600d6101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555050506124058761352e565b507f55d243082e019fce4009ccea5368b92e436c17586a1e793c7deda16df4e5d67581604051612435919061496d565b60405180910390a16001985050505050505050505b90565b6007805461245a90614e6e565b80601f016020809104026020016040519081016040528092919081815260200182805461248690614e6e565b80156124d35780601f106124a8576101008083540402835291602001916124d3565b820191906000526020600020905b8154815290600101906020018083116124b657829003601f168201915b505050505081565b60006124ea600160ff1661142a565b156124f457600080fd5b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461254e57600080fd5b6001600e60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060019050919050565b6000806125bb83611c3c565b905080600f60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561264657600080fd5b61264e61219d565b5080600f60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546126db9190614fa6565b925050819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600f60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546040516127bb919061496d565b60405180910390a3600191505092915050565b6000806000806127dc611b6f565b905060028160ff16036127f557600093505050506128c9565b60008160ff16111561283c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161283390614eeb565b60405180910390fd5b61284461219d565b5061284e85611c3c565b925061285b338785612ecb565b91508573ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516128ba919061496d565b60405180910390a38193505050505b92915050565b60115481565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000806000806000601260009054906101000a900460ff1615612923576000945050505050612a6a565b61292e600354611b48565b925060008303612945576000945050505050612a6a565b600260009054906101000a9004600f0b905060008611801561296657508286105b1561296f578592505b61297883612d48565b9150612993600d60009054906101000a9004600f0b83612e17565b935061299e84612ef0565b93506129b9600260009054906101000a9004600f0b85612e17565b600260006101000a8154816fffffffffffffffffffffffffffffffff0219169083600f0b6fffffffffffffffffffffffffffffffff160217905550603c83612a01919061507a565b600354612a0e9190614fda565b60038190555080600f0b836003547f1c9c74563c32efd114cb36fb5e432d9386c8254d08456614804a33a3088ab736600260009054906101000a9004600f0b604051612a5a91906148d7565b60405180910390a4829450505050505b919050565b600a5481565b60135481565b6000612a8a600460ff1661142a565b15612a9457600080fd5b601260009054906101000a900460ff1615612aae57600080fd5b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612b0857600080fd5b7f000000000000000000000000000000000000000000000000000000000000000082612b34919061507a565b7f0000000000000000000000000000000000000000000000000000000000000000612b5f9190614fda565b90506011548111612b6f57600080fd5b806011819055505050565b6000600c54905090565b6000816000015163ffffffff169050919050565b612ba061462f565b612ba861462f565b82816000019063ffffffff16908163ffffffff168152505083816020019068ffffffffffffffffff16908168ffffffffffffffffff168152505067ffffffffffffffff8516816040019067ffffffffffffffff16908167ffffffffffffffff168152505080915050949350505050565b600060017f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000042612c699190614fa6565b612c7391906150eb565b612c7d9190614fda565b905090565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612d0257600080fd5b81600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550919050565b6000677fffffffffffffff821115612d5f57600080fd5b6040829060020a029050919050565b60008082600f0b13612d7f57600080fd5b60806fb17217f7d1cf79abc9e3b39803f2f6af612d9b8461364d565b600f0b02908060020a82049150509050919050565b60008082600f0b84600f0b0390507fffffffffffffffffffffffffffffffff80000000000000000000000000000000600f0b8112158015612e0457506f7fffffffffffffffffffffffffffffff600f0b8113155b612e0d57600080fd5b8091505092915050565b600080604083600f0b85600f0b029060008212600003808260020a82851804189250505090507fffffffffffffffffffffffffffffffff80000000000000000000000000000000600f0b8112158015612e8357506f7fffffffffffffffffffffffffffffff600f0b8113155b612e8c57600080fd5b8091505092915050565b60008082600f0b1215612ea857600080fd5b604082600f0b9060008212600003808260020a8285180418925050509050919050565b600080612ed88584612f7d565b50612ee38484613084565b5060019150509392505050565b60006840000000000000000082600f0b12612f0a57600080fd5b7fffffffffffffffffffffffffffffffffffffffffffffffc0000000000000000082600f0b1215612f3e5760009050612f78565b612f756080700171547652b82fe1777d0ffda0d23a7d1284600f0b029060008212600003808260020a828518041892505050613813565b90505b919050565b600080600080600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905060008503612fd9576000935050505061307e565b612fe286611a9f565b925084831015613027576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161301e9061526f565b60405180910390fd5b84836130339190614fa6565b600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600193505050505b92915050565b600080600080600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050600085036130e05760009350505050613142565b6130e986611a9f565b925084836130f79190614fda565b600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600193505050505b92915050565b600061315261462f565b600061315c611118565b9050600060016000805490506131729190614fa6565b81548110613183576131826151f4565b5b906000526020600020016040518060600160405290816000820160009054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160049054906101000a900468ffffffffffffffffff1668ffffffffffffffffff1668ffffffffffffffffff16815260200160008201600d9054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681525050915080826020019068ffffffffffffffffff16908168ffffffffffffffffff1681525050816000600160008054905061325e9190614fa6565b8154811061326f5761326e6151f4565b5b9060005260206000200160008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548168ffffffffffffffffff021916908368ffffffffffffffffff160217905550604082015181600001600d6101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555090505060019250505090565b60008082600f0b0361331d57600080fd5b600082600f0b604085600f0b9060020a028161333c5761333b6150bc565b5b0590507fffffffffffffffffffffffffffffffff80000000000000000000000000000000600f0b811215801561338557506f7fffffffffffffffffffffffffffffff600f0b8113155b61338e57600080fd5b8091505092915050565b6133a061462f565b6133a861462f565b6133b061462f565b600080600a54815481106133c7576133c66151f4565b5b906000526020600020016040518060600160405290816000820160009054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160049054906101000a900468ffffffffffffffffff1668ffffffffffffffffff1668ffffffffffffffffff16815260200160008201600d9054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff168152505092503073ffffffffffffffffffffffffffffffffffffffff1663e80952956040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381865afa1580156134d2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134f691906152bb565b6fffffffffffffffffffffffffffffffff16905061351383612b84565b81116135245781935050505061352b565b8293505050505b90565b600080600061353c84611de2565b613544611118565b61354e9190614fa6565b9150600b5461355c83611c3c565b6135669190614fa6565b9050613594601060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1682613084565b50836000015163ffffffff16601060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f9a2a887706623ad3ff7fc85652deeceabe9fe1e00466c597972079ee91ea40d384604051613608919061496d565b60405180910390a36001600a60008282546136239190614fda565b9250508190555080600b600082825461363c9190614fda565b925050819055508192505050919050565b60008082600f0b1361365e57600080fd5b60008083600f0b9050680100000000000000008112613699576040819060008212600003808260020a82851804189250505090506040820191505b64010000000081126136c7576020819060008212600003808260020a82851804189250505090506020820191505b6201000081126136f3576010819060008212600003808260020a82851804189250505090506010820191505b610100811261371e576008819060008212600003808260020a82851804189250505090506008820191505b60108112613748576004819060008212600003808260020a82851804189250505090506004820191505b60048112613772576002819060008212600003808260020a82851804189250505090506002820191505b60028112613781576001820191505b600060408084039060020a029050600083607f0386600f0b9060020a029050600067800000000000000090505b6000811315613806578182029150600060ff83908060020a8204915050905080607f0183908060020a8204915050925080820284019350506001819060008212600003808260020a82851804189250505090506137ae565b5081945050505050919050565b60006840000000000000000082600f0b1261382d57600080fd5b7fffffffffffffffffffffffffffffffffffffffffffffffc0000000000000000082600f0b1215613861576000905061462a565b60006f80000000000000000000000000000000905060006780000000000000008416600f0b13156138af57608070016a09e667f3bcc908b2fb1366ea957d3e8202908060020a820491505090505b60006740000000000000008416600f0b13156138e85760807001306fe0a31b7152de8d5a46305c85edec8202908060020a820491505090505b60006720000000000000008416600f0b13156139215760807001172b83c7d517adcdf7c8c50eb14a791f8202908060020a820491505090505b60006710000000000000008416600f0b131561395a57608070010b5586cf9890f6298b92b71842a983638202908060020a820491505090505b60006708000000000000008416600f0b13156139935760807001059b0d31585743ae7c548eb68ca417fd8202908060020a820491505090505b60006704000000000000008416600f0b13156139cc576080700102c9a3e778060ee6f7caca4f7a29bde88202908060020a820491505090505b60006702000000000000008416600f0b1315613a0557608070010163da9fb33356d84a66ae336dcdfa3f8202908060020a820491505090505b60006701000000000000008416600f0b1315613a3e576080700100b1afa5abcbed6129ab13ec11dc95438202908060020a820491505090505b600066800000000000008416600f0b1315613a7657608070010058c86da1c09ea1ff19d294cf2f679b8202908060020a820491505090505b600066400000000000008416600f0b1315613aae5760807001002c605e2e8cec506d21bfc89a23a00f8202908060020a820491505090505b600066200000000000008416600f0b1315613ae6576080700100162f3904051fa128bca9c55c31e5df8202908060020a820491505090505b600066100000000000008416600f0b1315613b1e5760807001000b175effdc76ba38e31671ca9397258202908060020a820491505090505b600066080000000000008416600f0b1315613b56576080700100058ba01fb9f96d6cacd4b180917c3d8202908060020a820491505090505b600066040000000000008416600f0b1315613b8e57608070010002c5cc37da9491d0985c348c68e7b38202908060020a820491505090505b600066020000000000008416600f0b1315613bc65760807001000162e525ee054754457d59952920268202908060020a820491505090505b600066010000000000008416600f0b1315613bfe57608070010000b17255775c040618bf4a4ade83fc8202908060020a820491505090505b6000658000000000008416600f0b1315613c355760807001000058b91b5bc9ae2eed81e9b7d4cfab8202908060020a820491505090505b6000654000000000008416600f0b1315613c6c576080700100002c5c89d5ec6ca4d7c8acc017b7c98202908060020a820491505090505b6000652000000000008416600f0b1315613ca357608070010000162e43f4f831060e02d839a9d16d8202908060020a820491505090505b6000651000000000008416600f0b1315613cda576080700100000b1721bcfc99d9f890ea069117638202908060020a820491505090505b6000650800000000008416600f0b1315613d1157608070010000058b90cf1e6d97f9ca14dbcc16288202908060020a820491505090505b6000650400000000008416600f0b1315613d485760807001000002c5c863b73f016468f6bac5ca2b8202908060020a820491505090505b6000650200000000008416600f0b1315613d7f576080700100000162e430e5a18f6119e3c02282a58202908060020a820491505090505b6000650100000000008416600f0b1315613db65760807001000000b1721835514b86e6d96efd1bfe8202908060020a820491505090505b60006480000000008416600f0b1315613dec576080700100000058b90c0b48c6be5df846c5b2ef8202908060020a820491505090505b60006440000000008416600f0b1315613e2257608070010000002c5c8601cc6b9e94213c72737a8202908060020a820491505090505b60006420000000008416600f0b1315613e585760807001000000162e42fff037df38aa2b219f068202908060020a820491505090505b60006410000000008416600f0b1315613e8e57608070010000000b17217fba9c739aa5819f44f98202908060020a820491505090505b60006408000000008416600f0b1315613ec45760807001000000058b90bfcdee5acd3c1cedc8238202908060020a820491505090505b60006404000000008416600f0b1315613efa576080700100000002c5c85fe31f35a6a30da1be508202908060020a820491505090505b60006402000000008416600f0b1315613f3057608070010000000162e42ff0999ce3541b9fffcf8202908060020a820491505090505b60006401000000008416600f0b1315613f66576080700100000000b17217f80f4ef5aadda455548202908060020a820491505090505b600063800000008416600f0b1315613f9b57608070010000000058b90bfbf8479bd5a81b51ad8202908060020a820491505090505b600063400000008416600f0b1315613fd05760807001000000002c5c85fdf84bd62ae30a74cc8202908060020a820491505090505b600063200000008416600f0b1315614005576080700100000000162e42fefb2fed257559bdaa8202908060020a820491505090505b600063100000008416600f0b131561403a5760807001000000000b17217f7d5a7716bba4a9ae8202908060020a820491505090505b600063080000008416600f0b131561406f576080700100000000058b90bfbe9ddbac5e109cce8202908060020a820491505090505b600063040000008416600f0b13156140a457608070010000000002c5c85fdf4b15de6f17eb0d8202908060020a820491505090505b600063020000008416600f0b13156140d95760807001000000000162e42fefa494f1478fde058202908060020a820491505090505b600063010000008416600f0b131561410e57608070010000000000b17217f7d20cf927c8e94c8202908060020a820491505090505b6000628000008416600f0b13156141425760807001000000000058b90bfbe8f71cb4e4b33d8202908060020a820491505090505b6000624000008416600f0b1315614176576080700100000000002c5c85fdf477b662b269458202908060020a820491505090505b6000622000008416600f0b13156141aa57608070010000000000162e42fefa3ae53369388c8202908060020a820491505090505b6000621000008416600f0b13156141de576080700100000000000b17217f7d1d351a389d408202908060020a820491505090505b6000620800008416600f0b131561421257608070010000000000058b90bfbe8e8b2d3d4ede8202908060020a820491505090505b6000620400008416600f0b13156142465760807001000000000002c5c85fdf4741bea6e77e8202908060020a820491505090505b6000620200008416600f0b131561427a576080700100000000000162e42fefa39fe95583c28202908060020a820491505090505b6000620100008416600f0b13156142ae5760807001000000000000b17217f7d1cfb72b45e18202908060020a820491505090505b60006180008416600f0b13156142e1576080700100000000000058b90bfbe8e7cc35c3f08202908060020a820491505090505b60006140008416600f0b131561431457608070010000000000002c5c85fdf473e242ea388202908060020a820491505090505b60006120008416600f0b13156143475760807001000000000000162e42fefa39f02b772c8202908060020a820491505090505b60006110008416600f0b131561437a57608070010000000000000b17217f7d1cf7d83c1a8202908060020a820491505090505b60006108008416600f0b13156143ad5760807001000000000000058b90bfbe8e7bdcbe2e8202908060020a820491505090505b60006104008416600f0b13156143e0576080700100000000000002c5c85fdf473dea871f8202908060020a820491505090505b60006102008416600f0b131561441357608070010000000000000162e42fefa39ef44d918202908060020a820491505090505b60006101008416600f0b1315614446576080700100000000000000b17217f7d1cf79e9498202908060020a820491505090505b600060808416600f0b131561447857608070010000000000000058b90bfbe8e7bce5448202908060020a820491505090505b600060408416600f0b13156144aa5760807001000000000000002c5c85fdf473de6eca8202908060020a820491505090505b600060208416600f0b13156144dc576080700100000000000000162e42fefa39ef366f8202908060020a820491505090505b600060108416600f0b131561450e5760807001000000000000000b17217f7d1cf79afa8202908060020a820491505090505b600060088416600f0b1315614540576080700100000000000000058b90bfbe8e7bcd6d8202908060020a820491505090505b600060048416600f0b131561457257608070010000000000000002c5c85fdf473de6b28202908060020a820491505090505b600060028416600f0b13156145a45760807001000000000000000162e42fefa39ef3588202908060020a820491505090505b600060018416600f0b13156145d657608070010000000000000000b17217f7d1cf79ab8202908060020a820491505090505b604083600f0b9060008212600003808260020a828518041892505050603f03600f0b81908060020a820491505090506f7fffffffffffffffffffffffffffffff600f0b81111561462557600080fd5b809150505b919050565b6040518060600160405280600063ffffffff168152602001600068ffffffffffffffffff168152602001600067ffffffffffffffff1681525090565b6000604051905090565b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6146af8161467a565b81146146ba57600080fd5b50565b6000813590506146cc816146a6565b92915050565b6000602082840312156146e8576146e7614675565b5b60006146f6848285016146bd565b91505092915050565b60008115159050919050565b614714816146ff565b82525050565b600060208201905061472f600083018461470b565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b8381101561476f578082015181840152602081019050614754565b60008484015250505050565b6000601f19601f8301169050919050565b600061479782614735565b6147a18185614740565b93506147b1818560208601614751565b6147ba8161477b565b840191505092915050565b600060208201905081810360008301526147df818461478c565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000614812826147e7565b9050919050565b61482281614807565b811461482d57600080fd5b50565b60008135905061483f81614819565b92915050565b6000819050919050565b61485881614845565b811461486357600080fd5b50565b6000813590506148758161484f565b92915050565b6000806040838503121561489257614891614675565b5b60006148a085828601614830565b92505060206148b185828601614866565b9150509250929050565b600081600f0b9050919050565b6148d1816148bb565b82525050565b60006020820190506148ec60008301846148c8565b92915050565b6148fb816148bb565b811461490657600080fd5b50565b600081359050614918816148f2565b92915050565b6000806040838503121561493557614934614675565b5b600061494385828601614866565b925050602061495485828601614909565b9150509250929050565b61496781614845565b82525050565b6000602082019050614982600083018461495e565b92915050565b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6149c58261477b565b810181811067ffffffffffffffff821117156149e4576149e361498d565b5b80604052505050565b60006149f761466b565b9050614a0382826149bc565b919050565b600063ffffffff82169050919050565b614a2181614a08565b8114614a2c57600080fd5b50565b600081359050614a3e81614a18565b92915050565b600068ffffffffffffffffff82169050919050565b614a6281614a44565b8114614a6d57600080fd5b50565b600081359050614a7f81614a59565b92915050565b600067ffffffffffffffff82169050919050565b614aa281614a85565b8114614aad57600080fd5b50565b600081359050614abf81614a99565b92915050565b600060608284031215614adb57614ada614988565b5b614ae560606149ed565b90506000614af584828501614a2f565b6000830152506020614b0984828501614a70565b6020830152506040614b1d84828501614ab0565b60408301525092915050565b600060608284031215614b3f57614b3e614675565b5b6000614b4d84828501614ac5565b91505092915050565b600080600060608486031215614b6f57614b6e614675565b5b6000614b7d86828701614830565b9350506020614b8e86828701614830565b9250506040614b9f86828701614866565b9150509250925092565b600060208284031215614bbf57614bbe614675565b5b6000614bcd84828501614866565b91505092915050565b600060208284031215614bec57614beb614675565b5b6000614bfa84828501614830565b91505092915050565b60008060408385031215614c1a57614c19614675565b5b6000614c2885828601614866565b9250506020614c3985828601614866565b9150509250929050565b614c4c81614a08565b82525050565b614c5b81614a44565b82525050565b614c6a81614a85565b82525050565b6000606082019050614c856000830186614c43565b614c926020830185614c52565b614c9f6040830184614c61565b949350505050565b600060ff82169050919050565b614cbd81614ca7565b82525050565b6000602082019050614cd86000830184614cb4565b92915050565b614ce781614807565b82525050565b6000602082019050614d026000830184614cde565b92915050565b60008060008060808587031215614d2257614d21614675565b5b6000614d3087828801614866565b9450506020614d4187828801614909565b9350506040614d5287828801614866565b9250506060614d6387828801614866565b91505092959194509250565b614d7881614a08565b82525050565b614d8781614a44565b82525050565b614d9681614a85565b82525050565b606082016000820151614db26000850182614d6f565b506020820151614dc56020850182614d7e565b506040820151614dd86040850182614d8d565b50505050565b6000606082019050614df36000830184614d9c565b92915050565b60006fffffffffffffffffffffffffffffffff82169050919050565b614e1e81614df9565b82525050565b6000602082019050614e396000830184614e15565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680614e8657607f821691505b602082108103614e9957614e98614e3f565b5b50919050565b7f4558504952454400000000000000000000000000000000000000000000000000600082015250565b6000614ed5600783614740565b9150614ee082614e9f565b602082019050919050565b60006020820190508181036000830152614f0481614ec8565b9050919050565b7f5a45524f5f464952535400000000000000000000000000000000000000000000600082015250565b6000614f41600a83614740565b9150614f4c82614f0b565b602082019050919050565b60006020820190508181036000830152614f7081614f34565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000614fb182614845565b9150614fbc83614845565b9250828203905081811115614fd457614fd3614f77565b5b92915050565b6000614fe582614845565b9150614ff083614845565b925082820190508082111561500857615007614f77565b5b92915050565b7f4552525f41434345535300000000000000000000000000000000000000000000600082015250565b6000615044600a83614740565b915061504f8261500e565b602082019050919050565b6000602082019050818103600083015261507381615037565b9050919050565b600061508582614845565b915061509083614845565b925082820261509e81614845565b915082820484148315176150b5576150b4614f77565b5b5092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006150f682614845565b915061510183614845565b925082615111576151106150bc565b5b828204905092915050565b7f4552525f494e56414c49445f5354415445000000000000000000000000000000600082015250565b6000615152601183614740565b915061515d8261511c565b602082019050919050565b6000602082019050818103600083015261518181615145565b9050919050565b7f4552525f414c52454144595f4c4f434b45440000000000000000000000000000600082015250565b60006151be601283614740565b91506151c982615188565b602082019050919050565b600060208201905081810360008301526151ed816151b1565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4552525f4f5645525350454e4400000000000000000000000000000000000000600082015250565b6000615259600d83614740565b915061526482615223565b602082019050919050565b600060208201905081810360008301526152888161524c565b9050919050565b61529881614df9565b81146152a357600080fd5b50565b6000815190506152b58161528f565b92915050565b6000602082840312156152d1576152d0614675565b5b60006152df848285016152a6565b9150509291505056fea2646970667358221220d5408287c06e54da7f8dc7cae17f7195a3e73269d5dd5fa683fc25dee0cafca364736f6c63430008110033 -\ No newline at end of file +60c06040523480156200001157600080fd5b506040516200610538038062006105833981810160405281019062000037919062000909565b6801000000000000000083600f0b126200005057600080fd5b6200005a6200061e565b33600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600e6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff021916908315150217905550866006908162000126919062000c2a565b50856007908162000138919062000c2a565b508460ff166008819055504260038190555060035460808181525050603c8362000163919062000d40565b60a081815250506200018a60016200034f6401000000000262002d4d176401000000009004565b600260006101000a8154816fffffffffffffffffffffffffffffffff0219169083600f0b6fffffffffffffffffffffffffffffffff160217905550620001e484620003766401000000000262002d73176401000000009004565b600d60006101000a8154816fffffffffffffffffffffffffffffffff0219169083600f0b6fffffffffffffffffffffffffffffffff1602179055506200024e6000600260009054906101000a9004600f0b60006001620003ca640100000000026401000000009004565b90506000819080600181540180825580915050600190039060005260206000200160009091909190915060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548168ffffffffffffffffff021916908368ffffffffffffffffff160217905550604082015181600001600d6101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505081601060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055505050505050505062000d8b565b6000677fffffffffffffff8211156200036757600080fd5b6040829060020a029050919050565b60008082600f0b136200038857600080fd5b60806fb17217f7d1cf79abc9e3b39803f2f6af620003b5846200044e640100000000026401000000009004565b600f0b02908060020a82049150509050919050565b620003d46200061e565b620003de6200061e565b82816000019063ffffffff16908163ffffffff168152505083816020019068ffffffffffffffffff16908168ffffffffffffffffff168152505067ffffffffffffffff8516816040019067ffffffffffffffff16908167ffffffffffffffff168152505080915050949350505050565b60008082600f0b136200046057600080fd5b60008083600f0b90506801000000000000000081126200049c576040819060008212600003808260020a82851804189250505090506040820191505b6401000000008112620004cb576020819060008212600003808260020a82851804189250505090506020820191505b620100008112620004f8576010819060008212600003808260020a82851804189250505090506010820191505b610100811262000524576008819060008212600003808260020a82851804189250505090506008820191505b601081126200054f576004819060008212600003808260020a82851804189250505090506004820191505b600481126200057a576002819060008212600003808260020a82851804189250505090506002820191505b600281126200058a576001820191505b600060408084039060020a029050600083607f0386600f0b9060020a029050600067800000000000000090505b600081131562000611578182029150600060ff83908060020a8204915050905080607f0183908060020a8204915050925080820284019350506001819060008212600003808260020a8285180418925050509050620005b7565b5081945050505050919050565b6040518060600160405280600063ffffffff168152602001600068ffffffffffffffffff168152602001600067ffffffffffffffff1681525090565b6000604051905090565b600080fd5b600080fd5b600080fd5b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b620006c38262000678565b810181811067ffffffffffffffff82111715620006e557620006e462000689565b5b80604052505050565b6000620006fa6200065a565b9050620007088282620006b8565b919050565b600067ffffffffffffffff8211156200072b576200072a62000689565b5b620007368262000678565b9050602081019050919050565b60005b838110156200076357808201518184015260208101905062000746565b60008484015250505050565b60006200078662000780846200070d565b620006ee565b905082815260208101848484011115620007a557620007a462000673565b5b620007b284828562000743565b509392505050565b600082601f830112620007d257620007d16200066e565b5b8151620007e48482602086016200076f565b91505092915050565b600060ff82169050919050565b6200080581620007ed565b81146200081157600080fd5b50565b6000815190506200082581620007fa565b92915050565b600081600f0b9050919050565b62000843816200082b565b81146200084f57600080fd5b50565b600081519050620008638162000838565b92915050565b6000819050919050565b6200087e8162000869565b81146200088a57600080fd5b50565b6000815190506200089e8162000873565b92915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620008d182620008a4565b9050919050565b620008e381620008c4565b8114620008ef57600080fd5b50565b6000815190506200090381620008d8565b92915050565b60008060008060008060c0878903121562000929576200092862000664565b5b600087015167ffffffffffffffff8111156200094a576200094962000669565b5b6200095889828a01620007ba565b965050602087015167ffffffffffffffff8111156200097c576200097b62000669565b5b6200098a89828a01620007ba565b95505060406200099d89828a0162000814565b9450506060620009b089828a0162000852565b9350506080620009c389828a016200088d565b92505060a0620009d689828a01620008f2565b9150509295509295509295565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000600282049050600182168062000a3657607f821691505b60208210810362000a4c5762000a4b620009ee565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b60008160020a8302905092915050565b60006008830262000ab97fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000a77565b62000ac5868362000a77565b95508019841693508086168417925050509392505050565b6000819050919050565b600062000b0862000b0262000afc8462000869565b62000add565b62000869565b9050919050565b6000819050919050565b62000b248362000ae7565b62000b3c62000b338262000b0f565b84845462000a87565b825550505050565b600090565b62000b5362000b44565b62000b6081848462000b19565b505050565b5b8181101562000b885762000b7c60008262000b49565b60018101905062000b66565b5050565b601f82111562000bd75762000ba18162000a52565b62000bac8462000a67565b8101602085101562000bbc578190505b62000bd462000bcb8562000a67565b83018262000b65565b50505b505050565b60008160020a8304905092915050565b600062000bff6000198460080262000bdc565b1980831691505092915050565b600062000c1a838362000bec565b9150826002028217905092915050565b62000c3582620009e3565b67ffffffffffffffff81111562000c515762000c5062000689565b5b62000c5d825462000a1d565b62000c6a82828562000b8c565b600060209050601f83116001811462000ca2576000841562000c8d578287015190505b62000c99858262000c0c565b86555062000d09565b601f19841662000cb28662000a52565b60005b8281101562000cdc5784890151825560018201915060208501945060208101905062000cb5565b8683101562000cfc578489015162000cf8601f89168262000bec565b8355505b6001600288020188555050505b505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600062000d4d8262000869565b915062000d5a8362000869565b925082820262000d6a8162000869565b9150828204841483151762000d845762000d8362000d11565b5b5092915050565b60805160a05161532362000de260003960008181611aec015281816122da015281816128dc01528181612b0f0152612c23015260008181611b1801528181612b3b01528181612c440152612c8901526153236000f3fe608060405234801561001057600080fd5b50600436106103a0576000357c0100000000000000000000000000000000000000000000000000000000900480636a2d094e116101fb578063983b2d561161012c578063d5abeb01116100ca578063e69571af11610099578063e69571af14610ba9578063e809529514610bd9578063eda4e6d614610bf7578063f2fde38b14610c15576103a0565b8063d5abeb0114610b21578063d7a52fa914610b3f578063d89135cd14610b5b578063e54063a614610b79576103a0565b8063b1cb0db311610106578063b1cb0db314610a97578063b470aade14610ab5578063c0ab707714610ad3578063d340ef8a14610b03576103a0565b8063983b2d5614610a07578063a457c2d714610a37578063a9059cbb14610a67576103a0565b806379ba5097116101995780638da5cb5b116101735780638da5cb5b146109915780638f0b2d5d146109af5780638f1df6bc146109cb57806395d89b41146109e9576103a0565b806379ba50971461092557806384dde4af1461094357806386fe212d14610961576103a0565b8063731f237c116101d5578063731f237c1461089b57806373f42561146108b95780637445e33a146108d757806374bedb9514610907576103a0565b80636a2d094e1461081f5780636f8b44b01461084f57806370a082311461086b576103a0565b80633092afd5116102d5578063449a52f81161027357806351b4541c1161024d57806351b4541c1461078357806358b2833b146107b35780635f408c04146107d15780636787a9be146107ef576103a0565b8063449a52f8146106f357806347a50517146107235780634abfbba214610753576103a0565b806331a5995d116102af57806331a5995d14610657578063395093511461067557806340452d91146106a557806342966c68146106d7576103a0565b80633092afd5146105d95780633133348714610609578063313ce56714610639576103a0565b80631989c6a81161034257806323a859441161031c57806323a859441461051957806323b872dd146105495780632a7aec09146105795780632c1758c1146105a9576103a0565b80631989c6a8146104ad578063213d1e1e146104cb578063229ba197146104fb576103a0565b806309f28f3c1161037e57806309f28f3c1461042357806311c566151461044157806318160ddd1461047157806318cbbcfc1461048f576103a0565b806301ffc9a7146103a557806306fdde03146103d5578063095ea7b3146103f3575b600080fd5b6103bf60048036038101906103ba91906146d7565b610c45565b6040516103cc919061471f565b60405180910390f35b6103dd610deb565b6040516103ea91906147ca565b60405180910390f35b61040d60048036038101906104089190614880565b610e79565b60405161041a919061471f565b60405180910390f35b61042b6110b5565b60405161043891906148dc565b60405180910390f35b61045b60048036038101906104569190614923565b6110c8565b6040516104689190614972565b60405180910390f35b610479611118565b6040516104869190614972565b60405180910390f35b61049761112f565b6040516104a49190614972565b60405180910390f35b6104b5611134565b6040516104c29190614972565b60405180910390f35b6104e560048036038101906104e09190614b2e565b611140565b6040516104f29190614972565b60405180910390f35b610503611159565b60405161051091906148dc565b60405180910390f35b610533600480360381019061052e9190614b2e565b61116c565b604051610540919061471f565b60405180910390f35b610563600480360381019061055e9190614b5b565b6111d4565b604051610570919061471f565b60405180910390f35b610593600480360381019061058e9190614b2e565b6113f2565b6040516105a091906148dc565b60405180910390f35b6105c360048036038101906105be9190614bae565b61142a565b6040516105d0919061471f565b60405180910390f35b6105f360048036038101906105ee9190614bdb565b61145d565b604051610600919061471f565b60405180910390f35b610623600480360381019061061e9190614c08565b611567565b6040516106309190614972565b60405180910390f35b6106416115d3565b60405161064e9190614972565b60405180910390f35b61065f6115d9565b60405161066c9190614972565b60405180910390f35b61068f600480360381019061068a9190614880565b6115df565b60405161069c919061471f565b60405180910390f35b6106bf60048036038101906106ba9190614bae565b611775565b6040516106ce93929190614c75565b60405180910390f35b6106f160048036038101906106ec9190614bae565b6117e4565b005b61070d60048036038101906107089190614880565b611920565b60405161071a919061471f565b60405180910390f35b61073d60048036038101906107389190614bdb565b611a9f565b60405161074a9190614972565b60405180910390f35b61076d60048036038101906107689190614bae565b611ae8565b60405161077a9190614972565b60405180910390f35b61079d60048036038101906107989190614bae565b611b48565b6040516107aa9190614972565b60405180910390f35b6107bb611b69565b6040516107c89190614972565b60405180910390f35b6107d9611b6f565b6040516107e69190614cc8565b60405180910390f35b61080960048036038101906108049190614bae565b611c3c565b6040516108169190614972565b60405180910390f35b61083960048036038101906108349190614bae565b611c7d565b6040516108469190614972565b60405180910390f35b61086960048036038101906108649190614bae565b611c9e565b005b61088560048036038101906108809190614bdb565b611d66565b6040516108929190614972565b60405180910390f35b6108a3611dd0565b6040516108b09190614972565b60405180910390f35b6108c1611de1565b6040516108ce9190614972565b60405180910390f35b6108f160048036038101906108ec9190614b2e565b611de7565b6040516108fe9190614972565b60405180910390f35b61090f611e15565b60405161091c9190614972565b60405180910390f35b61092d611e1b565b60405161093a919061471f565b60405180910390f35b61094b611fc2565b6040516109589190614cf2565b60405180910390f35b61097b60048036038101906109769190614bae565b611fe8565b6040516109889190614972565b60405180910390f35b6109996120c7565b6040516109a69190614cf2565b60405180910390f35b6109c960048036038101906109c49190614bdb565b6120ed565b005b6109d36121a2565b6040516109e0919061471f565b60405180910390f35b6109f1612452565b6040516109fe91906147ca565b60405180910390f35b610a216004803603810190610a1c9190614bdb565b6124e0565b604051610a2e919061471f565b60405180910390f35b610a516004803603810190610a4c9190614880565b6125b4565b604051610a5e919061471f565b60405180910390f35b610a816004803603810190610a7c9190614880565b6127d3565b604051610a8e919061471f565b60405180910390f35b610a9f6128d4565b604051610aac9190614972565b60405180910390f35b610abd6128da565b604051610aca9190614972565b60405180910390f35b610aed6004803603810190610ae89190614bae565b6128fe565b604051610afa9190614972565b60405180910390f35b610b0b612a74565b604051610b189190614972565b60405180910390f35b610b29612a7a565b604051610b369190614972565b60405180910390f35b610b596004803603810190610b549190614bae565b612a80565b005b610b63612b7f565b604051610b709190614972565b60405180910390f35b610b936004803603810190610b8e9190614b2e565b612b89565b604051610ba09190614972565b60405180910390f35b610bc36004803603810190610bbe9190614d0d565b612b9d565b604051610bd09190614de3565b60405180910390f35b610be1612c1d565b604051610bee9190614e29565b60405180910390f35b610bff612c87565b604051610c0c9190614972565b60405180910390f35b610c2f6004803603810190610c2a9190614bdb565b612cab565b604051610c3c919061471f565b60405180910390f35b600063c6bb4b707c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603610c995760019050610de6565b63449a52f87c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603610ceb5760019050610de6565b6301ffc9a77c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603610d3d5760019050610de6565b639493f8b27c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603610d8f5760019050610de6565b6337a47be47c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603610de15760019050610de6565b600090505b919050565b60068054610df890614e73565b80601f0160208091040260200160405190810160405280929190818152602001828054610e2490614e73565b8015610e715780601f10610e4657610100808354040283529160200191610e71565b820191906000526020600020905b815481529060010190602001808311610e5457829003601f168201915b505050505081565b6000806000610e86611b6f565b905060028160ff1603610e9e576000925050506110af565b60008160ff161115610ee5576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610edc90614ef0565b60405180910390fd5b6000600f60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541115610fae5760008414610fad576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610fa490614f5c565b60405180910390fd5b5b610fb66121a2565b50610fc084611c3c565b915081600f60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925866040516110a09190614972565b60405180910390a36001925050505b92915050565b600260009054906101000a9004600f0b81565b6000806110ef6110d785612d4d565b6110ea6110e46001612d4d565b86612db5565b612e1c565b90506110fa81612e9b565b67ffffffffffffffff168461110f9190614fab565b91505092915050565b6000600c5460095461112a9190614fab565b905090565b600f81565b60008080549050905090565b6000816020015168ffffffffffffffffff169050919050565b600d60009054906101000a9004600f0b81565b600080826000015163ffffffff16111561118957600090506111cf565b6000826020015168ffffffffffffffffff1611156111aa57600090506111cf565b6000826040015167ffffffffffffffff1611156111ca57600090506111cf565b600190505b919050565b6000806000806111e2611b6f565b905060028160ff16036111fb57600093505050506113eb565b60008160ff161115611242576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161123990614ef0565b60405180910390fd5b61124a6121a2565b5061125485611c3c565b925082600f60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156112df57600080fd5b82600f60008973ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825461136b9190614fab565b9250508190555061137d878785612ed0565b91508573ffffffffffffffffffffffffffffffffffffffff168773ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516113dc9190614972565b60405180910390a38193505050505b9392505050565b60008067ffffffffffffffff836040015160070b169050600081600f0b036114215761141e6001612d4d565b90505b80915050919050565b6000600f821061143957600080fd5b6000820361144e57600f601454149050611458565b8160145483161490505b919050565b600061146c600160ff1661142a565b1561147657600080fd5b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614806114fd57503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b61150657600080fd5b6000600e60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060019050919050565b60008060008061157686612d4d565b925061158185612d4d565b915061159c600d60009054906101000a9004600f0b83612e1c565b90506115a781612ef5565b90506115b38382612e1c565b90506115be81612e9b565b67ffffffffffffffff16935050505092915050565b60085481565b60145481565b6000806115ea6121a2565b506115f483611c3c565b905080600f60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546116829190614fdf565b925050819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600f60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546040516117629190614972565b60405180910390a3600191505092915050565b6000818154811061178557600080fd5b906000526020600020016000915090508060000160009054906101000a900463ffffffff16908060000160049054906101000a900468ffffffffffffffffff169080600001600d9054906101000a900467ffffffffffffffff16905083565b60006117ee611b6f565b60ff16146117fb57600080fd5b600e60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1661185157600080fd5b600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205481111561189d57600080fd5b60006118a882611c3c565b90506118b43382612f82565b5081600c60008282546118c79190614fdf565b925050819055503373ffffffffffffffffffffffffffffffffffffffff167fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca5836040516119149190614972565b60405180910390a25050565b600080600061192d611b6f565b60ff161461193a57600080fd5b600e60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff166119c6576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016119bd9061505f565b60405180910390fd5b6119ce6121a2565b50600060135411156119f757601354836009546119eb9190614fdf565b11156119f657600080fd5b5b8260096000828254611a099190614fdf565b92505081905550611a1983611c3c565b9050611a258482613089565b508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fab8530f87dc9b59234c4623bf917212bb2536d647574c8e7e5da92c2ede0c9f885604051611a839190614972565b60405180910390a3611a9361314d565b50600191505092915050565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60007f000000000000000000000000000000000000000000000000000000000000000082611b16919061507f565b7f0000000000000000000000000000000000000000000000000000000000000000611b419190614fdf565b9050919050565b6000603c8242611b589190614fab565b611b6291906150f0565b9050919050565b60035481565b6000601260009054906101000a900460ff1615611b8f5760019050611c39565b600060115403611ba25760009050611c39565b6011544210611c3457611bcf603c600354611bbd91906150f0565b601154611bca9190614fab565b6128fe565b506001601260006101000a81548160ff0219169083151502179055507ff80dbaea4785589e52984ca36a31de106adc77759539a5c7d92883bf49692fe942604051611c1a9190614972565b60405180910390a1611c2a6121a2565b5060029050611c39565b600090505b90565b600080611c60611c4b84612d4d565b600260009054906101000a9004600f0b613311565b9050611c6b81612e9b565b67ffffffffffffffff16915050919050565b6000603c8242611c8d9190614fab565b611c9791906150f0565b9050919050565b611cab600860ff1661142a565b15611cb557600080fd5b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611d0f57600080fd5b611d17611118565b8111611d2257600080fd5b6013547f9722adea12ab7ef86fc45b88f0e0b567639e8dddaae60261e08c03d747fbbfe682604051611d549190614972565b60405180910390a28060138190555050565b6000806000806000611d7f611d7a87611a9f565b612d4d565b9350611d8c600354611b48565b9150611da784600260009054906101000a9004600f0b612e1c565b9250611dc5611db584612e9b565b67ffffffffffffffff1683611567565b945050505050919050565b6000611ddc60006128fe565b905090565b600c5481565b6000806000611df584611140565b9150611e00846113f2565b9050611e0c82826110c8565b92505050919050565b600b5481565b600080600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611e7857600080fd5b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600560009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35090565b601060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60006010821061202d576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120249061516d565b60405180910390fd5b6000601454831614612074576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161206b906151d9565b60405180910390fd5b816014600082825417925050819055507fdd5e7cad9599c1dcaa8d5adcd88f157fc5fada62511c0d3edb1bfd3b778acc416014546040516120b59190614972565b60405180910390a16014549050919050565b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6120fa600260ff1661142a565b1561210457600080fd5b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461215e57600080fd5b80601060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b60006121ac614634565b6121b4614634565b6121bc614634565b60008060008060006121cc611dd0565b506121d561339d565b97506121e08861116c565b156121f65760009850505050505050505061244f565b6000600a548154811061220c5761220b6151f9565b5b906000526020600020016040518060600160405290816000820160009054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160049054906101000a900468ffffffffffffffffff1668ffffffffffffffffff1668ffffffffffffffffff16815260200160008201600d9054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff168152505095506122b988612b89565b94506001856122c89190614fdf565b90506122d3866113f2565b9350603c857f0000000000000000000000000000000000000000000000000000000000000000612303919061507f565b61230d91906150f0565b9150612338612333600d60009054906101000a9004600f0b61232e85612d4d565b612e1c565b612ef5565b925061234e600084612348611118565b84612b9d565b96506000879080600181540180825580915050600190039060005260206000200160009091909190915060008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548168ffffffffffffffffff021916908368ffffffffffffffffff160217905550604082015181600001600d6101000a81548167ffffffffffffffff021916908367ffffffffffffffff160217905550505061240a87613533565b507f55d243082e019fce4009ccea5368b92e436c17586a1e793c7deda16df4e5d6758160405161243a9190614972565b60405180910390a16001985050505050505050505b90565b6007805461245f90614e73565b80601f016020809104026020016040519081016040528092919081815260200182805461248b90614e73565b80156124d85780601f106124ad576101008083540402835291602001916124d8565b820191906000526020600020905b8154815290600101906020018083116124bb57829003601f168201915b505050505081565b60006124ef600160ff1661142a565b156124f957600080fd5b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461255357600080fd5b6001600e60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060019050919050565b6000806125c083611c3c565b905080600f60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101561264b57600080fd5b6126536121a2565b5080600f60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546126e09190614fab565b925050819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925600f60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546040516127c09190614972565b60405180910390a3600191505092915050565b6000806000806127e1611b6f565b905060028160ff16036127fa57600093505050506128ce565b60008160ff161115612841576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161283890614ef0565b60405180910390fd5b6128496121a2565b5061285385611c3c565b9250612860338785612ed0565b91508573ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef876040516128bf9190614972565b60405180910390a38193505050505b92915050565b60115481565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000806000806000601260009054906101000a900460ff1615612928576000945050505050612a6f565b612933600354611b48565b92506000830361294a576000945050505050612a6f565b600260009054906101000a9004600f0b905060008611801561296b57508286105b15612974578592505b61297d83612d4d565b9150612998600d60009054906101000a9004600f0b83612e1c565b93506129a384612ef5565b93506129be600260009054906101000a9004600f0b85612e1c565b600260006101000a8154816fffffffffffffffffffffffffffffffff0219169083600f0b6fffffffffffffffffffffffffffffffff160217905550603c83612a06919061507f565b600354612a139190614fdf565b60038190555080600f0b836003547f1c9c74563c32efd114cb36fb5e432d9386c8254d08456614804a33a3088ab736600260009054906101000a9004600f0b604051612a5f91906148dc565b60405180910390a4829450505050505b919050565b600a5481565b60135481565b6000612a8f600460ff1661142a565b15612a9957600080fd5b601260009054906101000a900460ff1615612ab357600080fd5b600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612b0d57600080fd5b7f000000000000000000000000000000000000000000000000000000000000000082612b39919061507f565b7f0000000000000000000000000000000000000000000000000000000000000000612b649190614fdf565b90506011548111612b7457600080fd5b806011819055505050565b6000600c54905090565b6000816000015163ffffffff169050919050565b612ba5614634565b612bad614634565b82816000019063ffffffff16908163ffffffff168152505083816020019068ffffffffffffffffff16908168ffffffffffffffffff168152505067ffffffffffffffff8516816040019067ffffffffffffffff16908167ffffffffffffffff168152505080915050949350505050565b600060017f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000042612c6e9190614fab565b612c7891906150f0565b612c829190614fdf565b905090565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614612d0757600080fd5b81600560006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550919050565b6000677fffffffffffffff821115612d6457600080fd5b6040829060020a029050919050565b60008082600f0b13612d8457600080fd5b60806fb17217f7d1cf79abc9e3b39803f2f6af612da084613652565b600f0b02908060020a82049150509050919050565b60008082600f0b84600f0b0390507fffffffffffffffffffffffffffffffff80000000000000000000000000000000600f0b8112158015612e0957506f7fffffffffffffffffffffffffffffff600f0b8113155b612e1257600080fd5b8091505092915050565b600080604083600f0b85600f0b029060008212600003808260020a82851804189250505090507fffffffffffffffffffffffffffffffff80000000000000000000000000000000600f0b8112158015612e8857506f7fffffffffffffffffffffffffffffff600f0b8113155b612e9157600080fd5b8091505092915050565b60008082600f0b1215612ead57600080fd5b604082600f0b9060008212600003808260020a8285180418925050509050919050565b600080612edd8584612f82565b50612ee88484613089565b5060019150509392505050565b60006840000000000000000082600f0b12612f0f57600080fd5b7fffffffffffffffffffffffffffffffffffffffffffffffc0000000000000000082600f0b1215612f435760009050612f7d565b612f7a6080700171547652b82fe1777d0ffda0d23a7d1284600f0b029060008212600003808260020a828518041892505050613818565b90505b919050565b600080600080600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905060008503612fde5760009350505050613083565b612fe786611a9f565b92508483101561302c576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161302390615274565b60405180910390fd5b84836130389190614fab565b600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600193505050505b92915050565b600080600080600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050600085036130e55760009350505050613147565b6130ee86611a9f565b925084836130fc9190614fdf565b600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600193505050505b92915050565b6000613157614634565b6000613161611118565b9050600060016000805490506131779190614fab565b81548110613188576131876151f9565b5b906000526020600020016040518060600160405290816000820160009054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160049054906101000a900468ffffffffffffffffff1668ffffffffffffffffff1668ffffffffffffffffff16815260200160008201600d9054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff1681525050915080826020019068ffffffffffffffffff16908168ffffffffffffffffff168152505081600060016000805490506132639190614fab565b81548110613274576132736151f9565b5b9060005260206000200160008201518160000160006101000a81548163ffffffff021916908363ffffffff16021790555060208201518160000160046101000a81548168ffffffffffffffffff021916908368ffffffffffffffffff160217905550604082015181600001600d6101000a81548167ffffffffffffffff021916908367ffffffffffffffff16021790555090505060019250505090565b60008082600f0b0361332257600080fd5b600082600f0b604085600f0b9060020a0281613341576133406150c1565b5b0590507fffffffffffffffffffffffffffffffff80000000000000000000000000000000600f0b811215801561338a57506f7fffffffffffffffffffffffffffffff600f0b8113155b61339357600080fd5b8091505092915050565b6133a5614634565b6133ad614634565b6133b5614634565b600080600a54815481106133cc576133cb6151f9565b5b906000526020600020016040518060600160405290816000820160009054906101000a900463ffffffff1663ffffffff1663ffffffff1681526020016000820160049054906101000a900468ffffffffffffffffff1668ffffffffffffffffff1668ffffffffffffffffff16815260200160008201600d9054906101000a900467ffffffffffffffff1667ffffffffffffffff1667ffffffffffffffff168152505092503073ffffffffffffffffffffffffffffffffffffffff1663e80952956040518163ffffffff167c0100000000000000000000000000000000000000000000000000000000028152600401602060405180830381865afa1580156134d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906134fb91906152c0565b6fffffffffffffffffffffffffffffffff16905061351883612b89565b811161352957819350505050613530565b8293505050505b90565b600080600061354184611de7565b613549611118565b6135539190614fab565b9150600b5461356183611c3c565b61356b9190614fab565b9050613599601060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1682613089565b50836000015163ffffffff16601060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f9a2a887706623ad3ff7fc85652deeceabe9fe1e00466c597972079ee91ea40d38460405161360d9190614972565b60405180910390a36001600a60008282546136289190614fdf565b9250508190555080600b60008282546136419190614fdf565b925050819055508192505050919050565b60008082600f0b1361366357600080fd5b60008083600f0b905068010000000000000000811261369e576040819060008212600003808260020a82851804189250505090506040820191505b64010000000081126136cc576020819060008212600003808260020a82851804189250505090506020820191505b6201000081126136f8576010819060008212600003808260020a82851804189250505090506010820191505b6101008112613723576008819060008212600003808260020a82851804189250505090506008820191505b6010811261374d576004819060008212600003808260020a82851804189250505090506004820191505b60048112613777576002819060008212600003808260020a82851804189250505090506002820191505b60028112613786576001820191505b600060408084039060020a029050600083607f0386600f0b9060020a029050600067800000000000000090505b600081131561380b578182029150600060ff83908060020a8204915050905080607f0183908060020a8204915050925080820284019350506001819060008212600003808260020a82851804189250505090506137b3565b5081945050505050919050565b60006840000000000000000082600f0b1261383257600080fd5b7fffffffffffffffffffffffffffffffffffffffffffffffc0000000000000000082600f0b1215613866576000905061462f565b60006f80000000000000000000000000000000905060006780000000000000008416600f0b13156138b457608070016a09e667f3bcc908b2fb1366ea957d3e8202908060020a820491505090505b60006740000000000000008416600f0b13156138ed5760807001306fe0a31b7152de8d5a46305c85edec8202908060020a820491505090505b60006720000000000000008416600f0b13156139265760807001172b83c7d517adcdf7c8c50eb14a791f8202908060020a820491505090505b60006710000000000000008416600f0b131561395f57608070010b5586cf9890f6298b92b71842a983638202908060020a820491505090505b60006708000000000000008416600f0b13156139985760807001059b0d31585743ae7c548eb68ca417fd8202908060020a820491505090505b60006704000000000000008416600f0b13156139d1576080700102c9a3e778060ee6f7caca4f7a29bde88202908060020a820491505090505b60006702000000000000008416600f0b1315613a0a57608070010163da9fb33356d84a66ae336dcdfa3f8202908060020a820491505090505b60006701000000000000008416600f0b1315613a43576080700100b1afa5abcbed6129ab13ec11dc95438202908060020a820491505090505b600066800000000000008416600f0b1315613a7b57608070010058c86da1c09ea1ff19d294cf2f679b8202908060020a820491505090505b600066400000000000008416600f0b1315613ab35760807001002c605e2e8cec506d21bfc89a23a00f8202908060020a820491505090505b600066200000000000008416600f0b1315613aeb576080700100162f3904051fa128bca9c55c31e5df8202908060020a820491505090505b600066100000000000008416600f0b1315613b235760807001000b175effdc76ba38e31671ca9397258202908060020a820491505090505b600066080000000000008416600f0b1315613b5b576080700100058ba01fb9f96d6cacd4b180917c3d8202908060020a820491505090505b600066040000000000008416600f0b1315613b9357608070010002c5cc37da9491d0985c348c68e7b38202908060020a820491505090505b600066020000000000008416600f0b1315613bcb5760807001000162e525ee054754457d59952920268202908060020a820491505090505b600066010000000000008416600f0b1315613c0357608070010000b17255775c040618bf4a4ade83fc8202908060020a820491505090505b6000658000000000008416600f0b1315613c3a5760807001000058b91b5bc9ae2eed81e9b7d4cfab8202908060020a820491505090505b6000654000000000008416600f0b1315613c71576080700100002c5c89d5ec6ca4d7c8acc017b7c98202908060020a820491505090505b6000652000000000008416600f0b1315613ca857608070010000162e43f4f831060e02d839a9d16d8202908060020a820491505090505b6000651000000000008416600f0b1315613cdf576080700100000b1721bcfc99d9f890ea069117638202908060020a820491505090505b6000650800000000008416600f0b1315613d1657608070010000058b90cf1e6d97f9ca14dbcc16288202908060020a820491505090505b6000650400000000008416600f0b1315613d4d5760807001000002c5c863b73f016468f6bac5ca2b8202908060020a820491505090505b6000650200000000008416600f0b1315613d84576080700100000162e430e5a18f6119e3c02282a58202908060020a820491505090505b6000650100000000008416600f0b1315613dbb5760807001000000b1721835514b86e6d96efd1bfe8202908060020a820491505090505b60006480000000008416600f0b1315613df1576080700100000058b90c0b48c6be5df846c5b2ef8202908060020a820491505090505b60006440000000008416600f0b1315613e2757608070010000002c5c8601cc6b9e94213c72737a8202908060020a820491505090505b60006420000000008416600f0b1315613e5d5760807001000000162e42fff037df38aa2b219f068202908060020a820491505090505b60006410000000008416600f0b1315613e9357608070010000000b17217fba9c739aa5819f44f98202908060020a820491505090505b60006408000000008416600f0b1315613ec95760807001000000058b90bfcdee5acd3c1cedc8238202908060020a820491505090505b60006404000000008416600f0b1315613eff576080700100000002c5c85fe31f35a6a30da1be508202908060020a820491505090505b60006402000000008416600f0b1315613f3557608070010000000162e42ff0999ce3541b9fffcf8202908060020a820491505090505b60006401000000008416600f0b1315613f6b576080700100000000b17217f80f4ef5aadda455548202908060020a820491505090505b600063800000008416600f0b1315613fa057608070010000000058b90bfbf8479bd5a81b51ad8202908060020a820491505090505b600063400000008416600f0b1315613fd55760807001000000002c5c85fdf84bd62ae30a74cc8202908060020a820491505090505b600063200000008416600f0b131561400a576080700100000000162e42fefb2fed257559bdaa8202908060020a820491505090505b600063100000008416600f0b131561403f5760807001000000000b17217f7d5a7716bba4a9ae8202908060020a820491505090505b600063080000008416600f0b1315614074576080700100000000058b90bfbe9ddbac5e109cce8202908060020a820491505090505b600063040000008416600f0b13156140a957608070010000000002c5c85fdf4b15de6f17eb0d8202908060020a820491505090505b600063020000008416600f0b13156140de5760807001000000000162e42fefa494f1478fde058202908060020a820491505090505b600063010000008416600f0b131561411357608070010000000000b17217f7d20cf927c8e94c8202908060020a820491505090505b6000628000008416600f0b13156141475760807001000000000058b90bfbe8f71cb4e4b33d8202908060020a820491505090505b6000624000008416600f0b131561417b576080700100000000002c5c85fdf477b662b269458202908060020a820491505090505b6000622000008416600f0b13156141af57608070010000000000162e42fefa3ae53369388c8202908060020a820491505090505b6000621000008416600f0b13156141e3576080700100000000000b17217f7d1d351a389d408202908060020a820491505090505b6000620800008416600f0b131561421757608070010000000000058b90bfbe8e8b2d3d4ede8202908060020a820491505090505b6000620400008416600f0b131561424b5760807001000000000002c5c85fdf4741bea6e77e8202908060020a820491505090505b6000620200008416600f0b131561427f576080700100000000000162e42fefa39fe95583c28202908060020a820491505090505b6000620100008416600f0b13156142b35760807001000000000000b17217f7d1cfb72b45e18202908060020a820491505090505b60006180008416600f0b13156142e6576080700100000000000058b90bfbe8e7cc35c3f08202908060020a820491505090505b60006140008416600f0b131561431957608070010000000000002c5c85fdf473e242ea388202908060020a820491505090505b60006120008416600f0b131561434c5760807001000000000000162e42fefa39f02b772c8202908060020a820491505090505b60006110008416600f0b131561437f57608070010000000000000b17217f7d1cf7d83c1a8202908060020a820491505090505b60006108008416600f0b13156143b25760807001000000000000058b90bfbe8e7bdcbe2e8202908060020a820491505090505b60006104008416600f0b13156143e5576080700100000000000002c5c85fdf473dea871f8202908060020a820491505090505b60006102008416600f0b131561441857608070010000000000000162e42fefa39ef44d918202908060020a820491505090505b60006101008416600f0b131561444b576080700100000000000000b17217f7d1cf79e9498202908060020a820491505090505b600060808416600f0b131561447d57608070010000000000000058b90bfbe8e7bce5448202908060020a820491505090505b600060408416600f0b13156144af5760807001000000000000002c5c85fdf473de6eca8202908060020a820491505090505b600060208416600f0b13156144e1576080700100000000000000162e42fefa39ef366f8202908060020a820491505090505b600060108416600f0b13156145135760807001000000000000000b17217f7d1cf79afa8202908060020a820491505090505b600060088416600f0b1315614545576080700100000000000000058b90bfbe8e7bcd6d8202908060020a820491505090505b600060048416600f0b131561457757608070010000000000000002c5c85fdf473de6b28202908060020a820491505090505b600060028416600f0b13156145a95760807001000000000000000162e42fefa39ef3588202908060020a820491505090505b600060018416600f0b13156145db57608070010000000000000000b17217f7d1cf79ab8202908060020a820491505090505b604083600f0b9060008212600003808260020a828518041892505050603f03600f0b81908060020a820491505090506f7fffffffffffffffffffffffffffffff600f0b81111561462a57600080fd5b809150505b919050565b6040518060600160405280600063ffffffff168152602001600068ffffffffffffffffff168152602001600067ffffffffffffffff1681525090565b6000604051905090565b600080fd5b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b6146b48161467f565b81146146bf57600080fd5b50565b6000813590506146d1816146ab565b92915050565b6000602082840312156146ed576146ec61467a565b5b60006146fb848285016146c2565b91505092915050565b60008115159050919050565b61471981614704565b82525050565b60006020820190506147346000830184614710565b92915050565b600081519050919050565b600082825260208201905092915050565b60005b83811015614774578082015181840152602081019050614759565b60008484015250505050565b6000601f19601f8301169050919050565b600061479c8261473a565b6147a68185614745565b93506147b6818560208601614756565b6147bf81614780565b840191505092915050565b600060208201905081810360008301526147e48184614791565b905092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000614817826147ec565b9050919050565b6148278161480c565b811461483257600080fd5b50565b6000813590506148448161481e565b92915050565b6000819050919050565b61485d8161484a565b811461486857600080fd5b50565b60008135905061487a81614854565b92915050565b600080604083850312156148975761489661467a565b5b60006148a585828601614835565b92505060206148b68582860161486b565b9150509250929050565b600081600f0b9050919050565b6148d6816148c0565b82525050565b60006020820190506148f160008301846148cd565b92915050565b614900816148c0565b811461490b57600080fd5b50565b60008135905061491d816148f7565b92915050565b6000806040838503121561493a5761493961467a565b5b60006149488582860161486b565b92505060206149598582860161490e565b9150509250929050565b61496c8161484a565b82525050565b60006020820190506149876000830184614963565b92915050565b600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6149ca82614780565b810181811067ffffffffffffffff821117156149e9576149e8614992565b5b80604052505050565b60006149fc614670565b9050614a0882826149c1565b919050565b600063ffffffff82169050919050565b614a2681614a0d565b8114614a3157600080fd5b50565b600081359050614a4381614a1d565b92915050565b600068ffffffffffffffffff82169050919050565b614a6781614a49565b8114614a7257600080fd5b50565b600081359050614a8481614a5e565b92915050565b600067ffffffffffffffff82169050919050565b614aa781614a8a565b8114614ab257600080fd5b50565b600081359050614ac481614a9e565b92915050565b600060608284031215614ae057614adf61498d565b5b614aea60606149f2565b90506000614afa84828501614a34565b6000830152506020614b0e84828501614a75565b6020830152506040614b2284828501614ab5565b60408301525092915050565b600060608284031215614b4457614b4361467a565b5b6000614b5284828501614aca565b91505092915050565b600080600060608486031215614b7457614b7361467a565b5b6000614b8286828701614835565b9350506020614b9386828701614835565b9250506040614ba48682870161486b565b9150509250925092565b600060208284031215614bc457614bc361467a565b5b6000614bd28482850161486b565b91505092915050565b600060208284031215614bf157614bf061467a565b5b6000614bff84828501614835565b91505092915050565b60008060408385031215614c1f57614c1e61467a565b5b6000614c2d8582860161486b565b9250506020614c3e8582860161486b565b9150509250929050565b614c5181614a0d565b82525050565b614c6081614a49565b82525050565b614c6f81614a8a565b82525050565b6000606082019050614c8a6000830186614c48565b614c976020830185614c57565b614ca46040830184614c66565b949350505050565b600060ff82169050919050565b614cc281614cac565b82525050565b6000602082019050614cdd6000830184614cb9565b92915050565b614cec8161480c565b82525050565b6000602082019050614d076000830184614ce3565b92915050565b60008060008060808587031215614d2757614d2661467a565b5b6000614d358782880161486b565b9450506020614d468782880161490e565b9350506040614d578782880161486b565b9250506060614d688782880161486b565b91505092959194509250565b614d7d81614a0d565b82525050565b614d8c81614a49565b82525050565b614d9b81614a8a565b82525050565b606082016000820151614db76000850182614d74565b506020820151614dca6020850182614d83565b506040820151614ddd6040850182614d92565b50505050565b6000606082019050614df86000830184614da1565b92915050565b60006fffffffffffffffffffffffffffffffff82169050919050565b614e2381614dfe565b82525050565b6000602082019050614e3e6000830184614e1a565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680614e8b57607f821691505b602082108103614e9e57614e9d614e44565b5b50919050565b7f4558504952454400000000000000000000000000000000000000000000000000600082015250565b6000614eda600783614745565b9150614ee582614ea4565b602082019050919050565b60006020820190508181036000830152614f0981614ecd565b9050919050565b7f5a45524f5f464952535400000000000000000000000000000000000000000000600082015250565b6000614f46600a83614745565b9150614f5182614f10565b602082019050919050565b60006020820190508181036000830152614f7581614f39565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000614fb68261484a565b9150614fc18361484a565b9250828203905081811115614fd957614fd8614f7c565b5b92915050565b6000614fea8261484a565b9150614ff58361484a565b925082820190508082111561500d5761500c614f7c565b5b92915050565b7f4552525f41434345535300000000000000000000000000000000000000000000600082015250565b6000615049600a83614745565b915061505482615013565b602082019050919050565b600060208201905081810360008301526150788161503c565b9050919050565b600061508a8261484a565b91506150958361484a565b92508282026150a38161484a565b915082820484148315176150ba576150b9614f7c565b5b5092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b60006150fb8261484a565b91506151068361484a565b925082615116576151156150c1565b5b828204905092915050565b7f4552525f494e56414c49445f5354415445000000000000000000000000000000600082015250565b6000615157601183614745565b915061516282615121565b602082019050919050565b600060208201905081810360008301526151868161514a565b9050919050565b7f4552525f414c52454144595f4c4f434b45440000000000000000000000000000600082015250565b60006151c3601283614745565b91506151ce8261518d565b602082019050919050565b600060208201905081810360008301526151f2816151b6565b9050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4552525f4f5645525350454e4400000000000000000000000000000000000000600082015250565b600061525e600d83614745565b915061526982615228565b602082019050919050565b6000602082019050818103600083015261528d81615251565b9050919050565b61529d81614dfe565b81146152a857600080fd5b50565b6000815190506152ba81615294565b92915050565b6000602082840312156152d6576152d561467a565b5b60006152e4848285016152ab565b9150509291505056fea2646970667358221220f7dd37b962b03307b9a47a02325dafb33edc00117a2875af89644526ff48de4064736f6c63430008110033 +\ No newline at end of file diff --git a/python/run_tests.sh b/python/run_tests.sh @@ -1,47 +1,14 @@ #!/bin/bash -set -x +set -a set -e - -export PYTHONPATH=. - -#modes=(MultiNocap MultiCap SingleCap SingleNocap) -#modes=(SingleCap SingleNocap) # other contracts need to be updted -modes=(SingleNocap) # other contracts need to be updted -for m in ${modes[@]}; do - ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_basic.py - ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_growth.py - ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_amounts.py - ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_single.py -done - -#modes=(SingleCap) # other contracts need to be updted -modes=() -for m in ${modes[@]}; do - ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_period.py -done - -modes=(SingleNocap) # other contracts need to be updted -for m in ${modes[@]}; do - ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_redistribution_unit.py - ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_redistribution_single.py -done - -#modes=(MultiCap SingleCap) -modes=() -for m in ${modes[@]}; do - ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_cap.py +set -x +default_pythonpath=$PYTHONPATH:. +export PYTHONPATH=${default_pythonpath:-.} +>&2 echo using pythonpath $PYTHONPATH +for f in `ls tests/*.py`; do + python $f done - -#modes=(MultiCap MultiNocap) -#for m in ${modes[@]}; do -# ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_remainder.py -# ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_redistribution.py -#done - -python tests/test_expiry.py -python tests/test_seal.py -python tests/test_cap.py - -set +e set +x +set +e +set +a diff --git a/python/tests/test_demurrage_ext.py b/python/tests/test_demurrage_ext.py @@ -1,41 +0,0 @@ -# standard imports -import datetime -import unittest - -# external imports -from chainlib.eth.nonce import RPCNonceOracle - -# local imports -from erc20_demurrage_token import DemurrageToken -from erc20_demurrage_token.demurrage import DemurrageCalculator - -# test imports -from erc20_demurrage_token.unittest.base import TestDemurrage - - -class TestEmulate(TestDemurrage): - - def test_amount_since(self): - d = datetime.datetime.utcnow() - datetime.timedelta(seconds=29, hours=5, minutes=3, days=4) - c = DemurrageCalculator(0.00000050105908373373) - a = c.amount_since(100, d.timestamp()) - self.assert_within_lower(a, 99.69667, 0.1) - - - def test_amount_since_slow(self): - d = datetime.datetime.utcnow() - datetime.timedelta(seconds=29, hours=5, minutes=3, days=4) - c = DemurrageCalculator(0.00000050105908373373) - a = c.amount_since_slow(100, d.timestamp()) - self.assert_within_lower(a, 99.69667, 0.1) - - - def test_from_contract(self): - nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) - c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) - self.deploy(c, 'SingleNocap') - dc = DemurrageCalculator.from_contract(self.rpc, self.chain_spec, self.address, sender_address=self.accounts[0]) - self.assertEqual(dc.r_min, 0.02) - - -if __name__ == '__main__': - unittest.main() diff --git a/python/tests/test_period.py b/python/tests/test_period.py @@ -172,7 +172,7 @@ class TestPeriod(TestDemurrageDefault): r = self.rpc.do(o) self.assertEqual(r['status'], 1) - o = c.balance_of(self.address, ZERO_ADDRESS, sender_address=self.accounts[0]) + o = c.balance_of(self.address, self.sink_address, sender_address=self.accounts[0]) r = self.rpc.do(o) balance = c.parse_balance_of(r) self.assertGreater(balance, 0) @@ -222,7 +222,7 @@ class TestPeriod(TestDemurrageDefault): r = self.rpc.do(o) self.assertEqual(r['status'], 1) - o = c.balance_of(self.address, ZERO_ADDRESS, sender_address=self.accounts[0]) + o = c.balance_of(self.address, self.sink_address, sender_address=self.accounts[0]) r = self.rpc.do(o) balance = c.parse_balance_of(r) self.assertLess(balance, old_sink_balance) diff --git a/python/tests/test_redistribution.py b/python/tests/test_redistribution.py @@ -1,328 +0,0 @@ -# standard imports -import os -import unittest -import json -import logging - -# external imports -from chainlib.eth.constant import ZERO_ADDRESS -from chainlib.eth.nonce import RPCNonceOracle -from chainlib.eth.tx import receipt -from chainlib.eth.block import ( - block_latest, - block_by_number, - ) -from chainlib.eth.address import to_checksum_address -from hexathon import ( - strip_0x, - add_0x, - ) - -# local imports -from erc20_demurrage_token import DemurrageToken - -# test imports -from erc20_demurrage_token.unittest.base import TestDemurrageDefault - -logging.basicConfig(level=logging.DEBUG) -logg = logging.getLogger() - -testdir = os.path.dirname(__file__) - -class TestRedistribution(TestDemurrageDefault): - - - - def test_whole_is_parts(self): - nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) - c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) - - (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 100000000) - self.rpc.do(o) - o = receipt(tx_hash) - r = self.rpc.do(o) - self.assertEqual(r['status'], 1) - - (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[2], 100000000) - self.rpc.do(o) - o = receipt(tx_hash) - r = self.rpc.do(o) - self.assertEqual(r['status'], 1) - - nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) - c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) - (tx_hash, o) = c.transfer(self.address, self.accounts[1], self.accounts[3], 50000000) - r = self.rpc.do(o) - o = receipt(tx_hash) - r = self.rpc.do(o) - self.assertEqual(r['status'], 1) - - self.backend.time_travel(self.start_time + self.period_seconds + 1) - - o = block_latest() - r = self.rpc.do(o) - o = block_by_number(r) - r = self.rpc.do(o) - self.assertEqual(r['timestamp'], self.start_time + self.period_seconds) - - (tx_hash, o) = c.change_period(self.address, self.accounts[1]) - r = self.rpc.do(o) - o = receipt(tx_hash) - r = self.rpc.do(o) - self.assertEqual(r['status'], 1) - - (tx_hash, o) = c.apply_redistribution_on_account(self.address, self.accounts[1], self.accounts[1]) - r = self.rpc.do(o) - o = receipt(tx_hash) - r = self.rpc.do(o) - self.assertEqual(r['status'], 1) - - balance = 0 - for i in range(3): - o = c.balance_of(self.address, self.accounts[i+1], sender_address=self.accounts[0]) - r = self.rpc.do(o) - balance_item = c.parse_balance_of(r) - balance += balance_item - logg.debug('balance {} {} total {}'.format(i, balance_item, balance)) - - o = c.balance_of(self.address, self.sink_address, sender_address=self.accounts[0]) - r = self.rpc.do(o) - balance_item = c.parse_balance_of(r) - balance += balance_item - - self.assertEqual(balance, 200000000) - - -# def test_debug_periods(self): -# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) -# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) -# -# o = c.actual_period(self.address, sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# pactual = c.parse_actual_period(r) -# -# o = c.period_start(self.address, sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# pstart = c.parse_actual_period(r) -# -# o = c.period_duration(self.address, sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# pduration = c.parse_actual_period(r) -# -# o = block_latest() -# blocknumber = self.rpc.do(o) -# -# logg.debug('actual {} start {} duration {} blocknumber {}'.format(pactual, pstart, pduration, blocknumber)) -# -# -# # TODO: check receipt log outputs -# def test_redistribution_storage(self): -# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) -# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) -# o = c.redistributions(self.address, 0, sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# self.assertEqual(strip_0x(r), '000000000000000000000000f424000000000000000000000000000000000001') -# -# (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], 1000000) -# r = self.rpc.do(o) -# -# (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[2], 1000000) -# r = self.rpc.do(o) -# -# external_address = to_checksum_address('0x' + os.urandom(20).hex()) -# -# nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc) -# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) -# (tx_hash, o) = c.transfer(self.address, self.accounts[2], external_address, 1000000) -# r = self.rpc.do(o) -# -# nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) -# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) -# (tx_hash, o) = c.transfer(self.address, self.accounts[1], external_address, 999999) -# r = self.rpc.do(o) -# -# self.backend.time_travel(self.start_time + self.period_seconds + 1) -# -# o = c.redistributions(self.address, 0, sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# self.assertEqual(strip_0x(r), '000000000000000000000000f42400000000010000000000001e848000000001') -# -# o = c.redistributions(self.address, 0, sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# self.assertEqual(strip_0x(r), '000000000000000000000000f42400000000010000000000001e848000000001') -# -# -# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) -# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) -# (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[0], 1000000) -# r = self.rpc.do(o) -# -# o = c.redistributions(self.address, 1, sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# self.assertEqual(strip_0x(r), '000000000000000000000000ef4200000000000000000000002dc6c000000002') -# -# -# def test_redistribution_balance_on_zero_participants(self): -# supply = self.default_supply -# -# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) -# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) -# (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], supply) -# r = self.rpc.do(o) -# -# self.backend.time_travel(self.start_time + self.period_seconds + 1) -# (tx_hash, o) = c.apply_demurrage(self.address, self.accounts[0]) -# self.rpc.do(o) -# o = receipt(tx_hash) -# rcpt = self.rpc.do(o) -# self.assertEqual(rcpt['status'], 1) -# -# (tx_hash, o) = c.change_period(self.address, self.accounts[0]) -# self.rpc.do(o) -# o = receipt(tx_hash) -# r = self.rpc.do(o) -# self.assertEqual(r['status'], 1) -# -# o = c.total_supply(self.address, sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# total_supply = c.parse_total_supply(r) -# sink_increment = int(total_supply * (self.tax_level / 1000000)) -# self.assertEqual(supply, total_supply) -# -# for l in rcpt['logs']: -# if l['topics'][0] == '0xa0717e54e02bd9829db5e6e998aec0ae9de796b8d150a3cc46a92ab869697755': # event Decayed(uint256,uint256,uint256,uint256) -# period = int.from_bytes(bytes.fromhex(strip_0x(l['topics'][1])), 'big') -# self.assertEqual(period, 2) -# b = bytes.fromhex(strip_0x(l['data'])) -# remainder = int.from_bytes(b, 'big') -# self.assertEqual(remainder, int((1000000 - self.tax_level) * (10 ** 32))) -# -# o = c.balance_of(self.address, self.sink_address, sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# sink_balance = c.parse_balance_of(r) -# -# self.assertEqual(sink_balance, int(sink_increment * 0.98)) -# self.assertEqual(sink_balance, int(sink_increment * (1000000 - self.tax_level) / 1000000)) -# -# o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# balance = c.parse_balance_of(r) -# self.assertEqual(balance, supply - sink_increment) -# -# -# def test_redistribution_two_of_ten(self): -# mint_amount = 100000000 -# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) -# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) -# z = 0 -# for i in range(10): -# (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[i], mint_amount) -# self.rpc.do(o) -# z += mint_amount -# -# o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# initial_balance = c.parse_balance_of(r) -# -# spend_amount = 1000000 -# external_address = to_checksum_address('0x' + os.urandom(20).hex()) -# -# nonce_oracle = RPCNonceOracle(self.accounts[1], self.rpc) -# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) -# (tx_hash, o) = c.transfer(self.address, self.accounts[1], external_address, spend_amount) -# self.rpc.do(o) -# o = receipt(tx_hash) -# r = self.rpc.do(o) -# self.assertEqual(r['status'], 1) -# -# nonce_oracle = RPCNonceOracle(self.accounts[2], self.rpc) -# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) -# (tx_hash, o) = c.transfer(self.address, self.accounts[2], external_address, spend_amount) -# self.rpc.do(o) -# o = receipt(tx_hash) -# r = self.rpc.do(o) -# self.assertEqual(r['status'], 1) -# -# # No cheating! -# nonce_oracle = RPCNonceOracle(self.accounts[3], self.rpc) -# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) -# (tx_hash, o) = c.transfer(self.address, self.accounts[3], self.accounts[3], spend_amount) -# self.rpc.do(o) -# o = receipt(tx_hash) -# r = self.rpc.do(o) -# self.assertEqual(r['status'], 1) -# -# # No cheapskating! -# nonce_oracle = RPCNonceOracle(self.accounts[4], self.rpc) -# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) -# (tx_hash, o) = c.transfer(self.address, self.accounts[4], external_address, spend_amount-1) -# self.rpc.do(o) -# o = receipt(tx_hash) -# r = self.rpc.do(o) -# self.assertEqual(r['status'], 1) -# -# -# self.backend.time_travel(self.start_time + self.period_seconds + 1) -# -# (tx_hash, o) = c.apply_demurrage(self.address, self.accounts[4]) -# self.rpc.do(o) -# -# (tx_hash, o) = c.change_period(self.address, self.accounts[4]) -# self.rpc.do(o) -# -# o = c.balance_of(self.address, self.accounts[3], sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# bummer_balance = c.parse_balance_of(r) -# -# self.assertEqual(bummer_balance, mint_amount - (mint_amount * (self.tax_level / 1000000))) -# logg.debug('bal {} '.format(bummer_balance)) -# -# o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# bummer_balance = c.parse_balance_of(r) -# spender_balance = mint_amount - spend_amount -# spender_decayed_balance = int(spender_balance - (spender_balance * (self.tax_level / 1000000))) -# self.assertEqual(bummer_balance, spender_decayed_balance) -# logg.debug('bal {} '.format(bummer_balance)) -# -# (tx_hash, o) = c.apply_redistribution_on_account(self.address, self.accounts[4], self.accounts[1]) -# self.rpc.do(o) -# o = receipt(tx_hash) -# r = self.rpc.do(o) -# self.assertEqual(r['status'], 1) -# -# (tx_hash, o) = c.apply_redistribution_on_account(self.address, self.accounts[4], self.accounts[2]) -# self.rpc.do(o) -# o = receipt(tx_hash) -# r = self.rpc.do(o) -# self.assertEqual(r['status'], 1) -# -# o = c.redistributions(self.address, 0, sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# redistribution_data = c.parse_redistributions(r) -# logg.debug('redist data {}'.format(redistribution_data)) -# -# o = c.account_period(self.address, self.accounts[1], sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# account_period_data = c.parse_account_period(r) -# logg.debug('account period {}'.format(account_period_data)) -# -# o = c.actual_period(self.address, sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# actual_period = c.parse_actual_period(r) -# logg.debug('period {}'.format(actual_period)) -# -# redistribution = int((z / 2) * (self.tax_level / 1000000)) -# spender_new_base_balance = ((mint_amount - spend_amount) + redistribution) -# spender_new_decayed_balance = int(spender_new_base_balance - (spender_new_base_balance * (self.tax_level / 1000000))) -# -# o = c.balance_of(self.address, self.accounts[1], sender_address=self.accounts[0]) -# r = self.rpc.do(o) -# spender_actual_balance = c.parse_balance_of(r) -# logg.debug('rrr {} {}'.format(redistribution, spender_new_decayed_balance)) -# -# self.assertEqual(spender_actual_balance, spender_new_decayed_balance) -# - -if __name__ == '__main__': - unittest.main() diff --git a/solidity/DemurrageTokenMultiCap.sol b/solidity/DemurrageTokenMultiCap.sol @@ -1,633 +0,0 @@ -pragma solidity > 0.6.11; - -// SPDX-License-Identifier: GPL-3.0-or-later - -contract DemurrageTokenMultiCap { - - // Redistribution bit field, with associated shifts and masks - // (Uses sub-byte boundaries) - bytes32[] public redistributions; // uint1(isFractional) | uint95(unused) | uint20(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period) - uint8 constant shiftRedistributionPeriod = 0; - uint256 constant maskRedistributionPeriod = 0x00000000000000000000000000000000000000000000000000000000ffffffff; // (1 << 32) - 1 - uint8 constant shiftRedistributionValue = 32; - uint256 constant maskRedistributionValue = 0x00000000000000000000000000000000000000ffffffffffffffffff00000000; // ((1 << 72) - 1) << 32 - uint8 constant shiftRedistributionParticipants = 104; - uint256 constant maskRedistributionParticipants = 0x00000000000000000000000000000fffffffff00000000000000000000000000; // ((1 << 36) - 1) << 104 - uint8 constant shiftRedistributionDemurrage = 140; - uint256 constant maskRedistributionDemurrage = 0x000000000000000000000000fffff00000000000000000000000000000000000; // ((1 << 20) - 1) << 140 - uint8 constant shiftRedistributionIsFractional = 255; - uint256 constant maskRedistributionIsFractional = 0x8000000000000000000000000000000000000000000000000000000000000000; // 1 << 255 - - // Account bit field, with associated shifts and masks - // Mirrors structure of redistributions for consistency - mapping (address => bytes32) account; // uint152(unused) | uint32(period) | uint72(value) - uint8 constant shiftAccountValue = 0; - uint256 constant maskAccountValue = 0x0000000000000000000000000000000000000000000000ffffffffffffffffff; // (1 << 72) - 1 - uint8 constant shiftAccountPeriod = 72; - uint256 constant maskAccountPeriod = 0x00000000000000000000000000000000000000ffffffff000000000000000000; // ((1 << 32) - 1) << 72 - - // Cached demurrage amount, ppm with 38 digit resolution - uint128 public demurrageAmount; - - // Cached demurrage period; the period for which demurrageAmount was calculated - //uint128 public demurragePeriod; - // Cached demurrage timestamp; the timestamp for which demurrageAmount was last calculated - uint256 public demurrageTimestamp; - - // Implements EIP172 - address public owner; - - address newOwner; - - // Implements ERC20 - string public name; - - // Implements ERC20 - string public symbol; - - // Implements ERC20 - uint256 public decimals; - - // Implements ERC20 - uint256 public totalSupply; - - // Maximum amount of tokens that can be minted - uint256 public supplyCap; - - // Minimum amount of (demurraged) tokens an account must spend to participate in redistribution for a particular period - uint256 public minimumParticipantSpend; - - // 128 bit resolution of the demurrage divisor - // (this constant x 1000000 is contained within 128 bits) - uint256 constant ppmDivider = 100000000000000000000000000000000; - - // demurrage decimal width; 38 places - uint256 public immutable resolutionFactor = ppmDivider * 1000000; - - // Timestamp of start of periods (time which contract constructor was called) - uint256 public immutable periodStart; - - // Duration of a single redistribution period in seconds - uint256 public immutable periodDuration; - - // Demurrage in ppm per minute - uint256 public immutable taxLevel; - - // Addresses allowed to mint new tokens - mapping (address => bool) minter; - - // Storage for ERC20 approve/transferFrom methods - mapping (address => mapping (address => uint256 ) ) allowance; // holder -> spender -> amount (amount is subject to demurrage) - - // Address to send unallocated redistribution tokens - address sinkAddress; - - // Implements ERC20 - event Transfer(address indexed _from, address indexed _to, uint256 _value); - - // Implements ERC20 - event Approval(address indexed _owner, address indexed _spender, uint256 _value); - - // New tokens minted - event Mint(address indexed _minter, address indexed _beneficiary, uint256 _value); - - // New demurrage cache milestone calculated - event Decayed(uint256 indexed _period, uint256 indexed _periodCount, uint256 indexed _oldAmount, uint256 _newAmount); - - // When a new period threshold has been crossed - event Period(uint256 _period); - - // Redistribution applied on a single eligible account - event Redistribution(address indexed _account, uint256 indexed _period, uint256 _value); - - // Temporary event used in development, will be removed on prod - event Debug(bytes32 _foo); - - // EIP173 - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // EIP173 - - constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _taxLevelMinute, uint256 _periodMinutes, address _defaultSinkAddress, uint256 _supplyCap) public { - // ACL setup - owner = msg.sender; - minter[owner] = true; - - // ERC20 setup - name = _name; - symbol = _symbol; - decimals = _decimals; - - // Demurrage setup - demurrageTimestamp = block.timestamp; - periodStart = demurrageTimestamp; - periodDuration = _periodMinutes * 60; - demurrageAmount = uint128(ppmDivider * 1000000); // Represents 38 decimal places - //demurragePeriod = 1; - taxLevel = _taxLevelMinute; // Represents 38 decimal places - bytes32 initialRedistribution = toRedistribution(0, 1000000, 0, 1); - redistributions.push(initialRedistribution); - - // Misc settings - supplyCap = _supplyCap; - sinkAddress = _defaultSinkAddress; - minimumParticipantSpend = 10 ** uint256(_decimals); - } - - // Given address will be allowed to call the mintTo() function - function addMinter(address _minter) public returns (bool) { - require(msg.sender == owner); - minter[_minter] = true; - return true; - } - - // Given address will no longer be allowed to call the mintTo() function - function removeMinter(address _minter) public returns (bool) { - require(msg.sender == owner || _minter == msg.sender); - minter[_minter] = false; - return true; - } - - /// Implements ERC20 - function balanceOf(address _account) public view returns (uint256) { - uint256 baseBalance; - uint256 currentDemurragedAmount; - uint256 periodCount; - - baseBalance = baseBalanceOf(_account); - - //periodCount = actualPeriod() - demurragePeriod; - periodCount = getMinutesDelta(demurrageTimestamp); - - currentDemurragedAmount = uint128(decayBy(demurrageAmount, periodCount)); - - return (baseBalance * currentDemurragedAmount) / (ppmDivider * 1000000); - } - - /// Balance unmodified by demurrage - function baseBalanceOf(address _account) public view returns (uint256) { - return uint256(account[_account]) & maskAccountValue; - } - - /// Increases base balance for a single account - function increaseBaseBalance(address _account, uint256 _delta) private returns (bool) { - uint256 oldBalance; - uint256 newBalance; - uint256 workAccount; - - workAccount = uint256(account[_account]); - - if (_delta == 0) { - return false; - } - - oldBalance = baseBalanceOf(_account); - newBalance = oldBalance + _delta; - require(uint160(newBalance) > uint160(oldBalance), 'ERR_WOULDWRAP'); // revert if increase would result in a wrapped value - workAccount &= (~maskAccountValue); - workAccount |= (newBalance & maskAccountValue); - account[_account] = bytes32(workAccount); - return true; - } - - /// Decreases base balance for a single account - function decreaseBaseBalance(address _account, uint256 _delta) private returns (bool) { - uint256 oldBalance; - uint256 newBalance; - uint256 workAccount; - - workAccount = uint256(account[_account]); - - if (_delta == 0) { - return false; - } - - oldBalance = baseBalanceOf(_account); - require(oldBalance >= _delta, 'ERR_OVERSPEND'); // overspend guard - newBalance = oldBalance - _delta; - workAccount &= (~maskAccountValue); - workAccount |= (newBalance & maskAccountValue); - account[_account] = bytes32(workAccount); - return true; - } - - // Creates new tokens out of thin air, and allocates them to the given address - // Triggers tax - function mintTo(address _beneficiary, uint256 _amount) external returns (bool) { - uint256 baseAmount; - - require(minter[msg.sender]); - require(_amount + totalSupply <= supplyCap); - - changePeriod(); - baseAmount = toBaseAmount(_amount); - totalSupply += _amount; - increaseBaseBalance(_beneficiary, baseAmount); - emit Mint(msg.sender, _beneficiary, _amount); - saveRedistributionSupply(); - return true; - } - - // Deserializes the redistribution word - // uint1(isFractional) | uint95(unused) | uint20(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period) - function toRedistribution(uint256 _participants, uint256 _demurrageModifierPpm, uint256 _value, uint256 _period) private pure returns(bytes32) { - bytes32 redistribution; - - redistribution |= bytes32((_demurrageModifierPpm << shiftRedistributionDemurrage) & maskRedistributionDemurrage); - redistribution |= bytes32((_participants << shiftRedistributionParticipants) & maskRedistributionParticipants); - redistribution |= bytes32((_value << shiftRedistributionValue) & maskRedistributionValue); - redistribution |= bytes32(_period & maskRedistributionPeriod); - return redistribution; - } - - // Serializes the demurrage period part of the redistribution word - function toRedistributionPeriod(bytes32 redistribution) public pure returns (uint256) { - return uint256(redistribution) & maskRedistributionPeriod; - } - - // Serializes the supply part of the redistribution word - function toRedistributionSupply(bytes32 redistribution) public pure returns (uint256) { - return (uint256(redistribution) & maskRedistributionValue) >> shiftRedistributionValue; - } - - // Serializes the number of participants part of the redistribution word - function toRedistributionParticipants(bytes32 redistribution) public pure returns (uint256) { - return (uint256(redistribution) & maskRedistributionParticipants) >> shiftRedistributionParticipants; - } - - // Serializes the demurrage modifier part of the redistribution word - function toRedistributionDemurrageModifier(bytes32 redistribution) public pure returns (uint256) { - return (uint256(redistribution) & maskRedistributionDemurrage) >> shiftRedistributionDemurrage; - } - - // Client accessor to the redistributions array length - function redistributionCount() public view returns (uint256) { - return redistributions.length; - } - - // Add number of participants for the current redistribution period by one - function incrementRedistributionParticipants() private returns (bool) { - bytes32 currentRedistribution; - uint256 tmpRedistribution; - uint256 participants; - - currentRedistribution = redistributions[redistributions.length-1]; - participants = toRedistributionParticipants(currentRedistribution) + 1; - tmpRedistribution = uint256(currentRedistribution); - tmpRedistribution &= (~maskRedistributionParticipants); - tmpRedistribution |= ((participants << shiftRedistributionParticipants) & maskRedistributionParticipants); - - redistributions[redistributions.length-1] = bytes32(tmpRedistribution); - - return true; - } - - // Save the current total supply amount to the current redistribution period - function saveRedistributionSupply() private returns (bool) { - uint256 currentRedistribution; - - currentRedistribution = uint256(redistributions[redistributions.length-1]); - currentRedistribution &= (~maskRedistributionValue); - currentRedistribution |= (totalSupply << shiftRedistributionValue); - - redistributions[redistributions.length-1] = bytes32(currentRedistribution); - return true; - } - - // Get the demurrage period of the current block number - function actualPeriod() public view returns (uint128) { - return uint128((block.timestamp - periodStart) / periodDuration + 1); - } - - // Add an entered demurrage period to the redistribution array - function checkPeriod() private view returns (bytes32) { - bytes32 lastRedistribution; - uint256 currentPeriod; - - lastRedistribution = redistributions[redistributions.length-1]; - currentPeriod = this.actualPeriod(); - if (currentPeriod <= toRedistributionPeriod(lastRedistribution)) { - return bytes32(0x00); - } - return lastRedistribution; - } - - // Deserialize the pemurrage period for the given account is participating in - function accountPeriod(address _account) public view returns (uint256) { - return (uint256(account[_account]) & maskAccountPeriod) >> shiftAccountPeriod; - } - - // Save the given demurrage period as the currently participation period for the given address - function registerAccountPeriod(address _account, uint256 _period) private returns (bool) { - account[_account] &= bytes32(~maskAccountPeriod); - account[_account] |= bytes32((_period << shiftAccountPeriod) & maskAccountPeriod); - incrementRedistributionParticipants(); - return true; - } - - // Determine whether the unit number is rounded down, rounded up or evenly divides. - // Returns 0 if evenly distributed, or the remainder as a positive number - // A _numParts value 0 will be interpreted as the value 1 - function remainder(uint256 _numParts, uint256 _sumWhole) public pure returns (uint256) { - uint256 unit; - uint256 truncatedResult; - - if (_numParts == 0) { // no division by zero please - revert('ERR_NUMPARTS_ZERO'); - } - require(_numParts < _sumWhole); // At least you are never LESS than the sum of your parts. Think about that. - - unit = _sumWhole / _numParts; - truncatedResult = unit * _numParts; - return _sumWhole - truncatedResult; - } - - // Called in the edge case where participant number is 0. It will override the participant count to 1. - // Returns the remainder sent to the sink address - function applyDefaultRedistribution(bytes32 _redistribution) private returns (uint256) { - uint256 redistributionSupply; - uint256 redistributionPeriod; - uint256 unit; - uint256 truncatedResult; - - redistributionSupply = toRedistributionSupply(_redistribution); - - unit = (redistributionSupply * taxLevel) / 1000000; - truncatedResult = (unit * 1000000) / taxLevel; - - if (truncatedResult < redistributionSupply) { - redistributionPeriod = toRedistributionPeriod(_redistribution); // since we reuse period here, can possibly be optimized by passing period instead - redistributions[redistributionPeriod-1] &= bytes32(~maskRedistributionParticipants); // just to be safe, zero out all participant count data, in this case there will be only one - redistributions[redistributionPeriod-1] |= bytes32(maskRedistributionIsFractional | (1 << shiftRedistributionParticipants)); - } - - increaseBaseBalance(sinkAddress, unit / ppmDivider); - return unit; - } - - // sets the remainder bit for the given period and books the remainder to the sink address balance - // returns false if no change was made - function applyRemainderOnPeriod(uint256 _remainder, uint256 _period) private returns (bool) { - uint256 periodSupply; - - if (_remainder == 0) { - return false; - } - - // TODO: is this needed? - redistributions[_period-1] |= bytes32(maskRedistributionIsFractional); - - periodSupply = toRedistributionSupply(redistributions[_period-1]); - increaseBaseBalance(sinkAddress, periodSupply - _remainder); - return true; - } - - - // Calculate the time delta in whole minutes passed between given timestamp and current timestamp - function getMinutesDelta(uint256 _lastTimestamp) public view returns (uint256) { - return (block.timestamp - _lastTimestamp) / 60; - } - - // Calculate and cache the demurrage value corresponding to the (period of the) time of the method call - function applyDemurrage() public returns (bool) { - //uint128 epochPeriodCount; - uint256 periodCount; - uint256 lastDemurrageAmount; - uint256 newDemurrageAmount; - - //epochPeriodCount = actualPeriod(); - //periodCount = epochPeriodCount - demurragePeriod; - periodCount = getMinutesDelta(demurrageTimestamp); - if (periodCount == 0) { - return false; - } - lastDemurrageAmount = demurrageAmount; - demurrageAmount = uint128(decayBy(lastDemurrageAmount, periodCount)); - //demurragePeriod = epochPeriodCount; - demurrageTimestamp = demurrageTimestamp + (periodCount * 60); - //emit Decayed(epochPeriodCount, periodCount, lastDemurrageAmount, demurrageAmount); - emit Decayed(demurrageTimestamp, periodCount, lastDemurrageAmount, demurrageAmount); - return true; - } - - // Return timestamp of start of period threshold - function getPeriodTimeDelta(uint256 _periodCount) public view returns (uint256) { - return periodStart + (_periodCount * periodDuration); - } - - // Amount of demurrage cycles inbetween the current timestamp and the given target time - function demurrageCycles(uint256 _target) public view returns (uint256) { - return (block.timestamp - _target) / 60; - } - - // Recalculate the demurrage modifier for the new period - // After this, all REPORTED balances will have been reduced by the corresponding ratio (but the effecive totalsupply stays the same) - function changePeriod() public returns (bool) { - bytes32 currentRedistribution; - bytes32 nextRedistribution; - uint256 currentPeriod; - uint256 currentParticipants; - uint256 currentRemainder; - uint256 currentDemurrageAmount; - uint256 nextRedistributionDemurrage; - uint256 demurrageCounts; - uint256 periodTimestamp; - uint256 nextPeriod; - - applyDemurrage(); - - currentRedistribution = checkPeriod(); - if (currentRedistribution == bytes32(0x00)) { - return false; - } - - currentPeriod = toRedistributionPeriod(currentRedistribution); - nextPeriod = currentPeriod + 1; - periodTimestamp = getPeriodTimeDelta(currentPeriod); - - //applyDemurrage(); - currentDemurrageAmount = demurrageAmount; - - demurrageCounts = demurrageCycles(periodTimestamp); - if (demurrageCounts > 0) { - nextRedistributionDemurrage = growBy(currentDemurrageAmount, demurrageCounts) / ppmDivider; - } else { - nextRedistributionDemurrage = currentDemurrageAmount / ppmDivider; - } - - nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply, nextPeriod); - redistributions.push(nextRedistribution); - - currentParticipants = toRedistributionParticipants(currentRedistribution); - if (currentParticipants == 0) { - currentRemainder = applyDefaultRedistribution(currentRedistribution); - } else { - currentRemainder = remainder(currentParticipants, totalSupply); // we can use totalSupply directly because it will always be the same as the recorded supply on the current redistribution - applyRemainderOnPeriod(currentRemainder, currentPeriod); - } - emit Period(nextPeriod); - return true; - } - - // Reverse a value reduced by demurrage by the given period to its original value - function growBy(uint256 _value, uint256 _period) public view returns (uint256) { - uint256 valueFactor; - uint256 truncatedTaxLevel; - - valueFactor = 1000000; - truncatedTaxLevel = taxLevel / ppmDivider; - - for (uint256 i = 0; i < _period; i++) { - valueFactor = valueFactor + ((valueFactor * truncatedTaxLevel) / 1000000); - } - return (valueFactor * _value) / 1000000; - } - - // Calculate a value reduced by demurrage by the given period - // TODO: higher precision if possible - function decayBy(uint256 _value, uint256 _period) public view returns (uint256) { - uint256 valueFactor; - uint256 truncatedTaxLevel; - - valueFactor = 1000000; - truncatedTaxLevel = taxLevel / ppmDivider; - - for (uint256 i = 0; i < _period; i++) { - valueFactor = valueFactor - ((valueFactor * truncatedTaxLevel) / 1000000); - } - return (valueFactor * _value) / 1000000; - } - - // If the given account is participating in a period and that period has been crossed - // THEN increase the base value of the account with its share of the value reduction of the period - function applyRedistributionOnAccount(address _account) public returns (bool) { - bytes32 periodRedistribution; - uint256 supply; - uint256 participants; - uint256 baseValue; - uint256 value; - uint256 period; - uint256 demurrage; - - period = accountPeriod(_account); - if (period == 0 || period >= actualPeriod()) { - return false; - } - periodRedistribution = redistributions[period-1]; - participants = toRedistributionParticipants(periodRedistribution); - if (participants == 0) { - return false; - } - - supply = toRedistributionSupply(periodRedistribution); - demurrage = toRedistributionDemurrageModifier(periodRedistribution); - baseValue = ((supply / participants) * (taxLevel / 1000000)) / ppmDivider; - value = (baseValue * demurrage) / 1000000; - - // zero out period for the account - account[_account] &= bytes32(~maskAccountPeriod); - increaseBaseBalance(_account, value); - - emit Redistribution(_account, period, value); - return true; - } - - // Inflates the given amount according to the current demurrage modifier - function toBaseAmount(uint256 _value) public view returns (uint256) { - //return (_value * ppmDivider * 1000000) / toDemurrageAmount(demurrageModifier); - return (_value * ppmDivider * 1000000) / demurrageAmount; - } - - // Implements ERC20, triggers tax and/or redistribution - function approve(address _spender, uint256 _value) public returns (bool) { - uint256 baseValue; - - changePeriod(); - applyRedistributionOnAccount(msg.sender); - - baseValue = toBaseAmount(_value); - allowance[msg.sender][_spender] += baseValue; - emit Approval(msg.sender, _spender, _value); - return true; - } - - // Implements ERC20, triggers tax and/or redistribution - function transfer(address _to, uint256 _value) public returns (bool) { - uint256 baseValue; - bool result; - - changePeriod(); - applyRedistributionOnAccount(msg.sender); - - baseValue = toBaseAmount(_value); - result = transferBase(msg.sender, _to, baseValue); - emit Transfer(msg.sender, _to, _value); - return result; - } - - - // Implements ERC20, triggers tax and/or redistribution - function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { - uint256 baseValue; - bool result; - - changePeriod(); - applyRedistributionOnAccount(msg.sender); - - baseValue = toBaseAmount(_value); - require(allowance[_from][msg.sender] >= baseValue); - - result = transferBase(_from, _to, baseValue); - emit Transfer(_from, _to, _value); - return result; - } - - // ERC20 transfer backend for transfer, transferFrom - function transferBase(address _from, address _to, uint256 _value) private returns (bool) { - uint256 period; - - decreaseBaseBalance(_from, _value); - increaseBaseBalance(_to, _value); - - period = actualPeriod(); - if (_value >= minimumParticipantSpend && accountPeriod(_from) != period && _from != _to) { - registerAccountPeriod(_from, period); - } - return true; - } - - // Implements EIP173 - function transferOwnership(address _newOwner) public returns (bool) { - require(msg.sender == owner); - newOwner = _newOwner; - } - - // Implements OwnedAccepter - function acceptOwnership() public returns (bool) { - address oldOwner; - - require(msg.sender == newOwner); - oldOwner = owner; - owner = newOwner; - newOwner = address(0); - emit OwnershipTransferred(oldOwner, owner); - } - - // Implements EIP165 - function supportsInterface(bytes4 _sum) public pure returns (bool) { - if (_sum == 0xc6bb4b70) { // ERC20 - return true; - } - if (_sum == 0x449a52f8) { // Minter - return true; - } - if (_sum == 0x01ffc9a7) { // EIP165 - return true; - } - if (_sum == 0x9493f8b2) { // EIP173 - return true; - } - if (_sum == 0x37a47be4) { // OwnedAccepter - return true; - } - return false; - } -} diff --git a/solidity/DemurrageTokenMultiNocap.sol b/solidity/DemurrageTokenMultiNocap.sol @@ -1,614 +0,0 @@ -pragma solidity > 0.6.11; - -// SPDX-License-Identifier: GPL-3.0-or-later - -contract DemurrageTokenMultiNocap { - - // Redistribution bit field, with associated shifts and masks - // (Uses sub-byte boundaries) - bytes32[] public redistributions; // uint1(isFractional) | uint95(unused) | uint20(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period) - uint8 constant shiftRedistributionPeriod = 0; - uint256 constant maskRedistributionPeriod = 0x00000000000000000000000000000000000000000000000000000000ffffffff; // (1 << 32) - 1 - uint8 constant shiftRedistributionValue = 32; - uint256 constant maskRedistributionValue = 0x00000000000000000000000000000000000000ffffffffffffffffff00000000; // ((1 << 72) - 1) << 32 - uint8 constant shiftRedistributionParticipants = 104; - uint256 constant maskRedistributionParticipants = 0x00000000000000000000000000000fffffffff00000000000000000000000000; // ((1 << 36) - 1) << 104 - uint8 constant shiftRedistributionDemurrage = 140; - uint256 constant maskRedistributionDemurrage = 0x000000000000000000000000fffff00000000000000000000000000000000000; // ((1 << 20) - 1) << 140 - uint8 constant shiftRedistributionIsFractional = 255; - uint256 constant maskRedistributionIsFractional = 0x8000000000000000000000000000000000000000000000000000000000000000; // 1 << 255 - - // Account bit field, with associated shifts and masks - // Mirrors structure of redistributions for consistency - mapping (address => bytes32) account; // uint152(unused) | uint32(period) | uint72(value) - uint8 constant shiftAccountValue = 0; - uint256 constant maskAccountValue = 0x0000000000000000000000000000000000000000000000ffffffffffffffffff; // (1 << 72) - 1 - uint8 constant shiftAccountPeriod = 72; - uint256 constant maskAccountPeriod = 0x00000000000000000000000000000000000000ffffffff000000000000000000; // ((1 << 32) - 1) << 72 - - // Cached demurrage amount, ppm with 38 digit resolution - uint128 public demurrageAmount; - - // Cached demurrage period; the period for which demurrageAmount was calculated - uint128 public demurragePeriod; - - // Implements EIP172 - address public owner; - - address newOwner; - - // Implements ERC20 - string public name; - - // Implements ERC20 - string public symbol; - - // Implements ERC20 - uint256 public decimals; - - // Implements ERC20 - uint256 public totalSupply; - - // Minimum amount of (demurraged) tokens an account must spend to participate in redistribution for a particular period - uint256 public minimumParticipantSpend; - - // 128 bit resolution of the demurrage divisor - // (this constant x 1000000 is contained within 128 bits) - uint256 constant ppmDivider = 100000000000000000000000000000000; - - // demurrage decimal width; 38 places - uint256 public immutable resolutionFactor = ppmDivider * 1000000; - - // Timestamp of start of periods (time which contract constructor was called) - uint256 public immutable periodStart; - - // Duration of a single redistribution period in seconds - uint256 public immutable periodDuration; - - // Demurrage in ppm per minute - uint256 public immutable taxLevel; - - // Addresses allowed to mint new tokens - mapping (address => bool) minter; - - // Storage for ERC20 approve/transferFrom methods - mapping (address => mapping (address => uint256 ) ) allowance; // holder -> spender -> amount (amount is subject to demurrage) - - // Address to send unallocated redistribution tokens - address sinkAddress; - - // Implements ERC20 - event Transfer(address indexed _from, address indexed _to, uint256 _value); - - // Implements ERC20 - event Approval(address indexed _owner, address indexed _spender, uint256 _value); - - // New tokens minted - event Mint(address indexed _minter, address indexed _beneficiary, uint256 _value); - - // New demurrage cache milestone calculated - event Decayed(uint256 indexed _period, uint256 indexed _periodCount, uint256 indexed _oldAmount, uint256 _newAmount); - - // When a new period threshold has been crossed - event Period(uint256 _period); - - // Redistribution applied on a single eligible account - event Redistribution(address indexed _account, uint256 indexed _period, uint256 _value); - - // Temporary event used in development, will be removed on prod - event Debug(bytes32 _foo); - - // EIP173 - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // EIP173 - - constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _taxLevelMinute, uint256 _periodMinutes, address _defaultSinkAddress) public { - // ACL setup - owner = msg.sender; - minter[owner] = true; - - // ERC20 setup - name = _name; - symbol = _symbol; - decimals = _decimals; - - // Demurrage setup - periodStart = block.timestamp; - periodDuration = _periodMinutes * 60; - demurrageAmount = uint128(ppmDivider * 1000000); // Represents 38 decimal places - demurragePeriod = 1; - taxLevel = _taxLevelMinute; // Represents 38 decimal places - bytes32 initialRedistribution = toRedistribution(0, 1000000, 0, 1); - redistributions.push(initialRedistribution); - - // Misc settings - sinkAddress = _defaultSinkAddress; - minimumParticipantSpend = 10 ** uint256(_decimals); - } - - // Given address will be allowed to call the mintTo() function - function addMinter(address _minter) public returns (bool) { - require(msg.sender == owner); - minter[_minter] = true; - return true; - } - - // Given address will no longer be allowed to call the mintTo() function - function removeMinter(address _minter) public returns (bool) { - require(msg.sender == owner || _minter == msg.sender); - minter[_minter] = false; - return true; - } - - /// Implements ERC20 - function balanceOf(address _account) public view returns (uint256) { - uint256 baseBalance; - uint256 currentDemurragedAmount; - uint256 periodCount; - - baseBalance = baseBalanceOf(_account); - - periodCount = actualPeriod() - demurragePeriod; - - currentDemurragedAmount = uint128(decayBy(demurrageAmount, periodCount)); - - return (baseBalance * currentDemurragedAmount) / (ppmDivider * 1000000); - } - - /// Balance unmodified by demurrage - function baseBalanceOf(address _account) public view returns (uint256) { - return uint256(account[_account]) & maskAccountValue; - } - - /// Increases base balance for a single account - function increaseBaseBalance(address _account, uint256 _delta) private returns (bool) { - uint256 oldBalance; - uint256 newBalance; - uint256 workAccount; - - workAccount = uint256(account[_account]); - - if (_delta == 0) { - return false; - } - - oldBalance = baseBalanceOf(_account); - newBalance = oldBalance + _delta; - require(uint160(newBalance) > uint160(oldBalance), 'ERR_WOULDWRAP'); // revert if increase would result in a wrapped value - workAccount &= (~maskAccountValue); - workAccount |= (newBalance & maskAccountValue); - account[_account] = bytes32(workAccount); - return true; - } - - /// Decreases base balance for a single account - function decreaseBaseBalance(address _account, uint256 _delta) private returns (bool) { - uint256 oldBalance; - uint256 newBalance; - uint256 workAccount; - - workAccount = uint256(account[_account]); - - if (_delta == 0) { - return false; - } - - oldBalance = baseBalanceOf(_account); - require(oldBalance >= _delta, 'ERR_OVERSPEND'); // overspend guard - newBalance = oldBalance - _delta; - workAccount &= (~maskAccountValue); - workAccount |= (newBalance & maskAccountValue); - account[_account] = bytes32(workAccount); - return true; - } - - // Creates new tokens out of thin air, and allocates them to the given address - // Triggers tax - function mintTo(address _beneficiary, uint256 _amount) external returns (bool) { - uint256 baseAmount; - - require(minter[msg.sender]); - - changePeriod(); - baseAmount = _amount; - totalSupply += _amount; - increaseBaseBalance(_beneficiary, baseAmount); - emit Mint(msg.sender, _beneficiary, _amount); - saveRedistributionSupply(); - return true; - } - - // Deserializes the redistribution word - // uint1(isFractional) | uint95(unused) | uint20(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period) - function toRedistribution(uint256 _participants, uint256 _demurrageModifierPpm, uint256 _value, uint256 _period) private pure returns(bytes32) { - bytes32 redistribution; - - redistribution |= bytes32((_demurrageModifierPpm << shiftRedistributionDemurrage) & maskRedistributionDemurrage); - redistribution |= bytes32((_participants << shiftRedistributionParticipants) & maskRedistributionParticipants); - redistribution |= bytes32((_value << shiftRedistributionValue) & maskRedistributionValue); - redistribution |= bytes32(_period & maskRedistributionPeriod); - return redistribution; - } - - // Serializes the demurrage period part of the redistribution word - function toRedistributionPeriod(bytes32 redistribution) public pure returns (uint256) { - return uint256(redistribution) & maskRedistributionPeriod; - } - - // Serializes the supply part of the redistribution word - function toRedistributionSupply(bytes32 redistribution) public pure returns (uint256) { - return (uint256(redistribution) & maskRedistributionValue) >> shiftRedistributionValue; - } - - // Serializes the number of participants part of the redistribution word - function toRedistributionParticipants(bytes32 redistribution) public pure returns (uint256) { - return (uint256(redistribution) & maskRedistributionParticipants) >> shiftRedistributionParticipants; - } - - // Serializes the number of participants part of the redistribution word - function toRedistributionDemurrageModifier(bytes32 redistribution) public pure returns (uint256) { - return (uint256(redistribution) & maskRedistributionDemurrage) >> shiftRedistributionDemurrage; - } - - // Client accessor to the redistributions array length - function redistributionCount() public view returns (uint256) { - return redistributions.length; - } - - // Add number of participants for the current redistribution period by one - function incrementRedistributionParticipants() private returns (bool) { - bytes32 currentRedistribution; - uint256 tmpRedistribution; - uint256 participants; - - currentRedistribution = redistributions[redistributions.length-1]; - participants = toRedistributionParticipants(currentRedistribution) + 1; - tmpRedistribution = uint256(currentRedistribution); - tmpRedistribution &= (~maskRedistributionParticipants); - tmpRedistribution |= ((participants << shiftRedistributionParticipants) & maskRedistributionParticipants); - - redistributions[redistributions.length-1] = bytes32(tmpRedistribution); - - return true; - } - - // Save the current total supply amount to the current redistribution period - function saveRedistributionSupply() private returns (bool) { - uint256 currentRedistribution; - - currentRedistribution = uint256(redistributions[redistributions.length-1]); - currentRedistribution &= (~maskRedistributionValue); - currentRedistribution |= (totalSupply << shiftRedistributionValue); - - redistributions[redistributions.length-1] = bytes32(currentRedistribution); - return true; - } - - // Get the demurrage period of the current block number - function actualPeriod() public view returns (uint128) { - return uint128((block.timestamp - periodStart) / periodDuration + 1); - } - - // Add an entered demurrage period to the redistribution array - function checkPeriod() private view returns (bytes32) { - bytes32 lastRedistribution; - uint256 currentPeriod; - - lastRedistribution = redistributions[redistributions.length-1]; - currentPeriod = this.actualPeriod(); - if (currentPeriod <= toRedistributionPeriod(lastRedistribution)) { - return bytes32(0x00); - } - return lastRedistribution; - } - - // Deserialize the pemurrage period for the given account is participating in - function accountPeriod(address _account) public view returns (uint256) { - return (uint256(account[_account]) & maskAccountPeriod) >> shiftAccountPeriod; - } - - // Save the given demurrage period as the currently participation period for the given address - function registerAccountPeriod(address _account, uint256 _period) private returns (bool) { - account[_account] &= bytes32(~maskAccountPeriod); - account[_account] |= bytes32((_period << shiftAccountPeriod) & maskAccountPeriod); - incrementRedistributionParticipants(); - return true; - } - - // Determine whether the unit number is rounded down, rounded up or evenly divides. - // Returns 0 if evenly distributed, or the remainder as a positive number - // A _numParts value 0 will be interpreted as the value 1 - function remainder(uint256 _numParts, uint256 _sumWhole) public pure returns (uint256) { - uint256 unit; - uint256 truncatedResult; - - if (_numParts == 0) { // no division by zero please - revert('ERR_NUMPARTS_ZERO'); - } - require(_numParts < _sumWhole); // At least you are never LESS than the sum of your parts. Think about that. - - unit = _sumWhole / _numParts; - truncatedResult = unit * _numParts; - return _sumWhole - truncatedResult; - } - - // Called in the edge case where participant number is 0. It will override the participant count to 1. - // Returns the remainder sent to the sink address - function applyDefaultRedistribution(bytes32 _redistribution) private returns (uint256) { - uint256 redistributionSupply; - uint256 redistributionPeriod; - uint256 unit; - uint256 truncatedResult; - - redistributionSupply = toRedistributionSupply(_redistribution); - - unit = (redistributionSupply * taxLevel) / 1000000; - truncatedResult = (unit * 1000000) / taxLevel; - - if (truncatedResult < redistributionSupply) { - redistributionPeriod = toRedistributionPeriod(_redistribution); // since we reuse period here, can possibly be optimized by passing period instead - redistributions[redistributionPeriod-1] &= bytes32(~maskRedistributionParticipants); // just to be safe, zero out all participant count data, in this case there will be only one - redistributions[redistributionPeriod-1] |= bytes32(maskRedistributionIsFractional | (1 << shiftRedistributionParticipants)); - } - - increaseBaseBalance(sinkAddress, unit / ppmDivider); - return unit; - } - - // sets the remainder bit for the given period and books the remainder to the sink address balance - // returns false if no change was made - function applyRemainderOnPeriod(uint256 _remainder, uint256 _period) private returns (bool) { - uint256 periodSupply; - - if (_remainder == 0) { - return false; - } - - // TODO: is this needed? - redistributions[_period-1] |= bytes32(maskRedistributionIsFractional); - - periodSupply = toRedistributionSupply(redistributions[_period-1]); - increaseBaseBalance(sinkAddress, periodSupply - _remainder); - return true; - } - - - // Calculate and cache the demurrage value corresponding to the (period of the) time of the method call - function applyDemurrage() public returns (bool) { - uint128 epochPeriodCount; - uint128 periodCount; - uint256 lastDemurrageAmount; - uint256 newDemurrageAmount; - - epochPeriodCount = actualPeriod(); - periodCount = epochPeriodCount - demurragePeriod; - if (periodCount == 0) { - return false; - } - lastDemurrageAmount = demurrageAmount; - demurrageAmount = uint128(decayBy(lastDemurrageAmount, periodCount)); - demurragePeriod = epochPeriodCount; - emit Decayed(epochPeriodCount, periodCount, lastDemurrageAmount, demurrageAmount); - return true; - } - - // Return timestamp of start of period threshold - function getPeriodTimeDelta(uint256 _periodCount) public view returns (uint256) { - return periodStart + (_periodCount * periodDuration); - } - - // Amount of demurrage cycles inbetween the current timestamp and the given target time - function demurrageCycles(uint256 _target) public view returns (uint256) { - return (block.timestamp - _target) / 60; - } - - // Recalculate the demurrage modifier for the new period - // After this, all REPORTED balances will have been reduced by the corresponding ratio (but the effecive totalsupply stays the same) - function changePeriod() public returns (bool) { - bytes32 currentRedistribution; - bytes32 nextRedistribution; - uint256 currentPeriod; - uint256 currentParticipants; - uint256 currentRemainder; - uint256 currentDemurrageAmount; - uint256 nextRedistributionDemurrage; - uint256 demurrageCounts; - uint256 periodTimestamp; - uint256 nextPeriod; - - currentRedistribution = checkPeriod(); - if (currentRedistribution == bytes32(0x00)) { - return false; - } - - currentPeriod = toRedistributionPeriod(currentRedistribution); - nextPeriod = currentPeriod + 1; - periodTimestamp = getPeriodTimeDelta(currentPeriod); - - applyDemurrage(); - currentDemurrageAmount = demurrageAmount; - - demurrageCounts = demurrageCycles(periodTimestamp); - if (demurrageCounts > 0) { - nextRedistributionDemurrage = growBy(currentDemurrageAmount, demurrageCounts) / ppmDivider; - } else { - nextRedistributionDemurrage = currentDemurrageAmount / ppmDivider; - } - - nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply, nextPeriod); - redistributions.push(nextRedistribution); - - currentParticipants = toRedistributionParticipants(currentRedistribution); - if (currentParticipants == 0) { - currentRemainder = applyDefaultRedistribution(currentRedistribution); - } else { - currentRemainder = remainder(currentParticipants, totalSupply); // we can use totalSupply directly because it will always be the same as the recorded supply on the current redistribution - applyRemainderOnPeriod(currentRemainder, currentPeriod); - } - emit Period(nextPeriod); - return true; - } - - // Reverse a value reduced by demurrage by the given period to its original value - function growBy(uint256 _value, uint256 _period) public view returns (uint256) { - uint256 valueFactor; - uint256 truncatedTaxLevel; - - valueFactor = 1000000; - truncatedTaxLevel = taxLevel / ppmDivider; - - for (uint256 i = 0; i < _period; i++) { - valueFactor = valueFactor + ((valueFactor * truncatedTaxLevel) / 1000000); - } - return (valueFactor * _value) / 1000000; - } - - // Calculate a value reduced by demurrage by the given period - // TODO: higher precision if possible - function decayBy(uint256 _value, uint256 _period) public view returns (uint256) { - uint256 valueFactor; - uint256 truncatedTaxLevel; - - valueFactor = 1000000; - truncatedTaxLevel = taxLevel / ppmDivider; - - for (uint256 i = 0; i < _period; i++) { - valueFactor = valueFactor - ((valueFactor * truncatedTaxLevel) / 1000000); - } - return (valueFactor * _value) / 1000000; - } - - // If the given account is participating in a period and that period has been crossed - // THEN increase the base value of the account with its share of the value reduction of the period - function applyRedistributionOnAccount(address _account) public returns (bool) { - bytes32 periodRedistribution; - uint256 supply; - uint256 participants; - uint256 baseValue; - uint256 value; - uint256 period; - uint256 demurrage; - - period = accountPeriod(_account); - if (period == 0 || period >= actualPeriod()) { - return false; - } - periodRedistribution = redistributions[period-1]; - participants = toRedistributionParticipants(periodRedistribution); - if (participants == 0) { - return false; - } - - supply = toRedistributionSupply(periodRedistribution); - demurrage = toRedistributionDemurrageModifier(periodRedistribution); - baseValue = ((supply / participants) * (taxLevel / 1000000)) / ppmDivider; - value = (baseValue * demurrage) / 1000000; - - // zero out period for the account - account[_account] &= bytes32(~maskAccountPeriod); - increaseBaseBalance(_account, value); - - emit Redistribution(_account, period, value); - return true; - } - - // Inflates the given amount according to the current demurrage modifier - function toBaseAmount(uint256 _value) public view returns (uint256) { - //return (_value * ppmDivider * 1000000) / toDemurrageAmount(demurrageModifier); - return (_value * ppmDivider * 1000000) / demurrageAmount; - } - - // Implements ERC20, triggers tax and/or redistribution - function approve(address _spender, uint256 _value) public returns (bool) { - uint256 baseValue; - - changePeriod(); - applyRedistributionOnAccount(msg.sender); - - baseValue = toBaseAmount(_value); - allowance[msg.sender][_spender] += baseValue; - emit Approval(msg.sender, _spender, _value); - return true; - } - - // Implements ERC20, triggers tax and/or redistribution - function transfer(address _to, uint256 _value) public returns (bool) { - uint256 baseValue; - bool result; - - changePeriod(); - applyRedistributionOnAccount(msg.sender); - - baseValue = toBaseAmount(_value); - result = transferBase(msg.sender, _to, baseValue); - emit Transfer(msg.sender, _to, _value); - return result; - } - - - // Implements ERC20, triggers tax and/or redistribution - function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { - uint256 baseValue; - bool result; - - changePeriod(); - applyRedistributionOnAccount(msg.sender); - - baseValue = toBaseAmount(_value); - require(allowance[_from][msg.sender] >= baseValue); - - result = transferBase(_from, _to, baseValue); - emit Transfer(_from, _to, _value); - return result; - } - - // ERC20 transfer backend for transfer, transferFrom - function transferBase(address _from, address _to, uint256 _value) private returns (bool) { - uint256 period; - - decreaseBaseBalance(_from, _value); - increaseBaseBalance(_to, _value); - - period = actualPeriod(); - if (_value >= minimumParticipantSpend && accountPeriod(_from) != period && _from != _to) { - registerAccountPeriod(_from, period); - } - return true; - } - - // Implements EIP173 - function transferOwnership(address _newOwner) public returns (bool) { - require(msg.sender == owner); - newOwner = _newOwner; - } - - // Implements OwnedAccepter - function acceptOwnership() public returns (bool) { - address oldOwner; - - require(msg.sender == newOwner); - oldOwner = owner; - owner = newOwner; - newOwner = address(0); - emit OwnershipTransferred(oldOwner, owner); - } - - // Implements EIP165 - function supportsInterface(bytes4 _sum) public pure returns (bool) { - if (_sum == 0xc6bb4b70) { // ERC20 - return true; - } - if (_sum == 0x449a52f8) { // Minter - return true; - } - if (_sum == 0x01ffc9a7) { // EIP165 - return true; - } - if (_sum == 0x9493f8b2) { // EIP173 - return true; - } - if (_sum == 0x37a47be4) { // OwnedAccepter - return true; - } - return false; - } -} diff --git a/solidity/DemurrageTokenSingleCap.sol b/solidity/DemurrageTokenSingleCap.sol @@ -1,517 +0,0 @@ -pragma solidity > 0.6.11; - -// SPDX-License-Identifier: GPL-3.0-or-later - -contract DemurrageTokenSingleCap { - - // Redistribution bit field, with associated shifts and masks - // (Uses sub-byte boundaries) - bytes32[] public redistributions; // uint51(unused) | uint64(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period) - uint8 constant shiftRedistributionPeriod = 0; - uint256 constant maskRedistributionPeriod = 0x00000000000000000000000000000000000000000000000000000000ffffffff; // (1 << 32) - 1 - uint8 constant shiftRedistributionValue = 32; - uint256 constant maskRedistributionValue = 0x00000000000000000000000000000000000000ffffffffffffffffff00000000; // ((1 << 72) - 1) << 32 - uint8 constant shiftRedistributionDemurrage = 104; - uint256 constant maskRedistributionDemurrage = 0x0000000000ffffffffffffffffffffffffffff00000000000000000000000000; // ((1 << 20) - 1) << 140 - - // Account balances - mapping (address => uint256) account; - - // Cached demurrage amount, ppm with 38 digit resolution - uint128 public demurrageAmount; - - // Cached demurrage period; the period for which demurrageAmount was calculated - //uint128 public demurragePeriod; - // Cached demurrage timestamp; the timestamp for which demurrageAmount was last calculated - uint256 public demurrageTimestamp; - - // Implements EIP172 - address public owner; - - address newOwner; - - // Implements ERC20 - string public name; - - // Implements ERC20 - string public symbol; - - // Implements ERC20 - uint256 public decimals; - - // Implements ERC20 - uint256 public totalSupply; - - // Maximum amount of tokens that can be minted - uint256 public supplyCap; - - // Minimum amount of (demurraged) tokens an account must spend to participate in redistribution for a particular period - uint256 public minimumParticipantSpend; - - // 128 bit resolution of the demurrage divisor - // (this constant x 1000000 is contained within 128 bits) - uint256 constant nanoDivider = 100000000000000000000000000; // now nanodivider, 6 zeros less - - // remaining decimal positions of nanoDivider to reach 38, equals precision in growth and decay - uint256 constant growthResolutionFactor = 1000000000000; - - // demurrage decimal width; 38 places - uint256 public immutable resolutionFactor = nanoDivider * growthResolutionFactor; - - // Timestamp of start of periods (time which contract constructor was called) - uint256 public immutable periodStart; - - // Duration of a single redistribution period in seconds - uint256 public immutable periodDuration; - - // Demurrage in ppm per minute - uint256 public immutable taxLevel; - - // Addresses allowed to mint new tokens - mapping (address => bool) minter; - - // Storage for ERC20 approve/transferFrom methods - mapping (address => mapping (address => uint256 ) ) allowance; // holder -> spender -> amount (amount is subject to demurrage) - - // Address to send unallocated redistribution tokens - address sinkAddress; - - // Implements ERC20 - event Transfer(address indexed _from, address indexed _to, uint256 _value); - - // Implements ERC20 - event Approval(address indexed _owner, address indexed _spender, uint256 _value); - - // New tokens minted - event Mint(address indexed _minter, address indexed _beneficiary, uint256 _value); - - // New demurrage cache milestone calculated - event Decayed(uint256 indexed _period, uint256 indexed _periodCount, uint256 indexed _oldAmount, uint256 _newAmount); - - // When a new period threshold has been crossed - event Period(uint256 _period); - - // Redistribution applied on a single eligible account - event Redistribution(address indexed _account, uint256 indexed _period, uint256 _value); - - // Temporary event used in development, will be removed on prod - event Debug(bytes32 _foo); - - // EIP173 - event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // EIP173 - - constructor(string memory _name, string memory _symbol, uint8 _decimals, uint128 _taxLevelMinute, uint256 _periodMinutes, address _defaultSinkAddress, uint256 _supplyCap) public { - // ACL setup - owner = msg.sender; - minter[owner] = true; - - // ERC20 setup - name = _name; - symbol = _symbol; - decimals = _decimals; - - // Demurrage setup - demurrageTimestamp = block.timestamp; - periodStart = demurrageTimestamp; - periodDuration = _periodMinutes * 60; - //demurrageAmount = 100000000000000000000000000000000000000 - _taxLevelMinute; // Represents 38 decimal places, same as resolutionFactor - //demurrageAmount = 100000000000000000000000000000000000000; - demurrageAmount = 10000000000000000000000000000; - //demurragePeriod = 1; - taxLevel = _taxLevelMinute; // Represents 38 decimal places - bytes32 initialRedistribution = toRedistribution(0, demurrageAmount, 0, 1); - redistributions.push(initialRedistribution); - - // Misc settings - supplyCap = _supplyCap; - sinkAddress = _defaultSinkAddress; - minimumParticipantSpend = 10 ** uint256(_decimals); - } - - // Change sink address for redistribution - function setSinkAddress(address _sinkAddress) public { - require(msg.sender == owner); - sinkAddress = _sinkAddress; - } - - // Given address will be allowed to call the mintTo() function - function addMinter(address _minter) public returns (bool) { - require(msg.sender == owner); - minter[_minter] = true; - return true; - } - - // Given address will no longer be allowed to call the mintTo() function - function removeMinter(address _minter) public returns (bool) { - require(msg.sender == owner || _minter == msg.sender); - minter[_minter] = false; - return true; - } - - /// Implements ERC20 - function balanceOf(address _account) public view returns (uint256) { - uint256 baseBalance; - uint256 currentDemurragedAmount; - uint256 periodCount; - - baseBalance = baseBalanceOf(_account); - - //periodCount = actualPeriod() - demurragePeriod; - periodCount = getMinutesDelta(demurrageTimestamp); - - currentDemurragedAmount = uint128(decayBy(demurrageAmount * 10000000000, periodCount)); - - return (baseBalance * currentDemurragedAmount) / (nanoDivider * 1000000000000); - } - - /// Balance unmodified by demurrage - function baseBalanceOf(address _account) public view returns (uint256) { - return account[_account]; - } - - /// Increases base balance for a single account - function increaseBaseBalance(address _account, uint256 _delta) private returns (bool) { - uint256 oldBalance; - uint256 newBalance; - uint256 workAccount; - - workAccount = uint256(account[_account]); - - if (_delta == 0) { - return false; - } - - oldBalance = baseBalanceOf(_account); - account[_account] = oldBalance + _delta; - return true; - } - - /// Decreases base balance for a single account - function decreaseBaseBalance(address _account, uint256 _delta) private returns (bool) { - uint256 oldBalance; - uint256 newBalance; - uint256 workAccount; - - workAccount = uint256(account[_account]); - - if (_delta == 0) { - return false; - } - - oldBalance = baseBalanceOf(_account); - require(oldBalance >= _delta, 'ERR_OVERSPEND'); // overspend guard - account[_account] = oldBalance - _delta; - return true; - } - - // Creates new tokens out of thin air, and allocates them to the given address - // Triggers tax - function mintTo(address _beneficiary, uint256 _amount) external returns (bool) { - uint256 baseAmount; - - require(minter[msg.sender], 'ERR_ACCESS'); - require(_amount + totalSupply <= supplyCap, 'ERR_CAP'); - - changePeriod(); - baseAmount = toBaseAmount(_amount); - totalSupply += _amount; - increaseBaseBalance(_beneficiary, baseAmount); - emit Mint(msg.sender, _beneficiary, _amount); - saveRedistributionSupply(); - return true; - } - - // Deserializes the redistribution word - // uint95(unused) | uint20(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period) - function toRedistribution(uint256 _participants, uint256 _demurrageModifierPpm, uint256 _value, uint256 _period) public pure returns(bytes32) { - bytes32 redistribution; - - redistribution |= bytes32((_demurrageModifierPpm << shiftRedistributionDemurrage) & maskRedistributionDemurrage); - redistribution |= bytes32((_value << shiftRedistributionValue) & maskRedistributionValue); - redistribution |= bytes32(_period & maskRedistributionPeriod); - return redistribution; - } - - // Serializes the demurrage period part of the redistribution word - function toRedistributionPeriod(bytes32 redistribution) public pure returns (uint256) { - return uint256(redistribution) & maskRedistributionPeriod; - } - - // Serializes the supply part of the redistribution word - function toRedistributionSupply(bytes32 redistribution) public pure returns (uint256) { - return (uint256(redistribution) & maskRedistributionValue) >> shiftRedistributionValue; - } - - // Serializes the number of participants part of the redistribution word - function toRedistributionDemurrageModifier(bytes32 redistribution) public pure returns (uint256) { - return (uint256(redistribution) & maskRedistributionDemurrage) >> shiftRedistributionDemurrage; - } - - // Client accessor to the redistributions array length - function redistributionCount() public view returns (uint256) { - return redistributions.length; - } - - // Save the current total supply amount to the current redistribution period - function saveRedistributionSupply() private returns (bool) { - uint256 currentRedistribution; - uint256 grownSupply; - - //grownSupply = growBy(totalSupply, 1); - grownSupply = totalSupply; - currentRedistribution = uint256(redistributions[redistributions.length-1]); - currentRedistribution &= (~maskRedistributionValue); - currentRedistribution |= (grownSupply << shiftRedistributionValue); - - redistributions[redistributions.length-1] = bytes32(currentRedistribution); - return true; - } - - // Get the demurrage period of the current block number - function actualPeriod() public view returns (uint128) { - return uint128((block.timestamp - periodStart) / periodDuration + 1); - } - - // Add an entered demurrage period to the redistribution array - function checkPeriod() private view returns (bytes32) { - bytes32 lastRedistribution; - uint256 currentPeriod; - - lastRedistribution = redistributions[redistributions.length-1]; - currentPeriod = this.actualPeriod(); - if (currentPeriod <= toRedistributionPeriod(lastRedistribution)) { - return bytes32(0x00); - } - return lastRedistribution; - } - - function getDistribution(uint256 _supply, uint256 _demurrageAmount) public view returns (uint256) { - uint256 difference; - - difference = _supply * (resolutionFactor - (_demurrageAmount * 10000000000)); //(nanoDivider - ((resolutionFactor - _demurrageAmount) / nanoDivider)); - return difference / resolutionFactor; - } - - function getDistributionFromRedistribution(bytes32 _redistribution) public returns (uint256) { - uint256 redistributionSupply; - uint256 redistributionDemurrage; - - redistributionSupply = toRedistributionSupply(_redistribution); - redistributionDemurrage = toRedistributionDemurrageModifier(_redistribution); - return getDistribution(redistributionSupply, redistributionDemurrage); - } - - // Returns the amount sent to the sink address - function applyDefaultRedistribution(bytes32 _redistribution) private returns (uint256) { - uint256 unit; - - unit = getDistributionFromRedistribution(_redistribution); - increaseBaseBalance(sinkAddress, toBaseAmount(unit)); - return unit; - } - - // Calculate the time delta in whole minutes passed between given timestamp and current timestamp - function getMinutesDelta(uint256 _lastTimestamp) public view returns (uint256) { - return (block.timestamp - _lastTimestamp) / 60; - } - - // Calculate and cache the demurrage value corresponding to the (period of the) time of the method call - function applyDemurrage() public returns (bool) { - return applyDemurrageLimited(0); - } - - function applyDemurrageLimited(uint256 _rounds) public returns (bool) { - //uint128 epochPeriodCount; - uint256 periodCount; - uint256 lastDemurrageAmount; - - //epochPeriodCount = actualPeriod(); - //periodCount = epochPeriodCount - demurragePeriod; - - periodCount = getMinutesDelta(demurrageTimestamp); - if (periodCount == 0) { - return false; - } - lastDemurrageAmount = demurrageAmount; - // safety limit for exponential calculation to ensure that we can always - // execute this code no matter how much time passes. - if (_rounds > 0 && _rounds < periodCount) { - periodCount = _rounds; - } - - demurrageAmount = uint128(decayBy(lastDemurrageAmount, periodCount)); - //demurragePeriod = epochPeriodCount; - demurrageTimestamp = demurrageTimestamp + (periodCount * 60); - emit Decayed(demurrageTimestamp, periodCount, lastDemurrageAmount, demurrageAmount); - return true; - } - - // Return timestamp of start of period threshold - function getPeriodTimeDelta(uint256 _periodCount) public view returns (uint256) { - return periodStart + (_periodCount * periodDuration); - } - - // Amount of demurrage cycles inbetween the current timestamp and the given target time - function demurrageCycles(uint256 _target) public view returns (uint256) { - return (block.timestamp - _target) / 60; - } - - // Recalculate the demurrage modifier for the new period - function changePeriod() public returns (bool) { - bytes32 currentRedistribution; - bytes32 nextRedistribution; - uint256 currentPeriod; - uint256 currentDemurrageAmount; - uint256 nextRedistributionDemurrage; - uint256 demurrageCounts; - uint256 periodTimestamp; - uint256 nextPeriod; - - applyDemurrage(); - currentRedistribution = checkPeriod(); - if (currentRedistribution == bytes32(0x00)) { - return false; - } - - currentPeriod = toRedistributionPeriod(currentRedistribution); - nextPeriod = currentPeriod + 1; - periodTimestamp = getPeriodTimeDelta(currentPeriod); - - currentDemurrageAmount = demurrageAmount; - - demurrageCounts = demurrageCycles(periodTimestamp); - if (demurrageCounts > 0) { - nextRedistributionDemurrage = growBy(currentDemurrageAmount, demurrageCounts); - } else { - nextRedistributionDemurrage = currentDemurrageAmount; - } - - nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply, nextPeriod); - redistributions.push(nextRedistribution); - - applyDefaultRedistribution(nextRedistribution); - emit Period(nextPeriod); - return true; - } - - // Reverse a value reduced by demurrage by the given period to its original value - function growBy(uint256 _value, uint256 _period) public view returns (uint256) { - uint256 valueFactor; - uint256 truncatedTaxLevel; - - valueFactor = growthResolutionFactor; - truncatedTaxLevel = taxLevel / nanoDivider; - - for (uint256 i = 0; i < _period; i++) { - valueFactor = valueFactor + ((valueFactor * truncatedTaxLevel) / growthResolutionFactor); - } - return (valueFactor * _value) / growthResolutionFactor; - } - - // Calculate a value reduced by demurrage by the given period - function decayBy(uint256 _value, uint256 _period) public view returns (uint256) { - uint256 valueFactor; - uint256 truncatedTaxLevel; - - valueFactor = growthResolutionFactor; - truncatedTaxLevel = taxLevel / nanoDivider; - - for (uint256 i = 0; i < _period; i++) { - valueFactor = valueFactor - ((valueFactor * truncatedTaxLevel) / growthResolutionFactor); - } - return (valueFactor * _value) / growthResolutionFactor; - } - - // Inflates the given amount according to the current demurrage modifier - function toBaseAmount(uint256 _value) public view returns (uint256) { - return (_value * resolutionFactor) / (demurrageAmount * 10000000000); - } - - // Implements ERC20, triggers tax and/or redistribution - function approve(address _spender, uint256 _value) public returns (bool) { - uint256 baseValue; - - changePeriod(); - - baseValue = toBaseAmount(_value); - allowance[msg.sender][_spender] += baseValue; - emit Approval(msg.sender, _spender, _value); - return true; - } - - // Implements ERC20, triggers tax and/or redistribution - function transfer(address _to, uint256 _value) public returns (bool) { - uint256 baseValue; - bool result; - - changePeriod(); - - baseValue = toBaseAmount(_value); - result = transferBase(msg.sender, _to, baseValue); - emit Transfer(msg.sender, _to, _value); - return result; - } - - // Implements ERC20, triggers tax and/or redistribution - function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { - uint256 baseValue; - bool result; - - changePeriod(); - - baseValue = toBaseAmount(_value); - require(allowance[_from][msg.sender] >= baseValue); - - result = transferBase(_from, _to, baseValue); - emit Transfer(_from, _to, _value); - return result; - } - - // ERC20 transfer backend for transfer, transferFrom - function transferBase(address _from, address _to, uint256 _value) private returns (bool) { - uint256 period; - - decreaseBaseBalance(_from, _value); - increaseBaseBalance(_to, _value); - - //period = actualPeriod(); - return true; - } - - // Implements EIP173 - function transferOwnership(address _newOwner) public returns (bool) { - require(msg.sender == owner); - newOwner = _newOwner; - } - - // Implements OwnedAccepter - function acceptOwnership() public returns (bool) { - address oldOwner; - - require(msg.sender == newOwner); - oldOwner = owner; - owner = newOwner; - newOwner = address(0); - emit OwnershipTransferred(oldOwner, owner); - } - - // Implements EIP165 - function supportsInterface(bytes4 _sum) public pure returns (bool) { - if (_sum == 0xc6bb4b70) { // ERC20 - return true; - } - if (_sum == 0x449a52f8) { // Minter - return true; - } - if (_sum == 0x01ffc9a7) { // EIP165 - return true; - } - if (_sum == 0x9493f8b2) { // EIP173 - return true; - } - if (_sum == 0x37a47be4) { // OwnedAccepter - return true; - } - return false; - } -} diff --git a/solidity/DemurrageTokenSingleNocap.sol b/solidity/DemurrageTokenSingleNocap.sol @@ -189,12 +189,11 @@ contract DemurrageTokenSingleCap { function setMaxSupply(uint256 _cap) public { require(!isSealed(CAP_STATE)); require(msg.sender == owner); - require(_cap > supply); + require(_cap > totalSupply()); emit Cap(maxSupply, _cap); maxSupply = _cap; } - // Change sink address for redistribution function setSinkAddress(address _sinkAddress) public { require(!isSealed(SINK_STATE)); diff --git a/solidity/Makefile b/solidity/Makefile @@ -2,29 +2,12 @@ SOLC = /usr/bin/solc all: single_nocap -multi_nocap: - $(SOLC) DemurrageTokenMultiNocap.sol --abi --evm-version byzantium | awk 'NR>3' > DemurrageTokenMultiNocap.json - $(SOLC) DemurrageTokenMultiNocap.sol --bin --evm-version byzantium | awk 'NR>3' > DemurrageTokenMultiNocap.bin - truncate -s -1 DemurrageTokenMultiNocap.bin - -multi_cap: - $(SOLC) DemurrageTokenMultiCap.sol --abi --evm-version byzantium | awk 'NR>3' > DemurrageTokenMultiCap.json - $(SOLC) DemurrageTokenMultiCap.sol --bin --evm-version byzantium | awk 'NR>3' > DemurrageTokenMultiCap.bin - truncate -s -1 DemurrageTokenMultiCap.bin - -multi: multi_nocap multi_cap - single_nocap: $(SOLC) DemurrageTokenSingleNocap.sol --abi --evm-version byzantium | awk 'NR==4' > DemurrageTokenSingleNocap.json $(SOLC) DemurrageTokenSingleNocap.sol --bin --evm-version byzantium | awk 'NR==4' > DemurrageTokenSingleNocap.bin truncate -s -1 DemurrageTokenSingleNocap.bin -single_cap: - $(SOLC) DemurrageTokenSingleCap.sol --abi --evm-version byzantium | awk 'NR>3' > DemurrageTokenSingleCap.json - $(SOLC) DemurrageTokenSingleCap.sol --bin --evm-version byzantium | awk 'NR>3' > DemurrageTokenSingleCap.bin - truncate -s -1 DemurrageTokenSingleCap.bin - -single: single_nocap single_cap +single: single_nocap test: all python ../python/tests/test_basic.py