Subscribe to our free newsletter

To make sure you won't miss any valuable content we share with our community.

Interacting with Terra Network Using Terra Python SDK

In this article, we are going to get familiar with the Terra network and how it works. Then, we will see how we can interact with the Terra network using Terra Python SDK to interact with Terra Testnet called Bombay 12 and Mainnet called Columbus-5. In this article and the next ones, we are going to see how we can connect to the Terra network using Python, create an account and a wallet, create and sign a transaction, estimate gas fees, and swap tokens.

What Is Terra SDK?

Terra is a DeFi protocol on Blockchain that powers the universal payment systems which are based on fiat currencies using the fiat-based stable coins. This protocol uses a mixture of wide adoption of stable coins and at the same time censorship resistance of Bitcoin. Terra is built on Cosmos SDK and tendermint. The main language that the Terra smart contracts are written in, is Rust. However, there are SDKs for Python and JavaScript, and creating the smart contracts called WASM contracts is explained in more detail in JavaScript. But don’t worry most of the interactions and useful functions can be found in Python. Throughout this tutorial, we are going to see all the things we can do to develop the Terra network using Terra.py SDK.

Dealing with Terra Smart Contracts Using Terra Python SDK

If you have already worked with Ethereum smart contracts or have read our content on brownie, solidity, and Ethereum network smart contracts, you will so easily understand most of the concepts and tools that we are going to talk about. However, if you haven’t, you can still follow along with this tutorial and get familiar with the new world of smart contracts. As always, like all the blockchains that we have interacted with, we start with the test network to get used to the scripts and then learn how to switch to mainnet. To get started with Terra python SDK, let us install it using the following command:

pip3 install terra-sdk

There are also other requirements that we will cover later in our tutorial. Now let’s connect to Terra network:

terra = LCDClient("https://lcd.terra.dev", "columbus-5")
print(terra.tendermint.node_info())

Result:

{‘node_info’: {‘protocol_version’: {‘p2p’: ‘8’, ‘block’: ’11’, ‘app’: ‘0’}, ‘id’: ‘979a7f9c646623535ba2ddc69c10f3d505ccd97a’, ‘listen_addr’: ‘tcp://0.0.0.0:26656’, ‘network’: ‘columbus-5’, ‘version’: ‘0.34.14’, ‘channels’: ‘40202122233038606100’, ‘moniker’: ‘rpc5-read4’, ‘other’: {‘tx_index’: ‘on’, ‘rpc_address’: ‘tcp://0.0.0.0:26657’}}, ‘application_version’: {‘name’: ‘terra’, ‘server_name’: ‘terrad’, ‘version’: ‘0.5.17-36- gfc7ef220’, ‘commit’: ‘fc7ef220ddcd683d8e1a37a3ac484abb4e8b88ef’, ‘build_tags’: ‘netgo,ledger’, ‘go’: ‘go version go1.18 linux/amd64’, ‘build_deps’: [‘filippo.io/[email protected]’, ‘github.com/99designs/[email protected] => github.com/cosmos/[email protected]’, ‘github.com/ChainSafe/[email protected]’, ‘github.com/CosmWasm/[email protected]’, ‘github.com/Workiva/[email protected]’, ‘github.com/armon/[email protected]’, ‘github.com/beorn7/[email protected]’, ‘github.com/bgentry/[email protected]’, ‘github.com/btcsuite/[email protected]’, ‘github.com/cespare/xxhash/[email protected]’, ‘github.com/coinbase/[email protected]’, ‘github.com/confio/ics23/[email protected]’, ‘github.com/cosmos/[email protected]’, ‘github.com/cosmos/[email protected] => github.com/terra-money/[email protected]’, ‘github.com/cosmos/[email protected]’, ‘github.com/cosmos/[email protected]’, ‘github.com/cosmos/[email protected]’, ‘github.com/cosmos/[email protected] => github.com/terramoney/[email protected]’, ‘github.com/cosmos/[email protected]’, ‘github.com/davecgh/[email protected]’, ‘github.com/desertbit/[email protected] 20180107155436-c41aec40b27f’, ‘github.com/dvsekhvalnov/[email protected] 20200901110807-248326c1351b’, ‘github.com/felixge/[email protected]’, ‘github.com/fsnotify/[email protected]’, ‘github.com/go-kit/[email protected]’, ‘github.com/go-logfmt/[email protected]’, ‘github.com/godbus/[email protected] 20190726142602-4481cbc300e2’, ‘github.com/gogo/[email protected]’, ‘github.com/gogo/[email protected] => github.com/regen-network/[email protected] alpha.regen.1’, ‘github.com/golang/[email protected]’, ‘github.com/golang/[email protected]’, ‘github.com/google/[email protected]’, ‘github.com/google/[email protected]’, ‘github.com/gorilla/[email protected]’, ‘github.com/gorilla/[email protected]’, ‘github.com/gorilla/[email protected]’, ‘github.com/grpc-ecosystem/[email protected]’, ‘github.com/grpcecosystem/[email protected]’, ‘github.com/gsterjov/[email protected] 20161001094733-a6f4afe4910c’, ‘github.com/gtank/[email protected]’, ‘github.com/gtank/[email protected]’, ‘github.com/hashicorp/[email protected]’, ‘github.com/hashicorp/[email protected]’, ‘github.com/hashicorp/[email protected]’, ‘github.com/hdevalence/[email protected] 59a8610d2b87’, ‘github.com/improbable-eng/[email protected]’, ‘github.com/klauspost/[email protected]’, ‘github.com/lib/[email protected]’, ‘github.com/libp2p/[email protected]’, ‘github.com/magiconair/[email protected]’, ‘github.com/mattn/[email protected]’, ‘github.com/matttproud/[email protected]’, ‘github.com/mimoo/[email protected]’, ‘github.com/minio/[email protected]’, ‘github.com/mitchellh/[email protected]’, ‘github.com/mtibben/[email protected]’, ‘github.com/pelletier/[email protected]’, ‘github.com/pkg/[email protected]’, ‘github.com/pmezard/[email protected]’, ‘github.com/prometheus/[email protected]’, ‘github.com/prometheus/[email protected]’, ‘github.com/prometheus/[email protected]’, ‘github.com/prometheus/[email protected]’, ‘github.com/rakyll/[email protected]’, ‘github.com/rcrowley/[email protected] 20200313005456-10cdbea86bc0’, ‘github.com/regen-network/[email protected]’, ‘github.com/rs/[email protected]’, ‘github.com/rs/[email protected]’, ‘github.com/spf13/[email protected]’, ‘github.com/spf13/[email protected]’, ‘github.com/spf13/[email protected]’, ‘github.com/spf13/[email protected]’, ‘github.com/spf13/[email protected]’, ‘github.com/spf13/[email protected]’, ‘github.com/stretchr/[email protected]’, ‘github.com/subosito/[email protected]’, ‘github.com/syndtr/[email protected]’, ‘github.com/tendermint/[email protected]’, ‘github.com/tendermint/[email protected] 20191022145703-50d29ede1e15’, ‘github.com/tendermint/[email protected]’, ‘github.com/tendermint/[email protected] => github.com/terra-money/[email protected]’, ‘github.com/tendermint/[email protected] => github.com/terra-money/[email protected] performance.7’, ‘github.com/zondax/[email protected]’, ‘golang.org/x/[email protected] 20210817164053-32db794688a5’, ‘golang.org/x/[email protected] ad29c8ab022f’, ‘golang.org/x/[email protected]’, ‘golang.org/x/[email protected]’, ‘golang.org/x/[email protected]’, ‘google.golang.org/[email protected] 66f60bf46e71’, ‘google.golang.org/[email protected] => google.golang.org/[email protected]’, ‘google.golang.org/[email protected]’, ‘gopkg.in/[email protected]’, ‘gopkg.in/[email protected]’, ‘gopkg.in/[email protected] 20210107192922-496545a6307b’, ‘nhooyr.io/[email protected]’], ‘cosmos_sdk_version’: ‘v0.44.5’}}

For the first step, we can create an account by creating a mnemonic key:

from terra_sdk.client.lcd import LCDClient
from terra_sdk.key.mnemonic import MnemonicKey
mk = MnemonicKey()
print(mk.mnemonic)


Now, let’s test run the code:

python3 Test.py

Result:

tonight panda own all coconut home tackle gap food uncle vacant account clap napkin boring mango cable funny tooth vanish opinion genre differ impact

Notice that the above mnemonic key works the same as the private key and you will need to save it somewhere safe. Because if you forget it, you will never be able to recover the phrases. Besides, do not pass this mnemonic key to anyone. Because if you have money in it, they will be able to withdraw it anytime they want. We can get the account address by:

print(mk.acc_address)

Result:

terra1krrcxa8ccs7dwagtuevt2m4d78z0uwf4qe25yu

And the private key by:

print(mk.private_key)

Result:

b’\xf1\x92IyzN<\x14\xf2{N\x8a=\x86\x06\x9c \xab\n~/*\xb0\xb8b+z\xecS\x10\xcd,'

And the private key by:

print(mk.private_key)

Besides, make sure you save the mnemonic key somewhere safe. Let’s get some Luna from Terra Testnet Faucet (https://faucet.terra.money/). We need to copy the account address we’ve got in the box:

Terra network using Terra Python SDK

Once we get the message, we will be redirected to the terrascope which is similar to Etherscan and we can see the test Luna that we have been given:

Terra network using Terra Python SDK

Terra network using Terra Python SDK-transaction details

We can also see the details of our account if we click on the “to” address:

Terra network using Terra Python SDK-creating account

We can retrieve the balance of our account using the following code:

from terra_sdk.client.lcd import LCDClient
from terra_sdk.key.mnemonic import MnemonicKey
terra = LCDClient("https://lcd.terra.dev", " bombay-12")
mk = MnemonicKey(mnemonic="disorder solar ride ecology father pear conduct 
     anger distance soul slow outside market twenty badge alter busy inspire tag 
     enforce chicken shaft giggle measure")
print(terra.bank.balance(mk.acc_address))


Notice that in the above code, we have managed to call back the account using the mnemonic key we got when we used the mk = MnemonicKey() script. We have also connected to the bombay-12 Testnet. Now let’s run the code: python3 Test.py Result: Coins('1000000000uluna’)

What We Learned and What Will Be in the Afterwards

Up to now, for Terra network using Terra Python SDK, we have learned how to connect to Terra mainnet and testnet, create an account, and get the mnemonic key, private key, and the balance of the account. In the next part of this article, we are going to go deeper into transactions and interactions with the Terra smart contracts. We will create a wallet, create and sign a transaction, etc.

In this article we are going to continue our journey in Terra python SDK and interact with more of its functionalities. In this article and the next one we are going to see how we can connect to the Terra network, create an account and a wallet, create and sign transactions, estimate gas fees, and swap tokens. Of course, all of these operations are going to be executed on a testnet rather a mainnet in case we do lose real money.S

Introduction: In this article, we are going to cover more interactions with Terra Testnet by creating and signing transactions, transferring money, creating a wallet and an account, and send JSON messages to Terra smart contracts. 1. Setting up the network and the chain id: Up to now, we have learned how to connect to Terra chain mainnet and testnet, how to get some test Luna and how we can create test accounts. One thing to notice is how we can switch between different networks (mainnet to testnet). As we have mentioned before, there are 2 parameters for defining every network: 1. URL 2. Chain ID Terra chain has had many test networks such as tequila which is outdated, bombay (the newer one), and other custom networks created by developers. You can also create your own node using the guides on the Terra Documentation website (https://docs.terra.money/docs/full-node/run-a-full-terra-node/join-a-network.html# ) If you are on mainnet, you can enter as https://lcd.terra.dev URL and as Columbus-5 chain id and if you are on testnet, you can use https://bombay-lcd.terra.dev/ as URL and bombay-12 as your chain id. Notice that at the current time of writing, the Terra The faucet is on Bombay test network. So if you want to interact with the test ulunas sent from Bombay Faucet, you should use the specific URL and chain id of this test network. 2. Creating an Account on Terra Testnet: Using the below script, we can create an account, and get a mnemonic key for it. Make sure you copy the mnemonic somewhere so that you can use it later when you want to get some test Luna from Terra Faucet or when you want to get the balance of the account or create and sign a transaction using it.

from terra_sdk.client.lcd import LCDClient
from terra_sdk.key.mnemonic import MnemonicKey
terra = LCDClient("https://bombay-lcd.terra.dev/", "bombay-12")
Account1 = MnemonicKey()
print(Account1.mnemonic)


result: call oil decrease loud pull indicate diet post sign cereal adapt rug reform alcohol math illegal major anchor unhappy govern win round ritual various Now, we can use the Bombay Faucet to get some test uluna. And using the below script we can see the balance of account 1 containing uluna,

print(terra.bank.balance(Account1.acc_address)[0]['uluna'].amount)


result: 5000000 and using the below code, we can see all the assets of the account1: print(terra.bank.balance(Account1.acc_address)[0]) result: Coins('5000000uluna') 3. Transferring Luna from one account to another using python script: Now, we want to create a transaction using the Account1 with the mnemonic key that we have got and creating a new account called Account2. Notice that we define each account with its specific mnemonic key and here when we want to use the created account (Account1), we use the mnemonic key to reuse it, otherwise, a new account will be created with no funds in it.

Account1 = MnemonicKey(mnemonic="call oil decrease loud pull indicate diet post 
     sign cereal adapt rug reform alcohol math illegal 
     major anchor unhappy govern win round 
     ritual various")


And here comes the complete code to create a transaction and send a quarter of the balance of account 1 to account 2. We will also create a wallet for account 1 to be able to execute the transaction.

from terra_sdk.client.lcd import LCDClient
from terra_sdk.key.mnemonic import MnemonicKey
from terra_sdk.core.fee import Fee
from terra_sdk.core.bank import MsgSend
from terra_sdk.client.lcd.api.tx import CreateTxOptions

terra = LCDClient("https://bombay-lcd.terra.dev/", "bombay-12")
Account1 = MnemonicKey(mnemonic="call oil decrease loud pull indicate diet post 
     sign cereal adapt rug reform alcohol math illegal 
     major anchor unhappy govern win round 
     ritual various")
Account2 = MnemonicKey()
wallet = terra.wallet(Account1)
print(terra.bank.balance(Account1.acc_address)[0]['uluna'].amount)
print(terra.bank.balance(Account2.acc_address)[0]['uluna'].amount)
Amount_To_Send = (terra.bank.balance(Account1.acc_address)[0])/4
print(Amount_To_Send)

tx = wallet.create_and_sign_tx(CreateTxOptions(
     msgs = [MsgSend(from_address=Account1.acc_address, 
          to_address = Account2.acc_address, 
          amount=Amount_To_Send )],
     memo = "Only Testing",
     gas_prices = "1uluna", 
     gas_adjustment = "1.5")) 

result = terra.tx.broadcast(tx)
print(result)


result: 5000000 0 1250000uluna BlockTxBroadcastResult(height=8795417, txhash='10B07115283D05126EF4DCA72576BB3E4DD812B24B002614913020532 2999645', raw_log='[{"events":[{"type":"coin_received","attributes": [{"key":"receiver","value":"terra1e20tg4gat39ex4ezer724r5qs9dhnshjg065d4"}, {"key":"amount","value":"1250000uluna"}]},{"type":"coin_spent","attributes": [{"key":"spender","value":"terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr"}, {"key":"amount","value":"1250000uluna"}]},{"type":"message","attributes": [{"key":"action","value":"/cosmos.bank.v1beta1.MsgSend"}, {"key":"sender","value":"terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr"}, {"key":"module","value":"bank"}]},{"type":"transfer","attributes": [{"key":"recipient","value":"terra1e20tg4gat39ex4ezer724r5qs9dhnshjg065d4"}, {"key":"sender","value":"terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr"}, {"key":"amount","value":"1250000uluna"}]}]}]', gas_wanted=95229, gas_used=76149, logs=[TxLog(msg_index=0, log='', events=[{'type': 'coin_received', 'attributes': [{'key': 'receiver', 'value': 'terra1e20tg4gat39ex4ezer724r5qs9dhnshjg065d4'}, {'key': 'amount', 'value': '1250000uluna'}]}, {'type': 'coin_spent', 'attributes': [{'key': 'spender', 'value': 'terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr'}, {'key': 'amount', 'value': '1250000uluna'}]}, {'type': 'message', 'attributes': [{'key': 'action', 'value': '/cosmos.bank.v1beta1.MsgSend'}, {'key': 'sender', 'value': 'terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr'}, {'key': 'module', 'value': 'bank'}]}, {'type': 'transfer', 'attributes': [{'key': 'recipient', 'value': 'terra1e20tg4gat39ex4ezer724r5qs9dhnshjg065d4'}, {'key': 'sender', 'value': 'terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr'}, {'key': 'amount', 'value': '1250000uluna'}]}], events_by_type={'coin_received': {'receiver': ['terra1e20tg4gat39ex4ezer724r5qs9dhnshjg065d4'], 'amount': ['1250000uluna']}, 'coin_spent': {'spender': ['terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr'], 'amount': ['1250000uluna']}, 'message': {'action': ['/cosmos.bank.v1beta1.MsgSend'], 'sender': ['terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr'], 'module': ['bank']}, 'transfer': {'recipient': ['terra1e20tg4gat39ex4ezer724r5qs9dhnshjg065d4'], 'sender': ['terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr'], 'amount': ['1250000uluna']}})], code=0, codespace='', info=None, data=None, timestamp=None) As you can see after a few seconds, the result of the transaction is printed out and you can get some of the data you need from this very long output. If we want to check our transaction details we can head over to Terra Finder (https://finder.terra.money/ ) and paste the txhash from the resulting output. Notice that you should choose the network ( mainnet, testnet or local) from the top-right menu. and you will be able to see the details of the transaction similar to what we saw in Etherscan for Ethereum blockchain. Conclusion: In this section, we have learned how to connect to the Terra Tesnet blockchain called Bombay-12, create an account, create a wallet, transfer money from one account to another and create and sign a transaction using python Terra SDK. In the next part, we will use this SDK to swap the tokens in the Terra network as well as estimate the gas fees.

In this article we are going to get familiar with the Terra Network Using Terra Python SDK and how it works. Then, we will see how we can use Terra Python SDK to interact with Terra Testnet called Bombay-12 and Mainnet called Columbus-5. In this 3rd part of Terra Python SDK, we are going to fetch price data from Terra Network, estimate gas fees for our transactions, and swap tokens using the Python SDK for Terra.

Terra Network Using Terra Python SDK: the Last Part

In this 3rd part of Terra Python SDK, we are going to fetch price data from Terra Network, estimate the gas fee for our transactions, and swap tokens using the Python SDK for Terra.

Requesting Price Data

We can fetch the price of uluna from FCD terra node with the following script:

from terra_sdk.client.lcd import LCDClient
from terra_sdk.key.mnemonic import MnemonicKey
import requests
import json

terra = LCDClient("https://bombay-lcd.terra.dev/", "bombay-12")
Account1 = MnemonicKey(mnemonic="call oil decrease loud pull indicate diet post 
     sign cereal adapt rug reform alcohol 
     math illegal major anchor unhappy 
     govern win round ritual various")

Account2 = MnemonicKey()
wallet = terra.wallet(Account1)
print(requests.get('https://fcd.terra.dev/v1/market/price?denom=uusd&interval=1h').json())


Result: {'lastPrice': 90.48744281649324, 'oneDayVariation': '2.03086440151527', 'oneDayVariationRate': '0.02244360475114567212', 'prices': [{'denom': 'uusd', 'price': 88.96415361266797, 'datetime': 1650888000000}, {'denom': 'uusd', 'price': 89.0900844450184, 'datetime': 1650891600000}, {'denom': 'uusd', 'price': 90.66006870152763, 'datetime': 1650895200000}, {'denom': 'uusd', 'price': 92.52536809614227, 'datetime': 1650898800000}, {'denom': 'uusd', 'price': 94.04939366114037, 'datetime': 1650902400000}, {'denom': 'uusd', 'price': 94.3395565349883, 'datetime': 1650906000000}, {'denom': 'uusd', 'price': 94.4164196214135, 'datetime': 1650909600000}, {'denom': 'uusd', 'price': 95.56109206654655, 'datetime': 1650913200000}, {'denom': 'uusd', 'price': 95.81135596156976, 'datetime': 1650916800000}, {'denom': 'uusd', 'price': 95.28242522306937, 'datetime': 1650920400000}, {'denom': 'uusd', 'price': 95.58401419222808, 'datetime': 1650924000000}, {'denom': 'uusd', 'price': 96.91434728345023, 'datetime': 1650927600000}, {'denom': 'uusd', 'price': 96.80871579823581, 'datetime': 1650931200000}, {'denom': 'uusd', 'price': 96.17697287838071, 'datetime': 1650934800000}, {'denom': 'uusd', 'price': 96.03501028344412, 'datetime': 1650938400000}, {'denom': 'uusd', 'price': 96.22807016895509, 'datetime': 1650942000000}, {'denom': 'uusd', 'price': 96.06570662357241, 'datetime': 1650945600000}, {'denom': 'uusd', 'price': 95.70176476651612, 'datetime': 1650949200000}, {'denom': 'uusd', 'price': 95.60364542853246, 'datetime': 1650952800000}, {'denom': 'uusd', 'price': 96.30584856654487, 'datetime': 1650956400000}, {'denom': 'uusd', 'price': 96.28680905438578, 'datetime': 1650960000000}, {'denom': 'uusd', 'price': 96.0928405310103, 'datetime': 1650963600000}, {'denom': 'uusd', 'price': 96.37104756437165, 'datetime': 1650967200000}, {'denom': 'uusd', 'price': 96.34377771748161, 'datetime': 1650970800000}, {'denom': 'uusd', 'price': 95.68353760426069, 'datetime': 1650974400000}, {'denom': 'uusd', 'price': 95.93167829936388, 'datetime': 1650978000000}, {'denom': 'uusd', 'price': 93.96949533359299, 'datetime': 1650981600000}, {'denom': 'uusd', 'price': 91.55576611528446, 'datetime': 1650985200000}, {'denom': 'uusd', 'price': 89.93169609113293, 'datetime': 1650988800000}, {'denom': 'uusd', 'price': 89.23415738171494, 'datetime': 1650992400000}, {'denom': 'uusd', 'price': 89.06808181910975, 'datetime': 1650996000000}, {'denom': 'uusd', 'price': 88.48694667915775, 'datetime': 1650999600000}, {'denom': 'uusd', 'price': 88.11627958903455, 'datetime': 1651003200000}, {'denom': 'uusd', 'price': 89.09030726339398, 'datetime': 1651006800000}, {'denom': 'uusd', 'price': 89.06232865592419, 'datetime': 1651010400000}, {'denom': 'uusd', 'price': 88.54428184656634, 'datetime': 1651014000000}, {'denom': 'uusd', 'price': 88.778354665981, 'datetime': 1651017600000}, {'denom': 'uusd', 'price': 89.00243656718825, 'datetime': 1651021200000}, {'denom': 'uusd', 'price': 88.7637772736078, 'datetime': 1651024800000}, {'denom': 'uusd', 'price': 88.43322633009933, 'datetime': 1651028400000}, {'denom': 'uusd', 'price': 88.03606836235143, 'datetime': 1651032000000}, {'denom': 'uusd', 'price': 88.30151817608929, 'datetime': 1651035600000}, {'denom': 'uusd', 'price': 88.60340905121288, 'datetime': 1651039200000}, {'denom': 'uusd', 'price': 89.83111008867395, 'datetime': 1651042800000}, {'denom': 'uusd', 'price': 89.64450032950381, 'datetime': 1651046400000}, {'denom': 'uusd', 'price': 89.34933700739906, 'datetime': 1651050000000}, {'denom': 'uusd', 'price': 89.27527287128059, 'datetime': 1651053600000}, {'denom': 'uusd', 'price': 88.73870274154594, 'datetime': 1651057200000}, {'denom': 'uusd', 'price': 88.83721399177365, 'datetime': 1651060800000}, {'denom': 'uusd', 'price': 89.2133569858777, 'datetime': 1651064400000}]} Gas Fee Estimation on Terra Blockchain: Gas fee estimation is an important step when you want to execute a transaction on a blockchain. Using the following code in which we use “https://fcd.terra.dev/v1/txs/gas_prices”, we can get the gas fee in different tokens, so that we can later estimate the gas fee for our transaction:

from terra_sdk.client.lcd import LCDClient
from terra_sdk.key.mnemonic import MnemonicKey
import requests
import json
terra = LCDClient("https://bombay-lcd.terra.dev/", "bombay-12")
Account1 = MnemonicKey(mnemonic="call oil decrease loud pull indicate diet post
     sign cereal adapt rug reform alcohol math illegal major anchor unhappy govern win
     round ritual various")
Account2 = MnemonicKey()
wallet = terra.wallet(Account1)
fees = requests.get("https://fcd.terra.dev/v1/txs/gas_prices").json()
print(fees)


Result: {'uluna': '0.01133', 'usdr': '0.104938', 'uusd': '0.15', 'ukrw': '170.0', 'umnt': '428.571', 'ueur': '0.125', 'ucny': '0.98', 'ujpy': '16.37', 'ugbp': '0.11', 'uinr': '10.88', 'ucad': '0.19', 'uchf': '0.14', 'uaud': '0.19', 'usgd': '0.2', 'uthb': '4.62', 'usek': '1.25', 'unok': '1.25', 'udkk': '0.9', 'uidr': '2180.0', 'uphp': '7.6', 'uhkd': '1.17', 'umyr': '0.6', 'utwd': '4.0'} And you can see the .json output of the fee in different tokens. Now, it is time to estimate the gas fee using fee = str(float(fees[“uusd”])) + “uusd”

from terra_sdk.client.lcd import LCDClient
from terra_sdk.key.mnemonic import MnemonicKey
import requests
import json
from terra_sdk.core.bank import MsgSend
terra = LCDClient("https://bombay-lcd.terra.dev/", "bombay-12")
Account1 = MnemonicKey(mnemonic="call oil decrease loud pull indicate diet post
     sign cereal adapt rug reform alcohol math illegal major anchor unhappy govern win
     round ritual various")
Account2 = MnemonicKey()
wallet = terra.wallet(Account1)
fees = requests.get("https://fcd.terra.dev/v1/txs/gas_prices").json()
Amount_To_Send = (terra.bank.balance(Account1.acc_address)[0])/10
print(Amount_To_Send)
msg = MsgSend(from_address=Account1.acc_address, 
     to_address=Account2.acc_address, 
     amount=Amount_To_Send )

fee = str(float(fees["uusd"])) + "uusd"
print(fee)


Result: 67360uluna 0.15uusd Swapping Tokens on Terra blockchain: One of the most useful features of Terra blockchain is the ability to swap between different tokens of its chain. For example, here we want to swap our Bombay testnet uluna with test UST. The below script shows how we simply do that. To make sure that the swapping transaction has been successfully completed, we check the balance before and after the transaction.

from terra_sdk.client.lcd import LCDClient
from terra_sdk.key.mnemonic import MnemonicKey
from terra_sdk.core.market import MsgSwap
from terra_sdk.client.lcd.api.tx import CreateTxOptions
terra = LCDClient("https://bombay-lcd.terra.dev/", "bombay-12")
Account1 = MnemonicKey(mnemonic="call oil decrease loud pull indicate diet post
     sign cereal adapt rug reform alcohol math illegal major anchor unhappy govern win
     round ritual various")
msg = MsgSwap(Account1.acc_address, "100uluna", "uusd")
wallet = terra.wallet(Account1)
print(terra.bank.balance(Account1.acc_address)[0])
tx = wallet.create_and_sign_tx(CreateTxOptions(msgs = [msg],
     memo = "Swapping luna for UST",
     gas_prices = "1uluna",
     gas_adjustment = "1.3",
     fee_denoms = ["uluna", "uusd"]
     ))
result = terra.tx.broadcast(tx)
print(result)
print(terra.bank.balance(Account1.acc_address)[0])


Result: 421527uluna,17604uusd BlockTxBroadcastResult(height=8805631, txhash='D87F2D6ACD0EAA919EA6AAE66DC173DCF162A58668560C9284EA4 52D42279125', raw_log='[{"events":[{"type":"burn","attributes": [{"key":"burner","value":"terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s"}, {"key":"amount","value":"100uluna"}]},{"type":"coin_received","attributes": [{"key":"receiver","value":"terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s"}, {"key":"amount","value":"100uluna"}, {"key":"receiver","value":"terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s"}, {"key":"amount","value":"8864uusd"}, {"key":"receiver","value":"terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr"}, {"key":"amount","value":"8820uusd"}, {"key":"receiver","value":"terra1jgp27m8fykex4e4jtt0l7ze8q528ux2lh4zh0f"}, {"key":"amount","value":"44uusd"}]},{"type":"coin_spent","attributes": [{"key":"spender","value":"terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr"}, {"key":"amount","value":"100uluna"}, {"key":"spender","value":"terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s"}, {"key":"amount","value":"100uluna"}, {"key":"spender","value":"terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s"}, {"key":"amount","value":"8820uusd"}, {"key":"spender","value":"terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s"}, {"key":"amount","value":"44uusd"}]},{"type":"coinbase","attributes": [{"key":"minter","value":"terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s"}, {"key":"amount","value":"8864uusd"}]},{"type":"message","attributes": [{"key":"action","value":"/terra.market.v1beta1.MsgSwap"}, {"key":"sender","value":"terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr"}, {"key":"sender","value":"terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s"}, {"key":"sender","value":"terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s"}, {"key":"module","value":"market"}]},{"type":"swap","attributes": [{"key":"offer","value":"100uluna"}, {"key":"trader","value":"terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr"}, {"key":"recipient","value":"terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr"}, {"key":"swap_coin","value":"8820uusd"},{"key":"swap_fee","value":"44uusd"}]}, {"type":"transfer","attributes": [{"key":"recipient","value":"terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s"}, {"key":"sender","value":"terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr"}, {"key":"amount","value":"100uluna"}, {"key":"recipient","value":"terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr"}, {"key":"sender","value":"terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s"}, {"key":"amount","value":"8820uusd"}, {"key":"recipient","value":"terra1jgp27m8fykex4e4jtt0l7ze8q528ux2lh4zh0f"}, {"key":"sender","value":"terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s"}, {"key":"amount","value":"44uusd"}]}]}]', gas_wanted=126031, gas_used=108830, logs=[TxLog(msg_index=0, log='', events=[{'type': 'burn', 'attributes': [{'key': 'burner', 'value': 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'}, {'key': 'amount', 'value': '100uluna'}]}, {'type': 'coin_received', 'attributes': [{'key': 'receiver', 'value': 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'}, {'key': 'amount', 'value': '100uluna'}, {'key': 'receiver', 'value': 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'}, {'key': 'amount', 'value': '8864uusd'}, {'key': 'receiver', 'value': 'terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr'}, {'key': 'amount', 'value': '8820uusd'}, {'key': 'receiver', 'value': 'terra1jgp27m8fykex4e4jtt0l7ze8q528ux2lh4zh0f'}, {'key': 'amount', 'value': '44uusd'}]}, {'type': 'coin_spent', 'attributes': [{'key': 'spender', 'value': 'terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr'}, {'key': 'amount', 'value': '100uluna'}, {'key': 'spender', 'value': 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'}, {'key': 'amount', 'value': '100uluna'}, {'key': 'spender', 'value': 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'}, {'key': 'amount', 'value': '8820uusd'}, {'key': 'spender', 'value': 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'}, {'key': 'amount', 'value': '44uusd'}]}, {'type': 'coinbase', 'attributes': [{'key': 'minter', 'value': 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'}, {'key': 'amount', 'value': '8864uusd'}]}, {'type': 'message', 'attributes': [{'key': 'action', 'value': '/terra.market.v1beta1.MsgSwap'}, {'key': 'sender', 'value': 'terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr'}, {'key': 'sender', 'value': 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'}, {'key': 'sender', 'value': 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'}, {'key': 'module', 'value': 'market'}]}, {'type': 'swap', 'attributes': [{'key': 'offer', 'value': '100uluna'}, {'key': 'trader', 'value': 'terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr'}, {'key': 'recipient', 'value': 'terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr'}, {'key': 'swap_coin', 'value': '8820uusd'}, {'key': 'swap_fee', 'value': '44uusd'}]}, {'type': 'transfer', 'attributes': [{'key': 'recipient', 'value': 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'}, {'key': 'sender', 'value': 'terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr'}, {'key': 'amount', 'value': '100uluna'}, {'key': 'recipient', 'value': 'terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr'}, {'key': 'sender', 'value': 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'}, {'key': 'amount', 'value': '8820uusd'}, {'key': 'recipient', 'value': 'terra1jgp27m8fykex4e4jtt0l7ze8q528ux2lh4zh0f'}, {'key': 'sender', 'value': 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'}, {'key': 'amount', 'value': '44uusd'}]}], events_by_type={'burn': {'burner': ['terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'], 'amount': ['100uluna']}, 'coin_received': {'receiver': ['terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s', 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s', 'terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr', 'terra1jgp27m8fykex4e4jtt0l7ze8q528ux2lh4zh0f'], 'amount': ['100uluna', '8864uusd', '8820uusd', '44uusd']}, 'coin_spent': {'spender': ['terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr', 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s', 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s', 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'], 'amount': ['100uluna', '100uluna', '8820uusd', '44uusd']}, 'coinbase': {'minter': ['terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'], 'amount': ['8864uusd']}, 'message': {'action': ['/terra.market.v1beta1.MsgSwap','sender': ['terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr', 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s', 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'], 'module': ['market']}, 'swap': {'offer': ['100uluna'], 'trader': ['terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr'], 'recipient': ['terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr'], 'swap_coin': ['8820uusd'], 'swap_fee': ['44uusd']}, 'transfer': {'recipient': ['terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s', 'terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr', 'terra1jgp27m8fykex4e4jtt0l7ze8q528ux2lh4zh0f'], 'sender': ['terra16q9hkwe9t5jac20e08tz4qwflwkjxymw9qm3kr', 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s', 'terra1untf85jwv3kt0puyyc39myxjvplagr3wstgs5s'], 'amount': ['100uluna', '8820uusd', '44uusd']}})], code=0, codespace='', info=None, data=None, timestamp=None) 295396uluna,26424uusd And as you can see we have got some uusd in exchange for ulunas.

Learned to Work with Terra Network Using Terra Python SDK

In the last 3 articles, we have learned how to work with Terra Testnet and we have connected to Bombay-12 test network in addition to creating a wallet, an account, creating and signing a transaction, transferring money, swapping tokens and estimating gas fee for a transaction.

Download this Article in PDF format

metaverse

Curious about What We’re Up To?

In Arashtad we provide custom services in various design and development fields. 3D websites, 3D games, metaverses, and other types of WebGL and 3D applications are just some of our expertise.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Request a Quote
Blockchain Development

Interacting with Local Terra Smart Contracts Using Python

In this article, we first connect to local Terra using Docker. Then we try to interact with local Terra smart contracts using Python scripts and at the same time use the CosmWasm functions inside LocalTerra\src folder written in Rust programming language. Finally, we will manage to interact with it.

Connecting to Local Terra with Docker with Python

First off, To start our interaction with local Terra, we need to make sure docker is running: sudo usermod -aG docker $USER Make sure you sign in to your docker profile to be able to easily log in using the following command docker login. docker-compose up Result: terrad_1 | 8:48PM INF received proposal module=consensus proposal={"Type":32,"block_id":{"hash":"3F119B6520D17CCC29A23F016B9E40151E3A5784A3F83D65BD0DD2D282FB1A4F"," parts": {"hash":"5928F59D217852E3722E46A9134D40F9329D540555EAD1DAE4B5EFA8A41E88D4"," total":1}},"height":1948,"pol_round":- 1,"round":0,"signature":"cYh80NzZezistnyNzEP2yFDnsoc7JOB+/8J1wnell- Gq76IE3qvH9jU2KvQx6u4y+A9xYrIQEJonzxa//vOZfBw==","timestamp":"2022- 05-08T20:48:12.151658182Z"} terrad_1 | 8:48PM INF received complete proposal block hash=3F119B6520D17CCC29A23F016B9E40151E3A5784A3F83D65BD0DD2D282FB1A4F height=1948 module=consensus terrad_1 | 8:48PM INF finalizing commit of block hash=3F119B6520D17CCC29A23F016B9E40151E3A5784A3F83D65BD0DD2D282FB1A4F height=1948 module=consensus num_txs=0 root=332F90B4F0706BCB055547- FA8443A1F623FC3DAE4043162730F59D15612D5FAE terrad_1 | 8:48PM INF minted coins from module account amount=226578914uluna from=mint module=x/bank terrad_1 | 8:48PM INF executed block height=1948 module=state num_invalid_txs=0 num_valid_txs=0 terrad_1 | 8:48PM INF commit synced commit=436F6D6D697449447B5B393820323020343020323436203134362031303 32036342032313320393520313132203131372031353920393320313536203231312 03632203332203836203137203132352032333920323131203533203337203139352 03637203739203635203830203235322032203138355D3A3739437D terrad_1 | 8:48PM INF committed state app_hash=621428F6926740D55F70759F5D9CD33E2056117DEFD33525C3434F41 50FC02B9 height=1948 module=state num_txs=0 terrad_1 | 8:48PM INF indexed block height=1948 module=txindex terrad_1 | 8:48PM INF Timed out dur=4990.525864 height=1949 module=consensus round=0 step=1 terrad_1 | 8:48PM INF received proposal module=consensus proposal={"Type":32,"block_id":{"hash":"43FEBF954F5FFEA7350E4D8D38FCCA39E439F2D3026ADE9ED3E21D2706AC2596"," parts": {"hash":"112F8B0B6AA51D1D0FCCE2D049F739EC7FEB9B6C2092B1141A55E88AC453FF57"," total":1}},"height":1 949,"pol_round":-1,"round":0,"signature":"tuusiNhk5kRrKpLykCNWb/4b1nrfk22n- RCyaLh650ouCugzK7rGOHq+4eQvBu3HQ3/Y4mF/RFbA67tqO2+nrBQ==","timestamp":" 2022-05-08T20:48:17.164422986Z"} terrad_1 | 8:48PM INF received complete proposal block hash=43FEBF954F5FFEA7350E4D8D38FCCA39E439F2D3026ADE9ED3E21D2706AC2596 height=1949 module=consensus terrad_1 | 8:48PM INF finalizing commit of block hash=43FEBF954F5FFEA7350E4D8D38FCCA39E439F2D3026ADE9ED3E21D2706AC2596 height=1949 module=consensus num_txs=0 root=621428F6926740D55F70759F5D9CD33E2056117DEFD33525C3434F4150FC 02B9 terrad_1 | 8:48PM INF minted coins from module account amount=226578918uluna from=mint module=x/bank terrad_1 | 8:48PM INF executed block height=1949 module=state num_invalid_txs=0 num_valid_txs=0 terrad_1 | 8:48PM INF commit synced commit=436F6D6D697449447B5B352032323420323437203734203133382033312 03231322035322032343220313637203134342037382038332036392032303720313 93920313335203337203230342032382033362031353320333920313930203232382 0333020313434203538203138342032343620313835203230375D3A3739447D terrad_1 | 8:48PM INF committed state app_hash=05E0F74A8A1FD434F2A7904E5345CFC78725CC1C249927BEE41E903AB8F6B9CF height=1949 module=state num_txs=0 terrad_1 | 8:48PM INF indexed block height=1949 module=txindex terrad_1 | 8:48PM INF Timed out dur=4987.072234 height=1950 module=consensus round=0 step=1 terrad_1 | 8:48PM INF received proposal module=consensus proposal={"Type":32,"block_id":{"hash":"5E1C9EA0F662335- DAD4936E50C904AF1FF3D8BBF1083A967A8C4D8EA762942F7","parts": {"hash":"0C3DA2F2B1CDCDF56C432F85E3EE1B0A21064EF69E4DB2D5CAFBE0609FDDE699"," total":1}},"height":1950,"pol_round":- 1,"round":0,"signature":"ZQeA6WQaamDpW07PD64ezc+U1vs/IW3XzMp/yShIIMbJChJFiOJwDB+ dtipWPORdKHNrWVVFlei4drGlx70iCg==","timestamp":"2022- 05-08T20:48:22.181798507Z"} terrad_1 | 8:48PM INF received complete proposal block hash=5E1C9EA0F662335DAD4936E50C904AF1FF3D8BBF1083A967A8C4D8EA762942F7 height=1950 module=consensus terrad_1 | 8:48PM INF finalizing commit of block hash=5E1C9EA0F662335- DAD4936E50C904AF1FF3D8BBF1083A967A8C4D8EA762942F7 height=1950 module=consensus num_txs=0 root=05E0F74A8A1FD434F2A7904E5345CFC78725CC1C249927BEE41E903AB8F6B9CF terrad_1 | 8:48PM INF minted coins from module account amount=226578923uluna from=mint module=x/bank terrad_1 | 8:48PM INF executed block height=1950 module=state num_invalid_txs=0 num_valid_txs=0 terrad_1 | 8:48PM INF commit synced commit=436F6D6D697449447B5B323133203133342032343920393320313830203 13234203136342033372033342032313620313733203236203734203133322032343 22032352031383520313731203230322037302032333520313635203339203235352 0323532203233302035312037203134203135203537203130335D3A3739457D terrad_1 | 8:48PM INF committed state app_hash=D586F95DB47- CA42522D8AD1A4A84F219B9ABCA46EBA527FFFCE633070E0F3967 height=1950 module=state num_txs=0 terrad_1 | 8:48PM INF indexed block height=1950 module=txindex terrad_1 | 8:48PM INF Timed out dur=4983.73452 height=1951 module=consensus round=0 step=1 terrad_1 | 8:48PM INF received proposal module=consensus proposal={"Type":32,"block_id":{"hash":"2D43F5FF6625BD376D71F60269615A5E556BEFF85FC44461B05AF17E54958490"," parts": {"hash":"8377DFE5E66B80B3B9DA5894F5778DF1585741A1C718626AE14167158F7A5DAE"," total":1}},"height":1951,"pol_round ":-1,"round":0,"signature":"Xtc1Wggm2dD/n3YNFjvykNUmXFkN9gNuVL/ yKab4js2DMl2xGJ7ZAjDW/0RceJIBle9SWX13smUamxfsd2wbCQ==","timestamp":" 2022-05-08T20:48:27.197935715Z"} terrad_1 | 8:48PM INF received complete proposal block hash=2D43F5FF6625BD376D71F60269615A5E556BEFF85FC44461B05AF17E54958490 height=1951 module=consensus terrad_1 | 8:48PM INF finalizing commit of block hash=2D43F5FF6625BD376D71F60269615A5E556BEFF85FC44461B05AF17E54958490 height=1951 module=consensus num_txs=1 root=D586F95DB47- CA42522D8AD1A4A84F219B9ABCA46EBA527FFFCE633070E0F3967 terrad_1 | 8:48PM INF minted coins from module account amount=226578928uluna from=mint module=x/bank terrad_1 | 8:48PM INF executed block height=1951 module=state num_invalid_txs=0 num_valid_txs=1

Interacting with Local Terra Using Python: Storing the Contract

Then you can create a new folder inside the local Terra directory and name it testing. Then create another new folder inside the testing folder and name it artifacts. From the artifacts folder of some of the created projects in the local Terra directory (Like NewProjectName), copy the .wasm file and paste it into the new artifacts folder. After that create a new python file inside of the testing folder and name it contract.py. You can write the following code in the contract.py.
		
from terra_sdk.client.localterra import LocalTerra
from terra_sdk.util.contract import read_file_as_b64
from terra_sdk.core.fee import Fee
from terra_sdk.client.lcd.api.tx import CreateTxOptions
from terra_sdk.core.wasm import MsgStoreCode

lt = LocalTerra()
deployer = lt.wallets["test1"]

def store_contract(contract_name:str) -> str:
	contract_bytes = read_file_as_b64(f"artifacts/{contract_name}.wasm")
	store_code = MsgStoreCode(
	deployer.key.acc_address,
	contract_bytes
	)
	tx = deployer.create_and_sign_tx(CreateTxOptions(
		msgs = [store_code],
		fee = Fee(4000000,"10000000uluna")
		))
	result = lt.tx.broadcast(tx)
	print(result)

store_contract("Testing")

		
	
In the above code, at first, we connect to local Terra, then we create a wallet for the deployer of the contract (which is us). After that, we create a function called store contract, inside which we read the .wasm file in the artifacts folder and store it in a variable called contract_bytes. Then we create a message called the MsgStoreCode using the contract_bytes variable and after that, we create and sign a transaction using the store_code message. Finally, we broadcast the transaction and use the store_contract function with the name of the contract called Testing (The name of our project folder).

And run it in another bash, so as not to interfere with the docker running the local Terra, use the following command to start our first interaction with Terra network. python3 contract.py Result: BlockTxBroadcastResult(height=1626, txhash='1143BD1F71A9E951F3B99D7EEE4BF0EE304F0C6C4A63DADA96F7CE243F25F39A', raw_log='[{"events":[{"type":"message","attributes": [{"key":"action","value":"/terra.wasm.v1beta1.MsgStoreCode"}, {"key":"module","value":"wasm"}]},{"type":"store_code","attributes": [{"key":"sender","value":"terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v"}, {"key":"code_id","value":"1"}]}]}]', gas_wanted=4000000, gas_used=1606062, logs=[TxLog(msg_index=0, log='', events=[{'type': 'message', 'attributes': [{'key': 'action', 'value': '/terra.wasm.v1beta1.MsgStoreCode'}, {'key': 'module', 'value': 'wasm'}]}, {'type': 'store_code', 'attributes': [{'key': 'sender', 'value': 'terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v'}, {'key': 'code_id', 'value': '1'}]}], events_by_type={'message': {'action': ['/terra.wasm.v1beta1.MsgStoreCode'], 'module': ['wasm']}, 'store_code': {'sender': ['terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v'], 'code_id': ['1']}})], code=0, codespace='', info=None, data=None, timestamp= None) In the above dictionary, you can see data like txhash (transaction hash), code_id, sender of the store_code, and so on.

Interacting with Local Terra Using Python: Getting the Code ID

Now, let’s get the code id of the transaction using the very same code and making some small changes:
		
from terra_sdk.client.localterra import LocalTerra
from terra_sdk.util.contract import read_file_as_b64,get_code_id
from terra_sdk.core.fee import Fee
from terra_sdk.client.lcd.api.tx import CreateTxOptions
from terra_sdk.core.wasm import MsgStoreCode

lt = LocalTerra()
deployer = lt.wallets["test1"]

def store_contract(contract_name:str) -> str:
	contract_bytes = read_file_as_b64(f"artifacts/{contract_name}.wasm")
	store_code = MsgStoreCode(
	deployer.key.acc_address,
	contract_bytes
	)
	tx = deployer.create_and_sign_tx(CreateTxOptions(
		msgs = [store_code],
		fee = Fee(4000000,"10000000uluna")
		))
	result = lt.tx.broadcast(tx)
	code_id = get_code_id(result)
	return(code_id)

print(store_contract("Testing"))

		
	
We have written the very same code as the previous one with the difference that this time we have specifically returned the code_id in the dictionary and have printed it. Now, let’s run the code using the following command on the terminal, and remember not to lose your connection with local Terra (keep the docker up and running). python3 contract.py Result: 3 Let us try another time: python3 contract.py Result: 4 And one more time to make sure everything works as it should: python3 contract.py Result: 5

Sending JSON Messages to Local Terra Smart Contracts Using Python

The following article is the continuation of the previous one and here we try to interact with local Terra smart contracts using python scripts and at the same time using the CosmWasm functions inside LocalTerra\src folder written in Rust programming language. We will instantiate from the contract and execute it by sending JSON messages to Local Terra smart contracts.

Instantiating form the Contract of the LocalTerra

In the following code, we use the MsgInstantiateContract from terra_sdk.core.wasm to instantiate from the contract, using the code_id and return the contract address:
		
from terra_sdk.client.localterra import LocalTerra
from terra_sdk.util.contract import read_file_as_b64,get_code_id, get_contract_address
from terra_sdk.core.fee import Fee
from terra_sdk.client.lcd.api.tx import CreateTxOptions
from terra_sdk.core.wasm import MsgStoreCode,MsgInstantiateContract

lt = LocalTerra()
deployer = lt.wallets["test1"]

def store_contract(contract_name:str) -> str:
	contract_bytes = read_file_as_b64(f"artifacts/{contract_name}.wasm")
	store_code = MsgStoreCode(
		deployer.key.acc_address,
		contract_bytes
		)
	tx = deployer.create_and_sign_tx(CreateTxOptions(
		msgs = [store_code],
		fee = Fee(4000000,"10000000uluna")
		))
	result = lt.tx.broadcast(tx)
	code_id = get_code_id(result)
	return(code_id)

def instantiate_contract(code_id: str,init_msg)-> str:
	instantiate = MsgInstantiateContract(
		deployer.key.acc_address,
		deployer.key.acc_address,
		code_id = code_id,
		init_msg = init_msg,
		)
	tx = deployer.create_and_sign_tx(CreateTxOptions(
		msgs = [instantiate],
		fee = Fee(4000000,"10000000uluna")
		))
	result = lt.tx.broadcast(tx)
	contract_address = get_contract_address(result)
	return contract_address

code_id = store_contract("Testing")
contract_address = instantiate_contract(code_id,{"count": 15})
print(code_id, contract_address)

		
	
The goal of the above code is to instantiate from the contract using the code_id of the store_contract transaction. To do so, we define a function called instantiate_contract. Inside this function, we create a message with MsgInstantiateContract function. Then we will create and sign the transaction using the created message. Finally, we will broadcast the transaction and return the contract address. Notice that the function that we have defined has 2 attributes: code_id and init_msg. When we call this function, we can enter the code_id we have got from the output of store_contract. For init_msg, we enter the {"count": number} in which number can be any unsigned integer number. In the end, we print the code_id with the contract address which is the output of the instantiate_contract function.

Now, let’s try our script and see if it prints out the code_id and the contract address: python3 contract.py Result: 11 terra1wkgucw0zply6e5c4h30a4z5qljhazepw4jpf2s Try another time: python3 contract.py Result: 12 terra1mhf9cr9f70rd052rptnevcje2puq6heekrxdwa As you can see the code_id has incremented.

And another time: python3 contract.py Result: 13 terra1uw258kwqftsywjfv972y4d3f4mn0hrlkpt7y3y

Executing the Contract

Now, using the MsgExecuteContract, contract address, and account address, we execute the contract inside the local Terra directory.
		
from terra_sdk.client.localterra import LocalTerra
from terra_sdk.util.contract import read_file_as_b64,get_code_id, get_contract_address
from terra_sdk.core.fee import Fee
from terra_sdk.client.lcd.api.tx import CreateTxOptions
from terra_sdk.core.wasm import MsgStoreCode,MsgInstantiateContract, MsgExecuteContract

lt = LocalTerra()
deployer = lt.wallets["test1"]

def store_contract(contract_name:str) -> str:
	contract_bytes = read_file_as_b64(f"artifacts/{contract_name}.wasm")
	store_code = MsgStoreCode(
	deployer.key.acc_address,
	contract_bytes
	)
	tx = deployer.create_and_sign_tx(CreateTxOptions(
		msgs = [store_code],
		fee = Fee(4000000,"10000000uluna")
		))
	result = lt.tx.broadcast(tx)
	code_id = get_code_id(result)
	return(code_id)

def instantiate_contract(code_id: str,init_msg)-> str:
	instantiate = MsgInstantiateContract(
		deployer.key.acc_address,
		deployer.key.acc_address,
		code_id = code_id,
		init_msg = init_msg,
		)
	tx = deployer.create_and_sign_tx(CreateTxOptions(
		msgs = [instantiate],
		fee = Fee(4000000,"10000000uluna")
		))
	result = lt.tx.broadcast(tx)
	contract_address = get_contract_address(result)
	return contract_address

def execute_contract(sender,contract_addr: str, execute_msg):
	execute = MsgExecuteContract(
	sender = sender.key.acc_address, contract = contract_addr,execute_msg = execute_msg)
	tx = sender.create_and_sign_tx(CreateTxOptions(
		msgs = [execute],
		fee = Fee(4000000,"10000000uluna")
		))
	)
	result = lt.tx.broadcast(tx)
	return result

code_id = store_contract("Testing")
contract_address = instantiate_contract(code_id,{"count": 15})
execute = execute_contract(deployer,contract_address,{"increment":{}})
execute1 = execute_contract(deployer,contract_address,{"increment":{}})
execute2 = execute_contract(deployer,contract_address,{"increment":{}})
execute3 = execute_contract(deployer,contract_address,{"increment":{}})
execute4 = execute_contract(deployer,contract_address,{"increment":{}})

print(execute1)
print(lt.wasm.contract_query(contract_address,{"get_count":{}}))
print(code_id, contract_address)
		
	
In the above Python script, we will use the execute_contract next to store_contract and instantiate_contract functions. In the execute function, we have done the 3 main jobs we did for other functions, creating the message, creating and signing the transaction , and broadcasting it. The execute function has 3 attributes:
  1. 1. The sender (deployer of the contract).
  2. 2. The contract address that we have got from instantiate_contract function.
  3. 3. The execute message which can be increment, reset ,etc.
Outside the execute function, we call it 5 times with the increment message. And to see the results, we print the second execution and also the query result of the get_count message which is going to check if we have successfully executed the contract 5 times. If so, the result must show that the count = 20. (15 +1+1+1+1+1 = 20).

Now, let’s see if our code prints out the result of local Terra contract execution, as well as the code_id, query result, and the contract address: python3 contract.py Result: BlockTxBroadcastResult(height=8030, txhash='F51A179323A3489F4629FB0ECA4B83C64237961F9244409EFBDBF88EFC8CFA97', raw_log='[{"events": [{"type":"execute_contract","attributes":[{"key":"sender","value":"terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v"}, {"key":"contract_address","value":"terra1hteg5pjt0r27589yd67jwhq8an4j3w4u02qfmz"}]},{" type":"from_contract","attributes":[{"key":"contract_ address","value":"terra1hteg5pjt0r27589yd67jwhq8an4j3w4u02qfmz"}, {"key":"method","value":"try_increment"}]},{"type":"message","attributes": [{"key":"action","value":"/terra.wasm.v1beta1.MsgExecuteContract"},{"key":"module"," value":"wasm"},{"key":"sender","value":"terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v"}]},{" type":"wasm","attributes": [{"key":"contract_address","value":"terra1hteg5pjt0r27589yd67jwhq8an4j3w4u02qfmz"},{" key":"method","value":"try_increment"}]}]}]', gas_wanted=4000000, gas_used=102492, logs=[TxLog(msg_index=0, log='', events=[{'type': 'execute_contract', 'attributes': [{'key': 'sender', 'value': 'terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v'}, {'key': 'contract_address', 'value': 'terra1hteg5pjt0r27589yd67jwhq8an4j3w4u02qfmz'}]}, {'type': 'from_contract', 'attributes': [{'key': 'contract_address', 'value': 'terra1hteg5pjt0r27589yd67jwhq8an4j3w4u02qfmz'}, {'key': 'method', 'value': 'try_increment'}]}, {'type': 'message', 'attributes': [{'key': 'action', 'value': '/terra.wasm.v1beta1.MsgExecuteContract'}, {'key': 'module', 'value': 'wasm'}, {'key': 'sender', 'value': 'terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v'}]}, {'type': 'wasm', 'attributes': [{'key': 'contract_address', 'value': 'terra1hteg5pjt0r27589yd67jwhq8an4j3w4u02qfmz'}, {'key': 'method', 'value': 'try_increment'}]}], events_by_type={'execute_contract': {'sender': ['terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v'], 'contract_address': ['terra1hteg5pjt0r27589yd67jwhq8an4j3w4u02qfmz']}, 'from_contract': {'contract_address': ['terra1hteg5pjt0r27589yd67jwhq8an4j3w4u02qfmz'], 'method': ['try_increment']}, 'message': {'action': ['/terra.wasm.v1beta1.MsgExecuteContract'], 'module': ['wasm'], 'sender': ['terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v']}, 'wasm': {'contract_address': ['terra1hteg5pjt0r27589yd67jwhq8an4j3w4u02qfmz'], 'method': ['try_increment']}})], code=0, codespace='', info=None, data=None, timestamp=None) {'count': 20} 17 terra1hteg5pjt0r27589yd67jwhq8an4j3w4u02qfmz Notice that it takes a few seconds for the contract to be instantiated and the transaction to be executed. As we have determined the count = 15 in the instantiate_contract function and executed the contract with the increment message 5 times, The result of the query count will be 20.

Try one more time, and you will see the same result with the code_id incremented. python3 contract.py Result: BlockTxBroadcastResult(height=8269, txhash='73C112C7B0DA968209310068DFCA9C26378A47EA383C3A4DBE350DCFE54079E8', raw_log='[{"events": [{"type":"execute_contract","attributes":[{"key":"sender","value":"terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v"}, {"key":"contract_address","value":"terra1wfvvdh7vtq82xz3x6h63ul9xn8hxh5xhkwl5ra"}]},{" type":"from_contract","attributes": [{"key":"contract_address","value":"terra1wfvvdh7vtq82xz3x6h63ul9xn8hxh5xhkwl5ra"},{" key":"method","value":"try_increment"}]},{"type":"message","attributes": [{"key":"action","value":"/terra.wasm.v1beta1.MsgExecuteContract"},{"key":"module"," value":"wasm"},{"key":"sender","value":"terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v"}]},{" type":"wasm","attributes": [{"key":"contract_address","value":"terra1wfvvdh7vtq82xz3x6h63ul9xn8hxh5xhkwl5ra"},{" key":"method","value":"try_increment"}]}]}]', gas_wanted=4000000, gas_used=102492, logs=[TxLog(msg_index=0, log='', events=[{'type': 'execute_contract', 'attributes': [{'key': 'sender', 'value': 'terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v'}, {'key': 'contract_address', 'value': 'terra1wfvvdh7vtq82xz3x6h63ul9xn8hxh5xhkwl5ra'}]}, {'type': 'from_contract', 'attributes': [{'key': 'contract_address', 'value': 'terra1wfvvdh7vtq82xz3x6h63ul9xn8hxh5xhkwl5ra'}, {'key': 'method', 'value': 'try_increment'}]}, {'type': 'message', 'attributes': [{'key': 'action', 'value': '/terra.wasm.v1beta1.MsgExecuteContract'}, {'key': 'module', 'value': 'wasm'}, {'key': 'sender', 'value': 'terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v'}]}, {'type': 'wasm', 'attributes': [{'key': 'contract_address', 'value': 'terra1wfvvdh7vtq82xz3x6h63ul9xn8hxh5xhkwl5ra'}, {'key': 'method', 'value': 'try_increment'}]}], events_by_type={'execute_contract': {'sender': ['terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v'], 'contract_address': ['terra1wfvvdh7vtq82xz3x6h63ul9xn8hxh5xhkwl5ra']}, 'from_contract': {'contract_address': ['terra1wfvvdh7vtq82xz3x6h63ul9xn8hxh5xhkwl5ra'], 'method': ['try_increment']}, 'message': {'action': ['/terra.wasm.v1beta1.MsgExecuteContract'], 'module': ['wasm'], 'sender': ['terra1x46rqay4d3cssq8gxxvqz8xt6nwlz4td20k38v']}, 'wasm': {'contract_address': ['terra1wfvvdh7vtq82xz3x6h63ul9xn8hxh5xhkwl5ra'], 'method': ['try_increment']}})], code=0, codespace='', info=None, data=None, timestamp=None) {'count': 20} 18 terra1wfvvdh7vtq82xz3x6h63ul9xn8hxh5xhkwl5ra

Wrapping Up

In this article, we have managed to connect to local Terra using Docker and interact with it by the means of Python scripts. In detail, we have managed to store the contract and get the code_id of the transaction. In the next parts, we will focus on the other functions such as instantiating and executing the contract to complete the process of interacting with Terra smart contracts.

Finally, we have managed to interact with local Terra using the Python scripts. At first, we connected to local Terra using Docker. Then, we created a wallet, that, stored our contract, instantiated from the stored contract using the code_id, and executed it.

Download this Article in PDF format

3d websites

Care to Know Us More?

In Arashtad, we have gathered a professional team of developers who are working in fields such as 3D websites, 3D games, metaverses, and other types of WebGL and 3D applications as well as blockchain developemnet.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Fill in the Form
Blockchain Development

How to Set Up the Dependencies For Running Local Terra: All You Need to Know

In this tutorial-based article, we are going to set up the dependencies for running Local Terra. So, you will be guided through all the installations and commands necessary to start your interaction with local Terra smart contracts. Installing Go, Docker, Terrad, and git cloning local Terra, as well as terra-core repositories, are some of the main steps we are going to take in this tutorial.

Installing the Dependencies

For running local terra the main step is to install all of the dependencies required. The first step is to install go. You need to be careful that the version should be higher than 1.17.5.

The Dependencies for Running Local Terra #1: Go

To install Go, you should find the latest version for your operating system using this link. Then, the next step is to check the version to make sure it meets the requirement of our version (higher than 1.17.5). To do so, enter the following command in the terminal:

go version

Result:

go version go1.18.1 linux/amd64

If you see a result like the above, your installation has been successful. You should also have the Rust programming language installed using the below command (Notice that this command is used for Linux OS) in the terminal:

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

For other operating systems, follow the instructions on this link. Now, it is time to git clone the terra-core repository. In the terminal, enter the following command:

git clone https://github.com/terra-money/core terra-core

After that, change the directory to the terra-core folder by entering the below command in the terminal:

cd terra-core

To install the downloaded repository, enter the following in the terminal:

git checkout main make install

And, the most important step is to git clone the local Terra repository. We follow the same steps for this one as well:

git clone --depth 1 https://www.github.com/terra-money/LocalTerra cd LocalTerra

The Dependencies for Running Local Terra #2: Docker

One of the required dependencies for running local Terra is Docker. You should also have docker installed on your operating system. Here we explain the process for Linux:
For the installation guide in other operating systems than Linux, follow the instructions on the Docker.

dependencies for running local Terra

Installation Guide on Linux

This process consists of 5 steps that are described in the followings.

1. Dependencies:

At first, update the system to get the list of available packages and their version numbers. To do so, enter the following command in the terminal:

sudo apt update

To prepare for the installation run these commands in the terminal one after the other:

sudo apt -y install apt-transport-https ca-certificates curl software-properties-common
sudo apt -y remove docker docker-engine docker.io containerd runc

2. Add Docker’s Official GPG Key:

Also run the following commands in the terminal:

curl -fsSL https://download.docker.com/linux/ubuntu/gpg |
sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg

3. Add the Docker Repository to Linux:

To add the Docker repository for Linux, enter the following commands in the terminal:

echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/dockerarchive-keyring.gpg] https://download.docker.com/linux/ubuntu bionic stable" |

sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu bionic stable

4. Install the Docker Engine and the Docker Compose:

And finally, It is time to install docker by running these 2 commands in the terminal one after the other.

sudo apt update
sudo apt install docker-ce docker-ce-cli containerd.io

5. Checking the Installation:

To start with checking the installation, use the command below:

sudo usermod -aG docker
USER newgrp docker

To make sure the installation has been successful, run the following command in the terminal:

docker version

Result:

Client: Docker Engine - Community Version: 20.10.14 API version: 1.41 Go version: go1.16.15 Git commit: a224086 Built: Thu Mar 24 01:47:57 2022 OS/Arch: linux/amd64 Context: default Experimental: true Server: Engine: Version: 20.10.14 API version: 1.41 (minimum version 1.12) Go version: go1.16.15 Git commit: 87a90dc Built: Thu Mar 24 17:15:03 2022 OS/Arch: linux/amd64 Experimental: false containerd: Version: v1.5.11 GitCommit: 3df54a852345ae127d1fa3092b95168e4a88e2f8 runc: Version: 1.0.3 GitCommit: docker-init: Version: 0.19.0 GitCommit: de40ad

After installation for every use, you need to first sign in and run the following commands in the terminal:

sudo usermod -aG docker docker login docker-compose up

Run the above commands in the local Terra directory terminal.
Result:

terrad_1 | 1:47PM INF indexed block height=143 module=txindex terrad_1 | 1:47PM INF Timed out dur=4987.108435 height=144 module=consensus round=0 step=1 terrad_1 | 1:47PM INF received proposal module=consensus proposal={"Type":32,"block_id":{"hash":"F4DD36C8FB8D539F3F28ABB8BB7969A099AEE78B707FAE7F3C7DC8D36BF38B9D","parts": {"hash":"F92FDEA4153F172B082676706A3B35F54A0EF3D6639FA23A3FD88FFBB40ACA6E","total":1}},"height":144,"pol_round":- 1,"round":0,"signature":"nDA4YtVFM/a8TXCLMwTwydkfJSwOoI57zNkkKieyv0xy/ zbnCVIM10cpzh78+0wym1jT3fj16ZqH5OiFE3kaAg==","timestamp":"2022-05- 07T13:47:21.846543194Z"} terrad_1 | 1:47PM INF received complete proposal block hash=F4DD36C8FB8D539F3F28ABB8BB7969A099AEE78B707FAE7F3C7DC8D36BF38B9D height=144 module=consensus terrad_1 | 1:47PM INF finalizing commit of block hash=F4DD36C8FB8D539F3F28ABB8BB7969A099AEE78B707FAE7F3C7DC8D36BF38B9D height=144 module=consensus num_txs=0 root=EFAA4A74DD1E73032E0FF553A37CCDFAD443AF0EE395516EB247948ADAF45A21 terrad_1 | 1:47PM INF minted coins from module account amount=226570495uluna from=mint module=x/bank terrad_1 | 1:47PM INF executed block height=144 module=state num_invalid_txs=0 num_valid_txs=0 terrad_1 | 1:47PM INF commit synced commit=436F6D6D697449447B5B313138203835203538203234362036322032392 03133203136302033372031393920343920393520373020323030203231312032323 12032353320323039203231382031393720313538203134392032313720322039203 13238203232352031362031363420313339203131392032325D3A39307D terrad_1 | 1:47PM INF committed state app_hash=76553AF63E1D0- DA025C7315F46C8D3DDFDD1DAC59E95D9020980E110A48B7716 height=144 module=state num_txs=0 terrad_1 | 1:47PM INF indexed block height=144 module=txindex Now, you should install cargo: To install cargo, run these 3 commands in the terminal separately: cargo install cargo-generate --features vendored-openssl cargo install cargo-run-scrip

For the following command in the terminal, instead of YourProjectName, you can enter any name you want for your project to be created (make sure you run this command in the directory of local Terra you have just git cloned):

cargo generate --git https://github.com/CosmWasm/cw-template.git --name YourProjectName

Result:

Generating template ... [ 1/36] Done: .cargo/config [ 2/36] Done: .cargo [ 3/36] Skipped: .circleci/config.yml [ 4/36] Done: .circleci [ 1/36] Done: .cargo/config [ 2/36] Done: .cargo [ 3/36] Skipped: .circleci/config.yml [ 4/36] Done: .circleci [ 5/36] Done: .editorconfig [ 6/36] Done: .github/workflows/Basic.yml [ 7/36] Done: .github/workflows [ 8/36] Done: .github [ 9/36] Done: .gitignore [10/36] Done: .gitpod.Dockerfile [11/36] Done: .gitpod.yml [ 1/36] Done: .cargo/config [ 2/36] Done: .cargo [ 3/36] Skipped: .circleci/config.yml [ 4/36] Done: .circleci [ 5/36] Done: .editorconfig [ 6/36] Done: .github/workflows/Basic.yml [ 7/36] Done: .github/workflows [ 8/36] Done: .github [ 9/36] Done: .gitignore [10/36] Done: .gitpod.Dockerfile [11/36] Done: .gitpod.yml [12/36] Done: Cargo.lock [13/36] Done: Cargo.toml [14/36] Done: Developing.md [15/36] Done: Importing.md [16/36] Done: LICENSE [17/36] Done: NOTICE [ 1/36] Done: .cargo/config [ 2/36] Done: .cargo [ 3/36] Skipped: .circleci/config.yml [ 4/36] Done: .circleci [ 5/36] Done: .editorconfig [ 6/36] Done: .github/workflows/Basic.yml [ 7/36] Done: .github/workflows [ 8/36] Done: .github [ 9/36] Done: .gitignore [10/36] Done: .gitpod.Dockerfile [11/36] Done: .gitpod.yml [12/36] Done: Cargo.lock [13/36] Done: Cargo.toml [14/36] Done: Developing.md [15/36] Done: Importing.md [16/36] Done: LICENSE [17/36] Done: NOTICE [18/36] Done: Publishing.md [19/36] Done: README.md [20/36] Done: examples/schema.rs [21/36] Done: examples [22/36] Done: rustfmt.toml [23/36] Done: schema/count_response.json [24/36] Done: schema/execute_msg.json [ 1/36] Done: .cargo/config [ 2/36] Done: .cargo [ 3/36] Skipped: .circleci/config.yml [ 4/36] Done: .circleci [ 5/36] Done: .editorconfig [ 6/36] Done: .github/workflows/Basic.yml [ 7/36] Done: .github/workflows [ 8/36] Done: .github [ 9/36] Done: .gitignore [10/36] Done: .gitpod.Dockerfile [11/36] Done: .gitpod.yml [12/36] Done: Cargo.lock [13/36] Done: Cargo.toml [14/36] Done: Developing.md [15/36] Done: Importing.md [16/36] Done: LICENSE [17/36] Done: NOTICE [18/36] Done: Publishing.md [19/36] Done: README.md [20/36] Done: examples/schema.rs [21/36] Done: examples [22/36] Done: rustfmt.toml [23/36] Done: schema/count_response.json [24/36] Done: schema/execute_msg.json [25/36] Done: schema/instantiate_msg.json [26/36] Done: schema/query_msg.json [27/36] Done: schema/state.json [28/36] Done: schema [29/36] Done: src/contract.rs [30/36] Done: src/error.rs [31/36] Done: src/helpers.rs [32/36] Done: src/integration_tests.rs [33/36] Done: src/lib.rs [34/36] Done: src/msg.rs [35/36] Done: src/state.rs [36/36] Done: src Moving generated files into: `/home/mohamad/LocalTerra/my-project/np`... Initializing a fresh Git repository Done! New project created /home/mohamad/LocalTerra/my-project/np

CosmWasm Smart Contracts on Local Terra

In this section, we are going to get familiar with CosmWasm smart contracts written inRust programming language for interacting with local Terra through Python or JavaScript. These smart contracts have different functions that we need to send specific JSON messages to interact with them. In the end, we are going to create the artifacts folder containing the necessary binary files to interact with local Terra.

If you open the YourProjectName folder and in its src folder, the Msg.rs and contract.rs alongside with other Rust script files are found. The Msg.rs contract is related to the 3 kinds of messages we can send to our contract and it is composed of 3 main parts:

    1. InstantiateMsg struct:
This message sets the state in the smart contract meaning an initial state must be given to the smart contract when it is launched.
    2. ExecuteMsg enum:
This is a message that executes an action to the change of state, such as posting a message to the blockchain.
    3. QueryMsg Query Msg:
This message is for querying data from the chain.


use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InstantiateMsg {
     pub count: i32,
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
     Increment {},
     Reset { count: i32 },
}
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
     // GetCount returns the current count as a json-encoded number
     GetCount {},
}
// We define a custom struct for each query response
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct CountResponse {
     pub count: i32,
}


On Dependencies for Running Local Terra: What is Inside the Contract.rs File?

Another important Rust file is Contract.rs. In the next part, we are going to refer to the functions of this contract and use them in python. The important functions that we are going to interact with later using the python scripts are instantiated, execute, try increment, try_reset, and query_count. We will later see how we can interact with these functions with the JSON messages sent to them through python codes. As you can see the programming language that these smart contracts are written in is Rust.


use cosmwasm_std::entry_point;
use cosmwasm_std::{to_binary, Binary, Deps, DepsMut, Env, MessageInfo, Response, StdResult};
use cw2::set_contract_version;
use crate::error::ContractError;
use crate::msg::{CountResponse, ExecuteMsg, InstantiateMsg, QueryMsg};
use crate::state::{State, STATE};
// version info for migration info
const CONTRACT_NAME: &str = "crates.io:my-project";
const CONTRACT_VERSION: &str = env!("CARGO_PKG_VERSION");
#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
          deps: DepsMut,
          _env: Env,
          info: MessageInfo,
          msg: InstantiateMsg,
          ) -> Result {
     let state = State {
     count: msg.count,
     owner: info.sender.clone(),
     };

     set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
     STATE.save(deps.storage, &state)?;
     Ok(Response::new()
     .add_attribute("method", "instantiate")
     .add_attribute("owner", info.sender)
     .add_attribute("count", msg.count.to_string()))
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
          deps: DepsMut,
          _env: Env,
          info: MessageInfo,
          msg: ExecuteMsg,
          ) -> Result {
     match msg {
     ExecuteMsg::Increment {} => try_increment(deps),
     ExecuteMsg::Reset { count } => try_reset(deps, info, count),
     }
}

pub fn try_increment(deps: DepsMut) -> Result {
     STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {
          state.count += 1;
          Ok(state)})?;
          Ok(Response::new().add_attribute("method", "try_increment"))
     }
}

pub fn try_reset(deps: DepsMut, info: MessageInfo, count: i32) -> Result {
     STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {
          if info.sender != state.owner {
               return Err(ContractError::Unauthorized {});
          }
          state.count = count;
          Ok(state)
     })?;
     Ok(Response::new().add_attribute("method", "reset"))
}

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult {
     match msg {
          QueryMsg::GetCount {} => to_binary(&query_count(deps)?),
     }
}

fn query_count(deps: Deps) -> StdResult {
     let state = STATE.load(deps.storage)?;
     Ok(CountResponse { count: state.count })
}

#[cfg(test)]
mod tests {
     use super::*;
     use cosmwasm_std::testing::{mock_dependencies, mock_env, mock_info};
     use cosmwasm_std::{coins, from_binary};
     #[test]
     fn proper_initialization() {
          let mut deps = mock_dependencies(&[]);
          let msg = InstantiateMsg { count: 17 };
          let info = mock_info("creator", &coins(1000, "earth"));
          // we can just call .unwrap() to assert this was a success
          let res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
          assert_eq!(0, res.messages.len());
          // it worked, let's query the state
          let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap();
          let value: CountResponse = from_binary(&res).unwrap();
          assert_eq!(17, value.count);
     }
     #[test]
     fn increment() {
          let mut deps = mock_dependencies(&coins(2, "token"));
          let msg = InstantiateMsg { count: 17 };
          let info = mock_info("creator", &coins(2, "token"));
          let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
          // beneficiary can release it
          let info = mock_info("anyone", &coins(2, "token"));
          let msg = ExecuteMsg::Increment {};
          let _res = execute(deps.as_mut(), mock_env(), info, msg).unwrap();
          // should increase counter by 1
          let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap();
          let value: CountResponse = from_binary(&res).unwrap();
          assert_eq!(18, value.count);
     }
     #[test]
     fn reset() {
          let mut deps = mock_dependencies(&coins(2, "token"));
          let msg = InstantiateMsg { count: 17 };
          let info = mock_info("creator", &coins(2, "token"));
          let _res = instantiate(deps.as_mut(), mock_env(), info, msg).unwrap();
          // beneficiary can release it
          let unauth_info = mock_info("anyone", &coins(2, "token"));
          let msg = ExecuteMsg::Reset { count: 5 };
          let res = execute(deps.as_mut(), mock_env(), unauth_info, msg);
          match res {
               Err(ContractError::Unauthorized {}) => {}
                    _ => panic!("Must return unauthorized error"),
          }
          // only the original creator can reset the counter
          let auth_info = mock_info("creator", &coins(2, "token"));
          let msg = ExecuteMsg::Reset { count: 5 };
          let _res = execute(deps.as_mut(), mock_env(), auth_info, msg).unwrap();
          // should now be 5
          let res = query(deps.as_ref(), mock_env(), QueryMsg::GetCount {}).unwrap();
          let value: CountResponse = from_binary(&res).unwrap();
          assert_eq!(5, value.count);
     }
}


Creating a project folder using cargo

Now, let’s run the following command in the terminal to create binaries and other dependency files so that we can eventually interact with local Terra using python Terra SDK.

cargo run-script optimize

Result:

Running script 'optimize': 'docker run --rm -v "$(pwd)":/code --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry cosmwasm/rustoptimizer:0.12.5 ' Info: RUSTC_WRAPPER=sccache Info: sccache stats before build Compile requests 0 Compile requests executed 0 Cache hits 0 Cache misses 0 Cache timeouts 0 Cache read errors 0 Forced recaches 0 Cache write errors 0 Compilation failures 0 Cache errors 0 Non-cacheable compilations 0 Non-cacheable calls 0 Non-compilation calls 0 Unsupported compiler calls 0 Average cache write 0.000 s Average cache read miss 0.000 s Average cache read hit 0.000 s Failed distributed compilations 0 Cache location Local disk: "/root/.cache/sccache" Cache size 0 bytes Max cache size 10 GiB Building contract in /code ... Finished release [optimized] target(s) in 0.16s Creating intermediate hash for my_project.wasm ... ec1944cdda3c5f8f6968d59b990d6f92800f65d9655a53a45ee29984f6c5d882 ./target/wasm32-unknown-unknown/release/my_project.wasm Optimizing my_project.wasm ... Creating hashes ... fa445512e5b3274ddda6474335d8515c660c5fcdf03b8b2d7f468c12f3e55564 my_project.wasm Info: sccache stats after build Compile requests 0 Compile requests executed 0 Cache hits 0 Cache misses 0 Cache timeouts 0 Cache read errors 0 Forced recaches 0 Cache write errors 0 Compilation failures 0 Cache errors 0 Non-cacheable compilations 0 Non-cacheable calls 0 Non-compilation calls 0 Unsupported compiler calls 0 Average cache write 0.000 s Average cache read miss 0.000 s Average cache read hit 0.000 s Failed distributed compilations 0 Cache location Local disk: "/root/.cache/sccache" Cache size 0 bytes Max cache size 10 GiB done Finished, status of exit status: 0

And in the project name folder, we are going to see that a new folder has been created called artifacts. Now, we are ready to write our python codes to interact with local Terra and use CosmWasm based contracts written in Rust.

Conclusion:

In this tutorial, we have managed to install the dependencies required to run local Terra, such as GO and Rust programming languages, Docker, Cargo, and some Github repositories like terra-core and local-terra. We have also connected to Local Terra network using the docker-compose up command. In the end, we have created our project folder and files using the cargo generate command.
Moreover, we have taken a quick look at some of the important smart contracts written in Rust programming language and got familiar with their functions so that we can interact with them later using our python scripts. We also created the artifacts folder using the cargo command to have everything set up for interacting with the local Terra.

Download this Article in PDF format

Rust learning for CosmWasm smart contracts

Need More Rust Learning?

To fully take advantage of this article, you’d better get familiar with Rust Language applied to CosmWasm smart contracts. And the good news is that we have a complete tutorial for it.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Request a Quote
Blockchain Development

Learning Rust for CosmWasm Smart Contracts: A Perfect Guide

Terra network smart contracts are built on top of the CosmWasm template for smart contracts, which is derived from the Cosmos ecosystem. The programming language used for writing these kinds of contracts is Rust. So, learning Rust for CosmWasm smart contracts will be essential. In this article, we are going to get started with some of the basics of learning Rust programming language for CosmWasm smart contracts. Of course, this series of tutorials are not meant to teach you all the details about Rust, but enough for understanding CosmWasm smart contracts.

What Are CosmWasm Smart Contracts?

CosmWasm smart contracts are used by platforms like Cosmos, Terra, etc. In contrast to Solidity, they have some major distinctions like:
    1. Avoiding the reentrancy of attacks
    2. If you have read our articles on Solidity, you already know what safeMath is and why we use it (detecting overflow or underflows).

In CosmWasm smart contracts, Rust allows you to simply set in the overflow-checks = true Cargo manifest to abort the program if any overflow is detected. No way to opt-out of safe math.
    3. We don’t have Delegate Call logic in CosmWasm. And … mentioned in this documentation link.

Learning Rust for CosmWasm Smart Contracts

Before we get started with CosmWasm smart contracts, we need to learn the Rust programming language.

Installation of Rust Language

The installation of Rust on Linux or Mac OS is by using the following command: curl https://sh.rustup.rs -sSf | sh On Windows, you can use Visual Studio and also install rustup tool for windows and follow the steps on this link. Getting started with Scripts: We get started with the most minimalistic scripts, Hello World!:

fn main() {
     println!("Hello, world!");
}


Save the file as FirstScript.rs and open a terminal in that directory and enter the following command to run this script: rustc FirstScript.rs
./t
Result: "Hello, world!"

Learning Rust Basics: Comments

Here is how we write the comments in Rust

//This is a comment
/* This is a long,
Multi-line comment
*/

Result:
no output

Learning Rust Basics: Variable Declarations and Data Types

    1. Integers:
We have different types of integers in Rust. Below you can see, the unsigned integer 32, integer 32, usize which is an unsigned integer and the number of bytes depends on the architecture of the machine (32 or 64), and isize which is a signed integer with a number of bytes depending on the architecture of the system (32 or 64):

fn main() {
     let _a:u32 = 35;
     let _b:i32 = -25;
     let _ab:u32 = 10;
     let _c = 200; // i32 by default
     let _d:usize = 20; // The size of data depends on the architecture of Machine
     let _cd:usize = 220; // The size of data depends on the architecture of Machine 
     let _e:isize = 94; // The size of data depends on the architecture of Machine 
     println!("a plus b equals{}",_ab);
     println!("sum of {} and {} equals {}",_c,_d,_cd);
     println!("e is dclared as isize and it has the value of {}",_e); 
}


rustc t.rs
./t
a plus b equals10 sum of 200 and 20 equals 220 e is dclared as isize and it has the value of 94

Learning Rust Basics: Data Overflow


When writing CosmWasm smart contracts using Rust, since we are dealing with great numbers, sometimes even u16 integers are overflown. As a result, it is important that we get notified. Rust does this for us:

fn main() {
     let a:u8 = 255;
     let a:u8 = 256; 
     let b:u8 = 257; 
     println!("a equals {} ",a);
     println!("b equals {}",b);
     println!("c equals {}",c);
     println!("d equals {}",d);
}

Result: error[E0425]: cannot find value `c` in this scope --> t.rs:9:28 | 9 | println!("c equals {}",c); | ^ help: a local variable with a similar name exists: `a` error[E0425]: cannot find value `d` in this scope --> t.rs:10:28 | 10 | println!("d equals {}",d); | ^ help: a local variable with a similar name exists: `a` error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0425`.
    2. Strings, Booleans, and Floating points:

fn main() {
     let boolean = true; //boolean
     let name = "Alice"; // string 
     let age = 32; //int
     let balance:f32 = 12.456; //float , you can declare it as let balance = 12.456
     println!("{} is {} ",name,age);
     println!("Is She an active user? {}",boolean);
     println!("Her net worth is {}",balance);
}


rustc t.rs
./t
Result: Alice is 32 Is She an active user? true Her net worth is 12.456
    3. Strings in detail:

fn main(){
     let mut network = "Terra".to_string();
     network.push_str("Network");
     println!("{}",network);
}


rustc t.rs
./t
Result: TerraNetwork

Learning Rust Basics: String Length


fn main() {
     let network = " Cosmos";
     println!("length is {}",network.len());
}


Result: rustc t.rs
./t
Result: length is 7

Learning Rust Basics: Vectors and Split in Strings


fn main() {
     let networks = "Cosmos, Terra, Ethereum, BSC, AVAX";
     for platform in networks.split(","){
          println!("platform is {}",platform);
     }
     println!("\n");
     let platforms:Vec<&str>= networks.split(",").collect();
     println!("platform is {}",platforms[0]);
     println!("platform is {}",platforms[1]);
}


rustc t.rs
./t
Result: platform is Cosmos platform is Terra platform is Ethereum platform is BSC platform is AVAX platform is Cosmos platform is Terra

Functions, Tuples, Arrays, and Structs in Rust Language

Now, we are going to learn how to use functions, tuples, arrays, and structs. There is also an important concept of mutable variables which is very useful when dealing with different circumstances.

Learning Rust for CosmWasm: Functions and Mutable Parameters

If you declare the variable inside the function as mutable, disregarding the initial value that it has been given, it can be changed any number of times that you want to change it.

fn main(){
     let number:i32 = 5;
     mutate_number(number);
     println!("The value of number is:{}",number);
}
fn mutate_number(mut mutable_number: i32) {
     mutable_number = mutable_number*0;
     println!("mutable_number value is :{}",mutable_number);
}


In the above code, we have defined the 32 bytes integer variable called the “number” and we have given the value 5 to it. After that, we have used the function called the “mutate number” inside which, we have defined a mutable number of the same type as a number (i32). Then, inside the function, we have to change the number by multiplying it by 0 and then printed the result. After that, outside the function, we print the number which was not mutable. The result should show different outputs.
Since the number which was defined outside the function was not mutable, the mutable_number which was inside the function was mutable. So, the mutable_number inside the function must change (5*0 = 0) whereas the number outside of the function must not change (5 = 5). Now, let’s run the code using the following command: rustc practice.rs
./t
Result: mutable_number value is :0 The value of number is:5 As you can see, the output of the 2 prints differs from each other. If you want to change the number outside the function, you’ll need to declare the variable as mutable.

fn main() {
     let mut number:i32 = 5;
     mutate_number(&mut number);
     println!("The value of number is:{}",number);
}
fn mutate_number(mutable_number:&mut i32){
     *mutable_number = 0; 
}


Now, let’s run the code using the following command: rustc practice.rs
./t
Result: The value of number is:0 And as you can see, the output outside the function has changed to zero. Another useful syntax for functions that makes Rust similar to Solidity, is this type that identifies the type of the returned variable. Besides, the variable that you want to return must have no semicolon.

fn function_name() -> return_type {
     value //no semicolon means this value is returned
}


Learning Rust for CosmWasm: Tuples

In Rust, tuples are simply defined like the below examples.

fn main() {
     let tuple:(i32,f64,u8) = (243,-36.1,22);
     println!("{:?}",tuple);
}


In the above code, we have defined a tuple with different items in it such as i32, f64 and u8. Then we have assigned different numbers according to their type. As a result, in a tuple, we can define any type of data to any item of it.
Now, let’s run the code using the following command: rustc practice.rs
./t
Result: (243, -36.1, 22) We can also call an item of a tuple by using the tuple.n script, in which n is the nth member of the tuple. In the below example, we try to retrieve the first member of the tuple.

fn main() {
     let tuple:(i32,f64,u8) = (-325,4.9,22);
     println!("integer is :{:?}",tuple.0);
     println!("float is :{:?}",tuple.1);
     println!("unsigned integer is :{:?}",tuple.2);
}


Now, let’s run the code using the following command: rustc practice.rs
./t
Result: integer is :-325 float is :4.9 unsigned integer is :22 Another example; this time using a tuple from within a function:

fn main(){
     let b:(u32,bool,f64) = (110,true,-21.78);
     print(b);
}
fn print(z:(u32,bool,f64)){
     println!("Inside print method");
     println!("{:?}",z);
}


Now, let’s run the code using the following command: rustc practice.rs
./t
Result: Inside print method (110, true, -21.78) In the example below, we have defined a tuple and stored each of its items in separate variables, then we print the result:

fn main(){
     let b:(i32,bool,f64) = (110,true,-21.78);
     print(b);
}
fn print(z:(i32,bool,f64)){
     println!("Inside print method");
     let (age,is_male,cgpa) = z; 
     println!("Age is {} , isMale? {},cgpa is {}",age,is_male,cgpa);
}


Now, let’s run the code using the following command: rustc practice.rs
./t
Result: Inside print method Age is 110 , isMale? true,cgpa is -21.78

Learning Rust for CosmWasm: Arrays

In Rust, arrays are simply defined like the below example. As you can see, we use the syntax NameOfTheArray:[type:size_of_the_array] to define an array. In the example below, we first define the array and then print its contents and size.

fn main(){
     let arr:[i64;4] = [9,22,-67,4];
     println!("array is {:?}",arr);
     println!("array size is :{}",arr.len());
}


Now, let’s run the code using the following command: rustc practice.rs
./t
Result: array is [10, 20, 30, 40] array size is :4 In the following example, firstly we do the same as we did in the last example, and secondly, print every one of the items inside the array with their indices.

fn main(){
     let arr:[i64;4] = [9,22,-67,4];
     println!("array is {:?}",arr);
     println!("array size is :{}",arr.len());
     for index in 0..4 {
          println!("index is: {} & value is : {}",index,arr[index]);
     }
}

Now, let’s run the code using the following command in the terminal or command prompt: rustc practice.rs
./t
Result: array is [9, 22, -67, 4] array size is :4 index is: 0 & value is : 9 index is: 1 & value is : 22 index is: 2 & value is : -67 index is: 3 & value is : 4

Learning Rust for CosmWasm: Structs

Structs in Rust are like classes in python or other object-oriented languages. First, we define a struct, then we use it inside of a function. You can see in the below struct, we have first defined the category of the data for each person and then in the main function, we have defined the emp1 (or employee1) using the struct we have defined.

struct Employee {
     name:String,
     company:String,
     age:u32
}

fn main() {
     let emp1 = Employee {
          company:String::from("Microsoft"),
          name:String::from("Sarah"),
          age:32
     };
     println!("Name is :{} company is {} age is {}",emp1.name,emp1.company,emp1.age);
}


Now, let’s run the code using the following command in the console or the terminal. rustc practice.rs
./t
Result: Name is :Sarah company is Microsoft age is 32 And here goes a complete example of using functions and structs. In this example, we have done the same job as the previous example with the difference that we have used the struct for 2 employees (emp1 and emp2). In the end, we have displayed the data of the 2 employees using the display function.

struct Employee {
     name:String,
     company:String,
     age:u32
}

fn main() {
     let emp1 = Employee {
          company:String::from("Microsoft"),
          name:String::from("Sarah"),
          age:32
     };
     let emp2 = Employee{
          company:String::from("Amazon"),
          name:String::from("John"),
          age:28
     };
     display(emp1);
     display(emp2);
}
fn display( emp:Employee){
     println!("Name is :{} company is {} age is {}",emp.name,emp.company,emp.age);
}


Now, let’s run the code using the following command in the terminal: rustc practice.rs
./t
Result: Name is :Sarah company is Microsoft age is 32 Name is :John company is Amazon age is 28

Enums and Collections in Rust Language

I how Enums and collections like vectors, hash sets, and hash maps are used in Rust programming language. We are going to get deeper into using them by writing some scripts.

Learning Rust: Enums

Enum is nearly the same as the Enum that we had in Solidity. Here we use it for defining certain stuff like categories and types of objects.

Example 1

In example 1, we define the enum called GenderCategory and then define a struct called person, inside which, we use the GendeCategory enum and then instantiate from the struct Person inside of our main function.

#[derive(Debug)]
enum GenderCategory {
 Male,Female
}
#[derive(Debug)]
struct Person {
     name:String,
     gender:GenderCategory
}
fn main() {
     let p1 = Person {
          name:String::from("Collins"),
          gender:GenderCategory::Male
     };
     let p2 = Person {
          name:String::from("Sarah"),
          gender:GenderCategory::Female
     };
     println!("{:?}",p1);
     println!("{:?}",p2);
}

Now, let’s run the code using the following commands in the terminal. rustc t.rs
./t
Result: Person { name: "Collins", gender: Male } Person { name: "Sarah", gender: Female }

Example 2

In the 2nd example, we use enum for defining another category and use it inside of our print-size function for every clothing category with the message printed related to their size. In the end, we will print the size of every clothing according to its category in the main function.

enum Clothe {
     Formal,
     Informal
}

fn print_size(cloth:Clothe) {
     match cloth {
          Clothe::Formal => {
               println!("long clothe");
          },
          Clothe::Informal => {
               println!("short clothe");
          }
     }
}

fn main(){
     print_size(Clothe::Formal);
     print_size(Clothe::Informal);
}


Now, let’s run the code using the following command in the terminal: rustc t.rs
./t
Result: long clothe short clothe

Learning Rust: Collections

    1. Vector:
Vector is a resizable array that grows or shrinks in size and it has 5 main methods as follows:
    1. new(): creates a new vector.
    2. push(): appends an element to the back of a collection.
    3. remove(): removes and returns the element with a certain index.
    4. contains(): checks whether the vector has an element. And returns true or false according to the input.
    5. len(): returns the length of the vector.

In the next example, we define a mutable vector called v and then we will push new data to it. As we push new data to the vector, it increases in size. We can print its size with the vector_name.len() function and also the vector itself to see the change after each push.

fn main() {
     let mut v = Vec::new();
     v.push(34);
     println!("size of vector is :{}",v.len());
     println!("{:?}",v);
     v.push(25);
     println!("size of vector is :{}",v.len());
     println!("{:?}",v);
     v.push(12);
     println!("size of vector is :{}",v.len());
     println!("{:?}",v);
}


Now, let’s run the code using the following command in the terminal: rustc t.rs
./t
Result: size of vector is :1 [34] size of vector is :2 [34, 25] size of vector is :3 [34, 25, 12]

Another Example

In this example, at first, we define a vector with 3 items in it and then use the v.contains() function to check if it has a certain item in it or not.

fn main() {
     let v = vec![10,20,30];
     if v.contains(&10) {
          println!("found 10");
     }
     println!("{:?}",v);
}

Now, let’s run the code using the following command in the terminal: rustc t.rs
./t
Result: found 10 [10, 20, 30]
    2. Hash map:

Hash map in Rust works like a dictionary in python or mapping in Solidity. Meaning that it is a collection of key-value pairs. Hash maps have the following methods:
    1. insert()
    2. len()
    3. get ()
    4. iter()
    5. contains_key()
    6. remove()

Example 1

In this example, we define a new hash map that is mutable and then we add different properties with their values in it.

use std::collections::HashMap;
fn main() {
     let mut Users = HashMap::new();
     Users.insert("name","Sarah");
     Users.insert("Balance","1000");
     println!("size of map is {}",Users.len());
}


Now, let’s run the code using the following command in the terminal: rustc t.rs
./t
Result: size of map is 2

Example 2

In the 2nd example, we define another hash map and then try to remove one of the properties inside it using the .remove function.

use std::collections::HashMap;
fn main() {
     let mut Users = HashMap::new();
     Users.insert("name","Sarah");
     Users.insert("Balance","1000");
     Users.insert("Email","[email protected]");
     println!("length of the hashmap {}",Users.len());
     Users.remove(&"Email");
     println!("length of the hashmap after remove() {}",Users.len());
}


Now, let’s run the code using the following command in the terminal: rustc t.rs
./t
Result: length of the hashmap 3 length of the hashmap after remove() 2
    3. HashSets:

Hash set is another type of collection with unique values and it has the same methods as the Map sets. Let’s see an example:

use std::collections::HashSet;
fn main() {
     let mut names = HashSet::new();
     names.insert("Sarah");
     names.insert("Tim");
     names.insert("Mike");
     names.insert("Sarah");
     println!("{:?}",names);
}


As you can see in the above example, we do not have mappings as opposed to hash maps where we had a mapping between the properties and their values.
Now, let’s run the code using the following command in the terminal: rustc t.rs
./t
Result: {"Sarah", "Tim", "Mike"} As you can see we do not have the repeated name Sarah in the result. Because the items should be unique in the hash sets.

Learning How to Use Generics and Traits in Rust Language

learning Rust for CosmWasm smart contracts will lead us into knowing what Generics and traits are and how we can use them in a script. We are also going to work with some input and output functions to be able to interact with the user.

Learning Rust for CosmWasm: Generics

Generics is a way to write code for multiple types of data such as integers or strings. Suppose we want to store a set of data with different types. If we use a vector, the problem that we face is that we cannot push different types from the ones we had defined at first and then we can only push the type of data that is the same as the first declared variable.
In the below example we define a simple vector with all the data in it of the same type.

fn main(){
     let mut dataset: Vec = vec![12,13];
     dataset.push(14);
     println!("{:?}",dataset);
}


Now, let’s run the code using the following commands in the terminal: rustc t.rs
./t
Result: [12, 13, 14] Now if we want to push a string data to the vector, an error will be thrown.

fn main(){
     let mut dataset: Vec = vec![12,13];
     dataset.push(14);
     dataset.push("Mike"); 
     println!("{:?}",dataset);
}


Now, let’s run the code using the following commands in the terminal: rustc t.rs
./t
Result: error[E0308]: mismatched types --> t.rs:4:17 | 4 | dataset.push("Mike"); | ^^^^^^ expected `i32`, found `&str` error: aborting due to previous error For more information about this error, try `rustc --explain E0308`. We can fix this by declaring a struct inside which we declare the type T as a general type for every data type value.

struct Dataset {
     value:T,
}

fn main() {
     //generic type of i32
     let t:Dataset = Dataset{value:12};
     println!("value is :{} ",t.value);
     //generic type of String
     let t2:Dataset = Dataset{value:"Mike".to_string()};
     println!("value is :{} ",t2.value);
} 

Now, let’s run the code using the following commands in the terminal: rustc t.rs
./t
Result: value is :12 value is :Mike

Learning Rust for CosmWasm: Traits

Traits are like interfaces in object-oriented programming. First, we define a trait and then we implement it using impl. In the following example, we define a structure for our data; Employees. Then, we declare the trait containing the function that should be implemented and then the implementation of the trait for the data structure. Now if we define the main function, we can enter any data with the structure Employees, and then we apply the function inside the implementation.
In the below example, you can see that we have trait an impl (implementation of the trait). We also have a struct called Employee using which we define employee b1 in the main function, and we hire the b1 employee. Notice that in the impl we apply a trait on a struct, doing this makes the program written in Rust have a more beautiful structure.

fn main(){
     let b1 = Employee {
          id:86,
          name:"John"
     };
     b1.HireTheEmplyee();
}

struct Employee {
     name:&'static str,
     id:u32
}

trait Hire {
     fn HireTheEmplyee(&self);
}

impl Hire for Employee {
     fn HireTheEmplyee(&self){
          println!("Hiring Employee with id:{} and name {}",self.id,self.name)
     }
}


Now, let’s run the code using the following commands in the terminal. rustc t.rs
./t
Result: Hiring Employee with id:86 and name John

Learning Rust for CosmWasm: Input and Output

Reading from the Command Line:
The following code reads from the command line interface and prints it with an additional sentence. In the below example, we will define a new string which is a mutable called line. This string is going to be used to get data from the user in the terminal. Then, we will use let b1 = std::io::stdin().read_line(&mut line).unwrap() to get data from the user and then we will print it next a sentence.

fn main(){
     let mut line = String::new();
     println!("Enter your job title :");
     let b1 = std::io::stdin().read_line(&mut line).unwrap();
     println!("Your job title is , {}", line);
     println!("number of bytes is , {}", b1);
}


Now, let’s run the code using the following commands in the terminal: rustc t.rs
./t
Result: Enter your job title : Developer Your job title is , Developer number of bytes is , 10

Learning Rust for CosmWasm: Writing

Write is a new way to print something on the console and can be used as an alternative for “println!”. You can see the use case of write in the below example where we use write instead of “println!”.

use std::io::Write;
fn main() {
     let b1 = std::io::stdout().write("Rust ".as_bytes()).unwrap();
     let b2 = std::io::stdout().write(String::from("Tutorials").as_bytes()).unwrap();
     std::io::stdout().write(format!("\nbytes written {}",(b1+b2)).as_bytes()).unwrap();
}


Now, let’s run the code using the following commands in the terminal: rustc t.rs
./t
Result: Rust Tutorials bytes written 14

Another Example

In the example below, the std::env::args() returns the command line arguments.

fn main(){
     let cmd_line = std::env::args();
     println!("No of elements in arguments is :{}",cmd_line.len()); 
     let mut sum = 0;
     let mut has_read_first_arg = false;
     for arg in cmd_line {
          if has_read_first_arg { 
               sum += arg.parse::().unwrap();
          }
          has_read_first_arg = true; 
     }
     println!("sum is {}",sum);
}


Now, let’s run the code using the following commands in the terminal: rustc t.rs
./t
Result: No of elements in arguments is :1 sum is 0

Files (Write, Read, Append, Delete, and Copy) In Rust Language

In Rust, we have different modules for executing different operations on the files. These methods include opening, creating, reading, appending, and writing files. You can see the list of these modules and their code below:
    1. std::fs::File
    Methods:
    open()
    create()
    2. std::fs::remove_file
    Methods:
    remove_file()
    3. std::fs::OpenOptions
    Methods:
    append()
    4. std::io::Writes
    Methods:
    write_all()
    5. std::io::Read
    Methods:
    read_to_string()

Writing to A File

In the coming example, we are going to create a file called data.txt and enter some data in it. We will also check whether we face any errors during the creation of the file or not.

use std::io::Write;
fn main() {
     let mut file = std::fs::File::create("data.txt").expect("create failed");
     file.write_all("This is a file written".as_bytes()).expect("write failed");
     file.write_all("\n In Rust Programming language".as_bytes()).expect("write failed");
     println!("Successfully written to file" );
}

Now, let’s run the above script using the following command in the terminal: rustc t.rs
./t
Result in the Terminal: Successfully written to file Result in the data.txt text file: This is a file written In Rust Programming languag

Reading from A File

Now, we are going to read from the file we have just created and written to. We print the result in the terminal

use std::io::Read;
fn main(){
     let mut file = std::fs::File::open("data.txt").unwrap();
     let mut contents = String::new();
     file.read_to_string(&mut contents).unwrap();
     print!("{}", contents);
}


Now, let’s run the above script using the following command in the terminal: rustc t.rs
./t
Result in the Terminal: This is a file written In Rust Programming language

Append Data to A File

Now, we are going to append new data to the data.txt file and update it that way. Notice the Error handling script for OpenOptions and Write using the .expect() function.

use std::fs::OpenOptions;
use std::io::Write;
fn main() {
     let mut file = OpenOptions::new().append(true).open("data.txt").expect("cannot open file");
     file.write_all("\nCongrats!".as_bytes()).expect("write failed");
     file.write_all("\nYou have successfully appended the new 
     text.".as_bytes()).expect("write failed");
     println!("file append success");
}


Now, let’s run the above script using the following command in the terminal: rustc t.rs
./t
Result in the Terminal: file append success Result in the data.txt text file: This is a file written In Rust Programming language Congrats! You have successfully appended the new text.

Delete A File

Deleting a file is similar to the way we applied other functions on the files. We first write the use std::fs; and then use its method fs::remove_file.

use std::fs;
fn main() {
     fs::remove_file("data.txt").expect("could not remove file");
     println!("file is removed");
}


Let’s run the above script using the following command in the terminal: rustc t.rs
./t
Result in the Terminal: file is removed If you look at the directory, you will see that the file data.txt has indeed been removed.

Iterators

Iterators help us to iterate over different kinds of collections such as arrays, vectors, maps,etc. In the below example, we are going to use the iterator to read values from an array.

fn main() {
     let a = [11,12,17];
     let mut iter = a.iter(); 
     println!("{:?}",iter);
     println!("{:?}",iter.next());
     println!("{:?}",iter.next());
     println!("{:?}",iter.next());
     println!("{:?}",iter.next());
}

Let’s run the above script using the following command in the terminal: rustc t.rs
./t
Result in the Terminal: Iter([11, 12, 17]) Some(11) Some(12) Some(17) None

Closure

Closure refers to a function within another function. It is also known as inline function. In the below example, we are going to check if a number is odd or not and print the result.

fn main(){
     let is_odd = |x| {
          x%2==1
     };
     let Number = 27;
     println!("Is {} an odd number? {}",Number,is_odd(Number));
}


Now, let’s run the above script using the following command in the terminal: rustc t.rs
./t
Result: Is 27 an odd number? True

Example 2

In this simple example, we have defined a closure that adds up the input with 27. We will use this closure inside of the main function (opposite to the functions that were defined outside of the main function) and print the result for the input 12.

fn main(){
     let val = 27; 
     let SimpleClosure = |x| {
          x + val 
     };
     println!("{}",SimpleClosure(12));
}


Let’s run the above script using the following command in the terminal: rustc t.rs
./t
Result: 39

What Did We Learn?

Firstly, we have got familiar with CosmWasm smart contracts as well as the Rust programming language and some of the basics of the Rust programming language for CosmWasm smart contracts. We have also learned about the syntax and data types in Rust.
Secondly, we have learned how to use functions, tuples, arrays, and structs. We have also learned about an important concept of mutable variables which is very useful when dealing with different situations.
Thirdly, we have got familiar with the syntax used for enums, vectors, hash sets, and hash maps. With the aid of examples, the use cases for each of them have been made easier to understand.
Fourthly, we have learned about Generics, Generic functions, traits, inputs and outputs, writings, etc. Through the examples, these concepts have become much clear.
And finally, we have learned how to interact with files in Rust. The functions that we used helped us read, open, create, remove, write and append to files. We have also got familiar with the iterators and closure concept in Rust with the aid of some insightful examples.

Download this Article in PDF format

metaverse

What Do We Do in Arashtad?

3D websites, 3D games, metaverses, and other types of WebGL and 3D applications are what we develop in our 3D web development team.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Request a Quote
ThreeJS Development

How to Write Terra Smart Contracts: A Complete Tutorial

This article explains the connections between the python codes we wrote earlier (same for JavaScript) and the pre-built functions inside the src folder created with the cargo generate–git command. Reading this article will give you a deeper insight into what actually happens when we instantiate, execute or query a state of Terra smart contracts in python code and how it communicates with the CosmWasm libraries written in Rust. Being familiar with Rust programming language can help you better understand the steps we are going to take to interact with the CosmWasm smart contracts on the Terra network. We have provided a small Rust tutorial to make you more familiar with Rust scripts at CosmWasm Smart Contracts – learning Rust series.

What Are Terra Smart Contracts?

In the blockchain, Terra smart contracts are instances of a singleton object whose internal state is persisted on the blockchain. A singleton is a class that allows only a single instance of itself to be created and gives access to that created instance. Users can query or set the state changes by sending a JSON message. The details of these messages are going to be explained throughout this article. Terra smart contracts are defined by these 3 main functions:
    1. Instantiate(): a constructor which is used during contract instantiation to set the initial state.
    2. Execute(): it is used when a user wants to invoke a method on the smart contract.
    3. Query(): it is used when a user wants to get data out of a smart contract.

Creating the Template to Write Terra Smart Contracts

The 1st step of creating a smart contract is to create the template using the command below: cargo generate --git https://github.com/CosmWasm/cw-template.git --branch 0.16 -- name my-first-contract cd my-first-contract The above cargo command creates a structure and boilerplate for creating Terra smart contract. A sample smart contract has the following template, we should define a struct called state and in it, we should determine the count and the address of the owner of the contract. The below code can also be found in the src folder, state.rs file.

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use cosmwasm_std::Addr;
use cw_storage_plus::Item;
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct State {
     pub count: i32,
     pub owner: Addr,
}
pub const STATE: Item = Item::new("state");

Serialization & Deserialization

Notice that some data, like owner and count, can only be persisted as bytes, so we need to convert these data from human-readable format to bytes. That is why we use serialization and deserialization. The CosmWasm team has provided the utility crates such as cosmwasm_storage, which automatically provides serialization and deserialization for commonly used types such as Structs and numbers in Rust. Some of the useful traits applied by deriving attributes in #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)] are as follows:
    1. Serialize: for providing serialization
    2. Deserialize: for deserialization
    3. Clone: makes the struct copyable
    4. Debug: enables the struct to be printed to string
    5. PartialEq: provides equality comparison
    6. JsonSchema: automatically creates a JSON schema

InstantiateMsg

The instantiateMsg is provided for the users when they want to use MsgInstantiate Contract in their script (Python or JavaScript). This provides the initial state and the configuration of the contract. As opposed to Ethereum, in Terra Smart Contracts, the uploading of a smart contract code and the instantiation of it are considered 2 separate events. The reason for this is to allow different contracts to have the same base code by instantiating. The below code can be found in the src folder and inside the file called msg.rs.

use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct InstantiateMsg {
     pub count: i32,
}


To create Terra smart contracts, you can send a JSON message like this:

{
     "count": 100
}


Remember in the python scripts (contracts.py) when we sent the instantiateMsg and set the JSON message to {“count”: 15}:

contract_address = instantiate_contract(code_id,{"count": 15})


When the message InstantiateMsg is sent, the below rust script in the src/contract.rs will extract the message and set up the initial state. In this instantiate function, the count is the extracted count from the message and the address is the address of the account that sent the MsgInstantiateContract message.

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn instantiate(
               deps: DepsMut,
               _env: Env,
               info: MessageInfo,
               msg: InstantiateMsg,
          ) -> Result {
     let state = State {
     count: msg.count,
     owner: info.sender.clone(),
     };
     set_contract_version(deps.storage, CONTRACT_NAME, CONTRACT_VERSION)?;
     STATE.save(deps.storage, &state)?;
     Ok(Response::new()
     .add_attribute("method", "instantiate")
     .add_attribute("owner", info.sender)
     .add_attribute("count", msg.count.to_string()))
}


ExecuteMsg

The ExecuteMsg in python or Javascript is the message sent to the execute function in contract.rs through MsgExecuteContract (remember when we imported this in our contract.py). Opposite to what we had in InstantiateMsg, in the ExecuteMsg we have different messages for executing different functionalities(logics). The execute function in the contract.rs file demultiplexes these different messages to their related logic. In the msg.rs file, you will see the definition of the 2 messages Increment and Reset in a public enum.

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ExecuteMsg {
     Increment {},
     Reset { count: i32 },
}


And as you can see, in the contract.rs, Increment and Reset are connected to their appropriate functions in the match msg:

ExecuteMsg::Increment {} => try_increment(deps)
Increment {} => try_increment(deps),

The complete code of execute function goes like this:

#[cfg_attr(not(feature = "library"), entry_point)]
pub fn execute(
               deps: DepsMut,
               _env: Env,
               info: MessageInfo,
               msg: ExecuteMsg,
          ) -> Result {
     match msg {
          ExecuteMsg::Increment {} => try_increment(deps),
          ExecuteMsg::Reset { count } => try_reset(deps, info, count),
     }
}


Now, the messages that we send in a python or Javascript code, goes like this: increment message:

{
     "increment": {}
}


reset message:

{
     "reset": {"count": 5}
}

remember the kind of script we wrote in python to send the increment message.

execute = execute_contract(deployer,contract_address,{"increment":{}})


Now, let’s see how try_increment and try_reset functions work in the contract.rs file. In the try_increment function, the mutable state from state.rs is called. Then, it is updated (incremented) to a new number and then the function returns OK with a new state. Finally the execution is finalized as successful by the OK response.

pub fn try_increment(deps: DepsMut) -> Result {
     STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {
          state.count += 1;
          Ok(state)
     })?;
     Ok(Response::new().add_attribute("method", "try_increment"))
}


The try_reset works the same way as try_increment with the difference that it first checks whether the sender of the message is the owner or not.

pub fn try_reset(deps: DepsMut, info: MessageInfo, count: i32) -> Result {
     STATE.update(deps.storage, |mut state| -> Result<_, ContractError> {
          if info.sender != state.owner {
               return Err(ContractError::Unauthorized {});
          }
          state.count = count;
          Ok(state)
     })?;
     Ok(Response::new().add_attribute("method", "reset"))
}


How to Interact with Terra Smart Contracts

In the continuation of this journey, we are going to see how the QueryMsg function is defined in Rust in addition to how we can interact with it using Python or JavaScript. QueryMsg: For the query messages, there are two functionalities required:
    1. A format of receiving defined in the request GetCount {} inside the QueryMsg enum.
    2. And a variable pub count: i32 to respond to the query request which is defined inside the CountResponse struct. The scripts below can be found in the src folder inside the file called msg.rs

#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum QueryMsg {
     // GetCount returns the current count as a json-encoded number
     GetCount {},
}
// Define a custom struct for each query response
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, JsonSchema)]
pub struct CountResponse {
     pub count: i32,
}

The following script can be found inside the contract.rs file in the src folder. This script contains 2 functions, one for receiving the query request and two for returning the responding of the state of the contract from the state.rs file. The query function receives the query request and converts the message to binary and passes it to the query_count function which loads the state of the contract from the state.rs and returns the result with the OK response and the message in the { count: state.count } format.


#[cfg_attr(not(feature = "library"), entry_point)]
pub fn query(deps: Deps, _env: Env, msg: QueryMsg) -> StdResult {
     match msg {
          QueryMsg::GetCount {} => to_binary(&query_count(deps)?),
     }
}

fn query_count(deps: Deps) -> StdResult {
     let state = STATE.load(deps.storage)?;
     Ok(CountResponse { count: state.count })
}


The message we send for querying the state of the contract is in this format.

{
     "get_count": {}
}


And the respond we get is like the following:

{
     "count": 5
}

Building Terra Smart Contract

To build the contract, you can use the 2 commands below: cargo test
cargo wasm
The next thing we do is optimize our build by running the following command: cargo run-script optimize To be able to run the above command, you need to make sure you have installed docker.
If you are on arm-64 machine, you should run: docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ cosmwasm/rust-optimizer-arm64:0.12.4 And if you are on windows with docker daemon running on WSL1 run the following: docker run --rm -v "$(pwd)":/code \ --mount type=volume,source="$(basename "$(pwd)")_cache",target=/code/target \ --mount type=volume,source=registry_cache,target=/usr/local/cargo/registry \ cosmwasm/rust-optimizer-arm64:0.12.4 Any of the above commands if run properly (according to the use cases mentioned), should create a new folder called artifacts which contains the my-first-contract.wasm file. To make JSON Schema files, for serialization, you need to register each of the data structures by export_schema(&schema_for!(), &out_dir); you can see the registration in the examples folder, inside the file called schema.rs:

use std::env::current_dir;
use std::fs::create_dir_all;
use cosmwasm_schema::{export_schema, remove_schemas, schema_for};
use my_first_contract::msg::{CountResponse, HandleMsg, InitMsg, QueryMsg};
use my_first_contract::state::State;
fn main() {
     let mut out_dir = current_dir().unwrap();
     out_dir.push("schema");
     create_dir_all(&out_dir).unwrap();
     remove_schemas(&out_dir).unwrap();
     export_schema(&schema_for!(InstantiateMsg), &out_dir);
     export_schema(&schema_for!(ExecuteMsg), &out_dir);
     export_schema(&schema_for!(QueryMsg), &out_dir);
     export_schema(&schema_for!(State), &out_dir);
     export_schema(&schema_for!(CountResponse), &out_dir);
}

Now, using the following commands, you can create these data structure JSON files inside schema folder: cd examples Cargo schema Result: Finished dev [unoptimized + debuginfo] target(s) in 0.37s Running `/home/mohamad/LocalTerra/my-first-contract.wasm/target/debug/examples/schema` Created /home/mohamad/LocalTerra/my-first-contract.wasm/examples/schema/instantiate_msg.json Created /home/mohamad/LocalTerra/my-first-contract.wasm/examples/schema/execute_msg.json Created /home/mohamad/LocalTerra/my-first-contract.wasm/examples/schema/ query_msg.json Created /home/mohamad/LocalTerra/my-first-contract.wasm/examples/schema/ state.json Created /home/mohamad/LocalTerra/my-first-contract.wasm/examples/schema/ count_response.json Inside the schema folder, you should be able to see all the requested data structures JSON files.

count_response.json
execute_msg.json
instantiate_msg.json
query_msg.json
state.json

Inside the state.json, you can see different sections of the .json file, the most important of which is the requires containing the “count” and the “owner”. { "$schema": "http://json-schema.org/draft-07/schema#", "title": "State", "type": "object", "required": [ "count", "owner" ], "properties": { "count": { "type": "integer", "format": "int32" }, "owner": { "$ref": "#/definitions/Addr" } }, "definitions": { "Addr": { "description": "A human readable address.\n\nIn Cosmos, this is typically bech32 encoded. But for multi-chain smart contracts no assumptions should be made other than being UTF-8 encoded and of reasonable length.\n\nThis type represents a validated address. It can be created in the following ways 1. Use `Addr::unchecked(input)` 2. Use `let checked: Addr = deps.api.addr_validate(input)?` 3. Use `let checked: Addr = deps.api.addr_humanize(canonical_addr)?` 4. Deserialize from JSON. This must only be done from JSON that was validated before such as a contract's state. `Addr` must not be used in messages sent by the user because this would result in unvalidated instances.\n\nThis type is immutable. If you really need to mutate it (Re- ally? Are you sure?), create a mutable copy using `let mut mutable = Addr::to_string()` and operate on that `String` instance.", "type": "string" } } } Besides, you can see 2 required sections inside the execute_msg.json: 1. "increment" 2. "reset" { "$schema": "http://json-schema.org/draft-07/schema#", "title": "ExecuteMsg", "anyOf": [ { "type": "object", "required": [ "increment" ], "properties": { "increment": { "type": "object" } }, "additionalProperties": false }, { "type": "object", "required": [ "reset" ], "properties": { "reset": { "type": "object", "required": [ "count" ], "properties": { "count": { "type": "integer", "format": "int32" } } } }, "additionalProperties": false } ] }

Summing Up

we dived deep into the details of the local Terra smart contracts written in Rust or in other words the CosmWasm smart contracts. This type of understanding from scratch helps us interact and communicate better with local Terra and understand what actually happens behind the scenes when we send JSON messages with python scripts to instantiate or execute a smart contract or query data from it. Firstly, we showed how to create a template for initiating state. Then, we created a smart contract and learned how to interact with it. Besides, we learned how to use the QueryMsg function and build a Terra smart contract.

Download this Article in PDF format

metaverse

Curious about What We’re Up To?

In Arashtad, we provide custom services in various design and development fields. 3D websites, 3D games, metaverses, and other types of WebGL and 3D applications are just some of our expertise.

Arashtad Serivces
Drop us a message and tell us about your ideas.
Request a Quote
ThreeJS Development