Skip to main content

Proxy Contracts

Overview

Proxies contracts allow stakers to claim third-party token rewards. In this tutorial, we cover integrating a deployed proxy contract by submitting a proposal to the Astral Assembly.

Prerequisites

  • Intro to Dual Liquidity Mining
  • A liquidity mining mechanism to distribute your governance token to LPs in the Astroport pool. For new projects that want their liquidity mining mechanism to be integrated with Astroport, the simplest path is basing your liquidity mining contracts on the Mirror design. Alternatively, Anchor’s liquidity mining mechanism is simpler and should not require big changes to the example generator proxy contract.
  • A deployed generator proxy contract specific to your liquidity mining mechanism and Astroport pool. Astroport provides a Generator Proxy template that you can customize. Astroport also provides a full example proxy contract designed for the Mirror liquidity mining mechanism.

Using the Astroport Web App

Use when submitting a proposal to integrate a proxy contract through the Astroport Web App. This tutorial walks you through the Executable Messages field of the proposal.

Vec<ProposalMessage>

The Executable Messages field in our proposal takes in a vector containing objects of type ProposalMessage.

To integrate proxy contracts, we need to submit two proposal messages. Each ProposalMessage requires the following parameters:

  • order: The order of execution of the message
  • msg: Execution message of type CosmosMsg

_40
[
_40
{
_40
{
_40
"wasm": {
_40
"execute": {
_40
"contract_addr": generatorAddress,
_40
"msg": toBase64(
_40
{
_40
"set_allowed_reward_proxies": {
_40
"proxies": [
_40
"terra...",
_40
"terra..."
_40
]
_40
}
_40
}
_40
),
_40
"funds": []
_40
}
_40
}
_40
}
_40
},
_40
{
_40
{
_40
"wasm": {
_40
"execute": {
_40
"contract_addr": generatorAddress,
_40
"msg": toBase64(
_40
{
_40
"move_to_proxy": {
_40
"lp_token": exLPTokenAddress,
_40
"proxy": exProxyAddress
_40
}
_40
}
_40
),
_40
"funds": []
_40
}
_40
}
_40
}
_40
}
_40
]

wasm

CosmosMsg is an enum that supports various types of messages. To integrate proxy contracts, both of our CosmosMsg must be of type wasm. Furthermore, both of our wasm messages must be of type execute.

In the background, we are creating two wasm contract calls. One to the set_allowed_reward_proxies endpoint and another to the move_to_proxy endpoint (both found in the Generator contract).


_40
[
_40
{
_40
{
_40
"wasm": {
_40
"execute": {
_40
"contract_addr": generatorAddress,
_40
"msg": toBase64(
_40
{
_40
"set_allowed_reward_proxies": {
_40
"proxies": [
_40
"terra...",
_40
"terra..."
_40
]
_40
}
_40
}
_40
),
_40
"funds": []
_40
}
_40
}
_40
}
_40
},
_40
{
_40
{
_40
"wasm": {
_40
"execute": {
_40
"contract_addr": generatorAddress,
_40
"msg": toBase64(
_40
{
_40
"move_to_proxy": {
_40
"lp_token": exLPTokenAddress,
_40
"proxy": exProxyAddress
_40
}
_40
}
_40
),
_40
"funds": []
_40
}
_40
}
_40
}
_40
}
_40
]

execute

The execute operation in both of our messages requires the following parameters:

  • contract_addr: Address of the contract where the execute message is being sent to. For both of our messages, this would be the Generator contract.
  • msg: A binary encoded message containing our contract call.
  • funds: Any funds to send along with our transaction.

_40
[
_40
{
_40
{
_40
"wasm": {
_40
"execute": {
_40
"contract_addr": generatorAddress,
_40
"msg": toBase64(
_40
{
_40
"set_allowed_reward_proxies": {
_40
"proxies": [
_40
"terra...",
_40
"terra..."
_40
]
_40
}
_40
}
_40
),
_40
"funds": []
_40
}
_40
}
_40
}
_40
},
_40
{
_40
{
_40
"wasm": {
_40
"execute": {
_40
"contract_addr": generatorAddress,
_40
"msg": toBase64(
_40
{
_40
"move_to_proxy": {
_40
"lp_token": exLPTokenAddress,
_40
"proxy": exProxyAddress
_40
}
_40
}
_40
),
_40
"funds": []
_40
}
_40
}
_40
}
_40
}
_40
]

