Skip to content

Instantly share code, notes, and snippets.

@axic
Last active December 15, 2021 03:14
Show Gist options
  • Star 10 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save axic/16158c5c88fbc7b1d09dfa8c658bc363 to your computer and use it in GitHub Desktop.
Save axic/16158c5c88fbc7b1d09dfa8c658bc363 to your computer and use it in GitHub Desktop.
ewasm “WRC20” token contract coding challenge

ewasm “WRC20” token contract coding challenge

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.

External interface

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) and uint64 a 64 bit number

The two methods:

  • balance is encoded as keccak256("balance(address):(uint64)")[0, 4] address and returns uint64
  • transfer is encoded as keccak256("transfer(address,uint64)")[0, 4] address value

For test cases see below.

Logic in pseudocode

// 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)
}

Test cases

  1. Query balance of 0xeD09375DC6B20050d242d1611af97eE4A6E93CAd

Input: 9993021aed09375dc6b20050d242d1611af97ee4a6e93cad

Output: 00000000000f4240

  1. Transfer 500000 to 0xe929CF2544363bdCEE4a976515d5F97758Ef476c (sender as above)

Input: 5d359fbde929cf2544363bdcee4a976515d5f97758ef476c7a120

Output: empty

  1. Query balance of 0xeD09375DC6B20050d242d1611af97eE4A6E93CAd

Input: 9993021aed09375dc6b20050d242d1611af97ee4a6e93cad

Output: 000000000007a120

  1. Query balance of 0xe929CF2544363bdCEE4a976515d5F97758Ef476c

Input: 9993021ae929cf2544363bdcee4a976515d5f97758ef476c

Output: 000000000007a120

@jwasinger
Copy link

Usually the return type is not part of the function signature - is this different in eWASM?

Yes, this was trying to fix a shortcoming of the ABI.

@axic can you explain this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment