HYDRA Documentation
  • Introduction to Hydra Chain
  • HydraGon
    • Migrate to HydraGon
    • Staking Calculator
  • Legacy Hydra
  • FAQ
  • Hydra web wallet
    • Create New Wallet
      • Key File Option
      • Mnemonic Words Option
    • Access Your Wallet
      • From Mnemonic Words
      • From Private Key
      • From Key File
    • Send and Receive Assets
      • Receive Assets
      • Send Assets
    • Add HRC20 Token
    • Setup Offline Wallet
  • Hydra web browser extension
    • How to integrate with dApps
  • Hydra for Beginners
  • Ledger Nano Guide
  • Hydra Bridge
  • HydraDEX
    • Adding and Removing Liquidity
    • Liquidity Mining on HydraDEX
  • Useful Links (Legacy)
  • Essentials
    • UTXOs Accounting
    • Test & Main Networks
    • Desktop wallet basic usage
    • Wallet Encrypt, Backup and Restore
    • Hydra Core Wallet Commands
    • Adding Nodes
    • Encrypt and Unlock Hydra Wallet
    • Wallet Recovery With Salvagewallet
    • bech32 support
    • Repositories
    • Hydra Exchange Usage Guide
    • How to Add Options
    • How to Use bootstrap.dat
    • Command Lines (RPC API)
    • Guidance of Hydra Deployment and RPC Settings
    • How to Build Hydra on Raspbian
  • HRC20 Tokens
    • HRC20 Token
    • HRC20 Raw Transactions
    • HRC20 With Hydrachainjs
    • HRC20 DApp
  • HRC721 Tokens
    • HRC721 Token - How to deploy
  • How Transactions Work
  • Hydra Economy (Legacy)
    • The Flexible Supply Mechanism
    • Legacy Staking Calculator
  • Installation Guides
  • Guide for Linux
  • Guide for Raspberry Pi
  • Guide for MacOS
  • Staking HYDRA Coins
    • Setting up Staking
    • Staking with Windows VPS on AWS
    • Staking with Linux on DigitalOcean VPS
    • How to Stake Hydra on Linux
    • Stake With Linux VPS
    • How to Stake on FreeBSD
    • Hydra node health check
    • Superstaking
    • Delegating to Superstaker
    • Delegating via Mobile App or Web Browser
    • Lydra Basics
    • Understanding LYDRA — Key Concepts and Dynamics
  • Hydra Chain Core Team
  • KYC/AML Policy
  • Privacy Policy
  • API Documentation
    • Explorer API (in work)
      • General Blockchain Info
      • Fetching Transaction History for HYDRA and HRC20 tokens
      • Block Info
      • Transaction Info
    • Hydra DEX API
  • Community Tools
    • Github repository
    • Docker image for Hydra Node
    • Hydradex.org Custom Lists
  • Security Audits Hydra Bridge
Powered by GitBook
On this page
  • HRC20 With Hydrachainjs
  • Note On NodeJS Compatibility
  • Note on Code Editor
  • Setup The HRC20 CLI Project
  • Getting The Total Supply
  • Calling A Read-Only Method
  • Call Method With Arguments
  • Send VS Call
  • Mint Tokens With Send
  • Token Transfer
  • Observing Contract Events
  • Conclusion

Was this helpful?

  1. HRC20 Tokens

HRC20 With Hydrachainjs

PreviousHRC20 Raw TransactionsNextHRC20 DApp

Last updated 4 years ago

Was this helpful?

HRC20 With Hydrachainjs

In this chapter we will use to build a NodeJS CLI tool to interact with the HRC20 .

You can download the project code:

Note On NodeJS Compatibility

You'll need a version of node that supports . You should be ok if your version number is greater than 8.

My version is 8.6 (nothing special about this version...):

node --version

v8.6.0

I recommend that you download the version (8.9.3):

You can test whether async/await is supported or not by entering into the node REPL:

$ node

Then create an async function:

> async () => { }
[AsyncFunction]

If for some reason you need to run hydrajs on a platform that does not support async/await, please create an issue.

Note on Code Editor

Setup The HRC20 CLI Project

Let's clone the NodeJS project to the directory mytoken-js:

git clone https://github.com/hydra-chain/hydrachainjs-token-cli.git mytoken-js

The project dependencies are listed in package.json:

{
  ...

  "dependencies": {
    "minimist": "^1.2.0",
    "ora": "^1.3.0",
    "hydrajs": "^1.4.1"
  }
}

