Andromeda
Search…
Cw-20 Staking App
If unfimailar with the steps of deploying an app, go back to the first example where we explain in detail all the different parts of deploying an App.
If any of the messages in this example do not work, you might want to cross reference the messages with the ADO specific section which always contains the latest ADO versions to make sure they are correct. Other than that the logic will remain the same.

Defining our App

For this example, we will assume that we have a project that wants to create their own cw-20 token that can be staked for rewards.
The steps we will perform:
  • Mint some coins to different addresses
  • Stake some of the coins for rewards of our choice
  • Check that all the results work as intended

Instantiating the App

The components/ADOs we would need:
Although not necessary, if you are unfamiliar with these ADOs, it is suggested to read through each of the them before deploying an app.
Let us first start by representing the instantiation message for each of our ADOs:
Keep in mind that the app takes these messages as base64 encoded.

Cw-20 Token

I will start out by giving 10000 tokens to three addresses. We have not set a minter, meaning no additional tokens can ever be minted.
{
"name": "Staking Token",
"symbol":"STK",
"decimals":6,
"initial_balances":[
{
"address":"juno1hfngwh4f53smthvqde32y07rjcezdsnlztxtll",
"amount":"10000"
},
{
"address":"juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec",
"amount":"10000"
},
{
"address":"juno1nekdhtj60s692g28e4cq5yvpg9aqd4yeafu07t",
"amount":"10000"
}
]
}
As Base64:
eyJuYW1lIjogIlN0YWtpbmcgVG9rZW4iLCJzeW1ib2wiOiJTVEsiLCJkZWNpbWFscyI6NiwiaW5pdGlhbF9iYWxhbmNlcyI6W3siYWRkcmVzcyI6Imp1bm8xaGZuZ3doNGY1M3NtdGh2cWRlMzJ5MDdyamNlemRzbmx6dHh0bGwiLCJhbW91bnQiOiIxMDAwMCJ9LHsiYWRkcmVzcyI6Imp1bm8xOTd5NDR3NThkanF1cWtwdTh2NTZwZ3d2bTA1OGY2anN3bTBqZWMiLCJhbW91bnQiOiIxMDAwMCJ9LHsiYWRkcmVzcyI6Imp1bm8xbmVrZGh0ajYwczY5MmcyOGU0Y3E1eXZwZzlhcWQ0eWVhZnUwN3QiLCJhbW91bnQiOiIxMDAwMCJ9IF19ICA=

Cw-20 Staking

We have not set any additional rewards. This means that the rewards are the same as the staking token as most staking works. If you want, you can add up to 10 different reward tokens to the staking.
{
"staking_token":{
"identifier":"cw20"
}
}
As Base64:
eyJzdGFraW5nX3Rva2VuIjp7ImlkZW50aWZpZXIiOiJjdzIwIn19

App

