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
I wonder a bit about
keccak256("balance(address):(uint64)")[0, 4] address
Usually the return type is not part of the function signature - is this different in eWASM? Would love a link with some more information about this - could not find anything so far.