Install these dependencies:

npm install

Getting The Total Supply

Let's try to get the token's total supply. Run the script index.js:

node index.js supply

Error: Cannot find module './solar.json'

Oops, the script needs to load information about the contracts you have deployed.


const repo = require("./solar.json")


const myToken = new Contract(rpc, repo.contracts[
  "zeppelin-solidity/contracts/token/CappedToken.sol"
])
  • The require function loads solar.json as a JavaScript object.

You should link (or copy) solar.development.json generated in the previous chapter to the project directory as solar.json:

ln -s ~/hydrabook/examples/mytoken/solar.development.json solar.json

Now try again:

node index.js supply

supply 14000

Yay it works (hopefully).

Calling A Read-Only Method

The Solidity method we called is:

function totalSupply() public view returns(uint256)

The ABI definition (loaded from solar.json) is:

{
  "name": "totalSupply",
  "type": "function",
  "payable": false,
  "inputs": [],
  "outputs": [
    {
      "name": "",
      "type": "uint256",
      "indexed": false
    }
  ],
  "constant": true,
  "anonymous": false
}

And to call this method using JavaScript:



async function totalSupply() {
  const result = await myToken.call("totalSupply")

  
  const supply = result.outputs[0]

  console.log("supply", supply.toNumber())
}

The result object contains other useful information aside from the returned values.

Do console.log(result) to print it out:

{ address: 'a778c05f1d0f70f1133f4bbf78c1a9a7bf84aed3',
  executionResult:
   { gasUsed: 21689,
     excepted: 'None',
     newAddress: 'a778c05f1d0f70f1133f4bbf78c1a9a7bf84aed3',
     output: '00000000000000000000000000000000000000000000000000000000000036b0',
     codeDeposit: 0,
     gasRefunded: 0,
     depositSize: 0,
     gasForDeposit: 0 },
  transactionReceipt:
   { stateRoot: '5a0d9cd5df18165c75755f4345ca81da94f9247c1c031171fd6e2ce1a368844c',
     gasUsed: 21689,
     bloom: '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000',
     log: [] },
  outputs: [  ] }

If you hover your mouse cursor over the result variable, you should see that its type is IContractCallDecodedResult:

export interface IContractCallDecodedResult extends IRPCCallContractResult {
    outputs: any[];
}

export interface IRPCCallContractResult {
    address: string;
    executionResult: IExecutionResult;
    transactionReceipt: {
        stateRoot: string;
        gasUsed: string;
        bloom: string;
        log: any[];
    };
}

export interface IExecutionResult {
    gasUsed: number;
    excepted: string;
    newAddress: string;
    output: string;
    codeDeposit: number;
    gasRefunded: number;
    depositSize: number;
    gasForDeposit: number;
}

Call Method With Arguments

The balance subcommand checks how many tokens an account has:

node index.js balance dcd32b87270aeb980333213da2549c9907e09e94

balance: 13700

The JavaScript code that implements this:

async function balanceOf(owner) {
  const res = await myToken.call("balanceOf", [owner])

  
  const balance = res.outputs[0]

  console.log(`balance:`,  balance.toNumber())
}

The arguments to balanceOf are passed in as an array.

Send VS Call

Confusingly, there are two ways to invoke a method: send and call. These two names are inherited from Ethereum. A more descriptive way to name them is perhaps to call send "commit" and call "query".

  • call (or "query"): executes contract code on your own local hyrad node as a "simulation", returning results, but not changing the blockchain. This is free.

  • send (or "commit"): creates an actual transaction that would execute code globally on the network, changing the blockchain. This costs gas.

Next, we are going to mint some new tokens using hydrajs. And because minting token changes the blockchain, we'll use send.

Mint Tokens With Send

The mint command creates new tokens by using send to create a new transaction. Then it waits for that transaction to confirm:

node index.js mint dcd32b87270aeb980333213da2549c9907e09e94 10000