msg

The code in this section uses a custom function (toBase64) to display our binary messages - this function needs to be defined elsewhere to be used and is not accessible within the Astroport Web App.

Use for demonstration purposes only. The actual string representation of our messages will be an encoded binary, which will be covered below.


_40
[
_40
{
_40
{
_40
"wasm": {
_40
"execute": {
_40
"contract_addr": generatorAddress,
_40
"msg": toBase64(
_40
{
_40
"set_allowed_reward_proxies": {
_40
"proxies": [
_40
"terra...",
_40
"terra..."
_40
]
_40
}
_40
}
_40
),
_40
"funds": []
_40
}
_40
}
_40
}
_40
},
_40
{
_40
{
_40
"wasm": {
_40
"execute": {
_40
"contract_addr": generatorAddress,
_40
"msg": toBase64(
_40
{
_40
"move_to_proxy": {
_40
"lp_token": exLPTokenAddress,
_40
"proxy": exProxyAddress
_40
}
_40
}
_40
),
_40
"funds": []
_40
}
_40
}
_40
}
_40
}
_40
]

set_allowed_reward_proxies

To integrate proxy contracts, our first proposal msg must point to the set_allowed_reward_proxies endpoint in the Generator contract.

set_allowed_reward_proxies takes in a list of proxy contracts.

NOTE

Specifying a single address rewrites all active proxy contracts with the proxy contracts specified in the message. You can query the Generator contract (config: {}) to include previous proxy contract addresses.


_20
{
_20
{
_20
"wasm": {
_20
"execute": {
_20
"contract_addr": generatorAddress,
_20
"msg": toBase64(
_20
{
_20
"set_allowed_reward_proxies": {
_20
"proxies": [
_20
"terra...",
_20
"terra..."
_20
]
_20
}
_20
}
_20
),
_20
"funds": []
_20
}
_20
}
_20
}
_20
}

move_to_proxy

To integrate proxy contracts, our second proposal msg must point to the move_to_proxy endpoint in the Generator contract.

move_to_proxy takes in the following parameters:

  • lp_token: Address of LP token contract to migrate
  • proxy: Address of the deployed proxy contract

_18
{
_18
{
_18
"wasm": {
_18
"execute": {
_18
"contract_addr": generatorAddress,
_18
"msg": toBase64(
_18
{
_18
"move_to_proxy": {
_18
"lp_token": exLPToken,
_18
"proxy": exProxyAddress
_18
}
_18
}
_18
),
_18
"funds": []
_18
}
_18
}
_18
}
_18
}

Encoding our msgs

Our messages need to be encoded and and passed as inputs for our msg parameter within our each of our execute calls.

Since we are using the Astroport Web App to submit our proposal, we will encode our message manually using an online Base64 encoder.

Make sure to replace the substitute variables with contract addresses before encoding your message.


_40
[
_40
{
_40
{
_40
"wasm": {
_40
"execute": {
_40
"contract_addr": generatorAddress,
_40
"msg": toBase64(
_40
{
_40
"set_allowed_reward_proxies": {
_40
"proxies": [
_40
"terra...",
_40
"terra..."
_40
]
_40
}
_40
}
_40
),
_40
"funds": []
_40
}
_40
}
_40
}
_40
},
_40
{
_40
{
_40
"wasm": {
_40
"execute": {
_40
"contract_addr": generatorAddress,
_40
"msg": toBase64(
_40
{
_40
"move_to_proxy": {
_40
"lp_token": exLPTokenAddress,
_40
"proxy": exProxyAddress
_40
}
_40
}
_40
),
_40
"funds": []
_40
}
_40
}
_40
}
_40
}
_40
]

Final Result

