@inae/inae-js v0.1.0
Originally based on JS library for Cosmostation
Heavily modified by Hessegg
This is a preview of Javascript API for iNAE blockchain. This delivery is tailored for INAE blockchain.
At the moment this package contains samples on how to build transactions for the blockchain and how to make queries. The examples expect that:
- the blockchain has Chain ID
HostNet
, - the address prefix is
inae
, - the platform coin is called
INAE
with normalized denomuinae
- the blockchain node runs on the local host and provides REST API
at port
localhost:31012
Installation
To be written
NPM
The package is not published yet
npm install @inae/inae-js
Yarn
The package is not published yet
yarn add @inae/inae-js
Local
add to package.json
{
"name" : "...",
...
"dependencies" : {
...
"@inae/inae-js": "file:../inae-js",
}
}
# yarn env
$ yarn
# npm env
$ npm install
Generate Protobuf
DON'T DO IT unless you know what are you doing. The generated files require manual tuning.
$ ./protocgen.sh
Import
import { Blockchain, Query } from "@inae/inae-js";
Examples
The package comes with two examples.
example/hostnet.query.js
demonstrates how to make queries. Execute it with
yarn:
yarn example.query
example/hostnet.send.js
demonstrates how to issue a transaction that transfers
funds between two accounts. Execute it with yarn:
yarn example.send
Usage
Query information
import { Blockchain, Query } from "@inae/inae-js";
const chainId = "HostNet";
// The rest server URL MUST NOT end with slash
const bc = new Blockchain("http://localhost:31012", chainId);
const Q = new Query(bc)
The Q
object provides method query(path: string) : Promize<any>
that
performs query specified by the path and returns the result as object.
You can find the query paths in the protobuf files named query.proto
in ./proto
and ./third_party/proto
directories.
For example, ./proto/inae/convert/v1/query.proto
defines the following
query service:
service Query {
rpc RatePair(QueryRatePairRequest) returns (QueryRatePairResponse) {
option (google.api.http).get = "/inae/convert/v1/rate/pair/{from}/{to}";
}
rpc RateFrom(QueryRateFromRequest) returns (QueryRatesResponse) {
option (google.api.http).get = "/inae/convert/v1/rate/from/{from}";
}
rpc RateTo(QueryRateToRequest) returns (QueryRatesResponse) {
option (google.api.http).get = "/inae/convert/v1/rate/to/{to}";
}
rpc RateAll(QueryRateAllRequest) returns (QueryRatesResponse) {
option (google.api.http).get = "/inae/convert/v1/rate/all";
}
}
The query RatePair
returns conversion rate from one token to another. The query
path is defined by the option google.api.http
: "/inae/convert/v1/rate/pair/{from}/{to}"
.
It is a template. For the actual query the placeholders should be substituted by
the actual token symbols. E.g. the query path for the conversion rate from TEST
token to INAE coin is "/inae/convert/v1/rate/pair/inae/test"
.
This query returns the value of the type QueryRatePairResponse
:
message QueryRatePairResponse {
ConvertPair pair = 1;
}
message ConvertPair{
string from = 1;
string to = 2;
string rate = 3 [(gogoproto.customtype) = "Dec", (gogoproto.nullable) = false];
}
In JSON the response structure closly resembles the protobuf message.
const result = await Q.query("/inae/convert/v1/rate/pair/inae/test");
console.log(result)
{ pair: { from: 'test', to: 'inae', rate: '0.010000000000000000' } }
Predefined queries
Query
class provides a number of predefined queries. See the source code
src/query.js
for more details.
Transactions
Transactions consist of messages, authentication information and signatures.
Messages convoy the business logic of the transaction, the action that blockchain nodes should execute upon receiving the transaction.
Authentication information provides data about who authorized the transaction by digitally signing it.
Signatures are the actual digital signatures of the transaction authorizers.
The messages could be found in the protobuf files named tx.proto
in ./proto
and ./third_party/proto
directories.
One of the most important transaction message is MsgTransfer
that tells the blockchain to
transfer funds between accounts. It is defined in proto/inae/bankz/v1/tx.proto
package inae.bankz.v1;
// ...
// Msg defines the bank Msg service.
service Msg {
rpc Transfer(MsgTransfer) returns (MsgTransferResponse) {
option (google.api.http).post = "/inae/bankz/v1/transfer";
};
// ...
}
// MsgTransfer represents a message to send coins from one account to another.
message MsgTransfer {
// ...
string from_address = 1;
string to_address = 3;
cosmos.base.v1beta1.Coin coin = 4;
}
// MsgTransferResponse defines the Msg/Send response type.
message MsgTransferResponse {}
The package inae-js
provides a library of Javascript types generated from Protobuf
files:
import { message } from "@inae/inae-js/";
Example
Blockchain
class implements basic blockhain-related operations, including keys derivation,
transaction signing and broadcasting transactions to the blockchain.
messages
provide classes generated from the protobuf files.
import { Blockchain, Query } from "@inae/inae-js";
import { message } from "@inae/inae-js";
Setting up the blockchain and account. The keys and the address are derived from
the account mnemonics. In this example we use the mnemonic phrase for the account supply
defined for the localhost demonet.
// [WARNING] This mnemonic is just for the demo purpose. DO NOT USE THIS MNEMONIC for your own wallet.
// Supply account on the Hostnet
const mnemonic = "also decide pole hello ready month distance favorite blush lawn theory flee hundred move outdoor fiscal online labor patch roof toast case cement hidden";
Setting up the blockchain adapter: chain ID, REST API URL, address prefix and key derivation path. Change in any of the parameters will break compatibility with the localhost demonet.
const chainId = "HostNet";
// The rest server URL MUST NOT end with slash
const bc = new Blockchain("http://localhost:31012", chainId);
bc.setBech32MainPrefix("inae");
bc.setPath("m/44'/118'/0'/0/0");
const Q = new Query(bc);
Derive keys and address:
// Generate address, private and public keys
const address = bc.getAddress(mnemonic);
const privKey = bc.getECPairPriv(mnemonic);
// getPubKeyAny returns the public key wrapped into `any` protobuf type
const pubKeyAny = bc.getPubKeyAny(privKey);
Query the blockchain for the account information. It will be used for the digital signature.
// Load the account information from the blockchain
const account = await bc.getAccounts(address).then(obj => obj.account)
Create the transaction. The first step is to make the message of the type MsgTransfer
.
// Destination for the transaction
const toAddress = "inae1ty659nurtr5cstcxuqmlrkukfw924mkx9dlwdc";
console.log("Sending 1 iNAE from ", address, " to ", toAddress)
// Load the account information from the blockchain
const account = await bc.getAccounts(address).then(obj => obj.account)
// Prepare the transaction for signing
// signDoc = (1)txBody + (2)authInfo
// ---------------------------------- (1)txBody ----------------------------------
// The action of the transaction is presented by the `MsgTransfer` message.
// The path `message.inae.bankz.v1` is derived from protobuf package name.
// See `./proto/inae/bankz/v1/tx.proto`:
// `package inae.banks.v1`;
const msgTransfer = new message.inae.bankz.v1.MsgTransfer({
from_address: address,
// account01 in the HostNet
to_address: toAddress,
coin: { denom: "uinae", amount: String(1000000) } // 6 zeroes: 1000000 uinae = 1 INAE
});
// Wrap the message into `any`.
const msgSendAny = new message.google.protobuf.Any({
type_url: "/inae.bankz.v1.MsgTransfer",
value: message.inae.bankz.v1.MsgTransfer.encode(msgTransfer).finish()
});
const txBody = new message.cosmos.tx.v1beta1.TxBody({ messages: [msgSendAny], memo: "" });
The object txBody
keeps the list of messages and optional memo note to be included
in the transaction.
The procedure of making the signature doesn't require any knowledge about the messages. All transactions are signed in the same way.
// --------------------------------- (2)authInfo ---------------------------------
// Signer information hold the public key of the transaction sender
// Sequence it used to prevent replay attacks.
const signerInfo = new message.cosmos.tx.v1beta1.SignerInfo({
public_key: pubKeyAny,
mode_info: { single: { mode: message.cosmos.tx.signing.v1beta1.SignMode.SIGN_MODE_DIRECT } },
sequence: account.sequence
});
// Reserved for future use. At the moment set zero (empty) fees
const feeValue = new message.cosmos.tx.v1beta1.Fee({
amount: [{ denom: "uinae", amount: String(10000) }],
// amount: [],
gas_limit: 200000,
payer: "",
granter: ""
});
const authInfo = new message.cosmos.tx.v1beta1.AuthInfo({ signer_infos: [signerInfo], fee: feeValue });
// -------------------------------- sign --------------------------------
// The signature includes the account number to prevent signature collisions
const signedTxBytes = bc.sign(txBody, authInfo, account.account_number, privKey);
The method Blockchain.broadcast
delivers the signed transaction to the blockchain.
The mode BROADCAST_MODE_BLOCK
means wait until the transaction is included into a block.
The transaction hash is accessible as response.tx_response.txhash
.
// ----------------------- send to the blockchain ------------------------
const response = await bc.broadcast(signedTxBytes, "BROADCAST_MODE_BLOCK").then(response => response.json())
console.log(response)
This section of the example queries the blockchain about the transaction status.
If the field txstatus.tx_response.code
is zero, the transaction is handled and
committed to the blockchain state. If processing the transaction resulted in an error
this field is non-zero.
// --- Check the transaction status ---
const txid = response.tx_response.txhash;
if (response.tx_response.code != 0) {
console.error("Transaction failed: reason:", response.tx_response.raw_log);
if (response.tx_response.height == 0) {
console.error("Transaction failed: invalid transaction, rejected");
}
} else {
// query the transaction
const txstatus = await Q.tx(txid)
if (txstatus.tx_response.code == 0) {
console.log(`${txid}: success`);
} else {
const errorCode = txstatus.tx_response.code
const errorCodeSpace = txstatus.tx_response.errorCodeSpace
const errorInfo = txstatus.tx_response.info || "N/A"
console.log(`${txid}: error: codespace ${errorCodeSpace}, error ${errorCode}: ${errorInfo}`);
}
}
10 months ago