mint tx: 469d0e6a1e1a421c84cd009b983fc153aa5db7da26fa1f89837f2731fa75586c
{ amount: 0,
  fee: -0.081064,
  confirmations: 0,
  trusted: true,
  txid: '469d0e6a1e1a421c84cd009b983fc153aa5db7da26fa1f89837f2731fa75586c',
  walletconflicts: [],
  time: 1514442911,
  timereceived: 1514442911,
  'bip125-replaceable': 'no',
  details:
   [ { account: '',
       category: 'send',
       amount: 0,
       vout: 0,
       fee: -0.081064,
       abandoned: false } ],
  hex: '02000000014d195e5308764e1f64236c64b8975030dd8b8815d7cfa88ee838c029e64fa03f0200000047463043022052a137063b24e74c3953891230dae739ae3adfa2144c91805de4e46ae7c4b152021f0ccdf1b3e4dd86de7777f437447dd147955e9e112c2607bfd67ddc4e7d6e2001feffffff02000000000000000063010403400d0301284440c10f19000000000000000000000000dcd32b87270aeb980333213da2549c9907e09e94000000000000000000000000000000000000000000000000000000000000271014a778c05f1d0f70f1133f4bbf78c1a9a7bf84aed3c2606ecea8d1010000
1976a914dcd32b87270aeb980333213da2549c9907e09e9488acc3080000',
  method: 'mint',
  confirm: [Function: confirm] }
✔ confirm mint

We should see that the balance had increased:

node index.js balance dcd32b87270aeb980333213da2549c9907e09e94

balance: 23700

The mint function source code:

async function mint(toAddr, amount) {
  const tx = await myToken.send("mint", [toAddr, amount])

  console.log("mint tx:", tx.txid)
  console.log(tx)

  await tx.confirm(1)
}
  • tx is the transaction submitted.

  • tx.confirm(1) is a Promise that returns when there is one confirmation for the transaction.

Token Transfer

Let's transfer tokens from dcd32...9e94 to another account. The contract's transfer method takes two arguments:

  • _to address is the receiver of the tokens.

  • _value is the amount of tokens to transfer.

function transfer(address _to, uint256 _value) public returns (bool) {
  require(_to != address(0));
  require(_value <= balances[msg.sender]);

  
  balances[msg.sender] = balances[msg.sender].sub(_value);
  balances[_to] = balances[_to].add(_value);
  Transfer(msg.sender, _to, _value);
  return true;
}

Note that the API does not require the _from address. It is assumed that msg.sender is the source of the token balance to transfer from.

Ah, msg.sender, our old nemesis.

To act as dcb3...9e94, we need to explicitly specify an UTXO that has the same address. We can do this by using the senderAddress option.

async function transfer(fromAddr, toAddr, amount) {
  const tx = await myToken.send("transfer", [toAddr, amount], {
    senderAddress: fromAddr,
  })

  console.log("transfer tx:", tx.txid)
  console.log(tx)

  
  const confirmation = tx.confirm(1)
  ora.promise(confirmation, "confirm transfer")
  await confirmation
}

There are other options you can specify for send. The full type definition is IContractSendRequestOptions:

export interface IContractSendRequestOptions {
  
  amount?: number | string

  
  gasLimit?: number

  
  gasPrice?: number | string

  
  senderAddress?: string
}

To test transfer, let's generate a new receiver address, and convert it to hex:

./hydra-cli getnewaddress
qXuvswhQ9Vjza8AFj1vmUL4N531CDVoWsz

./hydra-cli gethexaddress qXuvswhQ9Vjza8AFj1vmUL4N531CDVoWsz
9d748f98e65c6875dbed7bfb6ffbeca426ff9cc6

To transfer 100 tokens from dcb3...9e94:

node index.js transfer \
 qdgznat81MfTHZUrQrLZDZteAx212X4Wjj \
 9d748f98e65c6875dbed7bfb6ffbeca426ff9cc6 \
 100

transfer tx: a1ba017b3974b98bf9c8edc824c3abc0ce17678a14e7cfac94b5900a290bdd07
✔ confirm transfer

Note that we MUST specify the senderAddress using base58 address format. We'll fix this in the future.

We can then verify that 9d74...9cc6 had indeed received the tokens:

node index.js balance 9d748f98e65c6875dbed7bfb6ffbeca426ff9cc6

balance: 100

And that the origin account's balance decremented by 100:

node index.js balance dcd32b87270aeb980333213da2549c9907e09e94

balance: 23600

Observing Contract Events

The CappedToken contract defines a few events. The Transfer event is emitted whenever fund is moved from one account to another (also when minting new tokens). The Transfer event:

event Transfer(
  address indexed from,
  address indexed to,
  uint256 value
);

Let's use qtumjs to subscribe to the stream of contract events, so we can react in a timely manner when a transfer occurs. The code:

async function streamEvents() {
  console.log("Subscribed to contract events")
  console.log("Ctrl-C to terminate events subscription")

  myToken.onLog((entry) => {
    console.log(entry)
  }, { minconf: 1 })
}