Once we encode our messages, our final Executable Message that we would submit to the Astroport Web App to integrate proxy rewards ends up looking something like the code in this section.

You can see our contract calls to the set_allowed_reward_proxies and move_to_proxy endpoints by decoding our msg using a Base64 decoder.


_24
[
_24
{
_24
{
_24
"wasm": {
_24
"execute": {
_24
"contract_addr": "terra1ksvlfex49desf4c452j6dewdjs6c48nafemetuwjyj6yexd7x3wqvwa7j9",
_24
"msg": "ewogICAgInNldF9hbGxvd2VkX3Jld2FyZF9wcm94aWVzIjogewogICAgICAgICJwcm94aWVzIjogWwogICAgICAgICAgJ3RlcnJhMTRld3ZxMzl2ZzIzajBoY2VzZWN2Nmhremt3a3ZybnV4emQ1c2RkbXJ5OWx4NnFyaGF4Y3FqZHg2ZXInLAogICAgICAgICAgJ3RlcnJhMTV5dXE2NGxwNzRkZjBkNXBkY213emVwODBqMGFhNGhzM2ZrdHF5dXB6NGE4MmF5dmR3MnM0cmR5a3YnLAogICAgICAgICAgJ3RlcnJhMTJqdnptMmN5MzN6c3B2cDhhc243bnM5OGpteWs0ODllczJjeTJqOGs5MjZtcjJuN21ldHFoYTQzMHEnLAogICAgICAgICAgJ3RlcnJhMXlhcTQyM2trbTV1bTgzdnhhYXAyMHJ6a255OWhobGhxYWQ4d3pkYzQzejIwY214bXVranF6NDZlMGYnLAogICAgICAgICAgJ3RlcnJhMXkzcGpuNmcwYXd6cGttZTJuZnA0bnp1NzVhZTZ3dWhkZnp0ZG4ycHFqdTV0bHpoa3BoanE1c3QydHMnCiAgICAgICAgXQogICAgfQp9",
_24
"funds": []
_24
}
_24
}
_24
}
_24
},
_24
{
_24
{
_24
"wasm": {
_24
"execute": {
_24
"contract_addr": "terra1ksvlfex49desf4c452j6dewdjs6c48nafemetuwjyj6yexd7x3wqvwa7j9",
_24
"msg": "ewogICAgIm1vdmVfdG9fcHJveHkiOiB7CiAgICAgICAgImxwX3Rva2VuIjogInRlcnJhMXM0bHMwYW1rNTZ2dmZndjVqdnNkYWN2cjM5cjNhNzZwNDl3Z3BsdjByMjdueHQ3ZzN1Z3Fwd3VsOTciLAogICAgICAgICJwcm94eSI6ICJ0ZXJyYTF5M3BqbjZnMGF3enBrbWUybmZwNG56dTc1YWU2d3VoZGZ6dGRuMnBxanU1dGx6aGtwaGpxNXN0MnRzIgogICAgfSAKfQ==",
_24
"funds": []
_24
}
_24
}
_24
}
_24
}
_24
]

Vec<ProposalMessage>

The Executable Messages field in our proposal takes in a vector containing objects of type ProposalMessage.

To integrate proxy contracts, we need to submit two proposal messages. Each ProposalMessage requires the following parameters:

  • order: The order of execution of the message
  • msg: Execution message of type CosmosMsg

wasm

CosmosMsg is an enum that supports various types of messages. To integrate proxy contracts, both of our CosmosMsg must be of type wasm. Furthermore, both of our wasm messages must be of type execute.

In the background, we are creating two wasm contract calls. One to the set_allowed_reward_proxies endpoint and another to the move_to_proxy endpoint (both found in the Generator contract).

execute

The execute operation in both of our messages requires the following parameters:

  • contract_addr: Address of the contract where the execute message is being sent to. For both of our messages, this would be the Generator contract.
  • msg: A binary encoded message containing our contract call.
  • funds: Any funds to send along with our transaction.

msg

The code in this section uses a custom function (toBase64) to display our binary messages - this function needs to be defined elsewhere to be used and is not accessible within the Astroport Web App.

