Andromeda
Search
⌃K

Crowdfunding App

Deploying a crowdfunding App using the Andromeda CLI.
We have covered earlier the App contract and its messages. We briefly discussed the steps to deploying an App. In this section we will be providing an example of how to deploy an App using the Andromeda CLI.
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.
The examples use uni-5 testnet. As of now, Juno has upgraded to uni-6. The steps of building the App remain the same.

The steps of deploying an app.

We already have a deployed the ADO DB contract with saved code IDs of the ADOs and a Primitive/Registry contract linked to it. These addresses can be found in Deployed Contracts section. This means that to deploy an App we only need to instantiate it through the CLI.

Defining our App

For this example we will be building a simple crowdfunding app. We will be creating NFTs that are sold in a crowdfund and then the proceeds are distributed between two vaults through a splitter, one as a central vault for the project and a yield vault. If the crowdfund does not reach the threshold of minimum tokens sold, the money is refunded to the buyers.

Defining the Messages

For this example, our App needs to have 5 components/ADOs:
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.
Some of your base64 messages will not be the exact same as the ones in the example as you will be using your own addresses.

Token

Don't forget to set the minter as the crowdfund ADO.
{
"name":"Example App",
"symbol":"SMMT",
"minter":{
"identifier":"crowdfund"
}
}
As Base64:
eyJuYW1lIjoiRXhhbXBsZSBBcHAiLCJzeW1ib2wiOiJTTU1UIiwibWludGVyIjp7ImlkZW50aWZpZXIiOiJjcm93ZGZ1bmQifX0=

Crowdfund

{
"token_address":{
"identifier":"tokens"
},
"can_mint_after_sale":true
}
As Base64:
eyJ0b2tlbl9hZGRyZXNzIjp7ImlkZW50aWZpZXIiOiJ0b2tlbnMifSwiY2FuX21pbnRfYWZ0ZXJfc2FsZSI6dHJ1ZX0=
Reserve and Yield are the assigned names for the vaults. They are two instances of the same ADO which is the Vault ADO.

Reserve Vault

{}
As Base64:
e30=

Yield Vault

{}
As Base64:
e30=

Splitter

We split the funds between the two vaults giving 90% to the yield vault and 10% to the reserve vault.
{
"recipients":[
{
"percent":"0.9",
"recipient":{
"a_d_o":{
"address":{
"identifier":"yield_vault"
},
"msg":"eyJkZXBvc2l0Ijp7InJlY2lwaWVudCI6eyJhZGRyIjoianVubzF6a3B0aHFzejN1ZDk3Zm02cDRreGNyYThhZTk5amd6YXV1Z3llbSJ9fX0="
}
}
},
{
"percent":"0.1",
"recipient":{
"a_d_o":{
"address":{
"identifier":"reserve_vault"
},
"msg":"eyJkZXBvc2l0Ijp7InJlY2lwaWVudCI6eyJhZGRyIjoianVubzF6a3B0aHFzejN1ZDk3Zm02cDRreGNyYThhZTk5amd6YXV1Z3llbSJ9fX0="}
}
}
]
}
As seen above there are 2 messages defined within the instantiation of the splitter. The JSON representation of these messages is the following:
{
"deposit":{
"recipient":{
"addr":"juno1zkpthqsz3ud97fm6p4kxcra8ae99jgzauugyem"
}
}
}
This message executes a deposit whenever the splitter sends funds to the vaults depositing the funds in the vault for the specified recipient. The recipient here is an address that has been created for this example which is the same address as the instantiator/owner. Use one of your addresses as a recipient.
Splitter Instantiation as Base64:
eyJyZWNpcGllbnRzIjpbeyJwZXJjZW50IjoiMC45IiwicmVjaXBpZW50Ijp7ImFfZF9vIjp7ImFkZHJlc3MiOnsiaWRlbnRpZmllciI6InlpZWxkX3ZhdWx0In0sIm1zZyI6ImV5SmtaWEJ2YzJsMElqcDdJbkpsWTJsd2FXVnVkQ0k2ZXlKaFpHUnlJam9pYW5WdWJ6RjZhM0IwYUhGemVqTjFaRGszWm0wMmNEUnJlR055WVRoaFpUazVhbWQ2WVhWMVozbGxiU0o5ZlgwPSJ9fX0seyJwZXJjZW50IjoiMC4xIiwicmVjaXBpZW50Ijp7ImFfZF9vIjp7ImFkZHJlc3MiOnsiaWRlbnRpZmllciI6InJlc2VydmVfdmF1bHQifSwibXNnIjoiZXlKa1pYQnZjMmwwSWpwN0luSmxZMmx3YVdWdWRDSTZleUpoWkdSeUlqb2lhblZ1YnpGNmEzQjBhSEZ6ZWpOMVpEazNabTAyY0RScmVHTnlZVGhoWlRrNWFtZDZZWFYxWjNsbGJTSjlmWDA9In19fV19
Now that we have the instantiation message of all the contracts, and the base64 representation for each, we are ready to deploy our app.

