Here is my proposal for a standard token contract:
balances = Hash(default_value=0)
allowances = Hash(default_value=0)
metadata = Hash()
@construct
def seed():
balances[ctx.caller] = 1_000_000
metadata['token_name'] = "TEST TOKEN"
metadata['token_symbol'] = "TST"
metadata['token_website'] = 'https://some.token.url'
metadata['operator'] = ctx.caller
@export
def balance_of(address: str):
return balances[address]
@export
def change_metadata(key: str, value: Any):
assert ctx.caller == metadata['operator'], 'Only operator can set metadata!'
metadata[key] = value
@export
def transfer(amount: float, to: str):
assert amount > 0, 'Cannot send negative balances!'
assert balances[ctx.caller] >= amount, 'Not enough coins to send!'
assert not to.startswith('0x'), 'Invalid address!'
balances[ctx.caller] -= amount
balances[to] += amount
@export
def approve(amount: float, to: str):
assert amount > 0, 'Cannot send negative balances!'
allowances[ctx.caller, to] += amount
@export
def transfer_from(amount: float, to: str, main_account: str):
approved = allowances[main_account, ctx.caller]
assert amount > 0, 'Cannot send negative balances!'
assert approved >= amount, f'You approved {approved} but need {amount}'
assert balances[main_account] >= amount, 'Not enough tokens to send!'
allowances[main_account, ctx.caller] -= amount
balances[main_account] -= amount
balances[to] += amount
-
After thinking about it, I removed the text return. I think it could be misleading. People will think it’s what happens. But in reality it’s just a text. Someone can adjust it and imply he is doing something he isn’t. Besides that, if we want to have a clear text for example on the explorer or DEX, we should take the contract and function and interpret it and show the interpretation of what was done but not rely on text someone provided.
-
I find it weird to save allowances and balances in the same variable. If we split it, it’s cleaner and we can much easier sum balances up etc. We could even do something like
balances.all()
and get total supply. -
Let’s make it easier for everyone to retrieve the balance for a given address with the
balance_of
function. Other contracts can use it after importing a token contract. -
I replaced one of the messages in
transfer_from
because i don’t like the weird.format
texts. It’s complicated, two lines (ugly!) and we should be using modern stuff like f-strings anyway. -
I removed
token_logo_url
from metadata since i think it should be best practice to put token logos into the contract so that we can be sure to have access to them always.
Besides all that, we should have code snippets ready to be included into a standard token contract. One of these things is having a sell()
and buy()
function in the contract that don’t need to be there but IF they are in there, a DEX should use them to execute buys / sells. That way, the token can easily implement taxes and other functionality that is specifically meant for trading on a DEX.