Add supply cap

This commit is contained in:
lash 2023-02-10 15:58:31 +00:00
parent 3353733405
commit e6eef48808
Signed by: lash
GPG Key ID: 21D2E7BB88C2A746
8 changed files with 96 additions and 61 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -77,16 +77,8 @@ class DemurrageToken(ERC20, SealedContract, ExpiryContract):
__abi = {} __abi = {}
__bytecode = {} __bytecode = {}
valid_modes = [
'MultiNocap',
'SingleNocap',
'MultiCap',
'SingleCap',
]
def constructor(self, sender_address, settings, cap=0, tx_format=TxFormat.JSONRPC): def constructor(self, sender_address, settings, tx_format=TxFormat.JSONRPC):
if int(cap) < 0:
raise ValueError('cap must be 0 or positive integer')
code = DemurrageToken.bytecode() code = DemurrageToken.bytecode()
enc = ABIContractEncoder() enc = ABIContractEncoder()
enc.string(settings.name) enc.string(settings.name)
@ -95,8 +87,6 @@ class DemurrageToken(ERC20, SealedContract, ExpiryContract):
enc.uint256(settings.demurrage_level) enc.uint256(settings.demurrage_level)
enc.uint256(settings.period_minutes) enc.uint256(settings.period_minutes)
enc.address(settings.sink_address) enc.address(settings.sink_address)
if cap > 0:
enc.uint256(cap)
code += enc.get() code += enc.get()
tx = self.template(sender_address, None, use_nonce=True) tx = self.template(sender_address, None, use_nonce=True)
tx = self.set_code(tx, code) tx = self.set_code(tx, code)
@ -109,23 +99,7 @@ class DemurrageToken(ERC20, SealedContract, ExpiryContract):
@staticmethod @staticmethod
def __to_contract_name(multi, cap): def abi(multi=True):
name = 'DemurrageToken'
if multi:
name += 'Multi'
else:
name += 'Single'
if cap:
name += 'Cap'
else:
name += 'Nocap'
logg.debug('bytecode name {}'.format(name))
return name
@staticmethod
def abi(multi=True, cap=False):
#name = DemurrageToken.__to_contract_name(multi, cap)
name = 'DemurrageTokenSingleNocap' name = 'DemurrageTokenSingleNocap'
if DemurrageToken.__abi.get(name) == None: if DemurrageToken.__abi.get(name) == None:
f = open(os.path.join(data_dir, name + '.json'), 'r') f = open(os.path.join(data_dir, name + '.json'), 'r')
@ -135,8 +109,7 @@ class DemurrageToken(ERC20, SealedContract, ExpiryContract):
@staticmethod @staticmethod
def bytecode(multi=True, cap=False): def bytecode(multi=True):
#name = DemurrageToken.__to_contract_name(multi, cap)
name = 'DemurrageTokenSingleNocap' name = 'DemurrageTokenSingleNocap'
if DemurrageToken.__bytecode.get(name) == None: if DemurrageToken.__bytecode.get(name) == None:
f = open(os.path.join(data_dir, name + '.bin'), 'r') f = open(os.path.join(data_dir, name + '.bin'), 'r')
@ -185,6 +158,17 @@ class DemurrageToken(ERC20, SealedContract, ExpiryContract):
return tx return tx
def set_max_supply(self, contract_address, sender_address, cap, tx_format=TxFormat.JSONRPC):
enc = ABIContractEncoder()
enc.method('setMaxSupply')
enc.typ(ABIContractType.UINT256)
enc.uint256(cap)
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 remove_minter(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC): def remove_minter(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC):
enc = ABIContractEncoder() enc = ABIContractEncoder()
enc.method('removeMinter') enc.method('removeMinter')
@ -503,8 +487,8 @@ class DemurrageToken(ERC20, SealedContract, ExpiryContract):
return self.call_noarg('demurrageTimestamp', contract_address, sender_address=sender_address) return self.call_noarg('demurrageTimestamp', contract_address, sender_address=sender_address)
def supply_cap(self, contract_address, sender_address=ZERO_ADDRESS): def max_supply(self, contract_address, sender_address=ZERO_ADDRESS):
return self.call_noarg('supplyCap', contract_address, sender_address=sender_address) return self.call_noarg('maxSupply', contract_address, sender_address=sender_address)
# def grow_by(self, contract_address, value, period, sender_address=ZERO_ADDRESS, id_generator=None): # def grow_by(self, contract_address, value, period, sender_address=ZERO_ADDRESS, id_generator=None):

