This document describes a simple contract in pseudocode and gives a couple of test cases for it.
The challenge is designed to be fairly easy to implement in a language of choice, compiled to wasm and interacted with. It is loosely based on the ERC20 token standard, named "WRC20", where the W stands for WebAssembly.
The contract has two features:
- querying the balance of an address
- transfering tokens
Upon deployment the address 0xeD09375DC6B20050d242d1611af97eE4A6E93CAd
(private key: 0xdffca753e40d47521d2dd94fe56b0131051d91df614ef0a5e1c301ba9575c550
) should have a balance of 1000000
.
The interface is loosely based on the Ethereum contract ABI, but is geared towards non-256-bit systems:
- has a 32 bit selector, which is the first 32 bits of the keccak-256 hash of the signature
- has multiple data types, but here we only use
address
(160 bit bytestring) anduint64
a 64 bit number
The two methods:
balance
is encoded askeccak256("balance(address):(uint64)")[0, 4] address
and returnsuint64
transfer
is encoded askeccak256("transfer(address,uint64)")[0, 4] address value
For test cases see below.
// main ewasm entry point
fun main() {
if (eei_calldatasize() < 4)
eei_revert(0, 0)
let selector = eei_calldatacopy(0, 4)
switch selector {
case 0x9993021a:
do_balance()
case 0x5d359fbd:
do_transfer()
default:
eei_revert(0, 0)
}
}
fun do_balance() {
if (eei_calldatasize() != 24)
eei_revert(0, 0)
let address = eei_calldatacopy(4, 20)
// make sure that address is 160 bits here,
// but storage key is 256 bits so need to pad it somehow
let balance = eei_storageload(address)
eei_return(balance)
}
fun do_transfer() {
if (eei_calldatasize() != 32)
eei_revert(0, 0)
let sender = eei_sender()
let recipient = eei_calldatacopy(4, 20)
let value = eei_calldatacopy(24, 8)
let sender_balance = eei_storageload(sender)
let recipient_balance = eei_storageload(recipient)
if (sender_balance < value)
eei_revert(0, 0)
sender_balance -= value
recipient_balance += value
eei_storagestore(sender, sender_balance)
eei_storagestore(recipient, recipient_balance)
}
- Query balance of
0xeD09375DC6B20050d242d1611af97eE4A6E93CAd
Input: 9993021aed09375dc6b20050d242d1611af97ee4a6e93cad
Output: 00000000000f4240
- Transfer
500000
to0xe929CF2544363bdCEE4a976515d5F97758Ef476c
(sender as above)
Input: 5d359fbde929cf2544363bdcee4a976515d5f97758ef476c7a120
Output: empty
- Query balance of
0xeD09375DC6B20050d242d1611af97eE4A6E93CAd
Input: 9993021aed09375dc6b20050d242d1611af97ee4a6e93cad
Output: 000000000007a120
- Query balance of
0xe929CF2544363bdCEE4a976515d5F97758Ef476c
Input: 9993021ae929cf2544363bdcee4a976515d5f97758ef476c
Output: 000000000007a120
Alternatively to maintain tooling compatibility we could use the standard "Contract ABI" for ERC20 with the only change that balances are
uint64
and notuint256
.In this case values would be 256-bit long, but since the type is
uint64
when parsing in a contract it could ignore the leading zeroes and could just returned leading zeroes without having bignum support.