Easy Ethereum transactions with Zenroom

Easy Ethereum transactions with Zenroom

In this post you will see how to use Zenroom to make transactions on Ethereum, using both its main coin (ETH) and derivative tokens (ERC20).

Eth in Zen: Build your own wallet with zenroom

Have you ever heard about Web3? Nowadays, too often, blockchains are seen as the solution to almost every problem. The result is that many people see it as a scam. As usual, the truth is in the middle.

It is important to remember the origins. When the concept of blockchain was first introduced, it solved a real problem: avoid double spending using a distributed ledger instead of classic double-entry bookkeeping. This is also defined as triple-entry accounting.

Zenroom - No-code Cryptographic virtual machine
Multiplatform cryptography for DB and blockchain, zk-SNARKS, blockchain interop, smart contracts in human language, fully isolated virtual machine

Scripts in this article can be executed from Golang, Rust, Python and Javascript using Zenroom language bindings with zero dependencies!

Gas: the fuel of Ethereum

Ethereum works as a distributed computer: all nodes of an Ethereum network are guaranteed to see the same result when a smart-contract runs.

What kind of smart-contracts can one run? Any, but there must be a way to stop a program that takes too long to reply. Gas is the price one needs to pay to run smart-contracts on Ethereum: it is a fee that has to be paid for each function executed.

The transfer of coins is a function since the birth of distributed ledger technologies, so any network based on the Ethereum protocol requires the payment of Gas for computation.

Hands on!

Let’s roll up our sleeves. Using the Ethereum Mainnet for an experiment is not recommended and for this reason many “testnets” were born. I will use the one named “Goerli”.

To run the scripts in this article one can use our online Apiroom.net IDE or download a Zenroom build and run it inside a terminal.

It is really up to you and your skill level with one of the languages listed above: one can also use Python, Javascript, Rust or Golang.

So chose your weapon! Embedding the Zenroom interpreter one can run Zencode scripts pretty much everywhere. And if any doubts arise a complete reference documentation of Zencode is available!

Zenroom 📝 Documentation
Zenroom is a portable and independent virtual machine for secure cryptographic operations. Zencode is the name of the language executed by Zenroom: it is simple to understand and can process large data structures while operating cryptographic transformations on them.

Create an Ethereum account

The first step is to create an Ethereum account that we will use on the Goerli testnet.

Scenario 'ethereum':
Given nothing
When I create the ethereum key
When I create the ethereum address
Then print data
Then print the 'keyring'

the result is a JSON in which you can identify mainly two values:

{
   "ethereum_address": "aa177c956ef8ea01246481ae8a3441d6023f5f48",
   "keyring": {
      "ethereum": "18524df06977b3b8a4e43cd6983ce8ed514c97e6b5e2be6131db46e10b1a456f"
   }
}

The first one is an “ethereum_address”: the public identifier of your account. The second one is the secret key, a value named “ethereum” inside the “keyring”, which you have to keep secret as it is used to sign transactions from your account.

Please note that Zencode does not print the secret keyring unless explicitly requested by the Then print the ‘keyring' statement.

Now save the public address and the secret key. Then run the code again and note down the new result. At this point you have two different secret keys and addresses.

Get some coins

The next step is to get some coins. For this, you can use a Goerli faucet, at the time of writing this worked, or just search for one online. Once found use one of the Ethereum addresses in the text field and click OK.

How to verify the faucet has given you coins? One can ask any node in the Ethereum network the balance of a known account.

Just write this JSON query in a file balance.json

{
  "jsonrpc": "2.0",
  "method": "eth_getBalance",
  "params": [
    "0x<your ethereum address>",
    "latest"
  ],
  "id": 42
}

Then use cURL (or any other http client) to query a node

cat balance.json \
| curl -sH "Content-Type: application/json" -X POST -d @- \
  https://rpc.ankr.com/eth_goerli

The output is something like

{
  "jsonrpc": "2.0",
  "id": 42,
  "result": "0x13edfe26e175e"
}

