var img = document.createElement('img'); img.src = "https://terradocs.matomo.cloud//piwik.php?idsite=1&rec=1&url=https://docs.terra.money" + location.pathname; img.style = "border:0"; img.alt = "tracker"; var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(img,s);
Skip to main content

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.

💡tip

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.

import {
MsgSend,
MnemonicKey,
Coins,
LCDClient,
} from '@terra-money/feather.js';

const lcd = new LCDClient({
'pisco-1': {
chainID: 'pisco-1',
lcd: 'https://pisco-lcd.terra.dev',
gasAdjustment: 1.75,
gasPrices: {
uluna: 0.015,
},
prefix: 'terra', // bech32 prefix, used by the LCD to understand which is the right chain to query
},
});
Copy

To configure the LCD for multiple chains, pass an object with multiple keys, each key being the chainID.

const lcd = new LCDClient({
'osmosis-1': {
chainID: 'osmosis-1',
lcd: 'https://lcd.osmosis.zone',
gasAdjustment: 1.75,
gasPrices: {
uosmo: 0.025,
},
prefix: 'osmo',
},
'phoenix-1': {
chainID: 'phoenix-1',
lcd: 'https://phoenix-lcd.terra.dev',
gasAdjustment: 1.75,
gasPrices: {
uluna: 0.015,
},
prefix: 'terra',
},
});
Copy

You can also use the default configuration if you only want to connect to Terra.

const lcd = LCDClient.fromDefaultConfig('testnet'); // connect to testnet
const lcd = LCDClient.fromDefaultConfig('mainnet'); // connect to mainnet
Copy

Get native wallet balance​

// The LCD will direct the query according to the bech32 prefix of the address
const [terraBalance] = await lcd.bank.balance(
'terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v',
);
console.log(terraBalance);

const [osmoBalance] = await lcd.bank.balance(
'osmo1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v',
);
console.log(osmoBalance);
Copy

Example response:

[{ denom: 'uluna', amount: '5030884' }];
[{ denom: 'uosmo', amount: '10000' }];
Copy

Queries that can't directly read the chain from the given address always require a chainID to be specified. See the below example.

// query the total circulating supply
const total = await lcd.bank.total('phoenix-1');
console.log(total);
Copy

Get wallet balance (CW20 tokens)​

// Check balance of token on pisco-1 using tokenAddress.
const tokenAddress = '<INSERT_TOKEN_ADDRESS>';
const walletAddress = 'terra1f44ddca9awepv2rnudztguq5rmrran2m20zzd6';
const response = await lcd.wasm.contractQuery(tokenAddress, {
balance: { address: walletAddress },
});

console.log(response);
Copy

Example response:

{
balance: '70258667';
}
Copy

Get transaction status​

// Replace with TX hash to lookup.
const hash = 'CAB264B3D92FF3DFE209DADE791A866876DE5DD2A320C1200F9C5EC5F0E7B14B';
const txInfo = await lcd.tx.txInfo(hash, 'pisco-1'); // specify chainID to direct LCD to the correct chain
console.log(txInfo);
Copy

Example response (modified for readability):

TxInfo {
height: 8276372,
txhash: 'CAB264B3D92FF3DFE209DADE791A866876DE5DD2A320C1200F9C5EC5F0E7B14B',
raw_log: '[]',
logs: [
TxLog {
msg_index: 0,
log: '',
events: [Array],
eventsByType: [Object]
}
],
gas_wanted: 177808,
gas_used: 128827,
tx: Tx {},
timestamp: '2022-03-17T18:34:06Z',
code: 0,
codespace: ''
}
Copy

Sending native tokens​

The following code example shows how to send native tokens:

import { LCDClient, MnemonicKey, MsgSend } from '@terra-money/feather.js';

// const lcd = new LCDClient(...);

const mk = new MnemonicKey({
mnemonic:
'satisfy adjust timber high purchase tuition stool faith fine install that you unaware feed domain license impose boss human eager hat rent enjoy dawn',
});

const wallet = lcd.wallet(mk);

// Transfer 1 Luna.
const send = new MsgSend(
wallet.key.accAddress('terra'), // requires prefix as a parameter
'terra1dcegyrekltswvyy0xy69ydgxn9x8x32zdtapd8',
{ uluna: '1000000' },
);