Use for demonstration purposes only. The actual string representation of our messages will be an encoded binary, which will be covered below.

set_allowed_reward_proxies

To integrate proxy contracts, our first proposal msg must point to the set_allowed_reward_proxies endpoint in the Generator contract.

set_allowed_reward_proxies takes in a list of proxy contracts.

NOTE

Specifying a single address rewrites all active proxy contracts with the proxy contracts specified in the message. You can query the Generator contract (config: {}) to include previous proxy contract addresses.

move_to_proxy

To integrate proxy contracts, our second proposal msg must point to the move_to_proxy endpoint in the Generator contract.

move_to_proxy takes in the following parameters:

  • lp_token: Address of LP token contract to migrate
  • proxy: Address of the deployed proxy contract

Encoding our msgs

Our messages need to be encoded and and passed as inputs for our msg parameter within our each of our execute calls.

Since we are using the Astroport Web App to submit our proposal, we will encode our message manually using an online Base64 encoder.

Make sure to replace the substitute variables with contract addresses before encoding your message.

Final Result

Once we encode our messages, our final Executable Message that we would submit to the Astroport Web App to integrate proxy rewards ends up looking something like the code in this section.

You can see our contract calls to the set_allowed_reward_proxies and move_to_proxy endpoints by decoding our msg using a Base64 decoder.


_40
[
_40
{
_40
{
_40
"wasm": {
_40
"execute": {
_40
"contract_addr": generatorAddress,
_40
"msg": toBase64(
_40
{
_40
"set_allowed_reward_proxies": {
_40
"proxies": [
_40
"terra...",
_40
"terra..."
_40
]
_40
}
_40
}
_40
),
_40
"funds": []
_40
}
_40
}
_40
}
_40
},
_40
{
_40
{
_40
"wasm": {
_40
"execute": {
_40
"contract_addr": generatorAddress,
_40
"msg": toBase64(
_40
{
_40
"move_to_proxy": {
_40
"lp_token": exLPTokenAddress,
_40
"proxy": exProxyAddress
_40
}
_40
}
_40
),
_40
"funds": []
_40
}
_40
}
_40
}
_40
}
_40
]

Submitting a Proposal Directly

An advantage of submitting a proposal directly using, for example, a js script is that you could perform calculations from query results, pass variables, encode your message automatically, and separate multiple proposal messages into their own modular variables. However, some additional steps are required to submit a proposal directly.

The Astroport Web App essentially wraps our executable message (covered above) within a submit_proposal message to the Assembly contract. To submit a proposal ourselves, we will have wrap our executable messages within a submit_proposal message as well.

send

To submit a proposal, you need to execute a contract call pointing to the send endpoint in the xASTRO token contract.

The send operation takes in a:

  • contract - Address where xASTRO tokens are being sent to (Assembly address)
  • amount - Amount to send/stake (30,000 xASTRO for mainnet)
  • msg - Binary encoded messages containing our contract call to submit_proposal

_17
{
_17
"send": {
_17
"contract": assemblyAddress,
_17
"amount": "1000", // testnet deposit amount
_17
"msg": toBase64(
_17
{
_17
"submit_proposal": {
_17
"title": "integrate proxy contract",
_17
"description": "Proposal to integrate 3rd party rewards",
_17
"link": "https://forum.astroport.fi/",
_17
"messages": [allow_proxies_msg, move_to_proxy_msg],
_17
"ibc_channel": "..."
_17
}
_17
}
_17
)
_17
}
_17
}

submit_proposal

Our encoded message performs a contract call to the submit_proposal endpoint in the Assembly contract.

submit_proposal takes in the following parameters:

  • title: Proposal title
  • description: Description for the proposal
  • link: Link to forum discussion
  • messages: Proposal message containing our binary contract call to set_allowed_reward_proxies and move_to_proxy (both found in the Generator contract).
  • ibc_channel: If the proposal should be executed on a remote chain, this field should specify the governance channel.

