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.


_18
import {
_18
MsgSend,
_18
MnemonicKey,
_18
Coins,
_18
LCDClient,
_18
} from '@terra-money/feather.js';
_18
_18
const 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.


_20
const 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.


_2
const lcd = LCDClient.fromDefaultConfig('testnet'); // connect to testnet
_2
const 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
_10
const [terraBalance] = await lcd.bank.balance(
_10
'terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v',
_10
);
_10
console.log(terraBalance);
_10
_10
const [osmoBalance] = await lcd.bank.balance(
_10
'osmo1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v',
_10
);
_10
console.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
_3
const total = await lcd.bank.total('phoenix-1');
_3
console.log(total);

Get wallet balance (CW20 tokens)


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

Example response:


_3
{
_3
balance: '70258667';
_3
}

Get transaction status


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

Example response (modified for readability):


_19
TxInfo {
_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:


_22
import { LCDClient, MnemonicKey, MsgSend } from '@terra-money/feather.js';
_22
_22
// const lcd = new LCDClient(...);
_22
_22
const 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
_22
const wallet = lcd.wallet(mk);
_22
_22
// Transfer 1 Luna.
_22
const send = new MsgSend(
_22
wallet.key.accAddress('terra'), // requires prefix as a parameter
_22
'terra1dcegyrekltswvyy0xy69ydgxn9x8x32zdtapd8',
_22
{ uluna: '1000000' },
_22
);
_22
_22
const tx = await wallet.createAndSignTx({ msgs: [send], chainID: 'pisco-1' });
_22
const result = await lcd.tx.broadcast(tx, 'pisco-1');
_22
_22
console.log(result);

Sending CW20 tokens

The following code example shows how to send CW20 tokens:


_37
import {
_37
LCDClient,
_37
MnemonicKey,
_37
MsgExecuteContract,
_37
} from '@terra-money/feather.js';
_37
_37
// const lcd = new LCDClient(...);
_37
_37
const 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
_37
const wallet = lcd.wallet(mk);
_37
_37
// Specify token address of desired token to send.
_37
const tokenAddress = '<INSERT_TOKEN_ADDRESS>';
_37
_37
// Transfer 1 token.
_37
const 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
_37
const tx = await wallet.createAndSignTx({
_37
msgs: [cw20Send],
_37
chainID: 'pisco-1',
_37
});
_37
const result = await lcd.tx.broadcast(tx, 'pisco-1');
_37
_37
console.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.


_53
import {
_53
MsgExecuteContract,
_53
MnemonicKey,
_53
Coins,
_53
LCDClient,
_53
} from '@terra-money/feather.js';
_53
_53
// const lcd = new LCDClient(...);
_53
_53
const 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
_53
const wallet = lcd.wallet(mk);
_53
_53
// LUNA <> OTHER_TOKEN
_53
const pool = '<INSERT_TOKEN_POOL_ADDRESS>';
_53
_53
// Fetch the number of each asset in the pool.
_53
const { assets } = await lcd.wasm.contractQuery(pool, { pool: {} });
_53
_53
// Calculate belief price using pool balances.
_53
const beliefPrice = (assets[0].amount / assets[1].amount).toFixed(18);
_53
_53
// Swap 1 LUNA with 1% slippage tolerance.
_53
const 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
_53
const tx = await wallet.createAndSignTx({
_53
msgs: [terraSwap],
_53
chainID: 'pisco-1',
_53
});
_53
const result = await lcd.tx.broadcast(tx, 'pisco-1');
_53
_53
console.log(result);

Decoding Protobuf-encoded messages

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


_17
import { LCDClient, Tx } from '@terra-money/feather.js';
_17
_17
// const lcd = new LCDClient(...);
_17
_17
const blockData = await lcd.tendermint.blockInfo(5923213);
_17
_17
const 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.
_17
const initMessages = txInfos
_17
.map((tx) => tx.body.messages)
_17
.flat()
_17
.find((i) => i.constructor.name === 'MsgInstantiateContract');
_17
_17
console.log(initMessages);

Validate a Terra address

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


_9
import { AccAddress } from '@terra-money/feather.js';
_9
// validate length, prefix and bech32 checksum. The second parameter is the expected bech32 prefix.
_9
AccAddress.validate('terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v', 'terra'); // true
_9
_9
// to check just length and bech32 checksum (if you don't know the chain).
_9
AccAddress.validate('terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v'); // true
_9
_9
// Get the prefix of an address. It will throw an error if the address is invalid.
_9
AccAddress.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
_19
wallet
_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
});