erc20-demurrage-token

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

commit 093fcbccd53b45377aed53001999156468e7b7b4
parent 3c871e575887db0e4bfe6ac472faf97c8a4e1f38
Author: nolash <dev@holbrook.no>
Date:   Sat,  5 Jun 2021 07:50:59 +0200

Bump eth-erc20. add initial single and cap contract adaptations

Diffstat:
Apython/erc20_demurrage_token/data/DemurrageTokenMultiCap.bin | 0
Apython/erc20_demurrage_token/data/DemurrageTokenMultiCap.json | 0
Mpython/erc20_demurrage_token/data/DemurrageTokenMultiNocap.bin | 4++--
Apython/erc20_demurrage_token/data/DemurrageTokenSingleNocap.bin | 2++
Apython/erc20_demurrage_token/data/DemurrageTokenSingleNocap.json | 1+
Mpython/erc20_demurrage_token/token.py | 47++++++++++++++++++++++++++++++++---------------
Mpython/requirements.txt | 2+-
Mpython/tests/base.py | 33++++++++++++++++++++++++++-------
Mpython/tests/test_redistribution.py | 21++++++++-------------
Apython/tests/test_single.py | 61+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asolidity/DemurrageTokenMultiCap.sol | 616+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msolidity/DemurrageTokenMultiNocap.sol | 2+-
Asolidity/DemurrageTokenSingleNocap.sol | 609+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msolidity/Makefile | 20++++++++++++++++++--
14 files changed, 1377 insertions(+), 41 deletions(-)