{
"name": "Staking App",
"app": [
{
"name": "cw20",
"ado_type": "cw20",
"instantiate_msg": "eyJuYW1lIjogIlN0YWtpbmcgVG9rZW4iLCJzeW1ib2wiOiJTVEsiLCJkZWNpbWFscyI6NiwiaW5pdGlhbF9iYWxhbmNlcyI6W3siYWRkcmVzcyI6Imp1bm8xaGZuZ3doNGY1M3NtdGh2cWRlMzJ5MDdyamNlemRzbmx6dHh0bGwiLCJhbW91bnQiOiIxMDAwMCJ9LHsiYWRkcmVzcyI6Imp1bm8xOTd5NDR3NThkanF1cWtwdTh2NTZwZ3d2bTA1OGY2anN3bTBqZWMiLCJhbW91bnQiOiIxMDAwMCJ9LHsiYWRkcmVzcyI6Imp1bm8xbmVrZGh0ajYwczY5MmcyOGU0Y3E1eXZwZzlhcWQ0eWVhZnUwN3QiLCJhbW91bnQiOiIxMDAwMCJ9IF19ICA"
},
{
"name": "staking",
"ado_type": "cw20_staking",
"instantiate_msg": "eyJzdGFraW5nX3Rva2VuIjp7ImlkZW50aWZpZXIiOiJjdzIwIn19"
}
],
"operators": [],
"primitive_contract": "juno144r95jrnk5erqhpuepe5mwz33e5mns7yuyhmxw7fmfx8w0duqwws9qd36x"
}
First let us upload the app contract to JUNO test-net:
We dont have to repeat this step for every app we build. We can use the same code-id of other apps, but for the sake of the example, I will be reuploading it as if this is my first app.
junod tx wasm store andromeda_app_contract.wasm --from juno1hfngwh4f53smthvqde32y07rjcezdsnlztxtll --chain-id=uni-3 --gas-prices 0.1ujunox --gas auto --gas-adjustment 1.3 -b block -y
Now let us instantiate the App using its message:
junod tx wasm instantiate 977 '{"name": "Staking App","app": [{"name": "cw20","ado_type": "cw20","instantiate_msg": "eyJuYW1lIjogIlN0YWtpbmcgVG9rZW4iLCJzeW1ib2wiOiJTVEsiLCJkZWNpbWFscyI6NiwiaW5pdGlhbF9iYWxhbmNlcyI6W3siYWRkcmVzcyI6Imp1bm8xaGZuZ3doNGY1M3NtdGh2cWRlMzJ5MDdyamNlemRzbmx6dHh0bGwiLCJhbW91bnQiOiIxMDAwMCJ9LHsiYWRkcmVzcyI6Imp1bm8xOTd5NDR3NThkanF1cWtwdTh2NTZwZ3d2bTA1OGY2anN3bTBqZWMiLCJhbW91bnQiOiIxMDAwMCJ9LHsiYWRkcmVzcyI6Imp1bm8xbmVrZGh0ajYwczY5MmcyOGU0Y3E1eXZwZzlhcWQ0eWVhZnUwN3QiLCJhbW91bnQiOiIxMDAwMCJ9IF19ICA"},{"name": "staking","ado_type": "cw20_staking","instantiate_msg": "eyJzdGFraW5nX3Rva2VuIjp7ImlkZW50aWZpZXIiOiJjdzIwIn19"}],"operators": [],"primitive_contract": "juno144r95jrnk5erqhpuepe5mwz33e5mns7yuyhmxw7fmfx8w0duqwws9qd36x"}' --label "Auction App" --from juno1hfngwh4f53smthvqde32y07rjcezdsnlztxtll --gas-prices 0.1ujunox --gas auto --gas-adjustment 1.3 -b block -y --no-admin

Interacting with the App

Andromeda contracts assign the original instantiator as contract owners. Due to how instantiation via a submessage operates in CosmWasm contracts, we must claim ownership of the components in order to execute messages that have authorization restrictions (owner restricted).

Claim Components

{
"claim_ownership":{}
}
junod tx wasm execute juno1fzkpu2fcppzf8wadurm6sw08pq3ww657r9ggk9z6w6pm9jxa4ckqs45u24 '{"claim_ownership":{}}' --from juno1hfngwh4f53smthvqde32y07rjcezdsnlztxtll --gas-prices 0.1ujunox --gas auto --gas-adjustment 1.3 -b block -y

User 1 stakes tokens

