LiquidAccounts Getting Started

  _       _                   _       _      _                                            _         
 | |     (_)   __ _   _   _  (_)   __| |    / \      ___    ___    ___    _   _   _ __   | |_   ___ 
 | |     | |  / _` | | | | | | |  / _` |   / _ \    / __|  / __|  / _ \  | | | | | '_ \  | __| / __|
 | |___  | | | (_| | | |_| | | | | (_| |  / ___ \  | (__  | (__  | (_) | | |_| | | | | | | |_  \__ \
 |_____| |_|  \__, |  \__,_| |_|  \__,_| /_/   \_\  \___|  \___|  \___/   \__,_| |_| |_|  \__| |___/
                 |_|                                                                                

LiquidAccounts are EOS accounts that are stored in vRAM instead of RAM. This drastically reduces the cost of creating accounts on EOS. Another great place to understand the service is in the unit tests.

Prerequisites

Unbox LiquidAccounts DAPP Service box

This box contains the LiquidAccounts smart contract libraries, DSP node logic, unit tests, and everything else needed to get started integrating / testing the DAPP Network LiquidAccounts in your smart contract.

mkdir vaccounts-dapp-service; cd vaccounts-dapp-service
# npm install -g @liquidapps/zeus-cmd
zeus box create
zeus unbox vaccounts-dapp-service
zeus test -c

LiquidAccount Consumer Example Contract used in unit tests

in zeus_boxes/contracts/eos/vaccountsconsumer/vaccountsconsumer.cpp The consumer contract is a great starting point for playing around with the LiquidAccount syntax.

/* DELAY REMOVAL OF USER DATA INTO VRAM */
/* ALLOWS FOR QUICKER ACCESS TO USER DATA WITHOUT THE NEED TO WARM DATA UP */
#define VACCOUNTS_DELAYED_CLEANUP 120

/* ADD NECESSARY LIQUIDACCOUNT / VRAM INCLUDES */
#include "../dappservices/vaccounts.hpp"
#include "../dappservices/ipfs.hpp"
#include "../dappservices/multi_index.hpp"

/* ADD LIQUIDACCOUNT / VRAM RELATED ACTIONS */
#define DAPPSERVICES_ACTIONS() \
  XSIGNAL_DAPPSERVICE_ACTION \
  IPFS_DAPPSERVICE_ACTIONS \
  VACCOUNTS_DAPPSERVICE_ACTIONS

#define DAPPSERVICE_ACTIONS_COMMANDS() \
  IPFS_SVC_COMMANDS()VACCOUNTS_SVC_COMMANDS() 
  
#define CONTRACT_NAME() vaccountsconsumer 


CONTRACT_START()
  
  /* THE FOLLOWING STRUCT DEFINES THE PARAMS THAT MUST BE PASSED */
  struct dummy_action_hello {
      name vaccount;
      uint64_t b;
      uint64_t c;
  
      EOSLIB_SERIALIZE( dummy_action_hello, (vaccount)(b)(c) )
  };
  
  /* DATA IS PASSED AS PAYLOADS INSTEAD OF INDIVIDUAL PARAMS */
  [[eosio::action]] void hello(dummy_action_hello payload) {
    /* require_vaccount is the equivalent of require_auth for EOS */
    require_vaccount(payload.vaccount);
    
    print("hello from ");
    print(payload.vaccount);
    print(" ");
    print(payload.b + payload.c);
    print("\n");
  }
  
  [[eosio::action]] void hello2(dummy_action_hello payload) {
    print("hello2(default action) from ");
    print(payload.vaccount);
    print(" ");
    print(payload.b + payload.c);
    print("\n");
  }
  
  [[eosio::action]] void init(dummy_action_hello payload) {
  }
  
  /* EACH ACTION MUST HAVE A STRUCT THAT DEFINES THE PAYLOAD SYNTAX TO BE PASSED */
  VACCOUNTS_APPLY(((dummy_action_hello)(hello))((dummy_action_hello)(hello2)))
  
CONTRACT_END((init)(hello)(hello2)(regaccount)(xdcommit)(xvinit)(xvauth))

Use LiquidAccounts between contracts

To enable the usage of LiquidAccounts between contracts, the subscriber contract (contract that wishes to use LiquidAccounts of another host contract) must add the #define VACCOUNTS_SUBSCRIBER definition to the smart contract. The subscriber contract must also be staked to a DSP offering the LiquidAccounts service, but this DSP does not need to be the same DSP as the DSP providing services to the host contract. In place of setting the CHAIN_ID with the xvinit action (detailed below), the account name of the host account providing the LiquidAccounts (not the DSP account) must be used in its place. The subscriber contract may also subscribe to a “cross-chain host” in the same manner.

Use LiquidAccounts across chains

To enable the usage of LiquidAccounts across chains, a “cross-chain host” must be deployed on each supported EOSIO chain. This “cross-chain host” will be configured to use the LiquidAccounts that have been registered on the “host chain” contract. To convert a LiquidAccount contract to a “cross-chain host” add the following defines to the contract:

#define LIQUIDX
#define USE_ADVANCED_IPFS
#define VACCOUNTS_CROSSCHAIN

and remove (regaccount) from the CONTRACT_END

Optional:

#define VACCOUNTS_CROSSCHAIN_NONCE_VARIANCE 5 // defaults to 5 if not defined

The VACCOUNTS_CROSSCHAIN_NONCE_VARIANCE is the difference of allowed variance from the calculated nonce at run time. For example, if the calculated nonce is 10 and the variance is 5, then if a transactions is attempted with a nonce of 4 or less, the transaction will be rejected. The variance’s purpose is to allow leeway in the event that multiple accounts are using the sidechain’s contract at the same time. This allows the nonce to be outdated between the time the client pulls the nonce and pushes the transaction, potentially a few blocks in the future.

No changes are required to the LiquidAccount contract on the “host chain”. All registration of new accounts must occur on the “host chain”. The final step is to use the xvinit action with the parameters chainid, hostchain, and hostcode. When the host chain is the EOSIO mainnet, hostchain will be mainnet. If another chain is used as the host chain, use the chain name as specified in the LiquidX chain mapping. The hostcode is the account name of the LiquidAccounts contract.

Compile

See the unit testing section for details on adding unit tests.

zeus compile
# test without compiling
zeus test
# compile and test with
zeus test -c

Deploy Contract

export DSP_ENDPOINT=https://kylin-dsp-2.liquidapps.io
export KYLIN_TEST_ACCOUNT=<ACCOUNT_NAME>
export KYLIN_TEST_PUBLIC_KEY=<ACTIVE_PUBLIC_KEY>
# Buy RAM:
cleos -u $DSP_ENDPOINT system buyram $KYLIN_TEST_ACCOUNT $KYLIN_TEST_ACCOUNT "200.0000 EOS" -p $KYLIN_TEST_ACCOUNT@active
# Set contract code and abi
cleos -u $DSP_ENDPOINT set contract $KYLIN_TEST_ACCOUNT vaccountsconsumer -p $KYLIN_TEST_ACCOUNT@active

# Set contract permissions, add eosio.code
cleos -u $DSP_ENDPOINT set account permission $KYLIN_TEST_ACCOUNT active "{\"threshold\":1,\"keys\":[{\"weight\":1,\"key\":\"$KYLIN_TEST_PUBLIC_KEY\"}],\"accounts\":[{\"permission\":{\"actor\":\"$KYLIN_TEST_ACCOUNT\",\"permission\":\"eosio.code\"},\"weight\":1}]}" owner -p $KYLIN_TEST_ACCOUNT@active

Test

First you’ll need to initialize the LiquidAccounts implementation with the chain_id of the platform you’re operating on. If you are using the #define VACCOUNTS_SUBSCRIBER definition to use LiquidAccounts from another host account, that host account must be used in place of the chain_id.

# kylin
export CHAIN_ID=5fff1dae8dc8e2fc4d5b23b2c7665c97f9e9d8edf2b6485a86ba311c25639191
cleos -u $DSP_ENDPOINT push action $KYLIN_TEST_ACCOUNT xvinit "[\"$CHAIN_ID\"]" -p $KYLIN_TEST_ACCOUNT
# if using VACCOUNTS_CROSSCHAIN
export CHAIN_ID=5fff1dae8dc8e2fc4d5b23b2c7665c97f9e9d8edf2b6485a86ba311c25639191
export HOST_CHAIN=chainname
export HOST_CODE=vaccnthost
cleos -u $DSP_ENDPOINT push action $KYLIN_TEST_ACCOUNT xvinit "[\"$CHAIN_ID\",\"$HOST_CHAIN\",\"$HOST_CODE\"]" -p $KYLIN_TEST_ACCOUNT
# if using VACCOUNTS_SUBSCRIBER
export HOST_ACCOUNT_NAME=kylinhost
cleos -u $DSP_ENDPOINT push action $KYLIN_TEST_ACCOUNT xvinit "[\"$HOST_ACCOUNT_NAME\"]" -p $KYLIN_TEST_ACCOUNT

Then you can begin registering accounts. You will need to do this either in a nodejs environment using the dapp-client-lib, or you can use the zeus vaccounts push-action. Here is an example of using the lib to register an account..

All payloads must include a key value pair with "vaccount":"vaccountname" or the transaction will fail. This is so the dapp-client can fetch the nonce associated with the LiquidAccount.

dapp-client

npm install -g @liquidapps/dapp-client

This example takes:

  • the contract name the LiquidAccount project is deployed to
  • the active private key of that account
  • the regaccount as the action name
  • the payload with the vaccount name

After registering an account, you may also use the library to push LiquidAccount transactions. In the linked example, you can see that the action name has changed to hello and the payload has changed to include the required parameters.

Zeus vaccounts push-action

Push LiquidAccount actions easily with zeus’s wrapper of the dapp-client library. You can pass a --dsp-url for your DAPP Service Provider’s API endpoint. Then pass the name of the contract that the LiquidAccount code is deployed to, the action name (regaccount for example), and the payload.

You also have the ability to store your private keys with or without encryption. If you choose to encrypt, you can pass the --encrypted flag when creating a new account to store the keys. You can provide a password by command line, or with the flag --password. If you use the account again, zeus will look for the key based on what network you are operating on. If it finds it, it will use that key to sign and prompt for a password if needed.

zeus vaccounts push-action <CONTRACT> <ACTION> <PAYLOAD> --dsp-url https://kylin-dsp-2.liquidapps.io

# optional flags:

--dsp-url # url to DAPP Service Provider's API endpoint
# default: https://kylin-dsp-2.liquidapps.io
--private-key # LiquidAccount private key, can be provided or auto generated
# will be auto generated and stored in the storage path if not provided
--encrypted # Encrypt the LiquidAccount keys with a password
# default: false
--password # password to encrypt the keys with
--network # network LiquidAccount contract deployed on (other options: kylin, jungle, mainnet)
# development (local)
--storage-path # path to the wallet which will store the LiquidAccount key
# default: path.join(require('os').homedir(), '.zeus')

# Example:
zeus vaccounts push-action test1v regaccount '{"vaccount":"vaccount1"}'
zeus vaccounts push-action vacctstst123 regaccount '{"vaccount":"vaccount2"}' --private-key 5KJL... -u https://kylin-dsp-2.liquidapps.io
zeus vaccounts push-action vacctstst123 regaccount '{"vaccount":"vaccount3"}' -u http://kylin-dsp-2.liquidapps.io/ --encrypted --network=kylin --password=password

Deserialize Payload | des-payload.js

This file is under the utility/tool and it is for deserializing xvexec data (vaccount action data).

Example:

deserializeVactionPayload('dappaccoun.t', '6136465e000000002a00000000000000aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e90600000000000000008090d9572d3ccdcd002370ae375c19feaa49e0d336557df8aa49010000000000000004454f5300000000026869', 'https://mainnet.eos.dfuse.io')

returns

{
   "payload":{
      "expiry":"1581659745",
      "nonce":"42",
      "chainid":"ACA376F206B8FC25A6ED44DBDC66547C36C6C33E3A119FFBEAEF943642F0E906",
      "action":{
         "account":"",
         "action_name":"transfervacc",
         "authorization":[

         ],
         "action_data":"70AE375C19FEAA49E0D336557DF8AA49010000000000000004454F5300000000026869"
      }
   },
   "deserializedAction":{
      "payload":{
         "vaccount":"dapjwaewayrb",
         "to":"dapjkzepavdy",
         "quantity":"0.0001 EOS",
         "memo":"hi"
      }
   }
}