diff --git a/python/erc20_demurrage_token/data/DemurrageTokenMultiCap.bin b/python/erc20_demurrage_token/data/DemurrageTokenMultiCap.bin diff --git a/python/erc20_demurrage_token/data/DemurrageTokenMultiCap.json b/python/erc20_demurrage_token/data/DemurrageTokenMultiCap.json diff --git a/python/erc20_demurrage_token/data/DemurrageTokenMultiNocap.bin b/python/erc20_demurrage_token/data/DemurrageTokenMultiNocap.bin @@ -1 +1 @@ -60e06040523480156200001157600080fd5b5060405162003d0238038062003d0283398181016040528101906200003791906200048e565b33600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600a6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555085600590805190602001906200010a92919062000327565b5084600690805190602001906200012392919062000327565b508360ff166007819055504260808181525050603c826200014591906200074d565b60a08181525050620f42406d04ee2d6d415b85acef81000000006200016b91906200074d565b600260006101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055506001600260106101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055508260c0818152505060006200020a6000620f4240600060016200029b640100000000026401000000009004565b9050600081908060018154018082558091505060019003906000526020600020016000909190919091505581600c60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508460ff16600a62000288919062000610565b6009819055505050505050505062000994565b60008073fffff00000000000000000000000000000000000608c60ff16869060020a021660010281179050710fffffffff00000000000000000000000000606860ff16879060020a0216600102811790506cffffffffffffffffff00000000602060ff16859060020a02166001028117905063ffffffff83166001028117905080915050949350505050565b82805462000335906200082f565b90600052602060002090601f016020900481019282620003595760008555620003a5565b82601f106200037457805160ff1916838001178555620003a5565b82800160010185558215620003a5579182015b82811115620003a457825182559160200191906001019062000387565b5b509050620003b49190620003b8565b5090565b5b80821115620003d3576000816000905550600101620003b9565b5090565b6000620003ee620003e8846200057f565b62000556565b9050828152602081018484840111156200040757600080fd5b62000414848285620007f9565b509392505050565b6000815190506200042d8162000946565b92915050565b600082601f8301126200044557600080fd5b815162000457848260208601620003d7565b91505092915050565b600081519050620004718162000960565b92915050565b60008151905062000488816200097a565b92915050565b60008060008060008060c08789031215620004a857600080fd5b600087015167ffffffffffffffff811115620004c357600080fd5b620004d189828a0162000433565b965050602087015167ffffffffffffffff811115620004ef57600080fd5b620004fd89828a0162000433565b95505060406200051089828a0162000477565b94505060606200052389828a0162000460565b93505060806200053689828a0162000460565b92505060a06200054989828a016200041c565b9150509295509295509295565b60006200056262000575565b905062000570828262000865565b919050565b6000604051905090565b600067ffffffffffffffff8211156200059d576200059c620008f9565b5b620005a88262000928565b9050602081019050919050565b6000808291508390505b60018511156200060757808604811115620005df57620005de6200089b565b5b6001851615620005ef5780820291505b8081029050620005ff8562000939565b9450620005bf565b94509492505050565b60006200061d82620007e2565b91506200062a83620007e2565b9250620006597fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff848462000661565b905092915050565b60008262000673576001905062000746565b8162000683576000905062000746565b81600181146200069c5760028114620006a757620006dd565b600191505062000746565b60ff841115620006bc57620006bb6200089b565b5b8360020a915084821115620006d657620006d56200089b565b5b5062000746565b5060208310610133831016604e8410600b8410161715620007175782820a9050838111156200071157620007106200089b565b5b62000746565b620007268484846001620005b5565b9250905081840481111562000740576200073f6200089b565b5b81810290505b9392505050565b60006200075a82620007e2565b91506200076783620007e2565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615620007a357620007a26200089b565b5b828202905092915050565b6000620007bb82620007c2565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b60005b8381101562000819578082015181840152602081019050620007fc565b8381111562000829576000848401525b50505050565b600060028204905060018216806200084857607f821691505b602082108114156200085f576200085e620008ca565b5b50919050565b620008708262000928565b810181811067ffffffffffffffff82111715620008925762000891620008f9565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b6000600282049050919050565b6200095181620007ae565b81146200095d57600080fd5b50565b6200096b81620007e2565b81146200097757600080fd5b50565b6200098581620007ec565b81146200099157600080fd5b50565b60805160a05160c0516132ff62000a0360003960008181610ca801528181610f030152818161109401528181611da401528181612426015261245e01526000818161137701528181611d520152611e3f0152600081816113a301528181611e600152611ea501526132ff6000f3fe608060405234801561001057600080fd5b506004361061025a576000357c0100000000000000000000000000000000000000000000000000000000900480636787a9be11610158578063983b2d56116100d5578063e3ab207a11610099578063e3ab207a146107c3578063e54d63f7146107f3578063e809529514610823578063eda4e6d614610841578063f2fde38b1461085f5761025a565b8063983b2d56146106f75780639da7d8ba14610727578063a2119e2f14610757578063a9059cbb14610775578063b470aade146107a55761025a565b8063731f237c1161011c578063731f237c1461066157806379ba50971461067f5780638da5cb5b1461069d5780638f1df6bc146106bb57806395d89b41146106d95761025a565b80636787a9be146105715780636a2d094e146105a15780636d5055a6146105d15780636ffa8a051461060157806370a08231146106315761025a565b80633092afd5116101e657806340fc89f5116101aa57806340fc89f514610493578063449a52f8146104c357806347a50517146104f35780634abfbba214610523578063573cc507146105535761025a565b80633092afd5146103b557806331333487146103e5578063313ce5671461041557806335b16eb81461043357806340452d91146104635761025a565b806318160ddd1161022d57806318160ddd146102fb5780631989c6a814610319578063216ea5ec14610337578063229ba1971461036757806323b872dd146103855761025a565b806301ffc9a71461025f57806306fdde031461028f578063095ea7b3146102ad57806309f28f3c146102dd575b600080fd5b61027960048036038101906102749190612ba7565b61088f565b6040516102869190612d75565b60405180910390f35b610297610a3a565b6040516102a49190612dab565b60405180910390f35b6102c760048036038101906102c29190612b42565b610ac8565b6040516102d49190612d75565b60405180910390f35b6102e5610bec565b6040516102f29190612e2d565b60405180910390f35b610303610c0e565b6040516103109190612e63565b60405180910390f35b610321610c14565b60405161032e9190612e63565b60405180910390f35b610351600480360381019061034c9190612c22565b610c20565b60405161035e9190612e63565b60405180910390f35b61036f610ca6565b60405161037c9190612e63565b60405180910390f35b61039f600480360381019061039a9190612af3565b610cca565b6040516103ac9190612d75565b60405180910390f35b6103cf60048036038101906103ca9190612aca565b610df4565b6040516103dc9190612d75565b60405180910390f35b6103ff60048036038101906103fa9190612c22565b610ee7565b60405161040c9190612e63565b60405180910390f35b61041d610f98565b60405161042a9190612e63565b60405180910390f35b61044d60048036038101906104489190612aca565b610f9e565b60405161045a9190612d75565b60405180910390f35b61047d60048036038101906104789190612bf9565b6111c4565b60405161048a9190612d90565b60405180910390f35b6104ad60048036038101906104a89190612b7e565b6111e8565b6040516104ba9190612e63565b60405180910390f35b6104dd60048036038101906104d89190612b42565b611219565b6040516104ea9190612d75565b60405180910390f35b61050d60048036038101906105089190612aca565b61131b565b60405161051a9190612e63565b60405180910390f35b61053d60048036038101906105389190612bf9565b611373565b60405161054a9190612e63565b60405180910390f35b61055b6113d3565b6040516105689190612e63565b60405180910390f35b61058b60048036038101906105869190612bf9565b6113d9565b6040516105989190612e63565b60405180910390f35b6105bb60048036038101906105b69190612bf9565b611445565b6040516105c89190612e63565b60405180910390f35b6105eb60048036038101906105e69190612aca565b611466565b6040516105f89190612e63565b60405180910390f35b61061b60048036038101906106169190612b7e565b6114d1565b6040516106289190612e63565b60405180910390f35b61064b60048036038101906106469190612aca565b611504565b6040516106589190612e63565b60405180910390f35b6106696115e5565b6040516106769190612d75565b60405180910390f35b610687611793565b6040516106949190612d75565b60405180910390f35b6106a561193a565b6040516106b29190612d5a565b60405180910390f35b6106c3611960565b6040516106d09190612d75565b60405180910390f35b6106e1611b17565b6040516106ee9190612dab565b60405180910390f35b610711600480360381019061070c9190612aca565b611ba5565b60405161071e9190612d75565b60405180910390f35b610741600480360381019061073c9190612b7e565b611c62565b60405161074e9190612e63565b60405180910390f35b61075f611c8e565b60405161076c9190612e2d565b60405180910390f35b61078f600480360381019061078a9190612b42565b611cb0565b60405161079c9190612d75565b60405180910390f35b6107ad611d50565b6040516107ba9190612e63565b60405180910390f35b6107dd60048036038101906107d89190612b7e565b611d74565b6040516107ea9190612e63565b60405180910390f35b61080d60048036038101906108089190612c22565b611d88565b60405161081a9190612e63565b60405180910390f35b61082b611e39565b6040516108389190612e2d565b60405180910390f35b610849611ea3565b6040516108569190612e63565b60405180910390f35b61087960048036038101906108749190612aca565b611ec7565b6040516108869190612d75565b60405180910390f35b600063c6bb4b707c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156108e45760019050610a35565b63449a52f87c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156109375760019050610a35565b6301ffc9a77c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916141561098a5760019050610a35565b639493f8b27c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156109dd5760019050610a35565b6337a47be47c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161415610a305760019050610a35565b600090505b919050565b60058054610a47906130c2565b80601f0160208091040260200160405190810160405280929190818152602001828054610a73906130c2565b8015610ac05780601f10610a9557610100808354040283529160200191610ac0565b820191906000526020600020905b815481529060010190602001808311610aa357829003601f168201915b505050505081565b600080610ad3611960565b50610add33610f9e565b50610ae7836113d9565b905080600b60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610b759190612e9a565b925050819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92585604051610bd99190612e63565b60405180910390a3600191505092915050565b600260009054906101000a90046fffffffffffffffffffffffffffffffff1681565b60085481565b60008080549050905090565b600080600080851415610c68576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c5f90612dcd565b60405180910390fd5b838510610c7457600080fd5b8484610c809190612ef0565b91508482610c8e9190612f21565b90508084610c9c9190612faf565b9250505092915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000806000610cd7611960565b50610ce133610f9e565b50610ceb846113d9565b915081600b60008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610d7657600080fd5b610d81868684611f69565b90508473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef86604051610de09190612e63565b60405180910390a380925050509392505050565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610e7d57503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b610e8657600080fd5b6000600a60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060019050919050565b6000806000620f424091506d04ee2d6d415b85acef81000000007f0000000000000000000000000000000000000000000000000000000000000000610f2c9190612ef0565b905060005b84811015610f7357620f42408284610f499190612f21565b610f539190612ef0565b83610f5e9190612faf565b92508080610f6b906130f4565b915050610f31565b50620f42408583610f849190612f21565b610f8e9190612ef0565b9250505092915050565b60075481565b600080600080600080600080610fb389611466565b91506000821480610fdd5750610fc7611e39565b6fffffffffffffffffffffffffffffffff168210155b15610ff25760009750505050505050506111bf565b60006001836110019190612faf565b81548110611038577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200154965061104e876111e8565b945060008514156110695760009750505050505050506111bf565b61107287611c62565b955061107d876114d1565b90506d04ee2d6d415b85acef8100000000620f42407f00000000000000000000000000000000000000000000000000000000000000006110bd9190612ef0565b86886110c99190612ef0565b6110d39190612f21565b6110dd9190612ef0565b9350620f424081856110ef9190612f21565b6110f99190612ef0565b92506cffffffff00000000000000000019600102600160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282541692505081905550611163898461200d565b50818973ffffffffffffffffffffffffffffffffffffffff167f9a2a887706623ad3ff7fc85652deeceabe9fe1e00466c597972079ee91ea40d3856040516111ab9190612e63565b60405180910390a360019750505050505050505b919050565b600081815481106111d457600080fd5b906000526020600020016000915090505481565b6000606860ff16710fffffffff00000000000000000000000000836001900416908060020a82049150509050919050565b600080600a60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1661127257600080fd5b61127a611960565b5082905082600860008282546112909190612e9a565b925050819055506112a1848261200d565b508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fab8530f87dc9b59234c4623bf917212bb2536d647574c8e7e5da92c2ede0c9f8856040516112ff9190612e63565b60405180910390a361130f612169565b50600191505092915050565b600068ffffffffffffffffff600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205460019004169050919050565b60007f0000000000000000000000000000000000000000000000000000000000000000826113a19190612f21565b7f00000000000000000000000000000000000000000000000000000000000000006113cc9190612e9a565b9050919050565b60095481565b6000600260009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16620f42406d04ee2d6d415b85acef81000000008461142a9190612f21565b6114349190612f21565b61143e9190612ef0565b9050919050565b6000603c82426114559190612faf565b61145f9190612ef0565b9050919050565b6000604860ff166cffffffff000000000000000000600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546001900416908060020a82049150509050919050565b6000608c60ff1673fffff00000000000000000000000000000000000836001900416908060020a82049150509050919050565b6000806000806115138561131b565b9250600260109054906101000a90046fffffffffffffffffffffffffffffffff1661153c611e39565b6115469190612f7b565b6fffffffffffffffffffffffffffffffff169050611594600260009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff1682610ee7565b6fffffffffffffffffffffffffffffffff169150620f42406d04ee2d6d415b85acef81000000006115c59190612f21565b82846115d19190612f21565b6115db9190612ef0565b9350505050919050565b60008060008060006115f5611e39565b9350600260109054906101000a90046fffffffffffffffffffffffffffffffff16846116219190612f7b565b92506000836fffffffffffffffffffffffffffffffff16141561164b576000945050505050611790565b600260009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16915061169a82846fffffffffffffffffffffffffffffffff16610ee7565b600260006101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff16021790555083600260106101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff16021790555081836fffffffffffffffffffffffffffffffff16856fffffffffffffffffffffffffffffffff167fa0717e54e02bd9829db5e6e998aec0ae9de796b8d150a3cc46a92ab869697755600260009054906101000a90046fffffffffffffffffffffffffffffffff1660405161177f9190612e48565b60405180910390a460019450505050505b90565b600080600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146117f057600080fd5b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35090565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000806000806000806000806000806000611979612251565b995060006001028a141561199a5760009a5050505050505050505050611b14565b6119a38a611d74565b97506001886119b29190612e9a565b90506119bd88611373565b91506119c76115e5565b50600260009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff169450611a0482611445565b92506000831115611a39576d04ee2d6d415b85acef8100000000611a288685611d88565b611a329190612ef0565b9350611a56565b6d04ee2d6d415b85acef810000000085611a539190612ef0565b93505b611a6560008560085484612381565b98506000899080600181540180825580915050600190039060005260206000200160009091909190915055611a998a6111e8565b96506000871415611ab457611aad8a61240d565b9550611ace565b611ac087600854610c20565b9550611acc8689612600565b505b7f55d243082e019fce4009ccea5368b92e436c17586a1e793c7deda16df4e5d67581604051611afd9190612e63565b60405180910390a160019a50505050505050505050505b90565b60068054611b24906130c2565b80601f0160208091040260200160405190810160405280929190818152602001828054611b50906130c2565b8015611b9d5780601f10611b7257610100808354040283529160200191611b9d565b820191906000526020600020905b815481529060010190602001808311611b8057829003601f168201915b505050505081565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611c0157600080fd5b6001600a60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060019050919050565b6000602060ff166cffffffffffffffffff00000000836001900416908060020a82049150509050919050565b600260109054906101000a90046fffffffffffffffffffffffffffffffff1681565b6000806000611cbd611960565b50611cc733610f9e565b50611cd1846113d9565b9150611cde338684611f69565b90508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef86604051611d3d9190612e63565b60405180910390a3809250505092915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600063ffffffff8260019004169050919050565b6000806000620f424091506d04ee2d6d415b85acef81000000007f0000000000000000000000000000000000000000000000000000000000000000611dcd9190612ef0565b905060005b84811015611e1457620f42408284611dea9190612f21565b611df49190612ef0565b83611dff9190612e9a565b92508080611e0c906130f4565b915050611dd2565b50620f42408583611e259190612f21565b611e2f9190612ef0565b9250505092915050565b600060017f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000042611e8a9190612faf565b611e949190612ef0565b611e9e9190612e9a565b905090565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611f2357600080fd5b81600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550919050565b600080611f768584612736565b50611f81848461200d565b50611f8a611e39565b6fffffffffffffffffffffffffffffffff1690506009548310158015611fb8575080611fb586611466565b14155b8015611ff057508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614155b1561200157611fff8582612867565b505b60019150509392505050565b600080600080600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054600190049050600085141561206e5760009350505050612163565b6120778661131b565b925084836120859190612e9a565b91508273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16116120f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120ec90612e0d565b60405180910390fd5b68ffffffffffffffffff198116905068ffffffffffffffffff82168117905080600102600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600193505050505b92915050565b600080600060016000805490506121809190612faf565b815481106121b7577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90600052602060002001546001900490506cffffffffffffffffff000000001981169050602060ff166008549060020a028117905080600102600060016000805490506122049190612faf565b8154811061223b577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200181905550600191505090565b60008060008060016000805490506122699190612faf565b815481106122a0577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b906000526020600020015491503073ffffffffffffffffffffffffffffffffffffffff1663e80952956040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801561230f57600080fd5b505afa158015612323573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123479190612bd0565b6fffffffffffffffffffffffffffffffff16905061236482611d74565b81116123785760006001029250505061237e565b81925050505b90565b60008073fffff00000000000000000000000000000000000608c60ff16869060020a021660010281179050710fffffffff00000000000000000000000000606860ff16879060020a0216600102811790506cffffffffffffffffff00000000602060ff16859060020a02166001028117905063ffffffff83166001028117905080915050949350505050565b600080600080600061241e86611c62565b9350620f42407f0000000000000000000000000000000000000000000000000000000000000000856124509190612f21565b61245a9190612ef0565b91507f0000000000000000000000000000000000000000000000000000000000000000620f42408361248c9190612f21565b6124969190612ef0565b9050838110156125ae576124a986611d74565b9250710fffffffff000000000000000000000000001960010260006001856124d19190612faf565b81548110612508577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200160008282541692505081905550606860ff1660019060020a027f800000000000000000000000000000000000000000000000000000000000000017600102600060018561255f9190612faf565b81548110612596577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90600052602060002001600082825417925050819055505b6125f3600c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166d04ee2d6d415b85acef8100000000846125ee9190612ef0565b61200d565b5081945050505050919050565b6000806000841415612616576000915050612730565b7f800000000000000000000000000000000000000000000000000000000000000060010260006001856126499190612faf565b81548110612680577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90600052602060002001600082825417925050819055506126f060006001856126a99190612faf565b815481106126e0577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200154611c62565b9050612729600c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1685836127249190612faf565b61200d565b5060019150505b92915050565b600080600080600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205460019004905060008514156127975760009350505050612861565b6127a08661131b565b9250848310156127e5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016127dc90612ded565b60405180910390fd5b84836127f19190612faf565b915068ffffffffffffffffff198116905068ffffffffffffffffff82168117905080600102600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600193505050505b92915050565b60006cffffffff00000000000000000019600102600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825416925050819055506cffffffff000000000000000000604860ff16839060020a0216600102600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282541792505081905550612938612943565b506001905092915050565b6000806000806000600160008054905061295d9190612faf565b81548110612994577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200154925060016129ac846111e8565b6129b69190612e9a565b905082600190049150710fffffffff000000000000000000000000001982169150710fffffffff00000000000000000000000000606860ff16829060020a0216821791508160010260006001600080549050612a129190612faf565b81548110612a49577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90600052602060002001819055506001935050505090565b600081359050612a7081613256565b92915050565b600081359050612a858161326d565b92915050565b600081359050612a9a81613284565b92915050565b600081519050612aaf8161329b565b92915050565b600081359050612ac4816132b2565b92915050565b600060208284031215612adc57600080fd5b6000612aea84828501612a61565b91505092915050565b600080600060608486031215612b0857600080fd5b6000612b1686828701612a61565b9350506020612b2786828701612a61565b9250506040612b3886828701612ab5565b9150509250925092565b60008060408385031215612b5557600080fd5b6000612b6385828601612a61565b9250506020612b7485828601612ab5565b9150509250929050565b600060208284031215612b9057600080fd5b6000612b9e84828501612a76565b91505092915050565b600060208284031215612bb957600080fd5b6000612bc784828501612a8b565b91505092915050565b600060208284031215612be257600080fd5b6000612bf084828501612aa0565b91505092915050565b600060208284031215612c0b57600080fd5b6000612c1984828501612ab5565b91505092915050565b60008060408385031215612c3557600080fd5b6000612c4385828601612ab5565b9250506020612c5485828601612ab5565b9150509250929050565b612c6781612fe3565b82525050565b612c7681612ff5565b82525050565b612c8581613001565b82525050565b6000612c9682612e7e565b612ca08185612e89565b9350612cb081856020860161308f565b612cb9816131ca565b840191505092915050565b6000612cd1601183612e89565b9150612cdc826131db565b602082019050919050565b6000612cf4600d83612e89565b9150612cff82613204565b602082019050919050565b6000612d17600d83612e89565b9150612d228261322d565b602082019050919050565b612d3681613037565b82525050565b612d458161307d565b82525050565b612d5481613073565b82525050565b6000602082019050612d6f6000830184612c5e565b92915050565b6000602082019050612d8a6000830184612c6d565b92915050565b6000602082019050612da56000830184612c7c565b92915050565b60006020820190508181036000830152612dc58184612c8b565b905092915050565b60006020820190508181036000830152612de681612cc4565b9050919050565b60006020820190508181036000830152612e0681612ce7565b9050919050565b60006020820190508181036000830152612e2681612d0a565b9050919050565b6000602082019050612e426000830184612d2d565b92915050565b6000602082019050612e5d6000830184612d3c565b92915050565b6000602082019050612e786000830184612d4b565b92915050565b600081519050919050565b600082825260208201905092915050565b6000612ea582613073565b9150612eb083613073565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115612ee557612ee461313d565b5b828201905092915050565b6000612efb82613073565b9150612f0683613073565b925082612f1657612f1561316c565b5b828204905092915050565b6000612f2c82613073565b9150612f3783613073565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612f7057612f6f61313d565b5b828202905092915050565b6000612f8682613037565b9150612f9183613037565b925082821015612fa457612fa361313d565b5b828203905092915050565b6000612fba82613073565b9150612fc583613073565b925082821015612fd857612fd761313d565b5b828203905092915050565b6000612fee82613053565b9050919050565b60008115159050919050565b6000819050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b60006fffffffffffffffffffffffffffffffff82169050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600061308882613037565b9050919050565b60005b838110156130ad578082015181840152602081019050613092565b838111156130bc576000848401525b50505050565b600060028204905060018216806130da57607f821691505b602082108114156130ee576130ed61319b565b5b50919050565b60006130ff82613073565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156131325761313161313d565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000601f19601f8301169050919050565b7f4552525f4e554d50415254535f5a45524f000000000000000000000000000000600082015250565b7f4552525f4f5645525350454e4400000000000000000000000000000000000000600082015250565b7f4552525f574f554c445752415000000000000000000000000000000000000000600082015250565b61325f81612fe3565b811461326a57600080fd5b50565b61327681613001565b811461328157600080fd5b50565b61328d8161300b565b811461329857600080fd5b50565b6132a481613037565b81146132af57600080fd5b50565b6132bb81613073565b81146132c657600080fd5b5056fea2646970667358221220310d576c967c2887d9c1152479ae7c4cead1fc0b53e589dddb6047fa4997750064736f6c63430008040033 -\ No newline at end of file +60e06040523480156200001157600080fd5b5060405162003d0238038062003d0283398181016040528101906200003791906200048e565b33600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600a6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555085600590805190602001906200010a92919062000327565b5084600690805190602001906200012392919062000327565b508360ff166007819055504260808181525050603c826200014591906200074d565b60a08181525050620f42406d04ee2d6d415b85acef81000000006200016b91906200074d565b600260006101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055506001600260106101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055508260c0818152505060006200020a6000620f4240600060016200029b640100000000026401000000009004565b9050600081908060018154018082558091505060019003906000526020600020016000909190919091505581600c60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508460ff16600a62000288919062000610565b6009819055505050505050505062000994565b60008073fffff00000000000000000000000000000000000608c60ff16869060020a021660010281179050710fffffffff00000000000000000000000000606860ff16879060020a0216600102811790506cffffffffffffffffff00000000602060ff16859060020a02166001028117905063ffffffff83166001028117905080915050949350505050565b82805462000335906200082f565b90600052602060002090601f016020900481019282620003595760008555620003a5565b82601f106200037457805160ff1916838001178555620003a5565b82800160010185558215620003a5579182015b82811115620003a457825182559160200191906001019062000387565b5b509050620003b49190620003b8565b5090565b5b80821115620003d3576000816000905550600101620003b9565b5090565b6000620003ee620003e8846200057f565b62000556565b9050828152602081018484840111156200040757600080fd5b62000414848285620007f9565b509392505050565b6000815190506200042d8162000946565b92915050565b600082601f8301126200044557600080fd5b815162000457848260208601620003d7565b91505092915050565b600081519050620004718162000960565b92915050565b60008151905062000488816200097a565b92915050565b60008060008060008060c08789031215620004a857600080fd5b600087015167ffffffffffffffff811115620004c357600080fd5b620004d189828a0162000433565b965050602087015167ffffffffffffffff811115620004ef57600080fd5b620004fd89828a0162000433565b95505060406200051089828a0162000477565b94505060606200052389828a0162000460565b93505060806200053689828a0162000460565b92505060a06200054989828a016200041c565b9150509295509295509295565b60006200056262000575565b905062000570828262000865565b919050565b6000604051905090565b600067ffffffffffffffff8211156200059d576200059c620008f9565b5b620005a88262000928565b9050602081019050919050565b6000808291508390505b60018511156200060757808604811115620005df57620005de6200089b565b5b6001851615620005ef5780820291505b8081029050620005ff8562000939565b9450620005bf565b94509492505050565b60006200061d82620007e2565b91506200062a83620007e2565b9250620006597fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff848462000661565b905092915050565b60008262000673576001905062000746565b8162000683576000905062000746565b81600181146200069c5760028114620006a757620006dd565b600191505062000746565b60ff841115620006bc57620006bb6200089b565b5b8360020a915084821115620006d657620006d56200089b565b5b5062000746565b5060208310610133831016604e8410600b8410161715620007175782820a9050838111156200071157620007106200089b565b5b62000746565b620007268484846001620005b5565b9250905081840481111562000740576200073f6200089b565b5b81810290505b9392505050565b60006200075a82620007e2565b91506200076783620007e2565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615620007a357620007a26200089b565b5b828202905092915050565b6000620007bb82620007c2565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b60005b8381101562000819578082015181840152602081019050620007fc565b8381111562000829576000848401525b50505050565b600060028204905060018216806200084857607f821691505b602082108114156200085f576200085e620008ca565b5b50919050565b620008708262000928565b810181811067ffffffffffffffff82111715620008925762000891620008f9565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b6000600282049050919050565b6200095181620007ae565b81146200095d57600080fd5b50565b6200096b81620007e2565b81146200097757600080fd5b50565b6200098581620007ec565b81146200099157600080fd5b50565b60805160a05160c0516132ff62000a0360003960008181610ca801528181610f030152818161109401528181611da401528181612426015261245e01526000818161137701528181611d520152611e3f0152600081816113a301528181611e600152611ea501526132ff6000f3fe608060405234801561001057600080fd5b506004361061025a576000357c0100000000000000000000000000000000000000000000000000000000900480636787a9be11610158578063983b2d56116100d5578063e3ab207a11610099578063e3ab207a146107c3578063e54d63f7146107f3578063e809529514610823578063eda4e6d614610841578063f2fde38b1461085f5761025a565b8063983b2d56146106f75780639da7d8ba14610727578063a2119e2f14610757578063a9059cbb14610775578063b470aade146107a55761025a565b8063731f237c1161011c578063731f237c1461066157806379ba50971461067f5780638da5cb5b1461069d5780638f1df6bc146106bb57806395d89b41146106d95761025a565b80636787a9be146105715780636a2d094e146105a15780636d5055a6146105d15780636ffa8a051461060157806370a08231146106315761025a565b80633092afd5116101e657806340fc89f5116101aa57806340fc89f514610493578063449a52f8146104c357806347a50517146104f35780634abfbba214610523578063573cc507146105535761025a565b80633092afd5146103b557806331333487146103e5578063313ce5671461041557806335b16eb81461043357806340452d91146104635761025a565b806318160ddd1161022d57806318160ddd146102fb5780631989c6a814610319578063216ea5ec14610337578063229ba1971461036757806323b872dd146103855761025a565b806301ffc9a71461025f57806306fdde031461028f578063095ea7b3146102ad57806309f28f3c146102dd575b600080fd5b61027960048036038101906102749190612ba7565b61088f565b6040516102869190612d75565b60405180910390f35b610297610a3a565b6040516102a49190612dab565b60405180910390f35b6102c760048036038101906102c29190612b42565b610ac8565b6040516102d49190612d75565b60405180910390f35b6102e5610bec565b6040516102f29190612e2d565b60405180910390f35b610303610c0e565b6040516103109190612e63565b60405180910390f35b610321610c14565b60405161032e9190612e63565b60405180910390f35b610351600480360381019061034c9190612c22565b610c20565b60405161035e9190612e63565b60405180910390f35b61036f610ca6565b60405161037c9190612e63565b60405180910390f35b61039f600480360381019061039a9190612af3565b610cca565b6040516103ac9190612d75565b60405180910390f35b6103cf60048036038101906103ca9190612aca565b610df4565b6040516103dc9190612d75565b60405180910390f35b6103ff60048036038101906103fa9190612c22565b610ee7565b60405161040c9190612e63565b60405180910390f35b61041d610f98565b60405161042a9190612e63565b60405180910390f35b61044d60048036038101906104489190612aca565b610f9e565b60405161045a9190612d75565b60405180910390f35b61047d60048036038101906104789190612bf9565b6111c4565b60405161048a9190612d90565b60405180910390f35b6104ad60048036038101906104a89190612b7e565b6111e8565b6040516104ba9190612e63565b60405180910390f35b6104dd60048036038101906104d89190612b42565b611219565b6040516104ea9190612d75565b60405180910390f35b61050d60048036038101906105089190612aca565b61131b565b60405161051a9190612e63565b60405180910390f35b61053d60048036038101906105389190612bf9565b611373565b60405161054a9190612e63565b60405180910390f35b61055b6113d3565b6040516105689190612e63565b60405180910390f35b61058b60048036038101906105869190612bf9565b6113d9565b6040516105989190612e63565b60405180910390f35b6105bb60048036038101906105b69190612bf9565b611445565b6040516105c89190612e63565b60405180910390f35b6105eb60048036038101906105e69190612aca565b611466565b6040516105f89190612e63565b60405180910390f35b61061b60048036038101906106169190612b7e565b6114d1565b6040516106289190612e63565b60405180910390f35b61064b60048036038101906106469190612aca565b611504565b6040516106589190612e63565b60405180910390f35b6106696115e5565b6040516106769190612d75565b60405180910390f35b610687611793565b6040516106949190612d75565b60405180910390f35b6106a561193a565b6040516106b29190612d5a565b60405180910390f35b6106c3611960565b6040516106d09190612d75565b60405180910390f35b6106e1611b17565b6040516106ee9190612dab565b60405180910390f35b610711600480360381019061070c9190612aca565b611ba5565b60405161071e9190612d75565b60405180910390f35b610741600480360381019061073c9190612b7e565b611c62565b60405161074e9190612e63565b60405180910390f35b61075f611c8e565b60405161076c9190612e2d565b60405180910390f35b61078f600480360381019061078a9190612b42565b611cb0565b60405161079c9190612d75565b60405180910390f35b6107ad611d50565b6040516107ba9190612e63565b60405180910390f35b6107dd60048036038101906107d89190612b7e565b611d74565b6040516107ea9190612e63565b60405180910390f35b61080d60048036038101906108089190612c22565b611d88565b60405161081a9190612e63565b60405180910390f35b61082b611e39565b6040516108389190612e2d565b60405180910390f35b610849611ea3565b6040516108569190612e63565b60405180910390f35b61087960048036038101906108749190612aca565b611ec7565b6040516108869190612d75565b60405180910390f35b600063c6bb4b707c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156108e45760019050610a35565b63449a52f87c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156109375760019050610a35565b6301ffc9a77c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916141561098a5760019050610a35565b639493f8b27c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156109dd5760019050610a35565b6337a47be47c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161415610a305760019050610a35565b600090505b919050565b60058054610a47906130c2565b80601f0160208091040260200160405190810160405280929190818152602001828054610a73906130c2565b8015610ac05780601f10610a9557610100808354040283529160200191610ac0565b820191906000526020600020905b815481529060010190602001808311610aa357829003601f168201915b505050505081565b600080610ad3611960565b50610add33610f9e565b50610ae7836113d9565b905080600b60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610b759190612e9a565b925050819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92585604051610bd99190612e63565b60405180910390a3600191505092915050565b600260009054906101000a90046fffffffffffffffffffffffffffffffff1681565b60085481565b60008080549050905090565b600080600080851415610c68576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c5f90612dcd565b60405180910390fd5b838510610c7457600080fd5b8484610c809190612ef0565b91508482610c8e9190612f21565b90508084610c9c9190612faf565b9250505092915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000806000610cd7611960565b50610ce133610f9e565b50610ceb846113d9565b915081600b60008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610d7657600080fd5b610d81868684611f69565b90508473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef86604051610de09190612e63565b60405180910390a380925050509392505050565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610e7d57503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b610e8657600080fd5b6000600a60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060019050919050565b6000806000620f424091506d04ee2d6d415b85acef81000000007f0000000000000000000000000000000000000000000000000000000000000000610f2c9190612ef0565b905060005b84811015610f7357620f42408284610f499190612f21565b610f539190612ef0565b83610f5e9190612faf565b92508080610f6b906130f4565b915050610f31565b50620f42408583610f849190612f21565b610f8e9190612ef0565b9250505092915050565b60075481565b600080600080600080600080610fb389611466565b91506000821480610fdd5750610fc7611e39565b6fffffffffffffffffffffffffffffffff168210155b15610ff25760009750505050505050506111bf565b60006001836110019190612faf565b81548110611038577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200154965061104e876111e8565b945060008514156110695760009750505050505050506111bf565b61107287611c62565b955061107d876114d1565b90506d04ee2d6d415b85acef8100000000620f42407f00000000000000000000000000000000000000000000000000000000000000006110bd9190612ef0565b86886110c99190612ef0565b6110d39190612f21565b6110dd9190612ef0565b9350620f424081856110ef9190612f21565b6110f99190612ef0565b92506cffffffff00000000000000000019600102600160008b73ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282541692505081905550611163898461200d565b50818973ffffffffffffffffffffffffffffffffffffffff167f9a2a887706623ad3ff7fc85652deeceabe9fe1e00466c597972079ee91ea40d3856040516111ab9190612e63565b60405180910390a360019750505050505050505b919050565b600081815481106111d457600080fd5b906000526020600020016000915090505481565b6000606860ff16710fffffffff00000000000000000000000000836001900416908060020a82049150509050919050565b600080600a60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff1661127257600080fd5b61127a611960565b5082905082600860008282546112909190612e9a565b925050819055506112a1848261200d565b508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fab8530f87dc9b59234c4623bf917212bb2536d647574c8e7e5da92c2ede0c9f8856040516112ff9190612e63565b60405180910390a361130f612169565b50600191505092915050565b600068ffffffffffffffffff600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205460019004169050919050565b60007f0000000000000000000000000000000000000000000000000000000000000000826113a19190612f21565b7f00000000000000000000000000000000000000000000000000000000000000006113cc9190612e9a565b9050919050565b60095481565b6000600260009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16620f42406d04ee2d6d415b85acef81000000008461142a9190612f21565b6114349190612f21565b61143e9190612ef0565b9050919050565b6000603c82426114559190612faf565b61145f9190612ef0565b9050919050565b6000604860ff166cffffffff000000000000000000600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546001900416908060020a82049150509050919050565b6000608c60ff1673fffff00000000000000000000000000000000000836001900416908060020a82049150509050919050565b6000806000806115138561131b565b9250600260109054906101000a90046fffffffffffffffffffffffffffffffff1661153c611e39565b6115469190612f7b565b6fffffffffffffffffffffffffffffffff169050611594600260009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff1682610ee7565b6fffffffffffffffffffffffffffffffff169150620f42406d04ee2d6d415b85acef81000000006115c59190612f21565b82846115d19190612f21565b6115db9190612ef0565b9350505050919050565b60008060008060006115f5611e39565b9350600260109054906101000a90046fffffffffffffffffffffffffffffffff16846116219190612f7b565b92506000836fffffffffffffffffffffffffffffffff16141561164b576000945050505050611790565b600260009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16915061169a82846fffffffffffffffffffffffffffffffff16610ee7565b600260006101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff16021790555083600260106101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff16021790555081836fffffffffffffffffffffffffffffffff16856fffffffffffffffffffffffffffffffff167fa0717e54e02bd9829db5e6e998aec0ae9de796b8d150a3cc46a92ab869697755600260009054906101000a90046fffffffffffffffffffffffffffffffff1660405161177f9190612e48565b60405180910390a460019450505050505b90565b600080600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146117f057600080fd5b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35090565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000806000806000806000806000806000611979612251565b995060006001028a141561199a5760009a5050505050505050505050611b14565b6119a38a611d74565b97506001886119b29190612e9a565b90506119bd88611373565b91506119c76115e5565b50600260009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff169450611a0482611445565b92506000831115611a39576d04ee2d6d415b85acef8100000000611a288685611d88565b611a329190612ef0565b9350611a56565b6d04ee2d6d415b85acef810000000085611a539190612ef0565b93505b611a6560008560085484612381565b98506000899080600181540180825580915050600190039060005260206000200160009091909190915055611a998a6111e8565b96506000871415611ab457611aad8a61240d565b9550611ace565b611ac087600854610c20565b9550611acc8689612600565b505b7f55d243082e019fce4009ccea5368b92e436c17586a1e793c7deda16df4e5d67581604051611afd9190612e63565b60405180910390a160019a50505050505050505050505b90565b60068054611b24906130c2565b80601f0160208091040260200160405190810160405280929190818152602001828054611b50906130c2565b8015611b9d5780601f10611b7257610100808354040283529160200191611b9d565b820191906000526020600020905b815481529060010190602001808311611b8057829003601f168201915b505050505081565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611c0157600080fd5b6001600a60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060019050919050565b6000602060ff166cffffffffffffffffff00000000836001900416908060020a82049150509050919050565b600260109054906101000a90046fffffffffffffffffffffffffffffffff1681565b6000806000611cbd611960565b50611cc733610f9e565b50611cd1846113d9565b9150611cde338684611f69565b90508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef86604051611d3d9190612e63565b60405180910390a3809250505092915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600063ffffffff8260019004169050919050565b6000806000620f424091506d04ee2d6d415b85acef81000000007f0000000000000000000000000000000000000000000000000000000000000000611dcd9190612ef0565b905060005b84811015611e1457620f42408284611dea9190612f21565b611df49190612ef0565b83611dff9190612e9a565b92508080611e0c906130f4565b915050611dd2565b50620f42408583611e259190612f21565b611e2f9190612ef0565b9250505092915050565b600060017f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000042611e8a9190612faf565b611e949190612ef0565b611e9e9190612e9a565b905090565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611f2357600080fd5b81600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550919050565b600080611f768584612736565b50611f81848461200d565b50611f8a611e39565b6fffffffffffffffffffffffffffffffff1690506009548310158015611fb8575080611fb586611466565b14155b8015611ff057508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614155b1561200157611fff8582612867565b505b60019150509392505050565b600080600080600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054600190049050600085141561206e5760009350505050612163565b6120778661131b565b925084836120859190612e9a565b91508273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16116120f5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016120ec90612e0d565b60405180910390fd5b68ffffffffffffffffff198116905068ffffffffffffffffff82168117905080600102600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600193505050505b92915050565b600080600060016000805490506121809190612faf565b815481106121b7577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90600052602060002001546001900490506cffffffffffffffffff000000001981169050602060ff166008549060020a028117905080600102600060016000805490506122049190612faf565b8154811061223b577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200181905550600191505090565b60008060008060016000805490506122699190612faf565b815481106122a0577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b906000526020600020015491503073ffffffffffffffffffffffffffffffffffffffff1663e80952956040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b15801561230f57600080fd5b505afa158015612323573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123479190612bd0565b6fffffffffffffffffffffffffffffffff16905061236482611d74565b81116123785760006001029250505061237e565b81925050505b90565b60008073fffff00000000000000000000000000000000000608c60ff16869060020a021660010281179050710fffffffff00000000000000000000000000606860ff16879060020a0216600102811790506cffffffffffffffffff00000000602060ff16859060020a02166001028117905063ffffffff83166001028117905080915050949350505050565b600080600080600061241e86611c62565b9350620f42407f0000000000000000000000000000000000000000000000000000000000000000856124509190612f21565b61245a9190612ef0565b91507f0000000000000000000000000000000000000000000000000000000000000000620f42408361248c9190612f21565b6124969190612ef0565b9050838110156125ae576124a986611d74565b9250710fffffffff000000000000000000000000001960010260006001856124d19190612faf565b81548110612508577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200160008282541692505081905550606860ff1660019060020a027f800000000000000000000000000000000000000000000000000000000000000017600102600060018561255f9190612faf565b81548110612596577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90600052602060002001600082825417925050819055505b6125f3600c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166d04ee2d6d415b85acef8100000000846125ee9190612ef0565b61200d565b5081945050505050919050565b6000806000841415612616576000915050612730565b7f800000000000000000000000000000000000000000000000000000000000000060010260006001856126499190612faf565b81548110612680577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90600052602060002001600082825417925050819055506126f060006001856126a99190612faf565b815481106126e0577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200154611c62565b9050612729600c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1685836127249190612faf565b61200d565b5060019150505b92915050565b600080600080600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205460019004905060008514156127975760009350505050612861565b6127a08661131b565b9250848310156127e5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016127dc90612ded565b60405180910390fd5b84836127f19190612faf565b915068ffffffffffffffffff198116905068ffffffffffffffffff82168117905080600102600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600193505050505b92915050565b60006cffffffff00000000000000000019600102600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825416925050819055506cffffffff000000000000000000604860ff16839060020a0216600102600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282541792505081905550612938612943565b506001905092915050565b6000806000806000600160008054905061295d9190612faf565b81548110612994577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200154925060016129ac846111e8565b6129b69190612e9a565b905082600190049150710fffffffff000000000000000000000000001982169150710fffffffff00000000000000000000000000606860ff16829060020a0216821791508160010260006001600080549050612a129190612faf565b81548110612a49577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90600052602060002001819055506001935050505090565b600081359050612a7081613256565b92915050565b600081359050612a858161326d565b92915050565b600081359050612a9a81613284565b92915050565b600081519050612aaf8161329b565b92915050565b600081359050612ac4816132b2565b92915050565b600060208284031215612adc57600080fd5b6000612aea84828501612a61565b91505092915050565b600080600060608486031215612b0857600080fd5b6000612b1686828701612a61565b9350506020612b2786828701612a61565b9250506040612b3886828701612ab5565b9150509250925092565b60008060408385031215612b5557600080fd5b6000612b6385828601612a61565b9250506020612b7485828601612ab5565b9150509250929050565b600060208284031215612b9057600080fd5b6000612b9e84828501612a76565b91505092915050565b600060208284031215612bb957600080fd5b6000612bc784828501612a8b565b91505092915050565b600060208284031215612be257600080fd5b6000612bf084828501612aa0565b91505092915050565b600060208284031215612c0b57600080fd5b6000612c1984828501612ab5565b91505092915050565b60008060408385031215612c3557600080fd5b6000612c4385828601612ab5565b9250506020612c5485828601612ab5565b9150509250929050565b612c6781612fe3565b82525050565b612c7681612ff5565b82525050565b612c8581613001565b82525050565b6000612c9682612e7e565b612ca08185612e89565b9350612cb081856020860161308f565b612cb9816131ca565b840191505092915050565b6000612cd1601183612e89565b9150612cdc826131db565b602082019050919050565b6000612cf4600d83612e89565b9150612cff82613204565b602082019050919050565b6000612d17600d83612e89565b9150612d228261322d565b602082019050919050565b612d3681613037565b82525050565b612d458161307d565b82525050565b612d5481613073565b82525050565b6000602082019050612d6f6000830184612c5e565b92915050565b6000602082019050612d8a6000830184612c6d565b92915050565b6000602082019050612da56000830184612c7c565b92915050565b60006020820190508181036000830152612dc58184612c8b565b905092915050565b60006020820190508181036000830152612de681612cc4565b9050919050565b60006020820190508181036000830152612e0681612ce7565b9050919050565b60006020820190508181036000830152612e2681612d0a565b9050919050565b6000602082019050612e426000830184612d2d565b92915050565b6000602082019050612e5d6000830184612d3c565b92915050565b6000602082019050612e786000830184612d4b565b92915050565b600081519050919050565b600082825260208201905092915050565b6000612ea582613073565b9150612eb083613073565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115612ee557612ee461313d565b5b828201905092915050565b6000612efb82613073565b9150612f0683613073565b925082612f1657612f1561316c565b5b828204905092915050565b6000612f2c82613073565b9150612f3783613073565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612f7057612f6f61313d565b5b828202905092915050565b6000612f8682613037565b9150612f9183613037565b925082821015612fa457612fa361313d565b5b828203905092915050565b6000612fba82613073565b9150612fc583613073565b925082821015612fd857612fd761313d565b5b828203905092915050565b6000612fee82613053565b9050919050565b60008115159050919050565b6000819050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b60006fffffffffffffffffffffffffffffffff82169050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600061308882613037565b9050919050565b60005b838110156130ad578082015181840152602081019050613092565b838111156130bc576000848401525b50505050565b600060028204905060018216806130da57607f821691505b602082108114156130ee576130ed61319b565b5b50919050565b60006130ff82613073565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214156131325761313161313d565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000601f19601f8301169050919050565b7f4552525f4e554d50415254535f5a45524f000000000000000000000000000000600082015250565b7f4552525f4f5645525350454e4400000000000000000000000000000000000000600082015250565b7f4552525f574f554c445752415000000000000000000000000000000000000000600082015250565b61325f81612fe3565b811461326a57600080fd5b50565b61327681613001565b811461328157600080fd5b50565b61328d8161300b565b811461329857600080fd5b50565b6132a481613037565b81146132af57600080fd5b50565b6132bb81613073565b81146132c657600080fd5b5056fea2646970667358221220fa11a5f958152eff7d70d797730b18f3d690cf25906af3e8f7a49ea02266ae9064736f6c63430008040033 +\ No newline at end of file diff --git a/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap.bin b/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap.bin @@ -0,0 +1 @@ +60e06040523480156200001157600080fd5b50604051620039b5380380620039b583398181016040528101906200003791906200048e565b33600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506001600a6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555085600590805190602001906200010a92919062000327565b5084600690805190602001906200012392919062000327565b508360ff166007819055504260808181525050603c826200014591906200074d565b60a08181525050620f42406d04ee2d6d415b85acef81000000006200016b91906200074d565b600260006101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055506001600260106101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff1602179055508260c0818152505060006200020a6000620f4240600060016200029b640100000000026401000000009004565b9050600081908060018154018082558091505060019003906000526020600020016000909190919091505581600c60006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055508460ff16600a62000288919062000610565b6009819055505050505050505062000994565b60008073fffff00000000000000000000000000000000000608c60ff16869060020a021660010281179050710fffffffff00000000000000000000000000606860ff16879060020a0216600102811790506cffffffffffffffffff00000000602060ff16859060020a02166001028117905063ffffffff83166001028117905080915050949350505050565b82805462000335906200082f565b90600052602060002090601f016020900481019282620003595760008555620003a5565b82601f106200037457805160ff1916838001178555620003a5565b82800160010185558215620003a5579182015b82811115620003a457825182559160200191906001019062000387565b5b509050620003b49190620003b8565b5090565b5b80821115620003d3576000816000905550600101620003b9565b5090565b6000620003ee620003e8846200057f565b62000556565b9050828152602081018484840111156200040757600080fd5b62000414848285620007f9565b509392505050565b6000815190506200042d8162000946565b92915050565b600082601f8301126200044557600080fd5b815162000457848260208601620003d7565b91505092915050565b600081519050620004718162000960565b92915050565b60008151905062000488816200097a565b92915050565b60008060008060008060c08789031215620004a857600080fd5b600087015167ffffffffffffffff811115620004c357600080fd5b620004d189828a0162000433565b965050602087015167ffffffffffffffff811115620004ef57600080fd5b620004fd89828a0162000433565b95505060406200051089828a0162000477565b94505060606200052389828a0162000460565b93505060806200053689828a0162000460565b92505060a06200054989828a016200041c565b9150509295509295509295565b60006200056262000575565b905062000570828262000865565b919050565b6000604051905090565b600067ffffffffffffffff8211156200059d576200059c620008f9565b5b620005a88262000928565b9050602081019050919050565b6000808291508390505b60018511156200060757808604811115620005df57620005de6200089b565b5b6001851615620005ef5780820291505b8081029050620005ff8562000939565b9450620005bf565b94509492505050565b60006200061d82620007e2565b91506200062a83620007e2565b9250620006597fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff848462000661565b905092915050565b60008262000673576001905062000746565b8162000683576000905062000746565b81600181146200069c5760028114620006a757620006dd565b600191505062000746565b60ff841115620006bc57620006bb6200089b565b5b8360020a915084821115620006d657620006d56200089b565b5b5062000746565b5060208310610133831016604e8410600b8410161715620007175782820a9050838111156200071157620007106200089b565b5b62000746565b620007268484846001620005b5565b9250905081840481111562000740576200073f6200089b565b5b81810290505b9392505050565b60006200075a82620007e2565b91506200076783620007e2565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615620007a357620007a26200089b565b5b828202905092915050565b6000620007bb82620007c2565b9050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b600060ff82169050919050565b60005b8381101562000819578082015181840152602081019050620007fc565b8381111562000829576000848401525b50505050565b600060028204905060018216806200084857607f821691505b602082108114156200085f576200085e620008ca565b5b50919050565b620008708262000928565b810181811067ffffffffffffffff82111715620008925762000891620008f9565b5b80604052505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6000601f19601f8301169050919050565b6000600282049050919050565b6200095181620007ae565b81146200095d57600080fd5b50565b6200096b81620007e2565b81146200097757600080fd5b50565b6200098581620007ec565b81146200099157600080fd5b50565b60805160a05160c051612fb9620009fc60003960008181610c9e01528181610eef01528181611bdb01528181612216015261224e0152600081816111eb01528181611b890152611c7601526000818161121701528181611c970152611cdc0152612fb96000f3fe608060405234801561001057600080fd5b506004361061025a576000357c0100000000000000000000000000000000000000000000000000000000900480636787a9be11610158578063983b2d56116100d5578063e3ab207a11610099578063e3ab207a146107c3578063e54d63f7146107f3578063e809529514610823578063eda4e6d614610841578063f2fde38b1461085f5761025a565b8063983b2d56146106f75780639da7d8ba14610727578063a2119e2f14610757578063a9059cbb14610775578063b470aade146107a55761025a565b8063731f237c1161011c578063731f237c1461066157806379ba50971461067f5780638da5cb5b1461069d5780638f1df6bc146106bb57806395d89b41146106d95761025a565b80636787a9be146105715780636a2d094e146105a15780636d5055a6146105d15780636ffa8a051461060157806370a08231146106315761025a565b80633092afd5116101e657806340fc89f5116101aa57806340fc89f514610493578063449a52f8146104c357806347a50517146104f35780634abfbba214610523578063573cc507146105535761025a565b80633092afd5146103b557806331333487146103e5578063313ce5671461041557806335b16eb81461043357806340452d91146104635761025a565b806318160ddd1161022d57806318160ddd146102fb5780631989c6a814610319578063216ea5ec14610337578063229ba1971461036757806323b872dd146103855761025a565b806301ffc9a71461025f57806306fdde031461028f578063095ea7b3146102ad57806309f28f3c146102dd575b600080fd5b61027960048036038101906102749190612861565b61088f565b6040516102869190612a2f565b60405180910390f35b610297610a3a565b6040516102a49190612a65565b60405180910390f35b6102c760048036038101906102c291906127fc565b610ac8565b6040516102d49190612a2f565b60405180910390f35b6102e5610be2565b6040516102f29190612ae7565b60405180910390f35b610303610c04565b6040516103109190612b1d565b60405180910390f35b610321610c0a565b60405161032e9190612b1d565b60405180910390f35b610351600480360381019061034c91906128dc565b610c16565b60405161035e9190612b1d565b60405180910390f35b61036f610c9c565b60405161037c9190612b1d565b60405180910390f35b61039f600480360381019061039a91906127ad565b610cc0565b6040516103ac9190612a2f565b60405180910390f35b6103cf60048036038101906103ca9190612784565b610de0565b6040516103dc9190612a2f565b60405180910390f35b6103ff60048036038101906103fa91906128dc565b610ed3565b60405161040c9190612b1d565b60405180910390f35b61041d610f84565b60405161042a9190612b1d565b60405180910390f35b61044d60048036038101906104489190612784565b610f8a565b60405161045a9190612a2f565b60405180910390f35b61047d600480360381019061047891906128b3565b611038565b60405161048a9190612a4a565b60405180910390f35b6104ad60048036038101906104a89190612838565b61105c565b6040516104ba9190612b1d565b60405180910390f35b6104dd60048036038101906104d891906127fc565b61108d565b6040516104ea9190612a2f565b60405180910390f35b61050d60048036038101906105089190612784565b61118f565b60405161051a9190612b1d565b60405180910390f35b61053d600480360381019061053891906128b3565b6111e7565b60405161054a9190612b1d565b60405180910390f35b61055b611247565b6040516105689190612b1d565b60405180910390f35b61058b600480360381019061058691906128b3565b61124d565b6040516105989190612b1d565b60405180910390f35b6105bb60048036038101906105b691906128b3565b6112b9565b6040516105c89190612b1d565b60405180910390f35b6105eb60048036038101906105e69190612784565b6112da565b6040516105f89190612b1d565b60405180910390f35b61061b60048036038101906106169190612838565b611345565b6040516106289190612b1d565b60405180910390f35b61064b60048036038101906106469190612784565b611378565b6040516106589190612b1d565b60405180910390f35b610669611459565b6040516106769190612a2f565b60405180910390f35b610687611607565b6040516106949190612a2f565b60405180910390f35b6106a56117ae565b6040516106b29190612a14565b60405180910390f35b6106c36117d4565b6040516106d09190612a2f565b60405180910390f35b6106e1611958565b6040516106ee9190612a65565b60405180910390f35b610711600480360381019061070c9190612784565b6119e6565b60405161071e9190612a2f565b60405180910390f35b610741600480360381019061073c9190612838565b611aa3565b60405161074e9190612b1d565b60405180910390f35b61075f611acf565b60405161076c9190612ae7565b60405180910390f35b61078f600480360381019061078a91906127fc565b611af1565b60405161079c9190612a2f565b60405180910390f35b6107ad611b87565b6040516107ba9190612b1d565b60405180910390f35b6107dd60048036038101906107d89190612838565b611bab565b6040516107ea9190612b1d565b60405180910390f35b61080d600480360381019061080891906128dc565b611bbf565b60405161081a9190612b1d565b60405180910390f35b61082b611c70565b6040516108389190612ae7565b60405180910390f35b610849611cda565b6040516108569190612b1d565b60405180910390f35b61087960048036038101906108749190612784565b611cfe565b6040516108869190612a2f565b60405180910390f35b600063c6bb4b707c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156108e45760019050610a35565b63449a52f87c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156109375760019050610a35565b6301ffc9a77c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916141561098a5760019050610a35565b639493f8b27c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191614156109dd5760019050610a35565b6337a47be47c010000000000000000000000000000000000000000000000000000000002827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19161415610a305760019050610a35565b600090505b919050565b60058054610a4790612d7c565b80601f0160208091040260200160405190810160405280929190818152602001828054610a7390612d7c565b8015610ac05780601f10610a9557610100808354040283529160200191610ac0565b820191906000526020600020905b815481529060010190602001808311610aa357829003601f168201915b505050505081565b600080610ad36117d4565b50610add8361124d565b905080600b60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254610b6b9190612b54565b925050819055508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92585604051610bcf9190612b1d565b60405180910390a3600191505092915050565b600260009054906101000a90046fffffffffffffffffffffffffffffffff1681565b60085481565b60008080549050905090565b600080600080851415610c5e576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610c5590612a87565b60405180910390fd5b838510610c6a57600080fd5b8484610c769190612baa565b91508482610c849190612bdb565b90508084610c929190612c69565b9250505092915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000806000610ccd6117d4565b50610cd78461124d565b915081600b60008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020541015610d6257600080fd5b610d6d868684611da0565b90508473ffffffffffffffffffffffffffffffffffffffff168673ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef86604051610dcc9190612b1d565b60405180910390a380925050509392505050565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161480610e6957503373ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16145b610e7257600080fd5b6000600a60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060019050919050565b6000806000620f424091506d04ee2d6d415b85acef81000000007f0000000000000000000000000000000000000000000000000000000000000000610f189190612baa565b905060005b84811015610f5f57620f42408284610f359190612bdb565b610f3f9190612baa565b83610f4a9190612c69565b92508080610f5790612dae565b915050610f1d565b50620f42408583610f709190612bdb565b610f7a9190612baa565b9250505092915050565b60075481565b600080610f96836112da565b90506000811480610fc05750610faa611c70565b6fffffffffffffffffffffffffffffffff168110155b15610fcf576000915050611033565b6cffffffff00000000000000000019600102600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254169250508190555060019150505b919050565b6000818154811061104857600080fd5b906000526020600020016000915090505481565b6000606860ff16710fffffffff00000000000000000000000000836001900416908060020a82049150509050919050565b600080600a60003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060009054906101000a900460ff166110e657600080fd5b6110ee6117d4565b5082905082600860008282546111049190612b54565b925050819055506111158482611e44565b508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fab8530f87dc9b59234c4623bf917212bb2536d647574c8e7e5da92c2ede0c9f8856040516111739190612b1d565b60405180910390a3611183611f59565b50600191505092915050565b600068ffffffffffffffffff600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205460019004169050919050565b60007f0000000000000000000000000000000000000000000000000000000000000000826112159190612bdb565b7f00000000000000000000000000000000000000000000000000000000000000006112409190612b54565b9050919050565b60095481565b6000600260009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16620f42406d04ee2d6d415b85acef81000000008461129e9190612bdb565b6112a89190612bdb565b6112b29190612baa565b9050919050565b6000603c82426112c99190612c69565b6112d39190612baa565b9050919050565b6000604860ff166cffffffff000000000000000000600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546001900416908060020a82049150509050919050565b6000608c60ff1673fffff00000000000000000000000000000000000836001900416908060020a82049150509050919050565b6000806000806113878561118f565b9250600260109054906101000a90046fffffffffffffffffffffffffffffffff166113b0611c70565b6113ba9190612c35565b6fffffffffffffffffffffffffffffffff169050611408600260009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff1682610ed3565b6fffffffffffffffffffffffffffffffff169150620f42406d04ee2d6d415b85acef81000000006114399190612bdb565b82846114459190612bdb565b61144f9190612baa565b9350505050919050565b6000806000806000611469611c70565b9350600260109054906101000a90046fffffffffffffffffffffffffffffffff16846114959190612c35565b92506000836fffffffffffffffffffffffffffffffff1614156114bf576000945050505050611604565b600260009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff16915061150e82846fffffffffffffffffffffffffffffffff16610ed3565b600260006101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff16021790555083600260106101000a8154816fffffffffffffffffffffffffffffffff02191690836fffffffffffffffffffffffffffffffff16021790555081836fffffffffffffffffffffffffffffffff16856fffffffffffffffffffffffffffffffff167fa0717e54e02bd9829db5e6e998aec0ae9de796b8d150a3cc46a92ab869697755600260009054906101000a90046fffffffffffffffffffffffffffffffff166040516115f39190612b02565b60405180910390a460019450505050505b90565b600080600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461166457600080fd5b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600460009054906101000a900473ffffffffffffffffffffffffffffffffffffffff16600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506000600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e060405160405180910390a35090565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060008060008060008060008060006117ed612041565b995060006001028a141561180e5760009a5050505050505050505050611955565b6118178a611bab565b97506001886118269190612b54565b9050611831886111e7565b915061183b611459565b50600260009054906101000a90046fffffffffffffffffffffffffffffffff166fffffffffffffffffffffffffffffffff169450611878826112b9565b925060008311156118ad576d04ee2d6d415b85acef810000000061189c8685611bbf565b6118a69190612baa565b93506118ca565b6d04ee2d6d415b85acef8100000000856118c79190612baa565b93505b6118d960008560085484612171565b9850600089908060018154018082558091505060019003906000526020600020016000909190919091505561190d8a6121fd565b95507f55d243082e019fce4009ccea5368b92e436c17586a1e793c7deda16df4e5d6758160405161193e9190612b1d565b60405180910390a160019a50505050505050505050505b90565b6006805461196590612d7c565b80601f016020809104026020016040519081016040528092919081815260200182805461199190612d7c565b80156119de5780601f106119b3576101008083540402835291602001916119de565b820191906000526020600020905b8154815290600101906020018083116119c157829003601f168201915b505050505081565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611a4257600080fd5b6001600a60008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006101000a81548160ff02191690831515021790555060019050919050565b6000602060ff166cffffffffffffffffff00000000836001900416908060020a82049150509050919050565b600260109054906101000a90046fffffffffffffffffffffffffffffffff1681565b6000806000611afe6117d4565b50611b088461124d565b9150611b15338684611da0565b90508473ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef86604051611b749190612b1d565b60405180910390a3809250505092915050565b7f000000000000000000000000000000000000000000000000000000000000000081565b600063ffffffff8260019004169050919050565b6000806000620f424091506d04ee2d6d415b85acef81000000007f0000000000000000000000000000000000000000000000000000000000000000611c049190612baa565b905060005b84811015611c4b57620f42408284611c219190612bdb565b611c2b9190612baa565b83611c369190612b54565b92508080611c4390612dae565b915050611c09565b50620f42408583611c5c9190612bdb565b611c669190612baa565b9250505092915050565b600060017f00000000000000000000000000000000000000000000000000000000000000007f000000000000000000000000000000000000000000000000000000000000000042611cc19190612c69565b611ccb9190612baa565b611cd59190612b54565b905090565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614611d5a57600080fd5b81600460006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550919050565b600080611dad85846123f0565b50611db88484611e44565b50611dc1611c70565b6fffffffffffffffffffffffffffffffff1690506009548310158015611def575080611dec866112da565b14155b8015611e2757508373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff1614155b15611e3857611e368582612521565b505b60019150509392505050565b600080600080600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546001900490506000851415611ea55760009350505050611f53565b611eae8661118f565b92508483611ebc9190612b54565b91508273ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1611611f2c576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401611f2390612ac7565b60405180910390fd5b68ffffffffffffffffff198116905068ffffffffffffffffff821681179050600193505050505b92915050565b60008060006001600080549050611f709190612c69565b81548110611fa7577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90600052602060002001546001900490506cffffffffffffffffff000000001981169050602060ff166008549060020a02811790508060010260006001600080549050611ff49190612c69565b8154811061202b577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200181905550600191505090565b60008060008060016000805490506120599190612c69565b81548110612090577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b906000526020600020015491503073ffffffffffffffffffffffffffffffffffffffff1663e80952956040518163ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040160206040518083038186803b1580156120ff57600080fd5b505afa158015612113573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612137919061288a565b6fffffffffffffffffffffffffffffffff16905061215482611bab565b81116121685760006001029250505061216e565b81925050505b90565b60008073fffff00000000000000000000000000000000000608c60ff16869060020a021660010281179050710fffffffff00000000000000000000000000606860ff16879060020a0216600102811790506cffffffffffffffffff00000000602060ff16859060020a02166001028117905063ffffffff83166001028117905080915050949350505050565b600080600080600061220e86611aa3565b9350620f42407f0000000000000000000000000000000000000000000000000000000000000000856122409190612bdb565b61224a9190612baa565b91507f0000000000000000000000000000000000000000000000000000000000000000620f42408361227c9190612bdb565b6122869190612baa565b90508381101561239e5761229986611bab565b9250710fffffffff000000000000000000000000001960010260006001856122c19190612c69565b815481106122f8577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200160008282541692505081905550606860ff1660019060020a027f800000000000000000000000000000000000000000000000000000000000000017600102600060018561234f9190612c69565b81548110612386577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90600052602060002001600082825417925050819055505b6123e3600c60009054906101000a900473ffffffffffffffffffffffffffffffffffffffff166d04ee2d6d415b85acef8100000000846123de9190612baa565b611e44565b5081945050505050919050565b600080600080600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020546001900490506000851415612451576000935050505061251b565b61245a8661118f565b92508483101561249f576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161249690612aa7565b60405180910390fd5b84836124ab9190612c69565b915068ffffffffffffffffff198116905068ffffffffffffffffff82168117905080600102600160008873ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550600193505050505b92915050565b60006cffffffff00000000000000000019600102600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825416925050819055506cffffffff000000000000000000604860ff16839060020a0216600102600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825417925050819055506125f26125fd565b506001905092915050565b600080600080600060016000805490506126179190612c69565b8154811061264e577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060005260206000200154925060016126668461105c565b6126709190612b54565b905082600190049150710fffffffff000000000000000000000000001982169150710fffffffff00000000000000000000000000606860ff16829060020a02168217915081600102600060016000805490506126cc9190612c69565b81548110612703577f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b90600052602060002001819055506001935050505090565b60008135905061272a81612f10565b92915050565b60008135905061273f81612f27565b92915050565b60008135905061275481612f3e565b92915050565b60008151905061276981612f55565b92915050565b60008135905061277e81612f6c565b92915050565b60006020828403121561279657600080fd5b60006127a48482850161271b565b91505092915050565b6000806000606084860312156127c257600080fd5b60006127d08682870161271b565b93505060206127e18682870161271b565b92505060406127f28682870161276f565b9150509250925092565b6000806040838503121561280f57600080fd5b600061281d8582860161271b565b925050602061282e8582860161276f565b9150509250929050565b60006020828403121561284a57600080fd5b600061285884828501612730565b91505092915050565b60006020828403121561287357600080fd5b600061288184828501612745565b91505092915050565b60006020828403121561289c57600080fd5b60006128aa8482850161275a565b91505092915050565b6000602082840312156128c557600080fd5b60006128d38482850161276f565b91505092915050565b600080604083850312156128ef57600080fd5b60006128fd8582860161276f565b925050602061290e8582860161276f565b9150509250929050565b61292181612c9d565b82525050565b61293081612caf565b82525050565b61293f81612cbb565b82525050565b600061295082612b38565b61295a8185612b43565b935061296a818560208601612d49565b61297381612e84565b840191505092915050565b600061298b601183612b43565b915061299682612e95565b602082019050919050565b60006129ae600d83612b43565b91506129b982612ebe565b602082019050919050565b60006129d1600d83612b43565b91506129dc82612ee7565b602082019050919050565b6129f081612cf1565b82525050565b6129ff81612d37565b82525050565b612a0e81612d2d565b82525050565b6000602082019050612a296000830184612918565b92915050565b6000602082019050612a446000830184612927565b92915050565b6000602082019050612a5f6000830184612936565b92915050565b60006020820190508181036000830152612a7f8184612945565b905092915050565b60006020820190508181036000830152612aa08161297e565b9050919050565b60006020820190508181036000830152612ac0816129a1565b9050919050565b60006020820190508181036000830152612ae0816129c4565b9050919050565b6000602082019050612afc60008301846129e7565b92915050565b6000602082019050612b1760008301846129f6565b92915050565b6000602082019050612b326000830184612a05565b92915050565b600081519050919050565b600082825260208201905092915050565b6000612b5f82612d2d565b9150612b6a83612d2d565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03821115612b9f57612b9e612df7565b5b828201905092915050565b6000612bb582612d2d565b9150612bc083612d2d565b925082612bd057612bcf612e26565b5b828204905092915050565b6000612be682612d2d565b9150612bf183612d2d565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0483118215151615612c2a57612c29612df7565b5b828202905092915050565b6000612c4082612cf1565b9150612c4b83612cf1565b925082821015612c5e57612c5d612df7565b5b828203905092915050565b6000612c7482612d2d565b9150612c7f83612d2d565b925082821015612c9257612c91612df7565b5b828203905092915050565b6000612ca882612d0d565b9050919050565b60008115159050919050565b6000819050919050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b60006fffffffffffffffffffffffffffffffff82169050919050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000819050919050565b6000612d4282612cf1565b9050919050565b60005b83811015612d67578082015181840152602081019050612d4c565b83811115612d76576000848401525b50505050565b60006002820490506001821680612d9457607f821691505b60208210811415612da857612da7612e55565b5b50919050565b6000612db982612d2d565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821415612dec57612deb612df7565b5b600182019050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b6000601f19601f8301169050919050565b7f4552525f4e554d50415254535f5a45524f000000000000000000000000000000600082015250565b7f4552525f4f5645525350454e4400000000000000000000000000000000000000600082015250565b7f4552525f574f554c445752415000000000000000000000000000000000000000600082015250565b612f1981612c9d565b8114612f2457600080fd5b50565b612f3081612cbb565b8114612f3b57600080fd5b50565b612f4781612cc5565b8114612f5257600080fd5b50565b612f5e81612cf1565b8114612f6957600080fd5b50565b612f7581612d2d565b8114612f8057600080fd5b5056fea2646970667358221220a418f3836db11876504f1b6d21ca04fa04215e4280eb811bf206fb54db6d0f6564736f6c63430008040033 +\ No newline at end of file diff --git a/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap.json b/python/erc20_demurrage_token/data/DemurrageTokenSingleNocap.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"},{"internalType":"uint8","name":"_decimals","type":"uint8"},{"internalType":"uint256","name":"_taxLevelMinute","type":"uint256"},{"internalType":"uint256","name":"_periodMinutes","type":"uint256"},{"internalType":"address","name":"_defaultSinkAddress","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_owner","type":"address"},{"indexed":true,"internalType":"address","name":"_spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"_foo","type":"bytes32"}],"name":"Debug","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"_period","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_periodCount","type":"uint256"},{"indexed":true,"internalType":"uint256","name":"_oldAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_newAmount","type":"uint256"}],"name":"Decayed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_minter","type":"address"},{"indexed":true,"internalType":"address","name":"_beneficiary","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Mint","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"_period","type":"uint256"}],"name":"Period","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_account","type":"address"},{"indexed":true,"internalType":"uint256","name":"_period","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Redistribution","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"_from","type":"address"},{"indexed":true,"internalType":"address","name":"_to","type":"address"},{"indexed":false,"internalType":"uint256","name":"_value","type":"uint256"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"accountPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"actualPeriod","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_minter","type":"address"}],"name":"addMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"applyDemurrage","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"applyRedistributionOnAccount","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_spender","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"baseBalanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"changePeriod","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_period","type":"uint256"}],"name":"decayBy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"demurrageAmount","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_target","type":"uint256"}],"name":"demurrageCycles","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"demurragePeriod","outputs":[{"internalType":"uint128","name":"","type":"uint128"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_periodCount","type":"uint256"}],"name":"getPeriodTimeDelta","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"},{"internalType":"uint256","name":"_period","type":"uint256"}],"name":"growBy","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minimumParticipantSpend","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_beneficiary","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"mintTo","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"periodDuration","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"periodStart","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"redistributionCount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"redistributions","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_numParts","type":"uint256"},{"internalType":"uint256","name":"_sumWhole","type":"uint256"}],"name":"remainder","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"_minter","type":"address"}],"name":"removeMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"_sum","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"taxLevel","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"toBaseAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"redistribution","type":"bytes32"}],"name":"toRedistributionDemurrageModifier","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"redistribution","type":"bytes32"}],"name":"toRedistributionParticipants","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"redistribution","type":"bytes32"}],"name":"toRedistributionPeriod","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"bytes32","name":"redistribution","type":"bytes32"}],"name":"toRedistributionSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_from","type":"address"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newOwner","type":"address"}],"name":"transferOwnership","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"}] diff --git a/python/erc20_demurrage_token/token.py b/python/erc20_demurrage_token/token.py @@ -40,13 +40,13 @@ class DemurrageTokenSettings: class DemurrageToken(ERC20): - __abi = None - __bytecode = None + __abi = {} + __bytecode = {} def constructor(self, sender_address, settings, redistribute=True, cap=0, tx_format=TxFormat.JSONRPC): - if not redistribute or cap: - raise NotImplementedError('token cap and sink only redistribution not yet implemented') - code = DemurrageToken.bytecode() + if int(cap) < 0: + raise ValueError('cap must be 0 or positive integer') + code = DemurrageToken.bytecode(multi=redistribute, cap=cap>0) enc = ABIContractEncoder() enc.string(settings.name) enc.string(settings.symbol) @@ -64,22 +64,39 @@ class DemurrageToken(ERC20): def gas(code=None): return 3500000 + + @staticmethod + def __to_contract_name(multi, cap): + name = 'DemurrageToken' + if multi: + name += 'Multi' + else: + name += 'Single' + if cap: + name += 'Cap' + else: + name += 'Nocap' + return name + + @staticmethod - def abi(): - if DemurrageToken.__abi == None: - f = open(os.path.join(data_dir, 'DemurrageTokenMultiNocap.json'), 'r') - DemurrageToken.__abi = json.load(f) + def abi(multi=True, cap=False): + name = DemurrageToken.__to_contract_name(multi, cap) + if DemurrageToken.__abi.get(name) == None: + f = open(os.path.join(data_dir, name + '.json'), 'r') + DemurrageToken.__abi[name] = json.load(f) f.close() - return DemurrageToken.__abi + return DemurrageToken.__abi[name] @staticmethod - def bytecode(): - if DemurrageToken.__bytecode == None: - f = open(os.path.join(data_dir, 'DemurrageTokenMultiNocap.bin'), 'r') - DemurrageToken.__bytecode = f.read() + def bytecode(multi=True, cap=False): + name = DemurrageToken.__to_contract_name(multi, cap) + if DemurrageToken.__bytecode.get(name) == None: + f = open(os.path.join(data_dir, name + '.bin'), 'r') + DemurrageToken.__bytecode[name] = f.read() f.close() - return DemurrageToken.__bytecode + return DemurrageToken.__bytecode[name] def add_minter(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC): diff --git a/python/requirements.txt b/python/requirements.txt @@ -1,3 +1,3 @@ chainlib~=0.0.3rc3 -eth-erc20~=0.0.9a3 +eth-erc20~=0.0.9a4 crypto-dev-signer~=0.4.14b3 diff --git a/python/tests/base.py b/python/tests/base.py @@ -35,6 +35,10 @@ class TestDemurrage(EthTesterCase): def setUp(self): super(TestDemurrage, self).setUp() + + self.tax_level = TAX_LEVEL + self.period = PERIOD + nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) self.settings = DemurrageTokenSettings() self.settings.name = 'Foo Token' @@ -64,16 +68,31 @@ class TestDemurrage(EthTesterCase): class TestDemurrageDefault(TestDemurrage): - def setUp(self): - super(TestDemurrageDefault, self).setUp() - - nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) - c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) - (tx_hash, o) = c.constructor(self.accounts[0], self.settings) + def __deploy(self, interface): + self.default_supply = 1000000000000 + self.supply_cap = self.default_supply * 10 + + (tx_hash, o) = interface.constructor(self.accounts[0], self.settings, redistribute=True, cap=0) + r = self.rpc.do(o) + o = receipt(tx_hash) r = self.rpc.do(o) + self.assertEqual(r['status'], 1) + self.addresses['MultiNocap'] = r['contract_address'] + (tx_hash, o) = interface.constructor(self.accounts[0], self.settings, redistribute=False, cap=0) + r = self.rpc.do(o) o = receipt(tx_hash) r = self.rpc.do(o) self.assertEqual(r['status'], 1) + self.addresses['SingleNocap'] = r['contract_address'] + + + def setUp(self): + super(TestDemurrageDefault, self).setUp() + + nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) + c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) - self.address = r['contract_address'] + self.addresses = {} + self.__deploy(c) + self.address = self.addresses['MultiNocap'] diff --git a/python/tests/test_redistribution.py b/python/tests/test_redistribution.py @@ -26,11 +26,6 @@ logg = logging.getLogger() testdir = os.path.dirname(__file__) -#BLOCKTIME = 5 # seconds -TAX_LEVEL = 10000 * 2 # 2% -#PERIOD = int(60/BLOCKTIME) * 60 * 24 * 30 # month -PERIOD = 1 - class TestRedistribution(TestDemurrageDefault): @@ -104,7 +99,7 @@ class TestRedistribution(TestDemurrageDefault): def test_redistribution_balance_on_zero_participants(self): - supply = 1000000000000 + supply = self.default_supply nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) @@ -127,7 +122,7 @@ class TestRedistribution(TestDemurrageDefault): 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 * (TAX_LEVEL / 1000000)) + sink_increment = int(total_supply * (self.tax_level / 1000000)) self.assertEqual(supply, total_supply) for l in rcpt['logs']: @@ -136,14 +131,14 @@ class TestRedistribution(TestDemurrageDefault): self.assertEqual(period, 2) b = bytes.fromhex(strip_0x(l['data'])) remainder = int.from_bytes(b, 'big') - self.assertEqual(remainder, int((1000000 - TAX_LEVEL) * (10 ** 32))) + 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 - TAX_LEVEL) / 1000000)) + 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) @@ -215,14 +210,14 @@ class TestRedistribution(TestDemurrageDefault): r = self.rpc.do(o) bummer_balance = c.parse_balance_of(r) - self.assertEqual(bummer_balance, mint_amount - (mint_amount * (TAX_LEVEL / 1000000))) + 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 * (TAX_LEVEL / 1000000))) + 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)) @@ -254,9 +249,9 @@ class TestRedistribution(TestDemurrageDefault): actual_period = c.parse_actual_period(r) logg.debug('period {}'.format(actual_period)) - redistribution = int((z / 2) * (TAX_LEVEL / 1000000)) + 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 * (TAX_LEVEL / 1000000))) + 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) diff --git a/python/tests/test_single.py b/python/tests/test_single.py @@ -0,0 +1,61 @@ +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 +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 tests.base import TestDemurrageDefault + +logging.basicConfig(level=logging.DEBUG) +logg = logging.getLogger() + +testdir = os.path.dirname(__file__) + + +class TestRedistributionSingle(TestDemurrageDefault): + + def test_single_even_if_multiple(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) + 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[1], external_address, 10000000) + r = self.rpc.do(o) + + 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[2], external_address, 20000000) + r = self.rpc.do(o) + + self.backend.time_travel(self.start_time + 61) + (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) + diff --git a/solidity/DemurrageTokenMultiCap.sol b/solidity/DemurrageTokenMultiCap.sol @@ -0,0 +1,616 @@ +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; + + // 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; + + // 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 + 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 + 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; + + 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 = _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/DemurrageTokenMultiNocap.sol b/solidity/DemurrageTokenMultiNocap.sol @@ -2,7 +2,7 @@ pragma solidity > 0.6.11; // SPDX-License-Identifier: GPL-3.0-or-later -contract RedistributedDemurrageToken { +contract DemurrageTokenMultiNocap { // Redistribution bit field, with associated shifts and masks // (Uses sub-byte boundaries) diff --git a/solidity/DemurrageTokenSingleNocap.sol b/solidity/DemurrageTokenSingleNocap.sol @@ -0,0 +1,609 @@ +pragma solidity > 0.6.11; + +// SPDX-License-Identifier: GPL-3.0-or-later + +contract DemurrageTokenSingleNocap { + + // 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; + + // 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; + //return uint256(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); + newBalance = oldBalance + _delta; + require(uint160(newBalance) > uint160(oldBalance), 'ERR_WOULDWRAP'); // revert if increase would result in a wrapped value + workAccount &= (~maskAccountValue); + workAccount |= (newBalance & maskAccountValue); + 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; + } + + // Returns the amount 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 + 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/Makefile b/solidity/Makefile @@ -1,16 +1,32 @@ SOLC = /usr/bin/solc -all: +all: multi_nocap multi_cap 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 DemurrageTokenMultiCap.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 DemurrageTokenMultiNocap.bin +multi: multi_nocap multi_cap + +single_nocap: + $(SOLC) DemurrageTokenSingleNocap.sol --abi --evm-version byzantium | awk 'NR>3' > DemurrageTokenSingleNocap.json + $(SOLC) DemurrageTokenSingleNocap.sol --bin --evm-version byzantium | awk 'NR>3' > DemurrageTokenSingleNocap.bin + truncate -s -1 DemurrageTokenSingleNocap.bin + test: all python ../python/tests/test_basic.py python ../python/tests/test_period.py python ../python/tests/test_redistribution.py + python ../python/tests/test_pure.py install: all - cp -v DemurrageTokenMultiNocap.{json,bin} ../python/sarafu_token/data/ + cp -v DemurrageToken*.{json,bin} ../python/erc20_demurrage_token/data/ .PHONY: test install