App

The primitive address used here might be outdated in the future. Check our deployed contracts to get the latest registry.
{
"name": "example_app",
"app": [
{
"name": "crowdfund",
"ado_type": "crowdfund",
"instantiate_msg": "eyJ0b2tlbl9hZGRyZXNzIjp7ImlkZW50aWZpZXIiOiJ0b2tlbnMifSwiY2FuX21pbnRfYWZ0ZXJfc2FsZSI6dHJ1ZX0="
},
{
"name": "tokens",
"ado_type": "cw721",
"instantiate_msg": "eyJuYW1lIjoiRXhhbXBsZSBBcHAiLCJzeW1ib2wiOiJTTU1UIiwibWludGVyIjp7ImlkZW50aWZpZXIiOiJjcm93ZGZ1bmQifX0="
},
{
"name": "yield_vault",
"ado_type": "vault",
"instantiate_msg": "e30="
},
{
"name": "reserve_vault",
"ado_type": "vault",
"instantiate_msg": "e30="
},
{
"name": "sale_splitter",
"ado_type": "splitter",
"instantiate_msg": "eyJyZWNpcGllbnRzIjpbeyJwZXJjZW50IjoiMC45IiwicmVjaXBpZW50Ijp7ImFfZF9vIjp7ImFkZHJlc3MiOnsiaWRlbnRpZmllciI6InlpZWxkX3ZhdWx0In0sIm1zZyI6ImV5SmtaWEJ2YzJsMElqcDdJbkpsWTJsd2FXVnVkQ0k2ZXlKaFpHUnlJam9pYW5WdWJ6RjZhM0IwYUhGemVqTjFaRGszWm0wMmNEUnJlR055WVRoaFpUazVhbWQ2WVhWMVozbGxiU0o5ZlgwPSJ9fX0seyJwZXJjZW50IjoiMC4xIiwicmVjaXBpZW50Ijp7ImFfZF9vIjp7ImFkZHJlc3MiOnsiaWRlbnRpZmllciI6InJlc2VydmVfdmF1bHQifSwibXNnIjoiZXlKa1pYQnZjMmwwSWpwN0luSmxZMmx3YVdWdWRDSTZleUpoWkdSeUlqb2lhblZ1YnpGNmEzQjBhSEZ6ZWpOMVpEazNabTAyY0RScmVHTnlZVGhoWlRrNWFtZDZZWFYxWjNsbGJTSjlmWDA9In19fV19"
}
],
"primitive_contract": "juno133fdsnvcah870exzcyxknydswyh778jfhwxzlhhgjuagh4482zpqp856dz"
}
There are several components found here. A simple name for the app, the components of the app itself and a reference to the “registry”. Each component of the app provides two values: a name used for referencing for other components within the app, and the base64 encoded instantiation message.

Instantiating the App