The resultkey contains the balance as an hexadecimal value (you can convert to decimal for example using a python interpreter). It can also be done using zenroom’s lua interpreter via zencode_exec(…) or the zenroom commandline tool:

print( BIG.new( O.from_hex('0x13edfe26e175e') ):decimal() )

You will see the result is a decimal number. If this more than zero: congratulations! you have just received some crypto coins!

Please know that 1 ether equals to 1000000000000000000 wei.

Send coins from an account to another

This part is a bit more difficult: the goal is to transfer Gas between the two accounts created before.

The first step is to ask a public node of the Ethereum network two things:

  1. the current “nonce” value of your account (eth_getTransactionCount)
  2. the recommended price for the transaction fee (eth_gasPrice)

We can obtain those information using cURL as we did for the balance. To get the nonce using eth_getTransactionCount write this JSON query into `getnonce.json`

{
  "jsonrpc": "2.0",
  "method": "eth_getTransactionCount",
  "params": [
    "0x<your ethereum address>",
    "latest"
  ],
  "id": 42
}

Then send it for instance via curl:

cat getnonce.json \
| curl -sH "Content-Type: application/json" -X POST -d @- \
  https://rpc.ankr.com/eth_goerli

For the transaction fee price use the following script. Please note this one shows how so use curl on the same line without creating a separated JSON query file and converting the value to decimal using the Python interpreter:

curl -sH "Content-Type: application/json" -X POST --data \
  '{"jsonrpc":"2.0","method":"eth_gasPrice","params":[],"id":42}' \
  https://rpc.ankr.com/eth_goerli \
| jq '.result' | xargs | python3 -c "print(int(input(), 16))"

Now you have the two values needed and converted them to a decimal number, you have to prepare the data input for Zencode in a JSON file transact.json filled with the values we know from previous scripts:

{
 "keyring": {
  "ethereum": "<your secret key>"
 },
 "ethereum_nonce": "<the nonce as decimal number>",
 "gas_price": "<the transaction fee gas price>",
 "receiver_address": "<the receiver address>",
 "endpoint": "https://rpc.ankr.com/eth_goerli",
 "amount": "500000",
 "gas limit": "50000"
}

In addition we see some new fields, they are:

  • “your secret key” is the secret etherum keyring of the public address used to receive coins from the faucet: the one that has funds to transfer
  • “receiver address” is the other public address that hasn’t been used yet
  • “amount” is the number of coins you want to transfer measured in wei
  • “gas limit” is the maximum units gas you intend to spend (each unit at the price of “gas price”)

Then use this Zencode script passing it the data in transact.json

Scenario ethereum:
Given I have the 'keyring'
Given I have a 'ethereum address' named 'receiver_address'
Given I have a 'ethereum nonce'
Given I have a 'gas price'
Given I have a 'gas limit'
Given I have a 'wei value' named 'amount'
When I create the ethereum address
When I create the ethereum transaction of 'amount' to 'receiver_address'
When I create the signed ethereum transaction for chain '5'
Then print the 'signed ethereum transaction'
Then print data

This can be done for instance using the zenroom commandline client and saving the Zencode script above in transact.zen :

zenroom -a transact.json -z transact.zen

Once executed the result will contain a lot of information, i.e:

{
  "amount": "500000",
  "ethereum_address": "7d6df85bdbce99151c813fd1dde6bc007c523c27",
  "ethereum_nonce": "6",
  "ethereum_transaction": {
    "gas_limit": "50000",
    "gas_price": "29970423419",
    "nonce": "6",
    "r": "f1efbbf04d1c70326d875c749a57b8edbbb25bb1feff0b6ac180c69b30f6fc0d",
    "s": "51261997a3662b1a04016ebf99cf90c454a738b95e7ad2253aaf4eefa6b853ef",
    "to": "996b5a6d21a760f5c28a3ef58849d23060ab9b91",
    "v": "2d",
    "value": "500000"
  },
  "gas_limit": "50000",
  "gas_price": "29970423419",
  "receiver_address": "996b5a6d21a760f5c28a3ef58849d23060ab9b91",
  "signed_ethereum_transaction": "f867068506fa605e7b82c35094996b5a6d21a760f5c28a3ef58849d23060ab9b918307a120802da0f1efbbf04d1c70326d875c749a57b8edbbb25bb1feff0b6ac180c69b30f6fc0da051261997a3662b1a04016ebf99cf90c454a738b95e7ad2253aaf4eefa6b853ef"
}

