Tutorial: Structuring executable messages for Assembly proposals, Part 1: Fine-tuning allocation points

Astroport
6 min readOct 28, 2022

--

In a previous tutorial on the Assembly contract, we covered the general structure of proposal messages. To execute a proposal, an Assembly member must send an ExecuteMsg to the xASTRO contract containing an embedded proposal message. This embedded message contains key information about the proposal, including the proposal’s title, description, link, and, in some cases, further embedded messages.

Previously, we alluded to using the messages field to include additional information regarding certain types of proposals. For example, automatic treasury disbursements. In this tutorial, we will cover a popular use case: fine-tuning allocation points for pools. These are pools that receive ASTRO emissions every block.

Note: The Astroport web app provides a user-friendly UI to submit proposals. However, Assembly members will still need to submit an executable message for certain types of proposals that require further information. This tutorial will give a full breakdown of submitting a proposal using Terra.js, from beginning to end, but readers can also skip to the two necessary sections (Generator / Messages Msg) and use the Astroport UI as well.

Let’s begin.

Overview

Before we begin, here is the entire code. If this looks new to you — no rush — we break this down step-by-step below:

// setup
const { LCDClient, MnemonicKey, MsgExecuteContract } = require('@terra-money/terra.js');
async function main() {const terra = new LCDClient({
URL: 'https://pisco-lcd.terra.dev',
chainID: 'pisco-1',
});
const mk = new MnemonicKey({
mnemonic: ''
});
const wallet = terra.wallet(mk);// contract addresses (testnet)
const assembly_address = 'terra195m6n5xq4rkjy47fn5y3s08tfmj3ryknj55jqvgq2y55zul9myzsgy06hk';
const xastro_address = 'terra1ctzthkc0nzseppqtqlwq9mjwy9gq8ht2534rtcj3yplerm06snmqfc5ucr';
const generator_address = 'terra1ksvlfex49desf4c452j6dewdjs6c48nafemetuwjyj6yexd7x3wqvwa7j9';
// Base64 encoder
function toEncodedBinary(object) {
return Buffer.from(JSON.stringify(object)).toString('base64');
}
// proposal messages
const generator_msg = {
"setup_pools": {
"pools": [
("lp_token_address", "1000"),
("lp_addr2", "10"),
... etc
]
}
}
const generator_binary = toEncodedBinary(generator_msg)const proposal_msgs = [
{
order: "1",
msg: {
wasm: {
execute: {
contract_addr: generator_address,
msg: generator_binary,
funds: []
}
}
}
}
]
const proposal_msg = {
"submit_proposal": {
"title": "testing",
"description": "testing",
"link": null,
"messages": proposal_msgs
}
}
const proposal_binary = toEncodedBinary(proposal_msg)const msg = {
"send": {
"contract": assembly_address,
"amount": "30000000000",
"msg": proposal_binary
}
}
// execute proposalconst execute = new MsgExecuteContract(
wallet.key.accAddress,
xastro_address,
msg,
);
// broadcast transaction
const executeTx = await wallet.createAndSignTx({
msgs: [execute]
})
.then(tx => terra.tx.broadcast(tx))
.then((result) => console.log(result.txhash));
}main().catch(console.error)

Step-by-Step

Set up

First, we use terra.js to connect to the Terra blockchain, connect a wallet, and sign and confirm transactions. Note: This guide uses the pisco-1 testnet. For a complete guide to setting up Terra.js, visit here.