_17
{
_17
"send": {
_17
"contract": assemblyAddress,
_17
"amount": "1000", // testnet deposit amount
_17
"msg": toBase64(
_17
{
_17
"submit_proposal": {
_17
"title": "integrate proxy contract",
_17
"description": "Proposal to integrate 3rd party rewards",
_17
"link": "https://forum.astroport.fi/",
_17
"messages": [allow_proxies_msg, move_to_proxy_msg],
_17
"ibc_channel": "..."
_17
}
_17
}
_17
)
_17
}
_17
}

allow_proxies_msg

If you're submitting a proposal directly through a script, you can separate each proposal message into an individual variable that gets inputted into the vector of messages in our proposal. Overall, this increases the modularity of our code.

allow_proxies_msg takes in our first proposal message. It contains an order of "1" and points to the set_allowed_reward_proxies endpoint in the Generator contract stored in a binary encoded msg.

set_allowed_reward_proxies takes in a list of proxy contracts.


_17
let allow_proxies_msg = {
_17
{
_17
"wasm": {
_17
"execute": {
_17
"contract_addr": generatorAddress,
_17
"msg": toBase64(
_17
{
_17
"set_allowed_reward_proxies": {
_17
"proxies": config.allowed_reward_proxies
_17
}
_17
}
_17
),
_17
"funds": []
_17
}
_17
}
_17
}
_17
}

Update config

Specifying a single address rewrites all active proxy contracts with the proxy contracts specified in the message. You can query the Generator contract to include previous proxy contract addresses.


_16
let config;
_16
_16
const query = await lcd.wasm.contractQuery(
_16
generatorAddress,
_16
{
_16
"config": {}
_16
}
_16
).then(result => { config = result });
_16
_16
// before
_16
console.log(config.allowed_reward_proxies);
_16
_16
config.allowed_reward_proxies.push(exProxyAddress);
_16
_16
// after
_16
console.log(config.allowed_reward_proxies);

move_to_proxy_msg

move_to_proxy_msg takes in our second proposal message. It contains an order of "2" and points to the move_to_proxy endpoint in the Generator contract stored in a binary encoded msg.

move_to_proxy takes in the following parameters:

  • lp_token: Address of LP token contract to migrate
  • proxy: Address of the deployed proxy contract

_18
let move_to_proxy_msg = {
_18
{
_18
"wasm": {
_18
"execute": {
_18
"contract_addr": generatorAddress,
_18
"msg": toBase64(
_18
{
_18
"move_to_proxy": {
_18
"lp_token": exLPToken,
_18
"proxy": exProxyAddress
_18
}
_18
}
_18
),
_18
"funds": []
_18
}
_18
}
_18
}
_18
}

Encding our msgs

There are several messages within our proposal that need to be encoded (submit_proposal, set_allowed_reward_proxies, and move_to_proxy)

The code in this section uses a custom function (toBase64) to display our binary message - this function needs to be defined elsewhere to be used. The actual string representation of our message would be an encoded binary.


_3
let toBase64 = (obj) => {
_3
return Buffer.from(JSON.stringify(obj)).toString("base64");
_3
};

Complete Example

If you want to submit a proposal on Terra, you can implement this operation using feather.js.