what interests you the most is the signed ethereum transaction value. This long hex sequence can be broadcasted by anyone in order to execute and move funds. Anyone could paste it into this curl command for instance:

curl -sH "Content-Type: application/json" -X POST --data \
'{"jsonrpc":"2.0","method":"eth_sendRawTransaction","params":["0x<your signed ethereum transaction>"],"id":1}' \
https://rpc.ankr.com/eth_goerli

When executed curl will return something like:

{"jsonrpc":"2.0","id":1,"result":"0x284cf7390f60f5080b866b02f3db102b20a499666e7e68d75d43a70e924fdf7d"}

The “result” value is a unique reference to our transaction and can be used to check its status: just paste it inside any blockchain explorer.

Now you can use the script in the previous section to read your balance again. You may notice that the sender gave up more tokens than the ones to be sent: this is because the transaction fee requires gas to be executed.

ERC 20: token built on top of Ethereum

Using Ethereum one can build other tokens (maybe with some more complex logic) which are just smart contracts with a known interface, the most common is named ERC20. This section will show how to transact the standard ERC20 token named “WeenusToken” using Zencode, but any ERC20 token will work.

WeenusToken has also a faucet that gives away free coins, but it is in the form of a smart-contract on the Goerli testnet. To receive funds from the fauces you should use the same contract shown in the previous section, but change the “amount” to 0 and the “receiver address” to aFF4481D10270F50f203E0763e2597776068CBc5 which is the WeenusToken faucet address.

The following step is to verify that the we have received the tokens. We can use cURL as before, but we have to create the request beforehand, for example using zenroom and the following script (which can be entered on the command line using zenroom -ior in a file and then executing it)

ETH=require('crypto_ethereum')
print(ETH.erc20.balanceOf(O.from_hex('<your ethereum address>')):hex())}

Write this JSON query into balanceerc20.json

{
  "method": "eth_call",
  "params": [
    {
      "from": null,
      "to": "0x<faucet address>",
      "data": "0x<result of zenroom ETH.erc20.balanceOf>"
    },
    "latest"
  ],
  "id": 1,
  "jsonrpc": "2.0"
}

And run cURL

cat balanceerc20.json \
| curl -sH "Content-Type: application/json" -X POST -d @- \
  https://rpc.ankr.com/eth_goerli

You should see that the result is something like

{"jsonrpc":"2.0","id":1,"result":"0x00000000000000000000000000000000000000000000003635c9adc5de985ee0"}

The last contract we analyze in this post is how to transfer, we can send some tokens to another address using a simple variation of the previous contract to send ETH. We won’t repeat how to get all the values, go watch the previous paragraph!

Scenario ethereum:
Given I have the 'keyring'
Given I have a 'ethereum address' named 'receiver_address'
Given I have a 'ethereum address' named 'faucet_address'
Given I have a 'ethereum nonce'
Given I have a 'gas price'
Given I have a 'gas limit'
Given I have a 'wei value' named 'amount'
When I create the ethereum address

When I create the ethereum transaction to 'faucet_address'
When I use the ethereum transaction to transfer 'amount' erc20 tokens to 'receiver_address'

When I create the signed ethereum transaction for chain '5'
Then print the 'signed ethereum transaction'
Then print data

With data

