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

View File

@ -34,7 +34,7 @@ PERIOD = 43200
class TestTokenDeploy:
"""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.period_seconds = period * 60
@ -60,14 +60,11 @@ class TestTokenDeploy:
except TypeError:
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):
tx_hash = 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)
o = receipt(tx_hash)
@ -93,8 +90,8 @@ class TestDemurrage(EthTesterCase):
except AttributeError as e:
pass
self.deployer = TestTokenDeploy(self.rpc, period=period, sink_address=self.accounts[9])
self.default_supply = self.deployer.default_supply
self.default_supply_cap = self.deployer.default_supply_cap
self.default_supply = 0
self.default_supply_cap = 0
self.start_block = None
self.address = None
self.start_time = None
@ -148,3 +145,6 @@ class TestDemurrageDefault(TestDemurrage):
c = DemurrageToken(self.chain_spec, signer=self.signer, nonce_oracle=nonce_oracle)
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_seal.py
python tests/test_cap.py
set +e
set +x

View File

@ -18,7 +18,7 @@ from hexathon import (
from erc20_demurrage_token import DemurrageToken
# test imports
from erc20_demurrage_token.unittest.base import TestDemurrageCap
from erc20_demurrage_token.unittest import TestDemurrageDefault
logging.basicConfig(level=logging.DEBUG)
logg = logging.getLogger()
@ -26,18 +26,27 @@ logg = logging.getLogger()
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)
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)
cap = c.parse_supply_cap(r)
self.assertEqual(cap, self.default_supply_cap)
def test_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.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)
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__':
unittest.main()

View File

@ -156,6 +156,37 @@ class TestSeal(TestDemurrageDefault):
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__':

View File

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