View File

@ -34,7 +34,7 @@ PERIOD = 43200
class TestTokenDeploy: class TestTokenDeploy:
"""tax level is ppm, 1000000 = 100%""" """tax level is ppm, 1000000 = 100%"""
def __init__(self, rpc, token_symbol='FOO', token_name='Foo Token', sink_address=ZERO_ADDRESS, supply=10**12, tax_level=TAX_LEVEL, period=PERIOD): def __init__(self, rpc, token_symbol='FOO', token_name='Foo Token', sink_address=ZERO_ADDRESS, tax_level=TAX_LEVEL, period=PERIOD):
self.tax_level = tax_level self.tax_level = tax_level
self.period_seconds = period * 60 self.period_seconds = period * 60
@ -60,14 +60,11 @@ class TestTokenDeploy:
except TypeError: except TypeError:
self.start_time = int(r['timestamp']) self.start_time = int(r['timestamp'])
self.default_supply = supply
self.default_supply_cap = 0
def deploy(self, rpc, deployer_address, interface, supply_cap=0): def deploy(self, rpc, deployer_address, interface, supply_cap=0):
tx_hash = None tx_hash = None
o = None o = None
(tx_hash, o) = interface.constructor(deployer_address, self.settings, cap=0) (tx_hash, o) = interface.constructor(deployer_address, self.settings)
r = rpc.do(o) r = rpc.do(o)
o = receipt(tx_hash) o = receipt(tx_hash)
@ -93,8 +90,8 @@ class TestDemurrage(EthTesterCase):
except AttributeError as e: except AttributeError as e:
pass pass
self.deployer = TestTokenDeploy(self.rpc, period=period, sink_address=self.accounts[9]) self.deployer = TestTokenDeploy(self.rpc, period=period, sink_address=self.accounts[9])
self.default_supply = self.deployer.default_supply self.default_supply = 0
self.default_supply_cap = self.deployer.default_supply_cap self.default_supply_cap = 0
self.start_block = None self.start_block = None
self.address = None self.address = None
self.start_time = None self.start_time = None
@ -148,3 +145,6 @@ class TestDemurrageDefault(TestDemurrage):
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
self.deploy(c) self.deploy(c)
self.default_supply = 10**12
self.default_supply_cap = self.default_supply

View File

@ -41,6 +41,7 @@ done
python tests/test_expiry.py python tests/test_expiry.py
python tests/test_seal.py python tests/test_seal.py
python tests/test_cap.py
set +e set +e
set +x set +x

View File

