From a8ff826dad6e425a399200a47124ee03f4f163c3 Mon Sep 17 00:00:00 2001 From: nolash Date: Sat, 6 Feb 2021 22:04:39 +0100 Subject: [PATCH] Use cached demurrage in account redistribution application --- python/tests/bench.py | 158 ++++++++++++++++++++++- solidity/RedistributedDemurrageToken.sol | 14 +- 2 files changed, 167 insertions(+), 5 deletions(-) diff --git a/python/tests/bench.py b/python/tests/bench.py index edddd86..b3e4316 100644 --- a/python/tests/bench.py +++ b/python/tests/bench.py @@ -47,6 +47,14 @@ class Test(unittest.TestCase): pass + def test_construct(self): + period = 10 + c = self.w3.eth.contract(abi=self.abi, bytecode=self.bytecode) + tx_hash = c.constructor('Foo Token', 'FOO', 6, TAX_LEVEL * (10 ** 32), period, self.sink_address).transact({'from': self.w3.eth.accounts[0]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + print('construct: {}'.format(r['gasUsed'])) + + def test_gas_changeperiod(self): period = 43200 for i in range(5): @@ -64,7 +72,155 @@ class Test(unittest.TestCase): tx_hash = contract.functions.changePeriod().transact() r = self.w3.eth.getTransactionReceipt(tx_hash) - print('{} ({}): {}'.format(i, 60 * (10 ** i), r['gasUsed'])) + print('changePeriod {} ({}): {}'.format(i, 60 * (10 ** i), r['gasUsed'])) + + + def test_mint(self): + period = 10 + c = self.w3.eth.contract(abi=self.abi, bytecode=self.bytecode) + tx_hash = c.constructor('Foo Token', 'FOO', 6, TAX_LEVEL * (10 ** 32), period, self.sink_address).transact({'from': self.w3.eth.accounts[0]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + contract = self.w3.eth.contract(abi=self.abi, address=r.contractAddress) + + start_block = self.w3.eth.blockNumber + b = self.w3.eth.getBlock(start_block) + start_time = b['timestamp'] + + tx_hash = contract.functions.mintTo(self.w3.eth.accounts[1], 1000000).transact({'from': self.w3.eth.accounts[0]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + print ('mintTo: {}'.format(r['gasUsed'])) + + + def test_transfer(self): + period = 10 + c = self.w3.eth.contract(abi=self.abi, bytecode=self.bytecode) + tx_hash = c.constructor('Foo Token', 'FOO', 6, TAX_LEVEL * (10 ** 32), period, self.sink_address).transact({'from': self.w3.eth.accounts[0]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + contract = self.w3.eth.contract(abi=self.abi, address=r.contractAddress) + + start_block = self.w3.eth.blockNumber + b = self.w3.eth.getBlock(start_block) + start_time = b['timestamp'] + + contract.functions.mintTo(self.w3.eth.accounts[1], 1000000).transact({'from': self.w3.eth.accounts[0]}) + + tx_hash = contract.functions.transfer(self.w3.eth.accounts[2], 1000000).transact({'from': self.w3.eth.accounts[1]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + print ('transfer: {}'.format(r['gasUsed'])) + + + def test_approve(self): + period = 10 + c = self.w3.eth.contract(abi=self.abi, bytecode=self.bytecode) + tx_hash = c.constructor('Foo Token', 'FOO', 6, TAX_LEVEL * (10 ** 32), period, self.sink_address).transact({'from': self.w3.eth.accounts[0]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + contract = self.w3.eth.contract(abi=self.abi, address=r.contractAddress) + + start_block = self.w3.eth.blockNumber + b = self.w3.eth.getBlock(start_block) + start_time = b['timestamp'] + + contract.functions.mintTo(self.w3.eth.accounts[1], 1000000).transact({'from': self.w3.eth.accounts[0]}) + + tx_hash = contract.functions.approve(self.w3.eth.accounts[2], 1000000).transact({'from': self.w3.eth.accounts[1]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + print ('approve: {}'.format(r['gasUsed'])) + + + def test_transferfrom(self): + period = 10 + c = self.w3.eth.contract(abi=self.abi, bytecode=self.bytecode) + tx_hash = c.constructor('Foo Token', 'FOO', 6, TAX_LEVEL * (10 ** 32), period, self.sink_address).transact({'from': self.w3.eth.accounts[0]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + contract = self.w3.eth.contract(abi=self.abi, address=r.contractAddress) + + start_block = self.w3.eth.blockNumber + b = self.w3.eth.getBlock(start_block) + start_time = b['timestamp'] + + contract.functions.mintTo(self.w3.eth.accounts[1], 1000000).transact({'from': self.w3.eth.accounts[0]}) + + contract.functions.approve(self.w3.eth.accounts[2], 1000000).transact({'from': self.w3.eth.accounts[1]}) + + tx_hash = contract.functions.transferFrom(self.w3.eth.accounts[1], self.w3.eth.accounts[3], 1000000).transact({'from': self.w3.eth.accounts[2]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + print ('transferFrom: {}'.format(r['gasUsed'])) + + + def test_redistribute_default(self): + period = 10 + c = self.w3.eth.contract(abi=self.abi, bytecode=self.bytecode) + tx_hash = c.constructor('Foo Token', 'FOO', 6, TAX_LEVEL * (10 ** 32), period, self.sink_address).transact({'from': self.w3.eth.accounts[0]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + contract = self.w3.eth.contract(abi=self.abi, address=r.contractAddress) + + start_block = self.w3.eth.blockNumber + b = self.w3.eth.getBlock(start_block) + start_time = b['timestamp'] + + for i in range(100): + addr = web3.Web3.toChecksumAddress('0x' + os.urandom(20).hex()) + contract.functions.mintTo(addr, 1000000 * (i+1)).transact({'from': self.w3.eth.accounts[0]}) + + self.eth_tester.time_travel(start_time + period * 60 + 1) + redistribution = contract.functions.redistributions(0).call() + tx_hash = contract.functions.changePeriod().transact({'from': self.w3.eth.accounts[2]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + print ('chainPeriod -> defaultRedistribution: {}'.format(r['gasUsed'])) + + + def test_redistribution_account(self): + period = 10 + c = self.w3.eth.contract(abi=self.abi, bytecode=self.bytecode) + tx_hash = c.constructor('Foo Token', 'FOO', 6, TAX_LEVEL * (10 ** 32), period, self.sink_address).transact({'from': self.w3.eth.accounts[0]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + contract = self.w3.eth.contract(abi=self.abi, address=r.contractAddress) + + start_block = self.w3.eth.blockNumber + b = self.w3.eth.getBlock(start_block) + start_time = b['timestamp'] + + contract.functions.mintTo(self.w3.eth.accounts[1], 1000000).transact({'from': self.w3.eth.accounts[0]}) + contract.functions.transfer(self.w3.eth.accounts[2], 1000000).transact({'from': self.w3.eth.accounts[1]}) + + for i in range(100): + addr = web3.Web3.toChecksumAddress('0x' + os.urandom(20).hex()) + contract.functions.mintTo(addr, 1000000 * (i+1)).transact({'from': self.w3.eth.accounts[0]}) + + self.eth_tester.time_travel(start_time + period * 60 + 1) + redistribution = contract.functions.redistributions(0).call() + tx_hash = contract.functions.applyRedistributionOnAccount(self.w3.eth.accounts[1]).transact({'from': self.w3.eth.accounts[2]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + self.assertEqual(r.logs[0].topics[0].hex(), '0x9a2a887706623ad3ff7fc85652deeceabe9fe1e00466c597972079ee91ea40d3') + print ('redistribute account: {}'.format(r['gasUsed'])) + + + def test_redistribution_account_transfer(self): + period = 10 + c = self.w3.eth.contract(abi=self.abi, bytecode=self.bytecode) + tx_hash = c.constructor('Foo Token', 'FOO', 6, TAX_LEVEL * (10 ** 32), period, self.sink_address).transact({'from': self.w3.eth.accounts[0]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + contract = self.w3.eth.contract(abi=self.abi, address=r.contractAddress) + + start_block = self.w3.eth.blockNumber + b = self.w3.eth.getBlock(start_block) + start_time = b['timestamp'] + + contract.functions.mintTo(self.w3.eth.accounts[1], 2000000).transact({'from': self.w3.eth.accounts[0]}) + contract.functions.transfer(self.w3.eth.accounts[2], 1000000).transact({'from': self.w3.eth.accounts[1]}) + + for i in range(10): + addr = web3.Web3.toChecksumAddress('0x' + os.urandom(20).hex()) + contract.functions.mintTo(addr, 1000000 * (i+1)).transact({'from': self.w3.eth.accounts[0]}) + + self.eth_tester.time_travel(start_time + period * 60 + 1) + redistribution = contract.functions.redistributions(0).call() + contract.functions.changePeriod().transact({'from': self.w3.eth.accounts[0]}) + tx_hash = contract.functions.transfer(self.w3.eth.accounts[3], 100000).transact({'from': self.w3.eth.accounts[1]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + self.assertEqual(r.logs[0].topics[0].hex(), '0x9a2a887706623ad3ff7fc85652deeceabe9fe1e00466c597972079ee91ea40d3') + print ('redistribute account: {}'.format(r['gasUsed'])) + if __name__ == '__main__': unittest.main() diff --git a/solidity/RedistributedDemurrageToken.sol b/solidity/RedistributedDemurrageToken.sol index 0508180..59fb6c8 100644 --- a/solidity/RedistributedDemurrageToken.sol +++ b/solidity/RedistributedDemurrageToken.sol @@ -30,6 +30,7 @@ contract RedistributedDemurrageToken { event Mint(address indexed _minter, address indexed _beneficiary, uint256 _value); event Debug(bytes32 _foo); event Decayed(uint256 indexed _period, uint256 indexed _periodCount, uint256 indexed _oldAmount, uint256 _newAmount); + event Period(uint256 _period); event Redistribution(address indexed _account, uint256 indexed _period, uint256 _value); constructor(string memory _name, string memory _symbol, uint8 _decimals, uint256 _taxLevelMinute, uint256 _periodMinutes, address _defaultSinkAddress) public { @@ -344,6 +345,7 @@ contract RedistributedDemurrageToken { uint256 nextRedistributionDemurrage; uint256 demurrageCounts; uint256 periodTimestamp; + uint256 nextPeriod; currentRedistribution = checkPeriod(); if (currentRedistribution == bytes32(0x00)) { @@ -351,6 +353,7 @@ contract RedistributedDemurrageToken { } currentPeriod = toRedistributionPeriod(currentRedistribution); + nextPeriod = currentPeriod + 1; periodTimestamp = getPeriodTimeDelta(currentPeriod); applyDemurrage(); @@ -363,8 +366,7 @@ contract RedistributedDemurrageToken { nextRedistributionDemurrage = currentDemurrageAmount / ppmDivider; } - nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply, currentPeriod + 1); - emit Debug(bytes32(currentDemurrageAmount)); + nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply, nextPeriod); redistributions.push(nextRedistribution); currentParticipants = toRedistributionParticipants(currentRedistribution); @@ -374,6 +376,7 @@ contract RedistributedDemurrageToken { 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; } @@ -416,6 +419,7 @@ contract RedistributedDemurrageToken { uint256 baseValue; uint256 value; uint256 period; + uint256 demurrage; period = accountPeriod(_account); if (period == 0 || period >= actualPeriod()) { @@ -428,8 +432,9 @@ contract RedistributedDemurrageToken { } supply = toRedistributionSupply(periodRedistribution); + demurrage = toRedistributionDemurrageModifier(periodRedistribution); baseValue = ((supply / participants) * (taxLevel / 1000000)) / ppmDivider; - value = decayBy(baseValue, period - 1); + value = (baseValue * demurrage) / 1000000; account[_account] &= bytes32(0xffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffffff); increaseBaseBalance(_account, value); @@ -467,7 +472,7 @@ contract RedistributedDemurrageToken { // TODO: Prefer to truncate the result, instead it seems to round to nearest :/ baseValue = toBaseAmount(_value); result = transferBase(msg.sender, _to, baseValue); - + emit Transfer(msg.sender, _to, _value); return result; } @@ -484,6 +489,7 @@ contract RedistributedDemurrageToken { require(allowance[_from][msg.sender] >= baseValue); result = transferBase(_from, _to, baseValue); + emit Transfer(_from, _to, _value); return result; }