Examples
Some Golang and Javascript integration examples are provided as a reference. They can be found at: - Hermez Golang examples - Hermez Javascript examples
Additionally, Hermez Mobile SDK example provides an example of how to import and use the Mobile SDK.
SDK
A full Javascript SDK and a Flutter Plugin for Hermez Mobile SDK are provided as part of the tools for integration with Hermez Network.
HermezJS is an open-source SDK to interact with Hermez Rollup network. It can be downloaded as an npm package, or via github.
Hermez Flutter SDK is a Flutter Plugin for Hermez Mobile SDK, and provides a cross-platform tool (iOS, Android) to communicate with the Hermez API and network.
There is an additional Golang SDK to interact with Hermez using Golang.
SDK How-To (Javascript)
In this tutorial we will walk through the process of using the SDK to: 1. Installing Hermezjs 2. Initializing Hermezjs 3. Check registered tokens 4. Creating a wallet 5. Making a deposit from Ethereum into the Hermez Network 6. Verifying the balance in a Hermez account 7. Withdrawing funds back to Ethereum network 8. Making transfers 9. Verifying transaction status 10. Authorizing the creation of Hermez accounts 11. Internal accounts
Install Hermezjs
npm i @hermeznetwork/hermezjs
Import modules
Load Hermezjs library
const hermez = require('@hermeznetwork/hermezjs')
Initialization
Create Transaction Pool
Initialize the storage where user transactions are stored. This needs to be initialized at the start of your application.
hermez.TxPool.initializeTransactionPool()
Configure Hermez Environment
In these examples, we are going to connect to Hermez Testnet
which is deployed in Rinkeby Ethereum Network. To configure Hermezjs
to work with the Testnet, we need to configure a Rinkeby Ethereum node, the Hermez API URL, and the addresses of the Hermez and Withdrawal Delayer smart contracts.
Hermez Testnet API URL is deployed at https://api.testnet.hermez.io/v1.
NOTE: In order to interact with Hermez Testnet, you will need to supply your own Rinkeby Ethereum node. You can check these links to help you set up a Rinkeby node (https://blog.infura.io/getting-started-with-infura-28e41844cc89, https://blog.infura.io/getting-started-with-infuras-ethereum-api).
Currently, Testnet Hermez smart contract is deployed at address 0x14a3b6f3328766c7421034e14472f5c14c5ba090
and Withdrawal Delayer contract is deployed at address 0x6ea0abf3ef52d24427043cad3ec26aa4f2c8e8fd
. These addresses could change in the future, so please check these addresses with a query of the API using the browser.
For the remainder of the examples, we will configure the basic Hermezjs parameters
const EXAMPLES_WEB3_URL = 'https://rinkeby.infura.io/v3/80496a41d0a134ccbc6e856ffd034696'
const EXAMPLES_HERMEZ_API_URL = 'https://api.testnet.hermez.io'
const EXAMPLES_HERMEZ_ROLLUP_ADDRESS = '0x14a3b6f3328766c7421034e14472f5c14c5ba090'
const EXAMPLES_HERMEZ_WDELAYER_ADDRESS = '0x6ea0abf3ef52d24427043cad3ec26aa4f2c8e8fd'
hermez.Providers.setProvider(EXAMPLES_WEB3_URL)
hermez.Environment.setEnvironment({
baseApiUrl: EXAMPLES_HERMEZ_API_URL,
contractAddresses: {
[hermez.Constants.ContractNames.Hermez]: EXAMPLES_HERMEZ_ROLLUP_ADDRESS,
[hermez.Constants.ContractNames.WithdrawalDelayer]: EXAMPLES_HERMEZ_WDELAYER_ADDRESS
}
})
Check token exists in Hermez Network
Before being able to operate on the Hermez Network, we must ensure that the token we want to operate with is listed. For that we make a call to the Hermez Coordinator API that will list all available tokens. All tokens in Hermez Network must be ERC20.
We can see there are 2 tokens registered. ETH
will always be configured at index 0. The second token is HEZ
. For the rest of the examples we will work with ETH
. In the future, more tokens will be included in Hermez.
const token = await hermez.CoordinatorAPI.getTokens()
const tokenERC20 = token.tokens[0]
console.log(token)
>>>>
{
tokens: [
{
itemId: 1,
id: 0,
ethereumBlockNum: 0,
ethereumAddress: '0x0000000000000000000000000000000000000000',
name: 'Ether',
symbol: 'ETH',
decimals: 18,
USD: 1787,
fiatUpdate: '2021-02-28T18:55:17.372008Z'
},
{
itemId: 2,
id: 1,
ethereumBlockNum: 8153596,
ethereumAddress: '0x2521bc90b4f5fb9a8d61278197e5ff5cdbc4fbf2',
name: 'Hermez Network Token',
symbol: 'HEZ',
decimals: 18,
USD: 5.365,
fiatUpdate: '2021-02-28T18:55:17.386805Z'
}
],
pendingItems: 0
Create a Wallet
We can create a new Hermez wallet by providing the Ethereum private key of an Ethereum account. This wallet will store the Ethereum and Baby JubJub keys for the Hermez account. The Ethereum address is used to authorize L1 transactions, and the Baby JubJub key is used to authorize L2 transactions. We will create two wallets.
NOTE You will need to supply two Rinkeby private keys to initialize both accounts. The keys provided here are invalid and are shown as an example.
const EXAMPLES_PRIVATE_KEY1 = 0x705d123e707e25fa37ca84461ac6eb83eb4921b65680cfdc594b60bea1bb4e52
const EXAMPLES_PRIVATE_KEY2 = 0x3a9270c05ac169097808da4b02e8f9146be0f8a38cfad3dcfc0b398076381fdd
// load first account
const wallet = await hermez.HermezWallet.createWalletFromEtherAccount(EXAMPLES_WEB3_URL, { type: 'WALLET', privateKey: EXAMPLES_PRIVATE_KEY1 })
const hermezWallet = wallet.hermezWallet
const hermezEthereumAddress = wallet.hermezEthereumAddress
// load second account
const wallet2 = await hermez.HermezWallet.createWalletFromEtherAccount(EXAMPLES_WEB3_URL, { type: 'WALLET', privateKey: EXAMPLES_PRIVATE_KEY2 })
const hermezWallet2 = wallet2.hermezWallet
const hermezEthereumAddress2 = wallet2.hermezEthereumAddress
Deposit Tokens from Ethereum into Hermez Network
Creating a Hermez account and depositing tokens is done simultaneously as an L1 transaction. In this example we are going to deposit 1 ETH
tokens into the newly created Hermez accounts.
// set amount to deposit
const amountDepositString = '1.0'
const amountDeposit = hermez.Utils.getTokenAmountBigInt(amountDepositString, 18)
const compressedDepositAmount = hermez.HermezCompressedAmount.compressAmount(amountDeposit)
// perform deposit account 1
await hermez.Tx.deposit(
compressedDepositAmount,
hermezEthereumAddress,
tokenERC20,
hermezWallet.publicKeyCompressedHex,
{ type: 'WALLET', privateKey: EXAMPLES_PRIVATE_KEY1 }
)
// perform deposit account 2
await hermez.Tx.deposit(
compressedDepositAmount,
hermezEthereumAddress2,
tokenERC20,
hermezWallet2.publicKeyCompressedHex,
{ type: 'WALLET', privateKey: EXAMPLES_PRIVATE_KEY2 }
)
Internally, the deposit funcion calls the Hermez smart contract to add the L1 transaction.
Verify Balance
A token balance can be obtained by querying the API and passing the hermezEthereumAddress
of the Hermez account.
// get sender account information
const infoAccountSender = (await hermez.CoordinatorAPI.getAccounts(hermezEthereumAddress, [tokenERC20.id]))
.accounts[0]
// get receiver account information
const infoAccountReceiver = (await hermez.CoordinatorAPI.getAccounts(hermezEthereumAddress2, [tokenERC20.id]))
.accounts[0]
console.log(infoAccountSender)
console.log(infoAccountReceiver)
>>>>>
{
accountIndex: 'hez:ETH:4253',
balance: '1099600000000000000',
bjj: 'hez:dMfPJlK_UtFqVByhP3FpvykOg5kAU3jMLD7OTx_4gwzO',
hezEthereumAddress: 'hez:0x74d5531A3400f9b9d63729bA9C0E5172Ab0FD0f6',
itemId: 4342,
nonce: 1,
token: {
USD: 1789,
decimals: 18,
ethereumAddress: '0x0000000000000000000000000000000000000000',
ethereumBlockNum: 0,
fiatUpdate: '2021-02-28T18:55:17.372008Z',
id: 0,
itemId: 1,
name: 'Ether',
symbol: 'ETH'
}
}
{
accountIndex: 'hez:ETH:4254',
balance: '1097100000000000000',
bjj: 'hez:HESLP_6Kp_nn5ANmSGiOnhhYvF3wF5Davf7xGi6lwh3U',
hezEthereumAddress: 'hez:0x12FfCe7D5d6d09564768d0FFC0774218458162d4',
itemId: 4343,
nonce: 6,
token: {
USD: 1789,
decimals: 18,
ethereumAddress: '0x0000000000000000000000000000000000000000',
ethereumBlockNum: 0,
fiatUpdate: '2021-02-28T18:55:17.372008Z',
id: 0,
itemId: 1,
name: 'Ether',
symbol: 'ETH'
}
}
We can see that the field accountIndex
is formed by the token symbol it holds and an index. A Hermez account can only hold one type of token.
Account indexes start at 256. Indexes 0-255 are reserved for internal use.
Note that the balances do not match with the ammount deposited of 1 ETH
because accounts already existed in Hermez Network before the deposit, so we performed a deposit on top
instead.
Alternatively, an account query can be filtered using the assigned accountIndex
const account1ByIdx = await hermez.CoordinatorAPI.getAccount(infoAccountSender.accountIndex)
const account2ByIdx = await hermez.CoordinatorAPI.getAccount(infoAccountReceiver.accountIndex)
console.log(account1ByIdx)
console.log(account2ByIdx)
>>>>>
{
accountIndex: 'hez:ETH:4253',
balance: '1099600000000000000',
bjj: 'hez:dMfPJlK_UtFqVByhP3FpvykOg5kAU3jMLD7OTx_4gwzO',
hezEthereumAddress: 'hez:0x74d5531A3400f9b9d63729bA9C0E5172Ab0FD0f6',
itemId: 4342,
nonce: 1,
token: {
USD: 1789,
decimals: 18,
ethereumAddress: '0x0000000000000000000000000000000000000000',
ethereumBlockNum: 0,
fiatUpdate: '2021-02-28T18:55:17.372008Z',
id: 0,
itemId: 1,
name: 'Ether',
symbol: 'ETH'
}
}
{
accountIndex: 'hez:ETH:4254',
balance: '1097100000000000000',
bjj: 'hez:HESLP_6Kp_nn5ANmSGiOnhhYvF3wF5Davf7xGi6lwh3U',
hezEthereumAddress: 'hez:0x12FfCe7D5d6d09564768d0FFC0774218458162d4',
itemId: 4343,
nonce: 6,
token: {
USD: 1789,
decimals: 18,
ethereumAddress: '0x0000000000000000000000000000000000000000',
ethereumBlockNum: 0,
fiatUpdate: '2021-02-28T18:55:17.372008Z',
id: 0,
itemId: 1,
name: 'Ether',
symbol: 'ETH'
}
}
Withdrawing
Withdrawing funds is a two step process: 1. Exit 2. Withdrawal
Exit
The Exit
transaction is used as a first step to retrieve the funds from Hermez Network
back to Ethereum.
There are two types of Exit
transactions:
- Normal Exit, referred as Exit
from now on. This is a L2 transaction type.
- Force Exit
, an L1 transaction type which has extended guarantees that will be processed by the Coordinator. We will
talk more about Force Exit
here
The Exit
is requested as follows:
// set amount to exit
const amountExit = hermez.HermezCompressedAmount.compressAmount(hermez.Utils.getTokenAmountBigInt('1.0', 18))
// set fee in transaction
const state = await hermez.CoordinatorAPI.getState()
const userFee = state.recommendedFee.existingAccount
// generate L2 transaction
const l2ExitTx = {
type: 'Exit',
from: infoAccountSender.accountIndex,
amount: amountExit,
fee: userFee
}
const exitResponse = await hermez.Tx.generateAndSendL2Tx(l2ExitTx, hermezWallet, infoAccountSender.token)
console.log(exitResponse)
>>>>
{
status: 200,
id: '0x0257305cdc43060a754a5c2ea6b0e0f6e28735ea8e75d841ca4a7377aa099d91b7',
nonce: 2
}
After submitting our Exit
request to the Coordinator, we can check the status of the transaction by calling
the Coordinator's Transaction Pool. The Coordinator's transaction pool stores all those transactions
that are waiting to be forged.
const txPool = await hermez.CoordinatorAPI.getPoolTransaction(exitResponse.id)
console.log(txPool)
>>>>>
{
amount: '1000000000000000000',
fee: 204,
fromAccountIndex: 'hez:ETH:4253',
fromBJJ: 'hez:dMfPJlK_UtFqVByhP3FpvykOg5kAU3jMLD7OTx_4gwzO',
fromHezEthereumAddress: 'hez:0x74d5531A3400f9b9d63729bA9C0E5172Ab0FD0f6',
id: '0x0257305cdc43060a754a5c2ea6b0e0f6e28735ea8e75d841ca4a7377aa099d91b7',
info: null,
nonce: 2,
requestAmount: null,
requestFee: null,
requestFromAccountIndex: null,
requestNonce: null,
requestToAccountIndex: null,
requestToBJJ: null,
requestToHezEthereumAddress: null,
requestTokenId: null,
signature: '38f23d06826be8ea5a0893ee67f4ede885a831523c0c626c102edb05e1cf890e418b5820e3e6d4b530386d0bc84b3c3933d655527993ad77a55bb735d5a67c03',
state: 'pend',
timestamp: '2021-03-16T12:31:50.407428Z',
toAccountIndex: 'hez:ETH:1',
toBjj: null,
toHezEthereumAddress: null,
token: {
USD: 1781.9,
decimals: 18,
ethereumAddress: '0x0000000000000000000000000000000000000000',
ethereumBlockNum: 0,
fiatUpdate: '2021-02-28T18:55:17.372008Z',
id: 0,
itemId: 1,
name: 'Ether',
symbol: 'ETH'
},
type: 'Exit'
}
We can see the state
field is set to pend
(meaning pending). There are 4 possible states:
1. pend : Pending
2. fging : Forging
3. fged : Forged
4. invl : Invalid
If we continue polling the Coordinator about the status of the transaction, the state will eventually be set to fged
.
We can also query the Coordinator to check whether or not our transaction has been forged. getHistoryTransaction
reports those transactions
that have been forged by the Coordinator.
const txExitConf = await hermez.CoordinatorAPI.getHistoryTransaction(txExitPool.id)
console.log(txExitConf)
And we can confirm our account status and check that the correct amount has been transfered out of the account.
console.log((await hermez.CoordinatorAPI.getAccounts(hermezEthereumAddress, [tokenERC20.id]))
.accounts[0])
Withdrawing Funds from Hermez
After doing any type of Exit
transaction, which moves the user's funds from their token account to a specific Exit Merkle tree, one needs to do a Withdraw
of those funds to an Ethereum L1 account.
To do a Withdraw
we need to indicate the accountIndex
that includes the Ethereum address where the funds will be transferred, the amount and type of tokens, and some information
to verify the ownership of those tokens. Additionally, there is one boolean flag. If set to true, the Withdraw
will be instantaneous.
const exitInfoN = (await hermez.CoordinatorAPI.getExits(infoAccountSender.hezEthereumAddress, true)).exits
const exitInfo = exitInfoN[exitInfoN.length - 1]
// set to perform instant withdraw
const isInstant = true
// perform withdraw
await hermez.Tx.withdraw(
exitInfo.balance,
exitInfo.accountIndex,
exitInfo.token,
hermezWallet.publicKeyCompressedHex,
exitInfo.batchNum,
exitInfo.merkleProof.siblings,
isInstant,
{ type: 'WALLET', privateKey: EXAMPLES_PRIVATE_KEY1 }
)
The funds should now appear in the Ethereum account that made the withdrawal.
Force Exit
This is the L1 equivalent of an Exit. With this option, the smart contract forces Coordinators to pick up L1 transactions before they pick up L2 transactions to ensure that L1 transactions will eventually be picked up.
This is a security measure. We don't expect users to need to make a Force Exit.
// set amount to force-exit
const amountForceExit = hermez.HermezCompressedAmount.compressAmount(hermez.Utils.getTokenAmountBigInt('1.0', 18))
// perform force-exit
await hermez.Tx.forceExit(
amountForceExit,
infoAccountSender.accountIndex,
tokenERC20,
{ type: 'WALLET', privateKey: EXAMPLES_PRIVATE_KEY1 }
)
The last step to recover the funds will be to send a new Withdraw
request to the smart contract as we did after the regular Exit
request.
```js
const exitInfoN = (await hermez.CoordinatorAPI.getExits(infoAccountSender.hezEthereumAddress, true)).exits
const exitInfo = exitInfoN[exitInfoN.length - 1]
// set to perform instant withdraw
const isInstant = true
// perform withdraw
await hermez.Tx.withdraw(
exitInfo.balance,
exitInfo.accountIndex,
exitInfo.token,
hermezWallet.publicKeyCompressedHex,
exitInfo.batchNum,
exitInfo.merkleProof.siblings,
isInstant,
{ type: 'WALLET', privateKey: EXAMPLES_PRIVATE_KEY1 }
)
Transfers
First, we compute the fees for the transaction. For this we consult the recommended fees from the Coordinator.
// fee computation
const state = await hermez.CoordinatorAPI.getState()
console.log(state.recommendedFee)
>>>>
{
existingAccount: 96.34567219671051,
createAccount: 192.69134439342102,
createAccountInternal: 240.86418049177627
}
The returned fees are the suggested fees for different transactions: - existingAccount : Make a transfer to an existing account - createAccount : Make a transfer to a non-existent account, and create a regular account - createAccountInternal : Make a transfer to an non-existent account and create internal account
The fee amounts are given in USD. However, fees are payed in the token of the transaction. So, we need to do a conversion.
const usdTokenExchangeRate = tokenERC20.USD
const fee = fees.existingAccount / usdTokenExchangeRate
Finally we make the final transfer transaction.
// set amount to transfer
const amountTransfer = hermez.HermezCompressedAmount.compressAmount(hermez.Utils.getTokenAmountBigInt('1.0', 18))
// generate L2 transaction
const l2TxTransfer = {
from: infoAccountSender.accountIndex,
to: infoAccountReceiver.accountIndex,
amount: amountTransfer,
fee: fee
}
const transferResponse = await hermez.Tx.generateAndSendL2Tx(l2TxTransfer, hermezWallet, infoAccountSender.token)
console.log(transferResponse)
>>>>>
{
status: 200,
id: '0x02e7c2c293173f21249058b1d71afd5b1f3c0de4f1a173bac9b9aa4a2d149483a2',
nonce: 3
}
The result status 200 shows that transaction has been correctly received. Additionally, we receive the nonce matching the transaction we sent,
and an id that we can use to verify the status of the transaction either using hermez.CoordinatorAPI.getHistoryTransaction()
or hermez.CoordinatorAPI.getPoolTransaction()
.
As we saw with the Exit
transaction, every transaction includes a ´nonce´. This nonce
is a protection mechanism to avoid replay attacks. Every L2 transaction will increase the nonce by 1.
Verifying Transaction Status
Transactions received by the Coordinator will be stored in its transaction pool while they haven't been processed. To check a transaction in the transaction pool we make a query to the Coordinator node.
const txXferPool = await hermez.CoordinatorAPI.getPoolTransaction(transferResponse.id)
console.log(txXferPool)
>>>>>
{
amount: '100000000000000',
fee: 202,
fromAccountIndex: 'hez:ETH:4253',
fromBJJ: 'hez:dMfPJlK_UtFqVByhP3FpvykOg5kAU3jMLD7OTx_4gwzO',
fromHezEthereumAddress: 'hez:0x74d5531A3400f9b9d63729bA9C0E5172Ab0FD0f6',
id: '0x02e7c2c293173f21249058b1d71afd5b1f3c0de4f1a173bac9b9aa4a2d149483a2',
info: null,
nonce: 3,
requestAmount: null,
requestFee: null,
requestFromAccountIndex: null,
requestNonce: null,
requestToAccountIndex: null,
requestToBJJ: null,
requestToHezEthereumAddress: null,
requestTokenId: null,
signature: 'c9e1a61ce2c3c728c6ec970ae646b444a7ab9d30aa6015eb10fb729078c1302978fe9fb0419b4d944d4f11d83582043a48546dff7dda22de7c1e1da004cd5401',
state: 'pend',
timestamp: '2021-03-16T13:20:33.336469Z',
toAccountIndex: 'hez:ETH:4254',
toBjj: 'hez:HESLP_6Kp_nn5ANmSGiOnhhYvF3wF5Davf7xGi6lwh3U',
toHezEthereumAddress: 'hez:0x12FfCe7D5d6d09564768d0FFC0774218458162d4',
token: {
USD: 1786,
decimals: 18,
ethereumAddress: '0x0000000000000000000000000000000000000000',
ethereumBlockNum: 0,
fiatUpdate: '2021-02-28T18:55:17.372008Z',
id: 0,
itemId: 1,
name: 'Ether',
symbol: 'ETH'
},
type: 'Transfer'
}
We can also check directly with the Coordinator in the database of forged transactions.
const transferConf = await hermez.CoordinatorAPI.getHistoryTransaction(transferResponse.id)
console.log(transferConf)
>>>>>
{
L1Info: null,
L1orL2: 'L2',
L2Info: { fee: 202, historicFeeUSD: 182.8352, nonce: 3 },
amount: '100000000000000',
batchNum: 4724,
fromAccountIndex: 'hez:ETH:4253',
fromBJJ: 'hez:dMfPJlK_UtFqVByhP3FpvykOg5kAU3jMLD7OTx_4gwzO',
fromHezEthereumAddress: 'hez:0x74d5531A3400f9b9d63729bA9C0E5172Ab0FD0f6',
historicUSD: 0.17855,
id: '0x02e7c2c293173f21249058b1d71afd5b1f3c0de4f1a173bac9b9aa4a2d149483a2',
itemId: 14590,
position: 1,
timestamp: '2021-03-16T13:24:48Z',
toAccountIndex: 'hez:ETH:4254',
toBJJ: 'hez:HESLP_6Kp_nn5ANmSGiOnhhYvF3wF5Davf7xGi6lwh3U',
toHezEthereumAddress: 'hez:0x12FfCe7D5d6d09564768d0FFC0774218458162d4',
token: {
USD: 1787.2,
decimals: 18,
ethereumAddress: '0x0000000000000000000000000000000000000000',
ethereumBlockNum: 0,
fiatUpdate: '2021-02-28T18:55:17.372008Z',
id: 0,
itemId: 1,
name: 'Ether',
symbol: 'ETH'
},
type: 'Transfer'
}
At this point, the balances in both accounts will be updated with the result of the transfer
// check balances
console.log((await hermez.CoordinatorAPI.getAccounts(wallet.hermezEthereumAddress, [tokenERC20.id])).accounts[0])
console.log((await hermez.CoordinatorAPI.getAccounts(wallet2.hermezEthereumAddress2, [tokenERC20.id])).accounts[0])
>>>>>
{
accountIndex: 'hez:ETH:4253',
balance: '477700000000000000',
bjj: 'hez:dMfPJlK_UtFqVByhP3FpvykOg5kAU3jMLD7OTx_4gwzO',
hezEthereumAddress: 'hez:0x74d5531A3400f9b9d63729bA9C0E5172Ab0FD0f6',
itemId: 4342,
nonce: 4,
token: {
USD: 1793,
decimals: 18,
ethereumAddress: '0x0000000000000000000000000000000000000000',
ethereumBlockNum: 0,
fiatUpdate: '2021-02-28T18:55:17.372008Z',
id: 0,
itemId: 1,
name: 'Ether',
symbol: 'ETH'
}
}
{
accountIndex: 'hez:ETH:256',
balance: '1874280899837791518',
bjj: 'hez:YN2DmRh0QgDrxz3NLDqH947W5oNys7YWqkxsQmFVeI_m',
hezEthereumAddress: 'hez:0x9F255048EC1141831A28019e497F3f76e559356E',
itemId: 1,
nonce: 2,
token: {
USD: 1793,
decimals: 18,
ethereumAddress: '0x0000000000000000000000000000000000000000',
ethereumBlockNum: 0,
fiatUpdate: '2021-02-28T18:55:17.372008Z',
id: 0,
itemId: 1,
name: 'Ether',
symbol: 'ETH'
}
}
Create Account Authorization
Imagine that Bob wants to send a transfer of Ether to Mary using Hermez, but Mary only has an Ethereum account but no Hermez account. To complete this transfer, Mary could open a Hermez account and proceed as the previous transfer example. Alternatively, Mary could authorize the Coordinator to create a Hermez account on her behalf so that she can receive Bob's transfer.
First we create a wallet for Mary:
// load second account
const wallet3 = await hermez.HermezWallet.createWalletFromEtherAccount(EXAMPLES_WEB3_URL, { type: 'WALLET', privateKey: EXAMPLES_PRIVATE_KEY3 })
const hermezWallet3 = wallet3.hermezWallet
const hermezEthereumAddress3 = wallet3.hermezEthereumAddress
The authorization for the creation of a Hermez account is done using the private key stored in the newly created Hermez wallet.
Note that the account is not created at this moment. The account will be created when Bob performs the transfer. Also, it is Bob that pays for the fees associated with the account creation.
const EXAMPLES_PRIVATE_KEY3 = '0x3d228fed4dc371f56b8f82f66ff17cd6bf1da7806d7eabb21810313dee819a53'
const signature = await hermezWallet3.signCreateAccountAuthorization(EXAMPLES_WEB3_URL, { type: 'WALLET', privateKey: EXAMPLES_PRIVATE_KEY3 })
const res = await hermez.CoordinatorAPI.postCreateAccountAuthorization(
hermezWallet3.hermezEthereumAddress,
hermezWallet3.publicKeyBase64,
signature
)
We can find out if the Coordinator has been authorized to create a Hermez account on behalf of a user by:
const authResponse = await hermez.CoordinatorAPI.getCreateAccountAuthorization(wallet3.hermezEthereumAddress)
console.log(authResponse)
>>>>
{
hezEthereumAddress: 'hez:0xd3B6DcfCA7Eb3207905Be27Ddfa69453625ffbf9',
bjj: 'hez:ct0ml6FjdUN6uGUHZ70qOq5-58cZ19SJDeldMH021oOk',
signature: '0x22ffc6f8d569a92c48a4e784a11a9e57b840fac21eaa7fedc9dc040c4a45d502744a35eeb0ab173234c0f687b252bd0364647bff8db270ffcdf1830257de28e41c',
timestamp: '2021-03-16T14:56:05.295946Z'
}
Once we verify the receiving Ethereum account has authorized the creation of a Hermez account, we can proceed with the transfer from Bob's account to Mary's account. For this, we set the destination address to Mary's Ethereum address and set the fee using the createAccount
value.
// set amount to transfer
const amountTransferAuth = hermez.HermezCompressedAmount.compressAmount(hermez.Utils.getTokenAmountBigInt('1.0', 18))
// generate L2 transaction
const l2AuthTxTransfer = {
from: infoAccountSender.accountIndex,
to: hermezWallet3.hermezEthereumAddress
amount: amountTransferAuth,
fee: state.recommendedFee.createAccount / usdTokenExchangeRate
}
const accountAuthTransferResponse = await hermez.Tx.generateAndSendL2Tx(l2AuthTxTransfer, hermezWallet, infoAccountSender.token)
console.log(accountAuthTransferResponse)
>>>>>
{
status: 200,
id: '0x025398af5b69f132d8c2c5b7b225df1436baf7d1774a6b017a233bf273b4675c8f',
nonce: 0
}
After the transfer has been forged, we can check Mary's account on Hermez
// get receiver account information
const infoAccountAuth = (await hermez.CoordinatorAPI.getAccounts(hermezWallet3.hermezEthereumAddress, [tokenERC20.id]))
.accounts[0]
console.log(infoAccountAuth)
>>>>>
{
accountIndex: 'hez:ETH:265',
balance: '1000000000000000',
bjj: 'hez:ct0ml6FjdUN6uGUHZ70qOq5-58cZ19SJDeldMH021oOk',
hezEthereumAddress: 'hez:0xd3B6DcfCA7Eb3207905Be27Ddfa69453625ffbf9',
itemId: 10,
nonce: 0,
token: {
USD: 1795.94,
decimals: 18,
ethereumAddress: '0x0000000000000000000000000000000000000000',
ethereumBlockNum: 0,
fiatUpdate: '2021-03-16T14:56:57.460862Z',
id: 0,
itemId: 1,
name: 'Ether',
symbol: 'ETH'
}
}
Create Internal Accounts
Until now we have seen that accounts have an Ethereum address and a Baby JubJub key. This is the case for normal accounts. However, there is a second type of account that only requires a Baby JubJub key. These accounts are called internal accounts
.
The advantage of these accounts is that they are much more inexpensive to create than a normal account
, since these accounts only exist on Hermez. The downside is that one cannot perform deposits or withdrawals from this type of account. However, there are some scenarios where these accounts are useful. For example, in those scenarios where one requires a temporary account. (for example, Exchanges could use these accounts to receive a transfer from users).
// Create Internal Account
// create new bjj private key to receive user transactions
const pvtBjjKey = Buffer.allocUnsafe(32).fill('1')
// create rollup internal account from bjj private key
const wallet4 = await hermez.HermezWallet.createWalletFromBjjPvtKey(pvtBjjKey)
const hermezWallet4 = wallet4.hermezWallet
// set amount to transfer
const amountTransferInternal = hermez.HermezCompressedAmount.compressAmount(hermez.Utils.getTokenAmountBigInt('1.0', 18))
// generate L2 transaction
const transferToInternal = {
from: infoAccountSender.accountIndex,
to: hermezWallet4.publicKeyBase64,
amount: amountTransferInternal,
fee: state.recommendedFee.createAccountInternal / usdTokenExchangeRate
}
const internalAccountResponse = await hermez.Tx.generateAndSendL2Tx(transferToInternal, hermezWallet, tokenERC20)
console.log(internalAccountResponse)
>>>>>
{
status: 200,
id: '0x02ac000f39eee60b198c85348443002991753de912337720b9ef85d48e9dcfe83e',
nonce: 0
}
Once the transaction is forged, we can check the account information
// get internal account information
const infoAccountInternal = (await hermez.CoordinatorAPI.getAccounts(hermezWallet4.publicKeyBase64, [tokenERC20.id]))
.accounts[0]
console.log(infoAccountInternal)
>>>>>>
{
accountIndex: 'hez:ETH:259',
balance: '1000000000000000000',
bjj: 'hez:KmbnR34pOUhSaaPOkeWbaeZVjMqojfyYy8sYIHRSlaKx',
hezEthereumAddress: 'hez:0xFFfFfFffFFfffFFfFFfFFFFFffFFFffffFfFFFfF',
itemId: 4,
nonce: 0,
token: {
USD: 1798.51,
decimals: 18,
ethereumAddress: '0x0000000000000000000000000000000000000000',
ethereumBlockNum: 0,
fiatUpdate: '2021-03-16T15:44:08.33507Z',
id: 0,
itemId: 1,
name: 'Ether',
symbol: 'ETH'
}
}
We can verify it is in fact an internal account
because the associated hezEthereumAddress
is hez:0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
.