// setup
const { LCDClient, MnemonicKey, MsgExecuteContract } = require('@terra-money/terra.js');
async function main() {const terra = new LCDClient({
URL: 'https://pisco-lcd.terra.dev',
chainID: 'pisco-1',
});
const mk = new MnemonicKey({
mnemonic: ''
});
const wallet = terra.wallet(mk);

Contract Addresses

Second, it’s generally a good idea to list all contract addresses that we will be using later in our code. For fine-tuning pool allocation points, a proposal will interact with 3 contracts: the Assembly, Generator, and xASTRO contracts. For a full list of mainnet and testnet addresses, visit here.

// contract addresses (testnet)
const assembly_address = 'terra195m6n5xq4rkjy47fn5y3s08tfmj3ryknj55jqvgq2y55zul9myzsgy06hk';
const xastro_address = 'terra1ctzthkc0nzseppqtqlwq9mjwy9gq8ht2534rtcj3yplerm06snmqfc5ucr';
const generator_address = 'terra1ksvlfex49desf4c452j6dewdjs6c48nafemetuwjyj6yexd7x3wqvwa7j9';

Base64 Encoder

Third, we will need to encode and nest several messages. For this guide, we will be using a custom function that will encode and pass our messages.

// Base64 encoder
function toEncodedBinary(object) {
return Buffer.from(JSON.stringify(object)).toString('base64');
}

Proposal Messages

Now we finally get to the core of the tutorial. The entire proposal contains a total of 4 messages:

  1. Generator Msg: Contains our setup_pools message to set up allocation points. This is the innermost layer in our nested messages.
  2. Proposal Msgs: Contains our executable message, including our Generator Msg in binary format. Can also be used with the Astroport web app to fill the messages field.
  3. Proposal Msg: Contains our submit_proposal message (title, description, etc.), including our Proposal Msgs array.
  4. Msg: Contains our final message to pass into our ExecuteMsg function, including our Proposal Msg in binary format. This is the outermost layer in our nested messages.

Note: Msg titles are arbitrary variable names.

Like before, we show the entire code first and break down each section below:

// proposal messages
const generator_msg = {
"setup_pools": {
"pools": [
("lp_token_address", "1000"),
("lp_addr2", "10"),
... etc
]
}
}
const generator_binary = toEncodedBinary(generator_msg)const proposal_msgs = [
{
order: "1",
msg: {
wasm: {
execute: {
contract_addr: generator_address,
msg: generator_binary,
funds: []
}
}
}
}
]
const proposal_msg = {
"submit_proposal": {
"title": "testing",
"description": "testing",
"link": null,
"messages": proposal_msgs
}
}
const proposal_binary = toEncodedBinary(proposal_msg)const msg = {
"send": {
"contract": assembly_address,
"amount": "30000000000",
"msg": proposal_binary
}
}

1. Generator Msg

The Generator contract allocates token rewards (ASTRO) for various LP tokens and distributes them pro-rata to LP stakers. The setup_pools endpoint within the Generator contract creates a new list of pools with allocation points and updates the contract’s config. “Pools” is a vector that contains LP token addresses and allocation points.

Lastly, we use our toEncodedBinary function to encode and nest our message.

const generator_msg = {
"setup_pools": {
"pools": [
("lp_token_address", "1000"),
("lp_addr2", "10"),
... etc
]
}
}
const generator_binary = toEncodedBinary(generator_msg)

There are two common mistakes to look for when crafting a Generator Msg:

  1. The message expects LP token addresses, not pair addresses.
  2. The message expects a vector of LP contract addresses. Specifying a single address rewrites all active pools with the pools specified in the message. You can query the generator contract to include previous LP token addresses and allocation points.
let config = await terra.wasm.contractQuery(terra, network.generatorAddress, {config: {}})config.active_pools.push(("new_lp_token", "new_alloc_points"))const generator_msg = {
"setup_pools": {
"pools":
("lp_token_address", "1000"),
config.active_pools
}
}

Note: In some cases, the community may want to decrease allocation points for particular pools, thus proposal submitters have to mutate config.active_pools list.

2. Proposal Msgs

Our proposal_msgs variable contains a proposal message array with our generator_address and generator_binary. This message does not need to be encoded and will be nested in a further message as is.

Note: This array will serve as the input to the messages field in our proposal message. This also means if you are submitting your proposal through the Astroport web app, you can stop here. Simply use the proposal_msgs array below to fill out the messages field when submitting your proposal using the Astroport governance UI.

const proposal_msgs = [
{
order: "1",
msg: {
wasm: {
execute: {
contract_addr: generator_address,
msg: generator_binary,
funds: []
}
}
}
}
]

3. Proposal Msg

The proposal_message object contains general information regarding our proposal, such as our proposal’s title, description, link, and the proposal_msgs array we defined above. If this proposal message seems familiar to you, it’s because it probably is! These fields are used in the Astroport web app to submit a proposal using a user-friendly UI.

We will also need to encode this message.

const proposal_msg = {
"submit_proposal": {
"title": "testing",
"description": "testing",
"link": null,
"messages": proposal_msgs
}
}
const proposal_binary = toEncodedBinary(proposal_msg)

4. Final Msg

Lastly, our final message will call the send function in the xASTRO contract. This message requires the address of the contract that we are sending xASTRO to (assembly_address), the amount of ASTRO to send (30,000 ASTRO required for a proposal), and a msg field with our proposal binary from above.

No need to encode this message.

const msg = {
"send": {
"contract": assembly_address,
"amount": "30000000000",
"msg": proposal_binary
}
}

Execute Proposal

Now that we have our proposal messages, we can finally execute our proposal! We use MsgExecuteContract and pass in our wallet, the contract address we are sending our message to (xASTRO address), and our msg variable. We store this in an execute variable that will be use when we broadcast our transaction.

// execute proposalconst execute = new MsgExecuteContract(
wallet.key.accAddress,
xastro_address,
msg,
);

Broadcast Transaction

Lastly, we sign and broadcast our transaction using our wallet and pass in the execute variable we created above.

// broadcast transaction
const executeTx = await wallet.createAndSignTx({
msgs: [execute]
})
.then(tx => terra.tx.broadcast(tx))
.then((result) => console.log(result.txhash));
}main().catch(console.error)

That’s it! Simply use the command line and node.js to execute the script and retrieve the transaction hash along with other information such as the proposal id.

Stay tuned for further tutorials and docs updates regarding executable messages and proposal types.

✦‍ Follow Astroport on Twitter and subscribe to the Astroport email newsletter to get the latest alerts from the mothership.‍

DISCLAIMER

Remember, Terra 2.0 and Astroport are experimental technologies. This article does not constitute investment advice and is subject to and limited by the Astroport disclaimers, which you should review before interacting with the protocol.

--

--

Astroport
Astroport

Written by Astroport

An automated, decentralised exchange protocol on the Terra blockchain. http://astroport.fi

No responses yet