If you do not have the CLI downloaded, go to the Introduction to Apps section to get the latest version.
First, let us open the CLI by running andr in our terminal. We then need to choose the chain we want to deploy on. Run "chain use" in the CLI and select the testnet that you want to deploy on. For this example I will be using the juno testnet uni-5.
If this is the first time using the CLI make sure to run "wallet add <wallet-name>"in order to create a wallet. Then go to that chain's faucet (usually located in their discord) and request tokens.
We have already uploaded the App ADO to the uni-5 testnet. The code I will use is 98. This code Id will most likely be outdated in the future. A simple way to check the latest code id for the App ADO is to query it from the ADODB using the the chain you want to use.
In the CLI, while connected to the chain of choice, run:
ado db getcodeid app
The code Id to use will be returned.
Now we can instantiate our App. We will be using our wasm command to instantiate our app:
wasm instantiate 98 '{"name": "example_app","app": [{"name": "crowdfund","ado_type": "crowdfund","instantiate_msg": "eyJ0b2tlbl9hZGRyZXNzIjp7ImlkZW50aWZpZXIiOiJ0b2tlbnMifSwiY2FuX21pbnRfYWZ0ZXJfc2FsZSI6dHJ1ZX0="},{"name": "tokens","ado_type": "cw721","instantiate_msg": "eyJuYW1lIjoiRXhhbXBsZSBBcHAiLCJzeW1ib2wiOiJTTU1UIiwibWludGVyIjp7ImlkZW50aWZpZXIiOiJjcm93ZGZ1bmQifX0="},{"name": "yield_vault","ado_type": "vault","instantiate_msg": "e30="},{"name": "reserve_vault","ado_type": "vault","instantiate_msg": "e30="},{"name": "sale_splitter","ado_type": "splitter","instantiate_msg": "eyJyZWNpcGllbnRzIjpbeyJwZXJjZW50IjoiMC45IiwicmVjaXBpZW50Ijp7ImFfZF9vIjp7ImFkZHJlc3MiOnsiaWRlbnRpZmllciI6InlpZWxkX3ZhdWx0In0sIm1zZyI6ImV5SmtaWEJ2YzJsMElqcDdJbkpsWTJsd2FXVnVkQ0k2ZXlKaFpHUnlJam9pYW5WdWJ6RjZhM0IwYUhGemVqTjFaRGszWm0wMmNEUnJlR055WVRoaFpUazVhbWQ2WVhWMVozbGxiU0o5ZlgwPSJ9fX0seyJwZXJjZW50IjoiMC4xIiwicmVjaXBpZW50Ijp7ImFfZF9vIjp7ImFkZHJlc3MiOnsiaWRlbnRpZmllciI6InJlc2VydmVfdmF1bHQifSwibXNnIjoiZXlKa1pYQnZjMmwwSWpwN0luSmxZMmx3YVdWdWRDSTZleUpoWkdSeUlqb2lhblZ1YnpGNmEzQjBhSEZ6ZWpOMVpEazNabTAyY0RScmVHTnlZVGhoWlRrNWFtZDZZWFYxWjNsbGJTSjlmWDA9In19fV19"}],"primitive_contract": "juno133fdsnvcah870exzcyxknydswyh778jfhwxzlhhgjuagh4482zpqp856dz"}'
Note that the instantiation message might not be the exact same for you since you might be using a different recipient address. Make sure to make the appropriate changes before running the command.
Upon instantiation, we will get a link to the explorer where we can check the transaction. Also the contract address of the App is displayed:
Open the explorer link where you can see that the instantiation was a success. Six contracts were instantiated in total which are the App and the 5 components we used.
You will be using the contract addresses that were instantiated for you instead of the ones in this tutorial.

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).

ClaimOwnership

To claim ownership of all the components, we keep the optional component field empty.
{
"claim_ownership":{}
}
Let us run this execute in the CLI:
wasm execute juno16k0hp6eetdrus3lga005ug0vpt4cz88nwjpmfr30szfs578p46nq90j5eh '{"claim_ownership":{}}'

Crowdfund

Now that the App has been created and we have ownership over all the components, we can proceed with minting the tokens for the crowdfund.

Mint

We will be minting three new tokens using the crowdfund component for this example.
Here we use a random image url.
{
"mint":[
{
"token_id":"1",
"extension":{
"name":"Token 1",
"publisher":"juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec",
"attributes":[],
"archived": false,
"image":"https://pic.onlinewebfonts.com/svg/img_522399.png"
}
},
{
"token_id":"2",
"extension":{
"name":"Token 2",
"publisher":"juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec",
"attributes":[],
"archived": false,
"image":"https://pic.onlinewebfonts.com/svg/img_522399.png"
}
},
{
"token_id":"3",
"extension":{
"name":"Token 3",
"publisher":"juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec",
"attributes":[],
"archived": false,
"image":"https://pic.onlinewebfonts.com/svg/img_522399.png"
}
}
]
}
Let us run the execute:
The contract address to execute on will be different in your build. Make sure to get the correct address for the crowdfund from the explorer.
wasm execute juno13u5rdsafnr6z9sj0xdkyspg99k2vvu7afj275k4zpr4huh0yqqaq2wegdu '{"mint":[{"token_id":"1","extension":{"name":"Token 1","publisher":"juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec","attributes":[],"image":"https://pic.onlinewebfonts.com/svg/img_522399.png"}},{"token_id":"2","extension":{"name":"Token 2","publisher":"juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec","attributes":[],"image":"https://pic.onlinewebfonts.com/svg/img_522399.png"}},{"token_id":"3","extension":{"name":"Token 3","publisher":"juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec","attributes":[],"image":"https://pic.onlinewebfonts.com/svg/img_522399.png"}}]}'
In this message multiple tokens are minted and their mint messages are proxied to the “tokens” contract found within the app. No owner is provided, although one can be declared, so the crowdfund contract is assigned as the owner of the tokens for the duration of the sale.
Since we have minted our tokens, we can now start the crowdfund sale.

