Common examples guide
Use the following common examples to learn how to use feather.js. If this is your first time using feather.js, follow the feather.js tutorial.
If you are new to developing on Terra visit the getting started guide.
Configuring LCDClient​
The following code example shows how to initialize the LCDClient
for one or multiple chains. The rest of the examples assume that you have initialized the LCDClient
using this example or similar code.
_18import {_18 MsgSend,_18 MnemonicKey,_18 Coins,_18 LCDClient,_18} from '@terra-money/feather.js';_18_18const lcd = new LCDClient({_18 'pisco-1': {_18 chainID: 'pisco-1',_18 lcd: 'https://pisco-lcd.terra.dev',_18 gasAdjustment: 1.75,_18 gasPrices: {_18 uluna: 0.015,_18 },_18 prefix: 'terra', // bech32 prefix, used by the LCD to understand which is the right chain to query_18 },_18});
To configure the LCD for multiple chains, pass an object with multiple keys, each key being the chainID.
_20const lcd = new LCDClient({_20 'osmosis-1': {_20 chainID: 'osmosis-1',_20 lcd: 'https://lcd.osmosis.zone',_20 gasAdjustment: 1.75,_20 gasPrices: {_20 uosmo: 0.025,_20 },_20 prefix: 'osmo',_20 },_20 'phoenix-1': {_20 chainID: 'phoenix-1',_20 lcd: 'https://phoenix-lcd.terra.dev',_20 gasAdjustment: 1.75,_20 gasPrices: {_20 uluna: 0.015,_20 },_20 prefix: 'terra',_20 },_20});
You can also use the default configuration if you only want to connect to Terra.
_2const lcd = LCDClient.fromDefaultConfig('testnet'); // connect to testnet_2const lcd = LCDClient.fromDefaultConfig('mainnet'); // connect to mainnet
Get native wallet balance​
_10// The LCD will direct the query according to the bech32 prefix of the address_10const [terraBalance] = await lcd.bank.balance(_10 'terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v',_10);_10console.log(terraBalance);_10_10const [osmoBalance] = await lcd.bank.balance(_10 'osmo1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v',_10);_10console.log(osmoBalance);
Example response:
_2[{ denom: 'uluna', amount: '5030884' }];_2[{ denom: 'uosmo', amount: '10000' }];
Queries that can't directly read the chain from the given address always require a chainID
to be specified. See the below example.
_3// query the total circulating supply_3const total = await lcd.bank.total('phoenix-1');_3console.log(total);
Get wallet balance (CW20 tokens)​
_8// Check balance of token on pisco-1 using tokenAddress._8const tokenAddress = '<INSERT_TOKEN_ADDRESS>';_8const walletAddress = 'terra1f44ddca9awepv2rnudztguq5rmrran2m20zzd6';_8const response = await lcd.wasm.contractQuery(tokenAddress, {_8 balance: { address: walletAddress },_8});_8_8console.log(response);
Example response:
_3{_3 balance: '70258667';_3}
Get transaction status​
_4// Replace with TX hash to lookup._4const hash = 'CAB264B3D92FF3DFE209DADE791A866876DE5DD2A320C1200F9C5EC5F0E7B14B';_4const txInfo = await lcd.tx.txInfo(hash, 'pisco-1'); // specify chainID to direct LCD to the correct chain_4console.log(txInfo);
Example response (modified for readability):
_19TxInfo {_19 height: 8276372,_19 txhash: 'CAB264B3D92FF3DFE209DADE791A866876DE5DD2A320C1200F9C5EC5F0E7B14B',_19 raw_log: '[]',_19 logs: [_19 TxLog {_19 msg_index: 0,_19 log: '',_19 events: [Array],_19 eventsByType: [Object]_19 }_19 ],_19 gas_wanted: 177808,_19 gas_used: 128827,_19 tx: Tx {},_19 timestamp: '2022-03-17T18:34:06Z',_19 code: 0,_19 codespace: ''_19}
Sending native tokens​
The following code example shows how to send native tokens:
_22import { LCDClient, MnemonicKey, MsgSend } from '@terra-money/feather.js';_22_22// const lcd = new LCDClient(...);_22_22const mk = new MnemonicKey({_22 mnemonic:_22 'satisfy adjust timber high purchase tuition stool faith fine install that you unaware feed domain license impose boss human eager hat rent enjoy dawn',_22});_22_22const wallet = lcd.wallet(mk);_22_22// Transfer 1 Luna._22const send = new MsgSend(_22 wallet.key.accAddress('terra'), // requires prefix as a parameter_22 'terra1dcegyrekltswvyy0xy69ydgxn9x8x32zdtapd8',_22 { uluna: '1000000' },_22);_22_22const tx = await wallet.createAndSignTx({ msgs: [send], chainID: 'pisco-1' });_22const result = await lcd.tx.broadcast(tx, 'pisco-1');_22_22console.log(result);
Sending CW20 tokens​
The following code example shows how to send CW20 tokens:
_37import {_37 LCDClient,_37 MnemonicKey,_37 MsgExecuteContract,_37} from '@terra-money/feather.js';_37_37// const lcd = new LCDClient(...);_37_37const mk = new MnemonicKey({_37 mnemonic:_37 'satisfy adjust timber high purchase tuition stool faith fine install that you unaware feed domain license impose boss human eager hat rent enjoy dawn',_37});_37_37const wallet = lcd.wallet(mk);_37_37// Specify token address of desired token to send._37const tokenAddress = '<INSERT_TOKEN_ADDRESS>';_37_37// Transfer 1 token._37const cw20Send = new MsgExecuteContract(_37 wallet.key.accAddress('terra'),_37 tokenAddress,_37 {_37 transfer: {_37 amount: '1000000',_37 recipient: wallet.key.accAddress('terra'),_37 },_37 },_37);_37_37const tx = await wallet.createAndSignTx({_37 msgs: [cw20Send],_37 chainID: 'pisco-1',_37});_37const result = await lcd.tx.broadcast(tx, 'pisco-1');_37_37console.log(result);
Swapping a native Terra asset for a CW20 token using Terraswap​
The following code example shows how to swap a native asset for CW20 using Terraswap.
Run this example on mainnet.
_53import {_53 MsgExecuteContract,_53 MnemonicKey,_53 Coins,_53 LCDClient,_53} from '@terra-money/feather.js';_53_53// const lcd = new LCDClient(...);_53_53const mk = new MnemonicKey({_53 mnemonic:_53 'satisfy adjust timber high purchase tuition stool faith fine install that you unaware feed domain license impose boss human eager hat rent enjoy dawn',_53});_53_53const wallet = lcd.wallet(mk);_53_53// LUNA <> OTHER_TOKEN_53const pool = '<INSERT_TOKEN_POOL_ADDRESS>';_53_53// Fetch the number of each asset in the pool._53const { assets } = await lcd.wasm.contractQuery(pool, { pool: {} });_53_53// Calculate belief price using pool balances._53const beliefPrice = (assets[0].amount / assets[1].amount).toFixed(18);_53_53// Swap 1 LUNA with 1% slippage tolerance._53const terraSwap = new MsgExecuteContract(_53 wallet.key.accAddress,_53 pool,_53 {_53 swap: {_53 max_spread: '0.01',_53 offer_asset: {_53 info: {_53 native_token: {_53 denom: 'uluna',_53 },_53 },_53 amount: '1000000',_53 },_53 belief_price: beliefPrice,_53 },_53 },_53 new Coins({ uluna: '1000000' }),_53);_53_53const tx = await wallet.createAndSignTx({_53 msgs: [terraSwap],_53 chainID: 'pisco-1',_53});_53const result = await lcd.tx.broadcast(tx, 'pisco-1');_53_53console.log(result);
Decoding Protobuf-encoded messages​
The following code example shows how to decode messages that have been encoded using Protobuf:
_17import { LCDClient, Tx } from '@terra-money/feather.js';_17_17// const lcd = new LCDClient(...);_17_17const blockData = await lcd.tendermint.blockInfo(5923213);_17_17const txInfos = blockData.block.data.txs.map((tx) =>_17 Tx.unpackAny({ value: Buffer.from(tx, 'base64') }),_17);_17_17// Find messages where a contract was initialized._17const initMessages = txInfos_17 .map((tx) => tx.body.messages)_17 .flat()_17 .find((i) => i.constructor.name === 'MsgInstantiateContract');_17_17console.log(initMessages);
Validate a Terra address​
The following code example shows how to do the basic verification of Terra or other bech32 prefixed addresses.
_9import { AccAddress } from '@terra-money/feather.js';_9// validate length, prefix and bech32 checksum. The second parameter is the expected bech32 prefix._9AccAddress.validate('terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v', 'terra'); // true_9_9// to check just length and bech32 checksum (if you don't know the chain)._9AccAddress.validate('terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v'); // true_9_9// Get the prefix of an address. It will throw an error if the address is invalid._9AccAddress.getPrefix('terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v'); // 'terra'
To validate bech32 encoded validator addresses, pubkey, etc. you can use the specific namespace:
- ValAddress: for
*valoper...
prefixed validator operator address - ValConsAddress: for
*valcons...
prefixed validator consensus address - AccPubKey: for
*pub...
prefixed account public key - ValPubKey: for
*valoperpub...
prefixed validator public key
Avoid Status 500: timed out waiting for tx to be included in a block​
Occasionally, the broadcast function of feather.js and terra.py will throw the error Status 500: timed out waiting for tx to be included in a block
. This occurs because the libraries use broadcast-mode = block
by default, which causes the LCD (to which you are broadcasting the transaction) to send an http
response to your request only when the transaction has been included in a block. If the chain is overloaded, the confirmation may take too long and trigger a timeout in the LCD.
To solve this problem, use broadcast-mode = sync
and then iterate a request to the LCD with the txhash
to ensure that it has been included in a block.
Javascript example:
_19// sign the tx_19wallet_19 .createAndSignTx(TX_HERE, CHAIN_ID_HERE)_19 // use broadcastSync() instead of broadcast()_19 .then((tx) => terra.tx.broadcastSync(tx, chainID))_19 .then(async (result) => {_19 while (true) {_19 // query txhash_19 const data = await terra.tx.txInfo(result.txhash).catch(() => {});_19 // if hash is onchain return data_19 if (data) return data;_19 // else wait 250ms and then repeat_19 await new Promise((resolve) => setTimeout(resolve, 250));_19 }_19 })_19 .then((result) => {_19 // this will be executed when the tx has been included into a block_19 console.log(result);_19 });