erc20-demurrage-token

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

commit 179e25adff67c0ff7dd5f710db478d80e9b68024
parent d795a77deb96e9b2dab9c03e622e8ac62c6025ce
Author: nolash <dev@holbrook.no>
Date:   Fri,  5 Feb 2021 09:22:36 +0100

Add approve, transferfrom

Diffstat:
Mpython/tests/test_basic.py | 33+++++++++++++++++++++++++++------
Msolidity/RedistributedDemurrageToken.sol | 42++++++++++++++++++++++++++++++++++++++----
2 files changed, 65 insertions(+), 10 deletions(-)

diff --git a/python/tests/test_basic.py b/python/tests/test_basic.py @@ -45,7 +45,7 @@ class Test(unittest.TestCase): provider = web3.Web3.EthereumTesterProvider(self.eth_tester) self.w3 = web3.Web3(provider) c = self.w3.eth.contract(abi=self.abi, bytecode=self.bytecode) - tx_hash = c.constructor('Foo Token', 'FOO', TAX_LEVEL, PERIOD).transact({'from': self.w3.eth.accounts[0]}) + tx_hash = c.constructor('Foo Token', 'FOO', 6, TAX_LEVEL, PERIOD).transact({'from': self.w3.eth.accounts[0]}) r = self.w3.eth.getTransactionReceipt(tx_hash) self.contract = self.w3.eth.contract(abi=self.abi, address=r.contractAddress) @@ -56,14 +56,12 @@ class Test(unittest.TestCase): pass - @unittest.skip('test') def test_hello(self): self.assertEqual(self.contract.functions.actualPeriod().call(), 1) self.eth_tester.mine_blocks(PERIOD) self.assertEqual(self.contract.functions.actualPeriod().call(), 2) - @unittest.skip('test') def test_mint(self): tx_hash = self.contract.functions.mintTo(self.w3.eth.accounts[1], 1024).transact() r = self.w3.eth.getTransactionReceipt(tx_hash) @@ -101,7 +99,32 @@ class Test(unittest.TestCase): self.assertEqual(r.status, 1) logg.debug('tx {}'.format(r)) - @unittest.skip('test') + + def test_transfer_from(self): + tx_hash = self.contract.functions.mintTo(self.w3.eth.accounts[1], 1024).transact() + r = self.w3.eth.getTransactionReceipt(tx_hash) + self.assertEqual(r.status, 1) + + tx_hash = self.contract.functions.approve(self.w3.eth.accounts[2], 500).transact({'from': self.w3.eth.accounts[1]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + self.assertEqual(r.status, 1) + logg.debug('tx {}'.format(r)) + + balance_alice = self.contract.functions.balanceOf(self.w3.eth.accounts[1]).call() + self.assertEqual(balance_alice, 1024) + + tx_hash = self.contract.functions.transferFrom(self.w3.eth.accounts[1], self.w3.eth.accounts[3], 500).transact({'from': self.w3.eth.accounts[2]}) + r = self.w3.eth.getTransactionReceipt(tx_hash) + self.assertEqual(r.status, 1) + logg.debug('tx {}'.format(r)) + + balance_alice = self.contract.functions.balanceOf(self.w3.eth.accounts[1]).call() + self.assertEqual(balance_alice, 524) + + balance_alice = self.contract.functions.balanceOf(self.w3.eth.accounts[3]).call() + self.assertEqual(balance_alice, 500) + + def test_apply_tax(self): self.eth_tester.mine_blocks(PERIOD) tx_hash = self.contract.functions.applyTax().transact() @@ -116,7 +139,6 @@ class Test(unittest.TestCase): self.assertEqual(self.contract.functions.demurrageModifier().call(), 960400) - @unittest.skip('test') def test_tax_balance(self): tx_hash = self.contract.functions.mintTo(self.w3.eth.accounts[1], 1000).transact() r = self.w3.eth.getTransactionReceipt(tx_hash) @@ -131,7 +153,6 @@ class Test(unittest.TestCase): self.assertEqual(balance, 980) - @unittest.skip('test') def test_taxed_transfer(self): tx_hash = self.contract.functions.mintTo(self.w3.eth.accounts[1], 1000000).transact() r = self.w3.eth.getTransactionReceipt(tx_hash) diff --git a/solidity/RedistributedDemurrageToken.sol b/solidity/RedistributedDemurrageToken.sol @@ -17,8 +17,9 @@ contract RedistributedDemurrageToken { uint256 public demurrageModifier; // PPM bytes32[] public redistributions; // uint1(usedDustSink) | uint1(isFractional) | uint38(participants) | uint160(value) | uint56(period) - mapping (address => bytes32) account; // uint12(period) | uint160(value) + mapping (address => bytes32) account; // uint20(unused) | uint56(period) | uint160(value) mapping (address => bool) minter; + mapping (address => mapping (address => uint256 ) ) allowance; // holder -> spender -> amount (amount is subject to demurrage) event Transfer(address indexed _from, address indexed _to, uint256 _value); event Approval(address indexed _owner, address indexed _spender, uint256 _value); @@ -27,7 +28,7 @@ contract RedistributedDemurrageToken { event Taxed(uint256 indexed _period); event Redistribution(address indexed _account, uint256 indexed _period, uint256 _value); - constructor(string memory _name, string memory _symbol, uint32 _taxLevel, uint256 _period) { + constructor(string memory _name, string memory _symbol, uint8 _decimals, uint32 _taxLevel, uint256 _period) { owner = msg.sender; minter[owner] = true; periodStart = block.number; @@ -35,7 +36,7 @@ contract RedistributedDemurrageToken { taxLevel = _taxLevel; name = _name; symbol = _symbol; - decimals = 6; + decimals = _decimals; demurrageModifier = 1000000; bytes32 initialRedistribution = toRedistribution(0, 0, 1); redistributions.push(initialRedistribution); @@ -250,6 +251,11 @@ contract RedistributedDemurrageToken { return true; } + // Inflates the given amount according to the current demurrage modifier + function toBaseAmount(uint256 _value) public view returns (uint256) { + return (_value * 1000000) / demurrageModifier; + } + // ERC20, triggers tax and/or redistribution function transfer(address _to, uint256 _value) public returns (bool) { uint256 baseValue; @@ -259,7 +265,7 @@ contract RedistributedDemurrageToken { applyRedistributionOnAccount(msg.sender); // TODO: Prefer to truncate the result, instead it seems to round to nearest :/ - baseValue = (_value * 1000000) / demurrageModifier; + baseValue = toBaseAmount(_value); result = transferBase(msg.sender, _to, baseValue); return result; @@ -281,4 +287,32 @@ contract RedistributedDemurrageToken { } return true; } + + // ERC20, triggers tax and/or redistribution + function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { + uint256 baseValue; + bool result; + + applyTax(); + applyRedistributionOnAccount(msg.sender); + + baseValue = toBaseAmount(_value); + require(allowance[_from][msg.sender] >= baseValue); + + result = transferBase(_from, _to, baseValue); + return result; + } + + // ERC20, triggers tax and/or redistribution + function approve(address _spender, uint256 _value) public returns (bool) { + uint256 baseValue; + + applyTax(); + applyRedistributionOnAccount(msg.sender); + + baseValue = toBaseAmount(_value); + allowance[msg.sender][_spender] += baseValue; + emit Approval(msg.sender, _spender, _value); + return true; + } }