Start Sale

"start_sale": {
"expiration": {
"at_height": 80000
},
"price": {
"amount": "1000",
"denom": "ujunox"
},
"min_tokens_sold": "0",
"recipient": {
"a_d_o": {
"address": {
"identifier": "sale_splitter"
},
"msg": "eyJzZW5kIjp7fX0="
}
}
}
With "msg" being base64 encoded message:
{"send":{}}
The block height in this example will be in the past for you, so make sure to replace it with a valid block height.
Let us run the execute:
wasm execute juno13u5rdsafnr6z9sj0xdkyspg99k2vvu7afj275k4zpr4huh0yqqaq2wegdu '{"start_sale": {"expiration": {"at_height": 80000},"price": {"amount": "1000","denom": "ujunox"},"min_tokens_sold": "0","recipient": {"a_d_o": {"address": {"identifier": "sale_splitter"},"msg": "eyJzZW5kIjp7fX0="}}}}'
This message commences a sale, listing each token for 1000ujunox with a minimum of 0 and expires at height 80000. The recipient defined in this message will be the receiver of the funds after the sale has ended. In this case the recipient is another ADO within the app, the splitter. The recipient is defined by the name provided in the app and an optional message to be attached to the funds. We can define the message as the “send” message for the splitter, which in turn divides the funds to the two vaults declared above.
The sale is up, time to purchase our minted tokens.

Purchase the tokens

{
"purchase":{
"num_tokens":1
}
}
wasm execute juno13u5rdsafnr6z9sj0xdkyspg99k2vvu7afj275k4zpr4huh0yqqaq2wegdu {"purchase":{"num_tokens":1}}
For purchasing the tokens I will be using three addresses representing buyers.

EndSale

Now that the tokens have been sold the sale can be ended:
{
"end_sale":{}
}
Two messages should be sent due to pagination concerns. One will distribute the tokens to the purchasers and the other sends the funds.
wasm execute juno13u5rdsafnr6z9sj0xdkyspg99k2vvu7afj275k4zpr4huh0yqqaq2wegdu '{"end_sale":{}}'
The three receiving addresses in the second transaction are: the splitter and the two vaults.
We have successfully:
  1. 1.
    Minted tokens for the crowdfund
  2. 2.
    Started a sale to sell the tokens
  3. 3.
    Purchased the tokens for 1000 ujunox each
  4. 4.
    Ended the sale
So looking back at our splitter we had divided our funds between two vault:
  • 90% for the yield vault
  • 10% for the reserve vault
Since each of the tokens was sold for 1000 ujunox, we should have 2700 ujunox in the yield vault and 300 ujunox in the reserve vault for our initial address. To make sure of the results let us use some queries.

Querying the vault balances

{
"balance":{
"address":"juno1zkpthqsz3ud97fm6p4kxcra8ae99jgzauugyem"
}
}
For the yield vault:
wasm query juno1ju7fn05hscckhtd5q9dutdgpp09s9403le080agnm6m8e6uaywcsrurk08 '{"balance":{"address":"juno1zkpthqsz3ud97fm6p4kxcra8ae99jgzauugyem"}}'
Result:
data:
  • amount: "2700"
  • denom: ujunox
For the reserve vault:
wasm query juno1mcmld8th8yyjf2l8aj3hpzzjyuq73fseakp5n8jhwkfkcdgnyutsqul7uj '{"balance":{"address":"juno1zkpthqsz3ud97fm6p4kxcra8ae99jgzauugyem"}}'
Result:
data:
  • amount: "300"
  • denom: ujunox
The results check out.
Conclusion
This example serves as a small tutorial for building an Andromeda app using the CLI. We will have many more examples like this coming soon representing the different apps that we can build using our ADOs. These tutorials are mostly made for developers looking to use and build using our ADOs. All what we did here and more can be done using our user friendly web-app with a few clicks. More on that will be found in the user docs section.