This repository proposes a standard for building transparent tokens on the Dusk chain, as well as a barebones implementation of said standard.
Dusk's chain supports cheaply verifying zero-knowledge proofs passed to a contract. Tokens leveraging this functionality will often obfuscate certain pieces of data - such as the amount being sent, or the recipient - while still keeping tranfers secure. Here we are not making use of this functionality, and are instead defining a classic token - here called a transparent token due to its properties.
The token is fungible, i.e. each token is exactly the same as another.
Have rust
and make
installed and run:
make
Contracts implementing this standard will allow the user to:
- Transfer tokens from one account to another
- Get the total supply of tokens
- Approve third-parties spending tokens of an account
- Get the current token balance of an account
Some of the functionality of the contract requires data to be sent to it that assures ownership of a
given public key, as well as ensuring the non-repeatability of certain calls. The data that a user
will use to interact with this contract is defined in the types
crate in this repository. The
example contract implementation in the contract
crate makes use of rkyv
serialization. This
is convenient for the implementation since rusk-abi
supports it natively, however it is not a
requirement, and implementors may choose any serialization they wish. This will result in different
gas costs. As a consequence, this specification does not require specific serialization from
contracts wishing to implement it.
The following functions will be defined by a contract implementing this specification. &self
and
&mut self
are used to denote whether a function mutates the state handled by the contract, and
closely matches its use in the implementation.
fn name(&self) -> String;
fn symbol(&self) -> String;
fn decimals(&self) -> u8;
fn total_supply(&self) -> u64;
fn account(&self, _: PublicKey) -> AccountData;
fn allowance(&self, _: Allowance) -> u64;
fn transfer(&mut self, _: Transfer);
fn transfer_from(&mut self, _: TransferFrom);
fn transfer_from_contract(&mut self, _: TransferFromContract);
fn approve(&mut self, _: Approve);
For this contract we use BLS12_381 public keys, since Dusk has native support for them. However, implementers of the token standard may choose a different type of cryptography for their own token.
On a transfer
, transfer_from
, and approve
events are emitted related to the action performed.
The data included with these events is defined with the TransferEvent
and ApproveEvent
.
Dusk supports both 32 and 64-bit WebAssembly contracts. Compiling this contract for 32-bit however,
comes with serious size constraints for the state the contract can manage - 4GiB. This would mean
that the contract would hit a limit in terms of the number of accounts it can manage that is too low
to be usable. As such, we include a script that downloads a compiler toolchain that supports 64-bit
WebAssembly and registers it with rustup
, and use make
to call this automatically when run.
Transaction sizes are a consideration for any chain, and given that deployment costs scale per byte deployed it is in the best interests of contract developers to minimize the payload. As such we include a script that downloads a tool that strips the compiled binary of any superfluous information such as debug symbols.
To prevent replay, external accounts have to use a nonce in their calls to the token contract. These nonces must be sequential, with the next successive number being +1 the previous. These nonces are kept in the contract's state, and function as an effective count of the number of calls an account has performed.