erc20-demurrage-token/python/erc20_demurrage_token/token.py
2022-12-05 18:25:04 +00:00

585 lines
19 KiB
Python
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# standard imports
import os
import logging
# external imports
from chainlib.eth.tx import (
TxFactory,
TxFormat,
)
from chainlib.hash import keccak256_string_to_hex
from chainlib.eth.contract import (
ABIContractEncoder,
ABIContractType,
abi_decode_single,
)
from chainlib.eth.constant import ZERO_ADDRESS
from chainlib.jsonrpc import JSONRPCRequest
from eth_erc20 import ERC20
from hexathon import (
add_0x,
strip_0x,
)
# local imports
from erc20_demurrage_token.data import data_dir
logg = logging.getLogger(__name__)
class DemurrageTokenSettings:
def __init__(self):
self.name = None
self.symbol = None
self.decimals = None
self.demurrage_level = None
self.period_minutes = None
self.sink_address = None
def __str__(self):
return 'name {} demurrage level {} period minutes {} sink address {}'.format(
self.name,
self.demurrage_level,
self.period_minutes,
self.sink_address,
)
class DemurrageToken(ERC20):
__abi = {}
__bytecode = {}
valid_modes = [
'MultiNocap',
'SingleNocap',
'MultiCap',
'SingleCap',
]
def constructor(self, sender_address, settings, redistribute=True, cap=0, tx_format=TxFormat.JSONRPC):
if int(cap) < 0:
raise ValueError('cap must be 0 or positive integer')
code = DemurrageToken.bytecode(multi=redistribute, cap=cap>0)
enc = ABIContractEncoder()
enc.string(settings.name)
enc.string(settings.symbol)
enc.uint256(settings.decimals)
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)
return self.finalize(tx, tx_format)
@staticmethod
def gas(code=None):
return 4000000
@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)
if DemurrageToken.__abi.get(name) == None:
f = open(os.path.join(data_dir, name + '.json'), 'r')
DemurrageToken.__abi[name] = json.load(f)
f.close()
return DemurrageToken.__abi[name]
@staticmethod
def bytecode(multi=True, cap=False):
name = DemurrageToken.__to_contract_name(multi, cap)
if DemurrageToken.__bytecode.get(name) == None:
f = open(os.path.join(data_dir, name + '.bin'), 'r')
DemurrageToken.__bytecode[name] = f.read()
f.close()
return DemurrageToken.__bytecode[name]
def add_minter(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC):
enc = ABIContractEncoder()
enc.method('addMinter')
enc.typ(ABIContractType.ADDRESS)
enc.address(address)
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')
enc.typ(ABIContractType.ADDRESS)
enc.address(address)
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 mint_to(self, contract_address, sender_address, address, value, tx_format=TxFormat.JSONRPC):
enc = ABIContractEncoder()
enc.method('mintTo')
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 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()
o['method'] = 'eth_call'
enc = ABIContractEncoder()
enc.method('toBaseAmount')
enc.typ(ABIContractType.UINT256)
enc.uint256(value)
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 remainder(self, contract_address, parts, whole, sender_address=ZERO_ADDRESS, id_generator=None):
j = JSONRPCRequest(id_generator)
o = j.template()
o['method'] = 'eth_call'
enc = ABIContractEncoder()
enc.method('remainder')
enc.typ(ABIContractType.UINT256)
enc.typ(ABIContractType.UINT256)
enc.uint256(parts)
enc.uint256(whole)
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 redistributions(self, contract_address, idx, sender_address=ZERO_ADDRESS, id_generator=None):
j = JSONRPCRequest(id_generator)
o = j.template()
o['method'] = 'eth_call'
enc = ABIContractEncoder()
enc.method('redistributions')
enc.typ(ABIContractType.UINT256)
enc.uint256(idx)
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 account_period(self, contract_address, address, sender_address=ZERO_ADDRESS, id_generator=None):
j = JSONRPCRequest(id_generator)
o = j.template()
o['method'] = 'eth_call'
enc = ABIContractEncoder()
enc.method('accountPeriod')
enc.typ(ABIContractType.ADDRESS)
enc.address(address)
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(self, contract_address, participants, demurrage_modifier_ppm, value, period, sender_address=ZERO_ADDRESS, id_generator=None):
j = JSONRPCRequest(id_generator)
o = j.template()
o['method'] = 'eth_call'
enc = ABIContractEncoder()
enc.method('toRedistribution')
enc.typ(ABIContractType.UINT256)
enc.typ(ABIContractType.UINT256)
enc.typ(ABIContractType.UINT256)
enc.typ(ABIContractType.UINT256)
enc.uint256(participants)
enc.uint256(demurrage_modifier_ppm)
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 to_redistribution_period(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('toRedistributionPeriod')
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')
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_supply(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('toRedistributionSupply')
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_demurrage_modifier(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('toRedistributionDemurrageModifier')
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 base_balance_of(self, contract_address, address, sender_address=ZERO_ADDRESS, id_generator=None):
j = JSONRPCRequest(id_generator)
o = j.template()
o['method'] = 'eth_call'
enc = ABIContractEncoder()
enc.method('baseBalanceOf')
enc.typ(ABIContractType.ADDRESS)
enc.address(address)
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 apply_demurrage(self, contract_address, sender_address, limit=0, tx_format=TxFormat.JSONRPC):
if limit == 0:
return self.transact_noarg('applyDemurrage', contract_address, sender_address)
enc = ABIContractEncoder()
enc.method('applyDemurrageLimited')
enc.typ(ABIContractType.UINT256)
enc.uint256(limit)
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 change_period(self, contract_address, sender_address):
return self.transact_noarg('changePeriod', contract_address, sender_address)
def apply_redistribution_on_account(self, contract_address, sender_address, address, tx_format=TxFormat.JSONRPC):
enc = ABIContractEncoder()
enc.method('applyRedistributionOnAccount')
enc.typ(ABIContractType.ADDRESS)
enc.address(address)
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 tax_level(self, contract_address, sender_address=ZERO_ADDRESS):
return self.call_noarg('taxLevel', contract_address, sender_address=sender_address)
def resolution_factor(self, contract_address, sender_address=ZERO_ADDRESS):
return self.call_noarg('resolutionFactor', contract_address, sender_address=sender_address)
def actual_period(self, contract_address, sender_address=ZERO_ADDRESS):
return self.call_noarg('actualPeriod', contract_address, sender_address=sender_address)
def period_start(self, contract_address, sender_address=ZERO_ADDRESS):
return self.call_noarg('periodStart', contract_address, sender_address=sender_address)
def period_duration(self, contract_address, sender_address=ZERO_ADDRESS):
return self.call_noarg('periodDuration', contract_address, sender_address=sender_address)
def demurrage_amount(self, contract_address, sender_address=ZERO_ADDRESS):
return self.call_noarg('demurrageAmount', contract_address, sender_address=sender_address)
def demurrage_timestamp(self, contract_address, sender_address=ZERO_ADDRESS):
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 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)
o = j.template()
o['method'] = 'eth_call'
enc = ABIContractEncoder()
enc.method('decayBy')
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 get_distribution(self, contract_address, supply, demurrage_amount, sender_address=ZERO_ADDRESS, id_generator=None):
j = JSONRPCRequest(id_generator)
o = j.template()
o['method'] = 'eth_call'
enc = ABIContractEncoder()
enc.method('getDistribution')
enc.typ(ABIContractType.UINT256)
enc.typ(ABIContractType.UINT256)
enc.uint256(supply)
enc.uint256(demurrage_amount)
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 get_distribution_from_redistribution(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('getDistributionFromRedistribution')
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
@classmethod
def parse_actual_period(self, v):
return abi_decode_single(ABIContractType.UINT256, v)
@classmethod
def parse_period_start(self, v):
return abi_decode_single(ABIContractType.UINT256, v)
@classmethod
def parse_period_duration(self, v):
return abi_decode_single(ABIContractType.UINT256, v)
@classmethod
def parse_demurrage_amount(self, v):
return abi_decode_single(ABIContractType.UINT256, v)
@classmethod
def parse_remainder(self, v):
return abi_decode_single(ABIContractType.UINT256, v)
@classmethod
def parse_to_base_amount(self, v):
return abi_decode_single(ABIContractType.UINT256, v)
@classmethod
def parse_redistributions(self, v):
return abi_decode_single(ABIContractType.BYTES32, v)
@classmethod
def parse_account_period(self, v):
return abi_decode_single(ABIContractType.ADDRESS, v)
@classmethod
def parse_to_redistribution_period(self, v):
return abi_decode_single(ABIContractType.UINT256, v)
@classmethod
def parse_to_redistribution_item(self, v):
return abi_decode_single(ABIContractType.UINT256, v)
@classmethod
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)
@classmethod
def parse_decay_by(self, v):
return abi_decode_single(ABIContractType.UINT256, v)
@classmethod
def parse_get_distribution(self, v):
return abi_decode_single(ABIContractType.UINT256, v)
@classmethod
def parse_tax_level(self, v):
return abi_decode_single(ABIContractType.UINT256, v)
@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)