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:
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