const tx = await wallet.createAndSignTx({ msgs: [send], chainID: 'pisco-1' });
const result = await lcd.tx.broadcast(tx, 'pisco-1');

console.log(result);
Copy

Sending CW20 tokens​

The following code example shows how to send CW20 tokens:

import {
LCDClient,
MnemonicKey,
MsgExecuteContract,
} from '@terra-money/feather.js';

// const lcd = new LCDClient(...);

const mk = new MnemonicKey({
mnemonic:
'satisfy adjust timber high purchase tuition stool faith fine install that you unaware feed domain license impose boss human eager hat rent enjoy dawn',
});

const wallet = lcd.wallet(mk);

// Specify token address of desired token to send.
const tokenAddress = '<INSERT_TOKEN_ADDRESS>';

// Transfer 1 token.
const cw20Send = new MsgExecuteContract(
wallet.key.accAddress('terra'),
tokenAddress,
{
transfer: {
amount: '1000000',
recipient: wallet.key.accAddress('terra'),
},
},
);

const tx = await wallet.createAndSignTx({
msgs: [cw20Send],
chainID: 'pisco-1',
});
const result = await lcd.tx.broadcast(tx, 'pisco-1');

console.log(result);
Copy

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.

import {
MsgExecuteContract,
MnemonicKey,
Coins,
LCDClient,
} from '@terra-money/feather.js';

// const lcd = new LCDClient(...);

const mk = new MnemonicKey({
mnemonic:
'satisfy adjust timber high purchase tuition stool faith fine install that you unaware feed domain license impose boss human eager hat rent enjoy dawn',
});

const wallet = lcd.wallet(mk);

// LUNA <> OTHER_TOKEN
const pool = '<INSERT_TOKEN_POOL_ADDRESS>';

// Fetch the number of each asset in the pool.
const { assets } = await lcd.wasm.contractQuery(pool, { pool: {} });

// Calculate belief price using pool balances.
const beliefPrice = (assets[0].amount / assets[1].amount).toFixed(18);

// Swap 1 LUNA with 1% slippage tolerance.
const terraSwap = new MsgExecuteContract(
wallet.key.accAddress,
pool,
{
swap: {
max_spread: '0.01',
offer_asset: {
info: {
native_token: {
denom: 'uluna',
},
},
amount: '1000000',
},
belief_price: beliefPrice,
},
},
new Coins({ uluna: '1000000' }),
);

const tx = await wallet.createAndSignTx({
msgs: [terraSwap],
chainID: 'pisco-1',
});
const result = await lcd.tx.broadcast(tx, 'pisco-1');

console.log(result);
Copy

Decoding Protobuf-encoded messages​

The following code example shows how to decode messages that have been encoded using Protobuf:

import { LCDClient, Tx } from '@terra-money/feather.js';

// const lcd = new LCDClient(...);

const blockData = await lcd.tendermint.blockInfo(5923213);

const txInfos = blockData.block.data.txs.map((tx) =>
Tx.unpackAny({ value: Buffer.from(tx, 'base64') }),
);

// Find messages where a contract was initialized.
const initMessages = txInfos
.map((tx) => tx.body.messages)
.flat()
.find((i) => i.constructor.name === 'MsgInstantiateContract');

console.log(initMessages);
Copy

Validate a Terra address​

The following code example shows how to do the basic verification of Terra or other bech32 prefixed addresses.

import { AccAddress } from '@terra-money/feather.js';
// validate length, prefix and bech32 checksum. The second parameter is the expected bech32 prefix.
AccAddress.validate('terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v', 'terra'); // true

// to check just length and bech32 checksum (if you don't know the chain).
AccAddress.validate('terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v'); // true

// Get the prefix of an address. It will throw an error if the address is invalid.
AccAddress.getPrefix('terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v'); // 'terra'
Copy

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:

// sign the tx
wallet
.createAndSignTx(TX_HERE, CHAIN_ID_HERE)
// use broadcastSync() instead of broadcast()
.then((tx) => terra.tx.broadcastSync(tx, chainID))
.then(async (result) => {
while (true) {
// query txhash
const data = await terra.tx.txInfo(result.txhash).catch(() => {});
// if hash is onchain return data
if (data) return data;
// else wait 250ms and then repeat
await new Promise((resolve) => setTimeout(resolve, 250));
}
})
.then((result) => {
// this will be executed when the tx has been included into a block
console.log(result);
});
Copy