_86
let toBase64 = (obj) => {
_86
return Buffer.from(JSON.stringify(obj)).toString("base64");
_86
};
_86
_86
let config;
_86
_86
const query = await lcd.wasm.contractQuery(
_86
generatorAddress,
_86
{
_86
"config": {}
_86
}
_86
).then(result => { config = result });
_86
_86
config.allowed_reward_proxies.push(exProxyAddress);
_86
_86
let allow_proxies_msg = {
_86
{
_86
"wasm": {
_86
"execute": {
_86
"contract_addr": generatorAddress,
_86
"msg": toBase64(
_86
{
_86
"set_allowed_reward_proxies": {
_86
"proxies": config.allowed_reward_proxies
_86
}
_86
}
_86
),
_86
"funds": []
_86
}
_86
}
_86
}
_86
}
_86
_86
let move_to_proxy_msg = {
_86
{
_86
"wasm": {
_86
"execute": {
_86
"contract_addr": generatorAddress,
_86
"msg": toBase64(
_86
{
_86
"move_to_proxy": {
_86
"lp_token": exLPToken,
_86
"proxy": exProxyAddress
_86
}
_86
}
_86
),
_86
"funds": []
_86
}
_86
}
_86
}
_86
}
_86
_86
const integrateProxyContract = new MsgExecuteContract(
_86
wallet.key.accAddress('terra'),
_86
xastroAddress,
_86
{
_86
"send": {
_86
"contract": assemblyAddress,
_86
"amount": "1000", // testnet deposit amount
_86
"msg": toBase64(
_86
{
_86
"submit_proposal": {
_86
"title": "integrate proxy contract",
_86
"description": "Proposal to integrate 3rd party rewards",
_86
"link": "https://forum.astroport.fi/",
_86
"messages": [allow_proxies_msg, move_to_proxy_msg],
_86
"ibc_channel": "..."
_86
}
_86
}
_86
)
_86
}
_86
}
_86
)
_86
_86
// BROADCAST TRANSACTION
_86
let fee = new Fee(2000000, { uluna: 100000 });
_86
_86
let tx = await wallet.createAndSignTx({
_86
msgs: [integrateProxyContract],
_86
fee,
_86
chainID: 'phoenix-1',
_86
});
_86
_86
let result = await lcd.tx.broadcast(tx, 'phoenix-1');
_86
_86
console.log(result);

send

To submit a proposal, you need to execute a contract call pointing to the send endpoint in the xASTRO token contract.

The send operation takes in a:

  • contract - Address where xASTRO tokens are being sent to (Assembly address)
  • amount - Amount to send/stake (30,000 xASTRO for mainnet)
  • msg - Binary encoded messages containing our contract call to submit_proposal

submit_proposal

Our encoded message performs a contract call to the submit_proposal endpoint in the Assembly contract.

submit_proposal takes in the following parameters:

  • title: Proposal title
  • description: Description for the proposal
  • link: Link to forum discussion
  • messages: Proposal message containing our binary contract call to set_allowed_reward_proxies and move_to_proxy (both found in the Generator contract).
  • ibc_channel: If the proposal should be executed on a remote chain, this field should specify the governance channel.

allow_proxies_msg

If you're submitting a proposal directly through a script, you can separate each proposal message into an individual variable that gets inputted into the vector of messages in our proposal. Overall, this increases the modularity of our code.

allow_proxies_msg takes in our first proposal message. It contains an order of "1" and points to the set_allowed_reward_proxies endpoint in the Generator contract stored in a binary encoded msg.

set_allowed_reward_proxies takes in a list of proxy contracts.

Update config

Specifying a single address rewrites all active proxy contracts with the proxy contracts specified in the message. You can query the Generator contract to include previous proxy contract addresses.

move_to_proxy_msg

move_to_proxy_msg takes in our second proposal message. It contains an order of "2" and points to the move_to_proxy endpoint in the Generator contract stored in a binary encoded msg.

move_to_proxy takes in the following parameters:

  • lp_token: Address of LP token contract to migrate
  • proxy: Address of the deployed proxy contract

Encding our msgs

There are several messages within our proposal that need to be encoded (submit_proposal, set_allowed_reward_proxies, and move_to_proxy)

The code in this section uses a custom function (toBase64) to display our binary message - this function needs to be defined elsewhere to be used. The actual string representation of our message would be an encoded binary.

Complete Example

If you want to submit a proposal on Terra, you can implement this operation using feather.js.


_17
{
_17
"send": {
_17
"contract": assemblyAddress,
_17
"amount": "1000", // testnet deposit amount
_17
"msg": toBase64(
_17
{
_17
"submit_proposal": {
_17
"title": "integrate proxy contract",
_17
"description": "Proposal to integrate 3rd party rewards",
_17
"link": "https://forum.astroport.fi/",
_17
"messages": [allow_proxies_msg, move_to_proxy_msg],
_17
"ibc_channel": "..."
_17
}
_17
}
_17
)
_17
}
_17
}