To stake tokens we perform a send message from Token contract and attach the Stake{} message to it. For user 1 we will stake 3000 tokens.
{
"send":{
"contract":"juno1wf526ma0mjskhlgrun9qkytelnfsl5pny3l2gwvpgjqwhcrx2q8qpqg53f",
"amount":"3000",
"msg":"ewogICAgInN0YWtlX3Rva2VucyI6e30KICAgIAp9"
}
}
Here the attached message is:
{
"stake_tokens":{}
}
Let us execute it:
junod tx wasm execute juno1cngmjgaf9jqh8ukc6j75ygke6vdajpkx3dkphhus45agghd2tuqs06kut7 '{"send":{"contract":"juno1wf526ma0mjskhlgrun9qkytelnfsl5pny3l2gwvpgjqwhcrx2q8qpqg53f","amount":"3000","msg":"ewogICAgInN0YWtlX3Rva2VucyI6e30KICAgIAp9"}}' --from juno1hfngwh4f53smthvqde32y07rjcezdsnlztxtll --gas-prices 0.1ujunox --gas auto --gas-adjustment 1.3 -b block -y
We perform the same steps to stake for users 2 and 3:

Checking Pending Rewards

Let us check the staker data:
junod query wasm contract-state smart juno1wf526ma0mjskhlgrun9qkytelnfsl5pny3l2gwvpgjqwhcrx2q8qpqg53f '{"staker":{"address":"juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec"}}'

Result:

data:
address: juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec
balance: "1000"
pending_rewards: []
share: "1000"
Here we check one of the stakers. As we can see, the pending rewards are empty and the balance is the same as the share.

Transfer Some Tokens as Rewards

Now let us send 1000 tokens to the contract. By doing this, we are telling the contract to use them as rewards for stakers.
{
"transfer":{
"recipient":"juno1wf526ma0mjskhlgrun9qkytelnfsl5pny3l2gwvpgjqwhcrx2q8qpqg53f",
"amount": "1000"
}
}
junod tx wasm execute juno1cngmjgaf9jqh8ukc6j75ygke6vdajpkx3dkphhus45agghd2tuqs06kut7 '{"transfer":{"recipient":"juno1wf526ma0mjskhlgrun9qkytelnfsl5pny3l2gwvpgjqwhcrx2q8qpqg53f","amount": "1000"}}' --from juno1hfngwh4f53smthvqde32y07rjcezdsnlztxtll --gas-prices 0.1ujunox --gas auto --gas-adjustment 1.3 -b block -y
Now since the token we sent is the same one being staked, then the rewards are distributed automatically to the stakers, and there is no need to call Claim or UpdateGlobalIndex.
Let us check the new balances of the users:

User 1:

junod query wasm contract-state smart juno1wf526ma0mjskhlgrun9qkytelnfsl5pny3l2gwvpgjqwhcrx2q8qpqg53f '{"staker":{"address":"juno1hfngwh4f53smthvqde32y07rjcezdsnlztxtll"}}'

Result:

data:
address: juno1hfngwh4f53smthvqde32y07rjcezdsnlztxtll
balance: "3300"
pending_rewards: []
share: "3000"

User 2:

junod query wasm contract-state smart juno1wf526ma0mjskhlgrun9qkytelnfsl5pny3l2gwvpgjqwhcrx2q8qpqg53f '{"staker":{"address":"juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec"}}'

Result:

data: address: juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec
balance: "1100"
pending_rewards: []
share: "1000"

User 3:

junod query wasm contract-state smart juno1wf526ma0mjskhlgrun9qkytelnfsl5pny3l2gwvpgjqwhcrx2q8qpqg53f '{"staker":{"address":"juno1nekdhtj60s692g28e4cq5yvpg9aqd4yeafu07t"}}'

Result:

data: address: juno1nekdhtj60s692g28e4cq5yvpg9aqd4yeafu07t
balance: "6600"
pending_rewards: []
share: "6000
As we can see each of the stakers got the amount proportional to the amount staked.

Conclusion

This was a very simple App that showcases our cw20-staking contract. If you want to play around with it more, you can built it using the allocated rewards and add up to 10 different token rewards. It would also allow you to spread the rewards distribution to be in cycles instead of instant as we got here. This App although simple, is needed in most web3 projects that launch their own token. With Andromeda, you can have it all setup easily like we saw in this example or even using our no-code builder App which takes minutes.
Copy link
Outline
Defining our App
Instantiating the App
Interacting with the App