@ -18,7 +18,7 @@ from hexathon import (
from erc20_demurrage_token import DemurrageToken from erc20_demurrage_token import DemurrageToken
# test imports # test imports
from erc20_demurrage_token.unittest.base import TestDemurrageCap from erc20_demurrage_token.unittest import TestDemurrageDefault
logging.basicConfig(level=logging.DEBUG) logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger() logg = logging.getLogger()
@ -26,18 +26,27 @@ logg = logging.getLogger()
testdir = os.path.dirname(__file__) testdir = os.path.dirname(__file__)
class TestCap(TestDemurrageCap): class TestCap(TestDemurrageDefault):
def test_cap_set(self): def test_cap(self):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
o = c.supply_cap(self.address, sender_address=self.accounts[0])
o = c.total_supply(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o)
logg.debug('r {}'.format(r))
(tx_hash, o) = c.set_max_supply(self.address, self.accounts[0], self.default_supply_cap)
r = self.rpc.do(o)
o = receipt(tx_hash)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
o = c.max_supply(self.address, sender_address=self.accounts[0])
r = self.rpc.do(o) r = self.rpc.do(o)
cap = c.parse_supply_cap(r) cap = c.parse_supply_cap(r)
self.assertEqual(cap, self.default_supply_cap) self.assertEqual(cap, self.default_supply_cap)
def test_cap(self):
nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc) nonce_oracle = RPCNonceOracle(self.accounts[0], self.rpc)
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle) 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], self.default_supply_cap) (tx_hash, o) = c.mint_to(self.address, self.accounts[0], self.accounts[1], self.default_supply_cap)
@ -53,15 +62,5 @@ class TestCap(TestDemurrageCap):
self.assertEqual(r['status'], 0) self.assertEqual(r['status'], 0)
def test_cap_first(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], self.default_supply_cap + 1)
r = self.rpc.do(o)
o = receipt(tx_hash)
r = self.rpc.do(o)
self.assertEqual(r['status'], 0)
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@ -156,6 +156,37 @@ class TestSeal(TestDemurrageDefault):
self.assertTrue(c.parse_is_sealed(r)) self.assertTrue(c.parse_is_sealed(r))
def test_seal_cap(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.set_max_supply(self.address, self.accounts[0], 100)
r = self.rpc.do(o)
o = receipt(tx_hash)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
(tx_hash, o) = c.set_max_supply(self.address, self.accounts[0], 200)
r = self.rpc.do(o)
o = receipt(tx_hash)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
(tx_hash, o) = c.seal(self.address, self.accounts[0], ContractState.CAP_STATE)
r = self.rpc.do(o)
o = receipt(tx_hash)
r = self.rpc.do(o)
self.assertEqual(r['status'], 1)
(tx_hash, o) = c.set_max_supply(self.address, self.accounts[0], 300)
r = self.rpc.do(o)
o = receipt(tx_hash)
r = self.rpc.do(o)
self.assertEqual(r['status'], 0)
o = c.is_sealed(self.address, ContractState.CAP_STATE, sender_address=self.accounts[0])
r = self.rpc.do(o)
self.assertTrue(c.parse_is_sealed(r))
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -84,6 +84,9 @@ contract DemurrageTokenSingleCap {
uint256 public expires; uint256 public expires;
bool expired; bool expired;
// supply xap
uint256 public maxSupply;
// Implements ERC20 // Implements ERC20
event Transfer(address indexed _from, address indexed _to, uint256 _value); event Transfer(address indexed _from, address indexed _to, uint256 _value);
@ -116,6 +119,8 @@ contract DemurrageTokenSingleCap {
event Expired(uint256 _timestamp); event Expired(uint256 _timestamp);
event Cap(uint256 indexed _oldCap, uint256 _newCap);
// property sealing // property sealing
uint256 public sealState; uint256 public sealState;
uint8 constant MINTER_STATE = 1; uint8 constant MINTER_STATE = 1;
@ -171,10 +176,22 @@ contract DemurrageTokenSingleCap {
} }
function setExpirePeriod(uint256 _expirePeriod) public { function setExpirePeriod(uint256 _expirePeriod) public {
uint256 r;
require(!isSealed(EXPIRY_STATE)); require(!isSealed(EXPIRY_STATE));
require(!expired); require(!expired);
require(msg.sender == owner); require(msg.sender == owner);
expires = periodStart + (_expirePeriod * periodDuration); r = periodStart + (_expirePeriod * periodDuration);
require(r > expires);
expires = r;
}
function setMaxSupply(uint256 _cap) public {
require(!isSealed(CAP_STATE));
require(msg.sender == owner);
require(_cap > supply);
emit Cap(maxSupply, _cap);
maxSupply = _cap;
} }
@ -283,10 +300,13 @@ contract DemurrageTokenSingleCap {
require(applyExpiry() == 0); require(applyExpiry() == 0);
require(minter[msg.sender], 'ERR_ACCESS'); require(minter[msg.sender], 'ERR_ACCESS');
changePeriod();
changePeriod(); if (maxSupply > 0) {
baseAmount = toBaseAmount(_amount); require(supply + _amount <= maxSupply);
}
supply += _amount; supply += _amount;
baseAmount = toBaseAmount(_amount);
increaseBaseBalance(_beneficiary, baseAmount); increaseBaseBalance(_beneficiary, baseAmount);
emit Mint(msg.sender, _beneficiary, _amount); emit Mint(msg.sender, _beneficiary, _amount);
saveRedistributionSupply(); saveRedistributionSupply();