{
 "keyring": {
  "ethereum": "<your secret key>"
 },
 "faucet_address": "aFF4481D10270F50f203E0763e2597776068CBc5",
 "ethereum_nonce": "<your nonce read before as decimal number>",
 "gas_price": "<the current gas price read before>",
 "receiver_address": "<the receiver address>",
 "endpoint": "https://rpc.ankr.com/eth_goerli",
 "amount": "500000",
 "gas limit": "50000"
}

As before the output should be something like

{
   "amount": "500000",
   "ethereum_address": "996b5a6d21a760f5c28a3ef58849d23060ab9b91",
   "ethereum_nonce": "7",
   "ethereum_transaction": {
      "data": "a9059cbb000000000000000000000000996b5a6d21a760f5c28a3ef58849d23060ab9b91000000000000000000000000000000000000000000000000000000000007a120",
      "gas_limit": "50000",
      "gas_price": "15482240709",
      "nonce": "7",
      "r": "0a308b33f4869da8f5272aba57e245312c06ff37ef347cf8883255ab60666ffe",
      "s": "269aa2043e54f81b00e2b0b0c264b022f53215a1587606956b12528d0f33a3f2",
      "to": "aff4481d10270f50f203e0763e2597776068cbc5",
      "v": "2d",
      "value": "0"
   },
   "faucet_address": "aff4481d10270f50f203e0763e2597776068cbc5",
   "gas_limit": "50000",
   "gas_price": "15482240709",
   "receiver_address": "996b5a6d21a760f5c28a3ef58849d23060ab9b91",
   "signed_ethereum_transaction": "f8a90785039ad03ec582c35094aff4481d10270f50f203e0763e2597776068cbc580b844a9059cbb000000000000000000000000996b5a6d21a760f5c28a3ef58849d23060ab9b91000000000000000000000000000000000000000000000000000000000007a1202da00a308b33f4869da8f5272aba57e245312c06ff37ef347cf8883255ab60666ffea0269aa2043e54f81b00e2b0b0c264b022f53215a1587606956b12528d0f33a3f2"
}

We can broadcast the transaction from the command line to make the actually happen. We have already seen how to do this

curl -sH "Content-Type: application/json" -X POST --data \
'{"jsonrpc":"2.0","method":"eth_sendRawTransaction","params":["0x<your signed ethereum transaction>"],"id":1}' \
https://rpc.ankr.com/eth_goerli

Finally, you are welcome to query your WeenusToken ERC20 balance to verify that the transfer has happened.

Conclusions

Adapting the examples in this tutorial to your case, you can do basic transactions of coins both on the Ethereum Mainnet and on ERC20 compatible tokens.

Zenroom can use both types and the instructions are slightly different.

Is this all we can do with Zenroom on Ethereum? No!

If you are interested then clap this article and get in touch with us on the Zenroom Telegram Chat or any other way shown on our Dyne.org website.

We have implemented more methods of the ERC20 standard for instance transferFrom which lets a user delegate a fund transfer, as well we have Zencode to use NFTs with ERC721.

Happy hacking!


Tune in to the discussion 💬

(These services are bridged: join your favorite and reach them all)

🗨️ Matrix
🗨️ Telegram
🗨️ Discord

Support Dyne 🫱🏿‍🫲🏾

🪙 Bitcoins: bc1qz9wz2f9swcefra2tfrhk4fx49evqsv03m9nx4l
Ko-Fi
🍴 Github.com
🧁 LiberaPay
🍥 Patreon.com

Follow Dyne.org 🗞️

Social Media everywhere!

🐘 Mastodon
🎬 Peertube
🐭 Lemmy
📸 Instagram
🐦 Xitter
👔 Linkedin
🪞 Facebook
✍️ Medium

Dyne.org

Dyne.org

Dyne.org is a digital community & free software foundry. We share tools, practices & narratives that empower artists, creatives & citizens in the digital age. - Website
Haparandadam 7-A1 1013AK Amsterdam
Alberto Lerda

Alberto Lerda

Cryptographer and Software Engineer - Website