Let's see it in action. Launch the events subscriber:

node index.js events

Subscribed to contract events
Ctrl-C to terminate events subscription

The program hangs there waiting for new events. In another terminal, mint more tokens:

node index.js mint dcd32b87270aeb980333213da2549c9907e09e94 10000

mint tx: c0e3007178a1b9e05b33e770f7a0e7d084f2d06732658524be042dc0e9864cc4

Wait for a bit for confirmations. In the events terminal, you should see both Mint and Transfer events printed out:

{ blockHash: 'd8135a1a0e4cddb82a6912fc7eb2bd7f717b7e85069dc2fa3b8f0f8c02acbd17',
  blockNumber: 2372,
  transactionHash: 'c0e3007178a1b9e05b33e770f7a0e7d084f2d06732658524be042dc0e9864cc4',
  transactionIndex: 2,
  from: 'dcd32b87270aeb980333213da2549c9907e09e94',
  to: 'a778c05f1d0f70f1133f4bbf78c1a9a7bf84aed3',
  cumulativeGasUsed: 39306,
  gasUsed: 39306,
  contractAddress: 'a778c05f1d0f70f1133f4bbf78c1a9a7bf84aed3',
  topics:
   [ '0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d4121396885',
     '000000000000000000000000dcd32b87270aeb980333213da2549c9907e09e94' ],
  data: '0000000000000000000000000000000000000000000000000000000000002710',
  event:
   { type: 'Mint',
     to: '0xdcd32b87270aeb980333213da2549c9907e09e94',
     amount:  } }
{ blockHash: 'd8135a1a0e4cddb82a6912fc7eb2bd7f717b7e85069dc2fa3b8f0f8c02acbd17',
  blockNumber: 2372,
  transactionHash: 'c0e3007178a1b9e05b33e770f7a0e7d084f2d06732658524be042dc0e9864cc4',
  transactionIndex: 2,
  from: 'dcd32b87270aeb980333213da2549c9907e09e94',
  to: 'a778c05f1d0f70f1133f4bbf78c1a9a7bf84aed3',
  cumulativeGasUsed: 39306,
  gasUsed: 39306,
  contractAddress: 'a778c05f1d0f70f1133f4bbf78c1a9a7bf84aed3',
  topics:
   [ 'ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef',
     '0000000000000000000000000000000000000000000000000000000000000000',
     '000000000000000000000000dcd32b87270aeb980333213da2549c9907e09e94' ],
  data: '0000000000000000000000000000000000000000000000000000000000002710',
  event:
   { type: 'Transfer',
     from: '0x0000000000000000000000000000000000000000',
     to: '0xdcd32b87270aeb980333213da2549c9907e09e94',
     value:  } }

If you are running your own hydrad node instead of the provided docker image, you'll need to enable -logevents for events logging to work.

Conclusion

In this chapter we've developed a simple NodeJS CLI tool to interact with an HRC20 contract.

  • hydrajs is a Promise-based API. Use async/await to write clean asynchronous code.

  • call is like "query", send is like "commit".

  • Use senderAddress to in call or send to specify the msg.owner.

Now that you know how to use hydrajs, you are ready to build a DApp, and be on your way to fame and riches!

For modern JavaScript development, you really owe it to yourself to try . hydrachainjs comes with static type definitions for its API, and with VSCode you get some of the most useful IDE features (e.g. type-accurate autocomplete) without the UX bloat:

It is recommended to try too. JavaScript is in fact an extremely powerful language. TypeScript is the sobered-up version, yet retaining the same dynamism and expressivity that JavaScript developers love.

Or yarn install if you prefer that. See:

See an example file

myToken.call("totalSupply") returns a , and await is a syntatic sugar to that waits for the asynchronous computation, then returns the result.

Solidity numbers (int, uint, etc.) are represented in JavaScript using .

The type definition for :

As we've learned in , HYDRA doesn't really have the idea of an "account". The msg.sender is the address of whatever UTXO that was used to pay for the transaction.

In the above code, the third argument of send allows you to specify the msg.sender. Remember to this address with UTXOs.

HydrachainJS
token we deployed previously
https://github.com/hydra-chain/hydrajs-token-cli
async/await
Long Term Support
VSCode
TypeScript
https://yarnpkg.com/en/docs/install
solar.development.json
Promise
BigNumber
IContractCallDecodedResult
prefund
The Owner UTXO Address