mirror of
git://holbrook.no/erc20-demurrage-token
synced 2024-11-25 01:16:45 +01:00
Return redistributions type correctly
This commit is contained in:
parent
5941a6abdf
commit
84b1a5b439
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -9,8 +9,7 @@ from chainlib.eth.constant import ZERO_ADDRESS
|
||||
# local imports
|
||||
from .token import DemurrageToken
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logg = logging.getLogger()
|
||||
logg = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class DemurrageCalculator:
|
||||
|
@ -10,6 +10,7 @@ from chainlib.eth.tx import (
|
||||
from chainlib.hash import keccak256_string_to_hex
|
||||
from chainlib.eth.contract import (
|
||||
ABIContractEncoder,
|
||||
ABIContractDecoder,
|
||||
ABIContractType,
|
||||
abi_decode_single,
|
||||
)
|
||||
@ -117,6 +118,34 @@ class DemurrageToken(ERC20):
|
||||
return DemurrageToken.__bytecode[name]
|
||||
|
||||
|
||||
def increase_allowance(self, contract_address, sender_address, address, value, tx_format=TxFormat.JSONRPC):
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('increaseAllowance')
|
||||
enc.typ(ABIContractType.ADDRESS)
|
||||
enc.typ(ABIContractType.UINT256)
|
||||
enc.address(address)
|
||||
enc.uint256(value)
|
||||
data = enc.get()
|
||||
tx = self.template(sender_address, contract_address, use_nonce=True)
|
||||
tx = self.set_code(tx, data)
|
||||
tx = self.finalize(tx, tx_format)
|
||||
return tx
|
||||
|
||||
|
||||
def decrease_allowance(self, contract_address, sender_address, address, value, tx_format=TxFormat.JSONRPC):
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('decreaseAllowance')
|
||||
enc.typ(ABIContractType.ADDRESS)
|
||||
enc.typ(ABIContractType.UINT256)
|
||||
enc.address(address)
|
||||
enc.uint256(value)
|
||||
data = enc.get()
|
||||
tx = self.template(sender_address, contract_address, use_nonce=True)
|
||||
tx = self.set_code(tx, data)
|
||||
tx = self.finalize(tx, tx_format)
|
||||
return tx
|
||||
|
||||
|
||||
def add_minter(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC):
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('addMinter')
|
||||
@ -155,6 +184,33 @@ class DemurrageToken(ERC20):
|
||||
return tx
|
||||
|
||||
|
||||
def burn(self, contract_address, sender_address, value, tx_format=TxFormat.JSONRPC):
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('burn')
|
||||
enc.typ(ABIContractType.UINT256)
|
||||
enc.uint256(value)
|
||||
data = enc.get()
|
||||
tx = self.template(sender_address, contract_address, use_nonce=True)
|
||||
tx = self.set_code(tx, data)
|
||||
tx = self.finalize(tx, tx_format)
|
||||
return tx
|
||||
|
||||
|
||||
def total_burned(self, contract_address, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('totalBurned')
|
||||
data = add_0x(enc.get())
|
||||
tx = self.template(sender_address, contract_address)
|
||||
tx = self.set_code(tx, data)
|
||||
o['params'].append(self.normalize(tx))
|
||||
o['params'].append('latest')
|
||||
o = j.finalize(o)
|
||||
return o
|
||||
|
||||
|
||||
def to_base_amount(self, contract_address, value, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
@ -255,8 +311,11 @@ class DemurrageToken(ERC20):
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('toRedistributionPeriod')
|
||||
enc.typ(ABIContractType.BYTES32)
|
||||
enc.bytes32(redistribution)
|
||||
v = strip_0x(redistribution)
|
||||
enc.typ_literal('(uint32,uint72,uint104)')
|
||||
enc.bytes32(v[:64])
|
||||
enc.bytes32(v[64:128])
|
||||
enc.bytes32(v[128:192])
|
||||
data = add_0x(enc.get())
|
||||
tx = self.template(sender_address, contract_address)
|
||||
tx = self.set_code(tx, data)
|
||||
@ -266,22 +325,26 @@ class DemurrageToken(ERC20):
|
||||
return o
|
||||
|
||||
|
||||
def to_redistribution_participants(self, contract_address, redistribution, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('toRedistributionParticipants')
|
||||
enc.typ(ABIContractType.BYTES32)
|
||||
enc.bytes32(redistribution)
|
||||
data = add_0x(enc.get())
|
||||
tx = self.template(sender_address, contract_address)
|
||||
tx = self.set_code(tx, data)
|
||||
o['params'].append(self.normalize(tx))
|
||||
o['params'].append('latest')
|
||||
o = j.finalize(o)
|
||||
return o
|
||||
|
||||
# def to_redistribution_participants(self, contract_address, redistribution, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
# j = JSONRPCRequest(id_generator)
|
||||
# o = j.template()
|
||||
# o['method'] = 'eth_call'
|
||||
# enc = ABIContractEncoder()
|
||||
# enc.method('toRedistributionParticipants')
|
||||
# v = strip_0x(redistribution)
|
||||
# enc.typ_literal('(uint32,uint72,uint104)')
|
||||
# #enc.typ(ABIContractType.BYTES32)
|
||||
# enc.bytes32(v[:64])
|
||||
# enc.bytes32(v[64:128])
|
||||
# enc.bytes32(v[128:192])
|
||||
# data = add_0x(enc.get())
|
||||
# tx = self.template(sender_address, contract_address)
|
||||
# tx = self.set_code(tx, data)
|
||||
# o['params'].append(self.normalize(tx))
|
||||
# o['params'].append('latest')
|
||||
# o = j.finalize(o)
|
||||
# return o
|
||||
#
|
||||
|
||||
def to_redistribution_supply(self, contract_address, redistribution, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
@ -289,8 +352,11 @@ class DemurrageToken(ERC20):
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('toRedistributionSupply')
|
||||
enc.typ(ABIContractType.BYTES32)
|
||||
enc.bytes32(redistribution)
|
||||
v = strip_0x(redistribution)
|
||||
enc.typ_literal('(uint32,uint72,uint104)')
|
||||
enc.bytes32(v[:64])
|
||||
enc.bytes32(v[64:128])
|
||||
enc.bytes32(v[128:192])
|
||||
data = add_0x(enc.get())
|
||||
tx = self.template(sender_address, contract_address)
|
||||
tx = self.set_code(tx, data)
|
||||
@ -306,8 +372,11 @@ class DemurrageToken(ERC20):
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('toRedistributionDemurrageModifier')
|
||||
enc.typ(ABIContractType.BYTES32)
|
||||
enc.bytes32(redistribution)
|
||||
v = strip_0x(redistribution)
|
||||
enc.typ_literal('(uint32,uint72,uint104)')
|
||||
enc.bytes32(v[:64])
|
||||
enc.bytes32(v[64:128])
|
||||
enc.bytes32(v[128:192])
|
||||
data = add_0x(enc.get())
|
||||
tx = self.template(sender_address, contract_address)
|
||||
tx = self.set_code(tx, data)
|
||||
@ -397,24 +466,24 @@ class DemurrageToken(ERC20):
|
||||
return self.call_noarg('supplyCap', contract_address, sender_address=sender_address)
|
||||
|
||||
|
||||
def grow_by(self, contract_address, value, period, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
o = j.template()
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('growBy')
|
||||
enc.typ(ABIContractType.UINT256)
|
||||
enc.typ(ABIContractType.UINT256)
|
||||
enc.uint256(value)
|
||||
enc.uint256(period)
|
||||
data = add_0x(enc.get())
|
||||
tx = self.template(sender_address, contract_address)
|
||||
tx = self.set_code(tx, data)
|
||||
o['params'].append(self.normalize(tx))
|
||||
o['params'].append('latest')
|
||||
o = j.finalize(o)
|
||||
return o
|
||||
|
||||
# def grow_by(self, contract_address, value, period, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
# j = JSONRPCRequest(id_generator)
|
||||
# o = j.template()
|
||||
# o['method'] = 'eth_call'
|
||||
# enc = ABIContractEncoder()
|
||||
# enc.method('growBy')
|
||||
# enc.typ(ABIContractType.UINT256)
|
||||
# enc.typ(ABIContractType.UINT256)
|
||||
# enc.uint256(value)
|
||||
# enc.uint256(period)
|
||||
# data = add_0x(enc.get())
|
||||
# tx = self.template(sender_address, contract_address)
|
||||
# tx = self.set_code(tx, data)
|
||||
# o['params'].append(self.normalize(tx))
|
||||
# o['params'].append('latest')
|
||||
# o = j.finalize(o)
|
||||
# return o
|
||||
#
|
||||
|
||||
def decay_by(self, contract_address, value, period, sender_address=ZERO_ADDRESS, id_generator=None):
|
||||
j = JSONRPCRequest(id_generator)
|
||||
@ -460,8 +529,11 @@ class DemurrageToken(ERC20):
|
||||
o['method'] = 'eth_call'
|
||||
enc = ABIContractEncoder()
|
||||
enc.method('getDistributionFromRedistribution')
|
||||
enc.typ(ABIContractType.BYTES32)
|
||||
enc.bytes32(redistribution)
|
||||
v = strip_0x(redistribution)
|
||||
enc.typ_literal('(uint32,uint72,uint104)')
|
||||
enc.bytes32(v[:64])
|
||||
enc.bytes32(v[64:128])
|
||||
enc.bytes32(v[128:192])
|
||||
data = add_0x(enc.get())
|
||||
tx = self.template(sender_address, contract_address)
|
||||
tx = self.set_code(tx, data)
|
||||
@ -504,7 +576,16 @@ class DemurrageToken(ERC20):
|
||||
|
||||
@classmethod
|
||||
def parse_redistributions(self, v):
|
||||
return abi_decode_single(ABIContractType.BYTES32, v)
|
||||
d = ABIContractDecoder()
|
||||
v = strip_0x(v)
|
||||
d.typ(ABIContractType.BYTES32)
|
||||
d.typ(ABIContractType.BYTES32)
|
||||
d.typ(ABIContractType.BYTES32)
|
||||
d.val(v[:64])
|
||||
d.val(v[64:128])
|
||||
d.val(v[128:192])
|
||||
r = d.decode()
|
||||
return ''.join(r)
|
||||
|
||||
|
||||
@classmethod
|
||||
@ -526,6 +607,7 @@ class DemurrageToken(ERC20):
|
||||
def parse_supply_cap(self, v):
|
||||
return abi_decode_single(ABIContractType.UINT256, v)
|
||||
|
||||
|
||||
@classmethod
|
||||
def parse_grow_by(self, v):
|
||||
return abi_decode_single(ABIContractType.UINT256, v)
|
||||
@ -549,3 +631,8 @@ class DemurrageToken(ERC20):
|
||||
@classmethod
|
||||
def parse_resolution_factor(self, v):
|
||||
return abi_decode_single(ABIContractType.UINT256, v)
|
||||
|
||||
|
||||
@classmethod
|
||||
def parse_total_burned(self, v):
|
||||
return abi_decode_single(ABIContractType.UINT256, v)
|
||||
|
@ -6,7 +6,8 @@ set -e
|
||||
export PYTHONPATH=.
|
||||
|
||||
#modes=(MultiNocap MultiCap SingleCap SingleNocap)
|
||||
modes=(SingleCap SingleNocap) # other contracts need to be updted
|
||||
#modes=(SingleCap SingleNocap) # other contracts need to be updted
|
||||
modes=(SingleNocap) # other contracts need to be updted
|
||||
for m in ${modes[@]}; do
|
||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_basic.py
|
||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_growth.py
|
||||
@ -14,7 +15,8 @@ for m in ${modes[@]}; do
|
||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_single.py
|
||||
done
|
||||
|
||||
modes=(SingleCap) # other contracts need to be updted
|
||||
#modes=(SingleCap) # other contracts need to be updted
|
||||
modes=()
|
||||
for m in ${modes[@]}; do
|
||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_period.py
|
||||
done
|
||||
@ -25,7 +27,8 @@ for m in ${modes[@]}; do
|
||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_redistribution_single.py
|
||||
done
|
||||
|
||||
modes=(MultiCap SingleCap)
|
||||
#modes=(MultiCap SingleCap)
|
||||
modes=()
|
||||
for m in ${modes[@]}; do
|
||||
ERC20_DEMURRAGE_TOKEN_TEST_MODE=$m python tests/test_cap.py
|
||||
done
|
||||
|
@ -277,6 +277,51 @@ class TestBasic(TestDemurrageDefault):
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
|
||||
def test_approve(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.approve(self.address, self.accounts[0], self.accounts[1], 500)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 600)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 0)
|
||||
|
||||
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 0)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 600)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
(tx_hash, o) = c.increase_allowance(self.address, self.accounts[0], self.accounts[1], 200)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
(tx_hash, o) = c.decrease_allowance(self.address, self.accounts[0], self.accounts[1], 800)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
(tx_hash, o) = c.approve(self.address, self.accounts[0], self.accounts[1], 42)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 1)
|
||||
|
||||
|
||||
def test_transfer_from(self):
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
@ -315,6 +360,12 @@ class TestBasic(TestDemurrageDefault):
|
||||
balance = c.parse_balance_of(r)
|
||||
self.assertEqual(balance, 500)
|
||||
|
||||
(tx_hash, o) = c.transfer_from(self.address, self.accounts[2], self.accounts[1], self.accounts[3], 1)
|
||||
self.rpc.do(o)
|
||||
o = receipt(tx_hash)
|
||||
r = self.rpc.do(o)
|
||||
self.assertEqual(r['status'], 0)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
@ -28,23 +28,23 @@ testdir = os.path.dirname(__file__)
|
||||
|
||||
class TestGrowth(TestDemurrageDefault):
|
||||
|
||||
def test_grow_by(self):
|
||||
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
|
||||
growth_factor = (1000000 + self.tax_level) / 1000000
|
||||
v = 1000000000
|
||||
o = c.grow_by(self.address, v, 1, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
g = c.parse_grow_by(r)
|
||||
self.assertEqual(int(v * growth_factor), g)
|
||||
|
||||
period = 10
|
||||
growth_factor = (1 + (self.tax_level) / 1000000) ** period
|
||||
o = c.grow_by(self.address, v, period, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
g = c.parse_grow_by(r)
|
||||
self.assertEqual(int(v * growth_factor), g)
|
||||
# def test_grow_by(self):
|
||||
# nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
|
||||
# c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
|
||||
#
|
||||
# growth_factor = (1000000 + self.tax_level) / 1000000
|
||||
# v = 1000000000
|
||||
# o = c.grow_by(self.address, v, 1, sender_address=self.accounts[0])
|
||||
# r = self.rpc.do(o)
|
||||
# g = c.parse_grow_by(r)
|
||||
# self.assertEqual(int(v * growth_factor), g)
|
||||
#
|
||||
# period = 10
|
||||
# growth_factor = (1 + (self.tax_level) / 1000000) ** period
|
||||
# o = c.grow_by(self.address, v, period, sender_address=self.accounts[0])
|
||||
# r = self.rpc.do(o)
|
||||
# g = c.parse_grow_by(r)
|
||||
# self.assertEqual(int(v * growth_factor), g)
|
||||
|
||||
|
||||
def test_decay_by(self):
|
||||
@ -63,7 +63,7 @@ class TestGrowth(TestDemurrageDefault):
|
||||
o = c.decay_by(self.address, v, period, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
g = c.parse_decay_by(r)
|
||||
self.assertEqual(int(v * growth_factor), g)
|
||||
self.assertEqual(int(v * growth_factor), g)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
@ -24,7 +24,7 @@ from erc20_demurrage_token import DemurrageToken
|
||||
# test imports
|
||||
from erc20_demurrage_token.unittest.base import TestDemurrageDefault
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logg = logging.getLogger()
|
||||
|
||||
testdir = os.path.dirname(__file__)
|
||||
@ -88,18 +88,31 @@ class TestRedistribution(TestDemurrageDefault):
|
||||
(tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[0], supply)
|
||||
self.rpc.do(o)
|
||||
|
||||
self.backend.time_travel(self.start_time + (self.period_seconds * 10))
|
||||
self.backend.time_travel(self.start_time + (self.period_seconds * 100))
|
||||
|
||||
for i in range(1, 11):
|
||||
logg.debug('checking period {}'.format(i))
|
||||
balance_minter = None
|
||||
balance_sink = None
|
||||
real_supply = None
|
||||
|
||||
for i in range(1, 101):
|
||||
(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)
|
||||
|
||||
i = 10
|
||||
o = c.balance_of(self.address, self.sink_address, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance_sink = c.parse_balance(r)
|
||||
|
||||
o = c.balance_of(self.address, self.accounts[0], sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
balance_minter = c.parse_balance(r)
|
||||
|
||||
real_supply = balance_sink + balance_minter
|
||||
logg.info('period {} testing sink {} mint {} adds up to supply {} of original {} (delta {})'.format(i, balance_sink, balance_minter, real_supply, supply, supply - real_supply))
|
||||
|
||||
i = 100
|
||||
o = c.redistributions(self.address, i, sender_address=self.accounts[0])
|
||||
redistribution = self.rpc.do(o)
|
||||
|
||||
@ -122,7 +135,7 @@ class TestRedistribution(TestDemurrageDefault):
|
||||
r = self.rpc.do(o)
|
||||
balance_minter = c.parse_balance(r)
|
||||
|
||||
logg.debug('testing sink {} mint {} adds up to supply {} with demurrage between {} and {}'.format(balance_sink, balance_minter, supply, demurrage_previous, demurrage))
|
||||
logg.debug('testing sink {} mint {} adds up to supply {} with demurrage between {} and {}'.format(balance_sink, balance_minter, real_supply, demurrage_previous, demurrage))
|
||||
|
||||
self.assert_within_lower(balance_minter + balance_sink, supply, 0.001)
|
||||
|
||||
|
@ -177,6 +177,7 @@ class TestRedistribution(TestDemurrageUnit):
|
||||
|
||||
o = c.redistributions(self.address, 0, sender_address=self.accounts[0])
|
||||
redistribution = self.rpc.do(o)
|
||||
logg.debug('redistribution {}'.format(redistribution))
|
||||
o = c.to_redistribution_supply(self.address, redistribution, sender_address=self.accounts[0])
|
||||
r = self.rpc.do(o)
|
||||
supply = c.parse_to_redistribution_item(r)
|
||||
|
@ -1,18 +1,24 @@
|
||||
pragma solidity > 0.6.11;
|
||||
pragma solidity >= 0.8.0;
|
||||
|
||||
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
contract DemurrageTokenSingleCap {
|
||||
|
||||
struct redistributionItem {
|
||||
uint32 period;
|
||||
uint72 value;
|
||||
uint104 demurrage;
|
||||
}
|
||||
redistributionItem[] public redistributions; // uint51(unused) | uint64(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period)
|
||||
// Redistribution bit field, with associated shifts and masks
|
||||
// (Uses sub-byte boundaries)
|
||||
bytes32[] public redistributions; // uint51(unused) | uint64(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period)
|
||||
uint8 constant shiftRedistributionPeriod = 0;
|
||||
uint256 constant maskRedistributionPeriod = 0x00000000000000000000000000000000000000000000000000000000ffffffff; // (1 << 32) - 1
|
||||
uint8 constant shiftRedistributionValue = 32;
|
||||
uint256 constant maskRedistributionValue = 0x00000000000000000000000000000000000000ffffffffffffffffff00000000; // ((1 << 72) - 1) << 32
|
||||
uint8 constant shiftRedistributionDemurrage = 104;
|
||||
uint256 constant maskRedistributionDemurrage = 0x0000000000ffffffffffffffffffffffffffff00000000000000000000000000; // ((1 << 20) - 1) << 140
|
||||
// bytes32[] public redistributions; // uint51(unused) | uint64(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period)
|
||||
// uint8 constant shiftRedistributionPeriod = 0;
|
||||
// uint256 constant maskRedistributionPeriod = 0x00000000000000000000000000000000000000000000000000000000ffffffff; // (1 << 32) - 1
|
||||
// uint8 constant shiftRedistributionValue = 32;
|
||||
// uint256 constant maskRedistributionValue = 0x00000000000000000000000000000000000000ffffffffffffffffff00000000; // ((1 << 72) - 1) << 32
|
||||
// uint8 constant shiftRedistributionDemurrage = 104;
|
||||
// uint256 constant maskRedistributionDemurrage = 0x0000000000ffffffffffffffffffffffffffff00000000000000000000000000; // ((1 << 36) - 1) << 140
|
||||
|
||||
// Account balances
|
||||
mapping (address => uint256) account;
|
||||
@ -38,7 +44,8 @@ contract DemurrageTokenSingleCap {
|
||||
uint256 public decimals;
|
||||
|
||||
// Implements ERC20
|
||||
uint256 public totalSupply;
|
||||
//uint256 public totalSupply;
|
||||
uint256 supply;
|
||||
|
||||
// Last executed period
|
||||
uint256 public lastPeriod;
|
||||
@ -46,6 +53,9 @@ contract DemurrageTokenSingleCap {
|
||||
// Last sink redistribution amount
|
||||
uint256 public totalSink;
|
||||
|
||||
// Value of burnt tokens (burnt tokens do not decay)
|
||||
uint256 public burned;
|
||||
|
||||
// 128 bit resolution of the demurrage divisor
|
||||
// (this constant x 1000000 is contained within 128 bits)
|
||||
uint256 constant nanoDivider = 100000000000000000000000000; // now nanodivider, 6 zeros less
|
||||
@ -95,10 +105,15 @@ contract DemurrageTokenSingleCap {
|
||||
// Temporary event used in development, will be removed on prod
|
||||
event Debug(bytes32 _foo);
|
||||
|
||||
// Emitted when tokens are burned
|
||||
event Burn(address indexed _burner, uint256 _value);
|
||||
|
||||
// EIP173
|
||||
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); // EIP173
|
||||
|
||||
constructor(string memory _name, string memory _symbol, uint8 _decimals, uint128 _taxLevelMinute, uint256 _periodMinutes, address _defaultSinkAddress) public {
|
||||
redistributionItem memory initialRedistribution;
|
||||
|
||||
// ACL setup
|
||||
owner = msg.sender;
|
||||
minter[owner] = true;
|
||||
@ -114,7 +129,7 @@ contract DemurrageTokenSingleCap {
|
||||
periodDuration = _periodMinutes * 60;
|
||||
demurrageAmount = uint128(nanoDivider) * 100;
|
||||
taxLevel = _taxLevelMinute; // Represents 38 decimal places
|
||||
bytes32 initialRedistribution = toRedistribution(0, demurrageAmount, 0, 1);
|
||||
initialRedistribution = toRedistribution(0, demurrageAmount, 0, 1);
|
||||
redistributions.push(initialRedistribution);
|
||||
|
||||
// Misc settings
|
||||
@ -206,7 +221,7 @@ contract DemurrageTokenSingleCap {
|
||||
|
||||
changePeriod();
|
||||
baseAmount = toBaseAmount(_amount);
|
||||
totalSupply += _amount;
|
||||
supply += _amount;
|
||||
increaseBaseBalance(_beneficiary, baseAmount);
|
||||
emit Mint(msg.sender, _beneficiary, _amount);
|
||||
saveRedistributionSupply();
|
||||
@ -215,46 +230,82 @@ contract DemurrageTokenSingleCap {
|
||||
|
||||
// Deserializes the redistribution word
|
||||
// uint95(unused) | uint20(demurrageModifier) | uint36(participants) | uint72(value) | uint32(period)
|
||||
function toRedistribution(uint256 _participants, uint256 _demurrageModifierPpm, uint256 _value, uint256 _period) public pure returns(bytes32) {
|
||||
bytes32 redistribution;
|
||||
// function toRedistribution(uint256 _participants, uint256 _demurrageModifierPpm, uint256 _value, uint256 _period) public pure returns(bytes32) {
|
||||
// bytes32 redistribution;
|
||||
//
|
||||
// redistribution |= bytes32((_demurrageModifierPpm << shiftRedistributionDemurrage) & maskRedistributionDemurrage);
|
||||
// redistribution |= bytes32((_value << shiftRedistributionValue) & maskRedistributionValue);
|
||||
// redistribution |= bytes32(_period & maskRedistributionPeriod);
|
||||
// return redistribution;
|
||||
// }
|
||||
|
||||
redistribution |= bytes32((_demurrageModifierPpm << shiftRedistributionDemurrage) & maskRedistributionDemurrage);
|
||||
redistribution |= bytes32((_value << shiftRedistributionValue) & maskRedistributionValue);
|
||||
redistribution |= bytes32(_period & maskRedistributionPeriod);
|
||||
function toRedistribution(uint256 _participants, uint256 _demurrageModifierPpm, uint256 _value, uint256 _period) public pure returns(redistributionItem memory) {
|
||||
redistributionItem memory redistribution;
|
||||
|
||||
redistribution.period = uint32(_period);
|
||||
redistribution.value = uint72(_value);
|
||||
redistribution.demurrage = uint104(_demurrageModifierPpm);
|
||||
return redistribution;
|
||||
|
||||
}
|
||||
//
|
||||
// // Serializes the demurrage period part of the redistribution word
|
||||
// function toRedistributionPeriod(bytes32 redistribution) public pure returns (uint256) {
|
||||
// return uint256(redistribution) & maskRedistributionPeriod;
|
||||
// }
|
||||
//
|
||||
|
||||
function toRedistributionPeriod(redistributionItem memory _redistribution) public pure returns (uint256) {
|
||||
return uint256(_redistribution.period);
|
||||
}
|
||||
|
||||
// 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) & kkRedistributionValue) >> shiftRedistributionValue;
|
||||
// }
|
||||
|
||||
function toRedistributionSupply(redistributionItem memory _redistribution) public pure returns (uint256) {
|
||||
return uint256(_redistribution.value);
|
||||
}
|
||||
|
||||
// Serializes the supply part of the redistribution word
|
||||
function toRedistributionSupply(bytes32 redistribution) public pure returns (uint256) {
|
||||
return (uint256(redistribution) & maskRedistributionValue) >> shiftRedistributionValue;
|
||||
//
|
||||
// // Serializes the number of participants part of the redistribution word
|
||||
// function toRedistributionDemurrageModifier(bytes32 redistribution) public pure returns (uint256) {
|
||||
// return (uint256(redistribution) & maskRedistributionDemurrage) >> shiftRedistributionDemurrage;
|
||||
// }
|
||||
|
||||
function toRedistributionDemurrageModifier(redistributionItem memory _redistribution) public pure returns (uint256) {
|
||||
return uint256(_redistribution.demurrage);
|
||||
}
|
||||
|
||||
// Serializes the number of participants part of the redistribution word
|
||||
function toRedistributionDemurrageModifier(bytes32 redistribution) public pure returns (uint256) {
|
||||
return (uint256(redistribution) & maskRedistributionDemurrage) >> shiftRedistributionDemurrage;
|
||||
}
|
||||
|
||||
// Client accessor to the redistributions array length
|
||||
function redistributionCount() public view returns (uint256) {
|
||||
return redistributions.length;
|
||||
}
|
||||
|
||||
// Save the current total supply amount to the current redistribution period
|
||||
//
|
||||
// // Save the current total supply amount to the current redistribution period
|
||||
// function saveRedistributionSupply() private returns (bool) {
|
||||
// uint256 currentRedistribution;
|
||||
// uint256 grownSupply;
|
||||
//
|
||||
// grownSupply = totalSupply();
|
||||
// currentRedistribution = uint256(redistributions[redistributions.length-1]);
|
||||
// currentRedistribution &= (~maskRedistributionValue);
|
||||
// currentRedistribution |= (grownSupply << shiftRedistributionValue);
|
||||
//
|
||||
// redistributions[redistributions.length-1] = bytes32(currentRedistribution);
|
||||
// return true;
|
||||
// }
|
||||
function saveRedistributionSupply() private returns (bool) {
|
||||
uint256 currentRedistribution;
|
||||
redistributionItem memory currentRedistribution;
|
||||
uint256 grownSupply;
|
||||
|
||||
grownSupply = totalSupply;
|
||||
currentRedistribution = uint256(redistributions[redistributions.length-1]);
|
||||
currentRedistribution &= (~maskRedistributionValue);
|
||||
currentRedistribution |= (grownSupply << shiftRedistributionValue);
|
||||
grownSupply = totalSupply();
|
||||
currentRedistribution = redistributions[redistributions.length-1];
|
||||
currentRedistribution.value = uint72(grownSupply);
|
||||
|
||||
redistributions[redistributions.length-1] = bytes32(currentRedistribution);
|
||||
redistributions[redistributions.length-1] = currentRedistribution;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -263,15 +314,16 @@ contract DemurrageTokenSingleCap {
|
||||
return uint128((block.timestamp - periodStart) / periodDuration + 1);
|
||||
}
|
||||
|
||||
// Add an entered demurrage period to the redistribution array
|
||||
function checkPeriod() private view returns (bytes32) {
|
||||
bytes32 lastRedistribution;
|
||||
// Retrieve next redistribution if the period threshold has been crossed
|
||||
function checkPeriod() private view returns (redistributionItem memory) {
|
||||
redistributionItem memory lastRedistribution;
|
||||
redistributionItem memory emptyRedistribution;
|
||||
uint256 currentPeriod;
|
||||
|
||||
lastRedistribution = redistributions[lastPeriod];
|
||||
currentPeriod = this.actualPeriod();
|
||||
if (currentPeriod <= toRedistributionPeriod(lastRedistribution)) {
|
||||
return bytes32(0x00);
|
||||
return emptyRedistribution;
|
||||
}
|
||||
return lastRedistribution;
|
||||
}
|
||||
@ -283,7 +335,7 @@ contract DemurrageTokenSingleCap {
|
||||
return difference / resolutionFactor;
|
||||
}
|
||||
|
||||
function getDistributionFromRedistribution(bytes32 _redistribution) public returns (uint256) {
|
||||
function getDistributionFromRedistribution(redistributionItem memory _redistribution) public returns (uint256) {
|
||||
uint256 redistributionSupply;
|
||||
uint256 redistributionDemurrage;
|
||||
|
||||
@ -293,7 +345,7 @@ contract DemurrageTokenSingleCap {
|
||||
}
|
||||
|
||||
// Returns the amount sent to the sink address
|
||||
function applyDefaultRedistribution(bytes32 _redistribution) private returns (uint256) {
|
||||
function applyDefaultRedistribution(redistributionItem memory _redistribution) private returns (uint256) {
|
||||
uint256 unit;
|
||||
uint256 baseUnit;
|
||||
|
||||
@ -348,37 +400,46 @@ contract DemurrageTokenSingleCap {
|
||||
return (block.timestamp - _target) / 60;
|
||||
}
|
||||
|
||||
function isEmptyRedistribution(redistributionItem memory _redistribution) public pure returns(bool) {
|
||||
if (_redistribution.period > 0) {
|
||||
return false;
|
||||
}
|
||||
if (_redistribution.value > 0) {
|
||||
return false;
|
||||
}
|
||||
if (_redistribution.demurrage > 0) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Recalculate the demurrage modifier for the new period
|
||||
// Note that the supply for the consecutive period will be taken at the time of code execution, and thus not necessarily at the time when the redistribution period threshold was crossed.
|
||||
function changePeriod() public returns (bool) {
|
||||
bytes32 currentRedistribution;
|
||||
bytes32 nextRedistribution;
|
||||
redistributionItem memory currentRedistribution;
|
||||
redistributionItem memory nextRedistribution;
|
||||
redistributionItem memory lastRedistribution;
|
||||
uint256 currentPeriod;
|
||||
uint256 currentDemurrageAmount;
|
||||
uint256 lastDemurrageAmount;
|
||||
uint256 nextRedistributionDemurrage;
|
||||
uint256 demurrageCounts;
|
||||
uint256 periodTimestamp;
|
||||
uint256 nextPeriod;
|
||||
|
||||
applyDemurrage();
|
||||
currentRedistribution = checkPeriod();
|
||||
if (currentRedistribution == bytes32(0x00)) {
|
||||
if (isEmptyRedistribution(currentRedistribution)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// calculate the decay from previous redistributino
|
||||
lastRedistribution = redistributions[lastPeriod];
|
||||
currentPeriod = toRedistributionPeriod(currentRedistribution);
|
||||
nextPeriod = currentPeriod + 1;
|
||||
periodTimestamp = getPeriodTimeDelta(currentPeriod);
|
||||
lastDemurrageAmount = toRedistributionDemurrageModifier(lastRedistribution);
|
||||
demurrageCounts = periodDuration / 60;
|
||||
nextRedistributionDemurrage = decayBy(lastDemurrageAmount, demurrageCounts);
|
||||
|
||||
currentDemurrageAmount = demurrageAmount;
|
||||
|
||||
demurrageCounts = demurrageCycles(periodTimestamp);
|
||||
if (demurrageCounts > 0) {
|
||||
nextRedistributionDemurrage = growBy(currentDemurrageAmount, demurrageCounts);
|
||||
} else {
|
||||
nextRedistributionDemurrage = currentDemurrageAmount;
|
||||
}
|
||||
|
||||
nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply, nextPeriod);
|
||||
nextRedistribution = toRedistribution(0, nextRedistributionDemurrage, totalSupply(), nextPeriod);
|
||||
redistributions.push(nextRedistribution);
|
||||
|
||||
applyDefaultRedistribution(nextRedistribution);
|
||||
@ -387,18 +448,18 @@ contract DemurrageTokenSingleCap {
|
||||
}
|
||||
|
||||
// Reverse a value reduced by demurrage by the given period to its original value
|
||||
function growBy(uint256 _value, uint256 _period) public view returns (uint256) {
|
||||
uint256 valueFactor;
|
||||
uint256 truncatedTaxLevel;
|
||||
|
||||
valueFactor = growthResolutionFactor;
|
||||
truncatedTaxLevel = taxLevel / nanoDivider;
|
||||
|
||||
for (uint256 i = 0; i < _period; i++) {
|
||||
valueFactor = valueFactor + ((valueFactor * truncatedTaxLevel) / growthResolutionFactor);
|
||||
}
|
||||
return (valueFactor * _value) / growthResolutionFactor;
|
||||
}
|
||||
// function growBy(uint256 _value, uint256 _period) public view returns (uint256) {
|
||||
// uint256 valueFactor;
|
||||
// uint256 truncatedTaxLevel;
|
||||
//
|
||||
// valueFactor = growthResolutionFactor;
|
||||
// truncatedTaxLevel = taxLevel / nanoDivider;
|
||||
//
|
||||
// for (uint256 i = 0; i < _period; i++) {
|
||||
// valueFactor = valueFactor + ((valueFactor * truncatedTaxLevel) / growthResolutionFactor);
|
||||
// }
|
||||
// return (valueFactor * _value) / growthResolutionFactor;
|
||||
// }
|
||||
|
||||
// Calculate a value reduced by demurrage by the given period
|
||||
function decayBy(uint256 _value, uint256 _period) public view returns (uint256) {
|
||||
@ -423,14 +484,45 @@ contract DemurrageTokenSingleCap {
|
||||
function approve(address _spender, uint256 _value) public returns (bool) {
|
||||
uint256 baseValue;
|
||||
|
||||
if (allowance[msg.sender][_spender] > 0) {
|
||||
require(_value == 0, 'ZERO_FIRST');
|
||||
}
|
||||
|
||||
changePeriod();
|
||||
|
||||
baseValue = toBaseAmount(_value);
|
||||
allowance[msg.sender][_spender] += baseValue;
|
||||
allowance[msg.sender][_spender] = baseValue;
|
||||
emit Approval(msg.sender, _spender, _value);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Reduce allowance by amount
|
||||
function decreaseAllowance(address _spender, uint256 _value) public returns (bool) {
|
||||
uint256 baseValue;
|
||||
|
||||
baseValue = toBaseAmount(_value);
|
||||
require(allowance[msg.sender][_spender] >= baseValue);
|
||||
|
||||
changePeriod();
|
||||
|
||||
allowance[msg.sender][_spender] -= baseValue;
|
||||
emit Approval(msg.sender, _spender, allowance[msg.sender][_spender]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Increase allowance by amount
|
||||
function increaseAllowance(address _spender, uint256 _value) public returns (bool) {
|
||||
uint256 baseValue;
|
||||
|
||||
changePeriod();
|
||||
|
||||
baseValue = toBaseAmount(_value);
|
||||
|
||||
allowance[msg.sender][_spender] += baseValue;
|
||||
emit Approval(msg.sender, _spender, allowance[msg.sender][_spender]);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Implements ERC20, triggers tax and/or redistribution
|
||||
function transfer(address _to, uint256 _value) public returns (bool) {
|
||||
uint256 baseValue;
|
||||
@ -454,7 +546,9 @@ contract DemurrageTokenSingleCap {
|
||||
baseValue = toBaseAmount(_value);
|
||||
require(allowance[_from][msg.sender] >= baseValue);
|
||||
|
||||
allowance[_from][msg.sender] -= baseValue;
|
||||
result = transferBase(_from, _to, baseValue);
|
||||
|
||||
emit Transfer(_from, _to, _value);
|
||||
return result;
|
||||
}
|
||||
@ -486,6 +580,29 @@ contract DemurrageTokenSingleCap {
|
||||
emit OwnershipTransferred(oldOwner, owner);
|
||||
}
|
||||
|
||||
// Explicitly and irretrievably burn tokens
|
||||
// Only token minters can burn tokens
|
||||
function burn(uint256 _value) public {
|
||||
require(minter[msg.sender]);
|
||||
require(_value <= account[msg.sender]);
|
||||
uint256 _delta = toBaseAmount(_value);
|
||||
|
||||
applyDemurrage();
|
||||
decreaseBaseBalance(msg.sender, _delta);
|
||||
burned += _value;
|
||||
emit Burn(msg.sender, _value);
|
||||
}
|
||||
|
||||
// Implements ERC20
|
||||
function totalSupply() public view returns (uint256) {
|
||||
return supply - burned;
|
||||
}
|
||||
|
||||
// Return total number of burned tokens
|
||||
function totalBurned() public view returns (uint256) {
|
||||
return burned;
|
||||
}
|
||||
|
||||
// Implements EIP165
|
||||
function supportsInterface(bytes4 _sum) public pure returns (bool) {
|
||||
if (_sum == 0xc6bb4b70) { // ERC20
|
||||
|
Loading…
Reference in New Issue
Block a user