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

Smart contracts

This document explains how to perform tasks related to working with smart contracts using Feather.js.

Upload code

You will first need a compiled WASM smart contract's binary to upload.

import {
LCDClient,
MsgStoreCode,
MnemonicKey,
isTxError,
} from '@terra-money/feather.js';
import * as fs from 'fs';

// test1 key from localterra accounts
const mk = new MnemonicKey({
mnemonic:
'notice oak worry limit wrap speak medal online prefer cluster roof addict wrist behave treat actual wasp year salad speed social layer crew genius',
});

// connect to localterra
const terra = new LCDClient({
localterra: {
lcd: 'http://localhost:1317',
chainID: 'localterra',
gasAdjustment: 1.75,
gasPrices: { uluna: 0.015 },
prefix: 'terra', // bech32 prefix
},
});

const wallet = terra.wallet(mk);

const storeCode = new MsgStoreCode(
wallet.key.accAddress('terra'),
fs.readFileSync('contract.wasm').toString('base64'),
);
const storeCodeTx = await wallet.createAndSignTx({
msgs: [storeCode],
chainID: 'pisco-1',
});
const storeCodeTxResult = await terra.tx.broadcast(storeCodeTx, 'pisco-1');

console.log(storeCodeTxResult);

if (isTxError(storeCodeTxResult)) {
throw new Error(
`store code failed. code: ${storeCodeTxResult.code}, codespace: ${storeCodeTxResult.codespace}, raw_log: ${storeCodeTxResult.raw_log}`,
);
}

const {
store_code: { code_id },
} = storeCodeTxResult.logs[0].eventsByType;
Copy

Create a contract

For Terra smart contracts, there is a distinction between uploading contract code and instantiating a contract. This separation allows multiple contracts to share the same uploaded code if there are only minor variations in their logic which can be configured at contract creation. This configuration is passed in an InitMsg and provides the initial state of the contract.

To create or instantiate a smart contract, you must first know the code_id of an uploaded code. You will reference this code_id in a MsgInstantiateContract alongside the InitMsg to create the contract. Upon successful creation, your contract will be located at an address that you specify.

import { MsgInstantiateContract } from '@terra-money/feather.js';

const instantiate = new MsgInstantiateContract(
wallet.key.accAddress('terra'),
code_id[0], // code ID
{
count: 0,
}, // InitMsg
{ uluna: 10000000 }, // init coins
false, // migratable
);

const instantiateTx = await wallet.createAndSignTx({
msgs: [instantiate],
chainID: 'pisco-1',
});
const instantiateTxResult = await terra.tx.broadcast(instantiateTx, 'pisco-1');

console.log(instantiateTxResult);

if (isTxError(instantiateTxResult)) {
throw new Error(
`instantiate failed. code: ${instantiateTxResult.code}, codespace: ${instantiateTxResult.codespace}, raw_log: ${instantiateTxResult.raw_log}`,
);
}

const {
instantiate_contract: { contract_address },
} = instantiateTxResult.logs[0].eventsByType;
Copy

Execute a contract

Smart contracts respond to JSON messages called HandleMsg which can exist as different types. The smart contract writer must provide end-users of the smart contract with the expected format of all the varieties of HandleMsg the contract is supposed to understand, in the form of a JSON schema. This schema is analogous to Ethereum contracts' ABI.

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

const execute = new MsgExecuteContract(
wallet.key.accAddress('terra'), // sender
contract_address[0], // contract account address
{ ...executeMsg }, // handle msg
{ uluna: 100000 }, // coins
);

const executeTx = await wallet.createAndSignTx({
msgs: [execute],
chainID: 'pisco-1',
});

const executeTxResult = await terra.tx.broadcast(executeTx, 'pisco-1');
Copy

Query data from a contract

A contract can define a query handler, which understands requests for data specified in a JSON message called a QueryMsg. Unlike the message handler, the query handler cannot modify the contract's or blockchain's state. Queries are read-only operations. Therefore, querying data from a contract does not use a message and transaction, but works directly through the LCDClient API.

const result = await terra.wasm.contractQuery(
contract_address[0],
{ query: { queryMsgArguments } }, // query msg
);
Copy