Andromeda
Search…
Crowdfunding App
This section will teach you how to deploy and interact with an App on the JUNO test-net.
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.
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 steps of deploying an app.

We already have a deployed the Factory 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 upload it and instantiate it.

Deploying the Contracts

For this example we will be deploying our contracts on the JUNO testnet uni-3. If you are unfamiliar with the JUNO or how to deploy contracts on JUNO make sure to check out their documentation found here which will teach you how to connect to the testnet and interact with contracts.

Defining our App

For this example we will be building a simple 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 crowdfunds does not reach the threshold of minimum tokens sold, the money is refunded to the buyers.

Instantiating the App

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.
The Base64 messages may be different for you depending on the JSON formatting. As long as the general format is correct, no error will occur.

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 Vault

{
"operators":[]
}
As Base64:
eyJvcGVyYXRvcnMiOltdfQ==

Yield Vault

{
"operators":[]
}
As Base64:
eyJvcGVyYXRvcnMiOltdfQ==

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":"eyJkZXBvc2l0Ijp7InJlY2lwaWVudCI6eyJhZGRyIjoianVubzE5N3k0NHc1OGRqcXVxa3B1OHY1NnBnd3ZtMDU4ZjZqc3dtMGplYyJ9fX0="
}
}
},
{
"percent":"0.1",
"recipient":{
"a_d_o":{
"address":{
"identifier":"reserve_vault"
},
"msg":"eyJkZXBvc2l0Ijp7InJlY2lwaWVudCI6eyJhZGRyIjoianVubzE5N3k0NHc1OGRqcXVxa3B1OHY1NnBnd3ZtMDU4ZjZqc3dtMGplYyJ9fX0="}
}
}
]
}
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":"juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec"
}
}
}
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.
Splitter Instantiation as Base64:
eyJyZWNpcGllbnRzIjpbeyJwZXJjZW50IjoiMC45IiwicmVjaXBpZW50Ijp7ImFfZF9vIjp7ImFkZHJlc3MiOnsiaWRlbnRpZmllciI6InlpZWxkX3ZhdWx0In0sIm1zZyI6ImV5SmtaWEJ2YzJsMElqcDdJbkpsWTJsd2FXVnVkQ0k2ZXlKaFpHUnlJam9pYW5WdWJ6RTVOM2swTkhjMU9HUnFjWFZ4YTNCMU9IWTFObkJuZDNadE1EVTRaalpxYzNkdE1HcGxZeUo5ZlgwPSJ9fX0seyJwZXJjZW50IjoiMC4xIiwicmVjaXBpZW50Ijp7ImFfZF9vIjp7ImFkZHJlc3MiOnsiaWRlbnRpZmllciI6InJlc2VydmVfdmF1bHQifSwibXNnIjoiZXlKa1pYQnZjMmwwSWpwN0luSmxZMmx3YVdWdWRDSTZleUpoWkdSeUlqb2lhblZ1YnpFNU4zazBOSGMxT0dScWNYVnhhM0IxT0hZMU5uQm5kM1p0TURVNFpqWnFjM2R0TUdwbFl5SjlmWDA9In19fV19
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

{
"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": "eyJvcGVyYXRvcnMiOltdfQ=="
},
{
"name": "reserve_vault",
"ado_type": "vault",
"instantiate_msg": "eyJvcGVyYXRvcnMiOltdfQ=="
},
{
"name": "sale_splitter",
"ado_type": "splitter",
"instantiate_msg": "eyJyZWNpcGllbnRzIjpbeyJwZXJjZW50IjoiMC45IiwicmVjaXBpZW50Ijp7ImFfZF9vIjp7ImFkZHJlc3MiOnsiaWRlbnRpZmllciI6InlpZWxkX3ZhdWx0In0sIm1zZyI6ImV5SmtaWEJ2YzJsMElqcDdJbkpsWTJsd2FXVnVkQ0k2ZXlKaFpHUnlJam9pYW5WdWJ6RTVOM2swTkhjMU9HUnFjWFZ4YTNCMU9IWTFObkJuZDNadE1EVTRaalpxYzNkdE1HcGxZeUo5ZlgwPSJ9fX0seyJwZXJjZW50IjoiMC4xIiwicmVjaXBpZW50Ijp7ImFfZF9vIjp7ImFkZHJlc3MiOnsiaWRlbnRpZmllciI6InJlc2VydmVfdmF1bHQifSwibXNnIjoiZXlKa1pYQnZjMmwwSWpwN0luSmxZMmx3YVdWdWRDSTZleUpoWkdSeUlqb2lhblZ1YnpFNU4zazBOSGMxT0dScWNYVnhhM0IxT0hZMU5uQm5kM1p0TURVNFpqWnFjM2R0TUdwbFl5SjlmWDA9In19fV19"
}
],
"operators": [],
"primitive_contract": "juno144r95jrnk5erqhpuepe5mwz33e5mns7yuyhmxw7fmfx8w0duqwws9qd36x"
}
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.
First we need to upload our app contract to the JUNO testnet:
In order to upload the contract to the testnet, you need to compile the contracts using the optimizer to get the wasm file.
When the wasm file for the app contract is ready we can run:
junod tx wasm store andromeda_app_contract.wasm --from juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec --chain-id=uni-3 --gas-prices 0.1ujunox --gas auto --gas-adjustment 1.3 -b block -y
Now that we have our App contract uploaded, it is time to instantiate it:
junod tx wasm instantiate 2481 '{"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": "eyJvcGVyYXRvcnMiOltdfQ=="},{"name": "reserve_vault","ado_type": "vault","instantiate_msg": "eyJvcGVyYXRvcnMiOltdfQ=="},{"name": "sale_splitter","ado_type": "splitter","instantiate_msg": "eyJyZWNpcGllbnRzIjpbeyJwZXJjZW50IjoiMC45IiwicmVjaXBpZW50Ijp7ImFfZF9vIjp7ImFkZHJlc3MiOnsiaWRlbnRpZmllciI6InlpZWxkX3ZhdWx0In0sIm1zZyI6ImV5SmtaWEJ2YzJsMElqcDdJbkpsWTJsd2FXVnVkQ0k2ZXlKaFpHUnlJam9pYW5WdWJ6RTVOM2swTkhjMU9HUnFjWFZ4YTNCMU9IWTFObkJuZDNadE1EVTRaalpxYzNkdE1HcGxZeUo5ZlgwPSJ9fX0seyJwZXJjZW50IjoiMC4xIiwicmVjaXBpZW50Ijp7ImFfZF9vIjp7ImFkZHJlc3MiOnsiaWRlbnRpZmllciI6InJlc2VydmVfdmF1bHQifSwibXNnIjoiZXlKa1pYQnZjMmwwSWpwN0luSmxZMmx3YVdWdWRDSTZleUpoWkdSeUlqb2lhblZ1YnpFNU4zazBOSGMxT0dScWNYVnhhM0IxT0hZMU5uQm5kM1p0TURVNFpqWnFjM2R0TUdwbFl5SjlmWDA9In19fV19"}],"operators": [],"primitive_contract": "juno144r95jrnk5erqhpuepe5mwz33e5mns7yuyhmxw7fmfx8w0duqwws9qd36x"}' --label "App" --from juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec --chain-id uni-3 --gas-prices 0.1ujunox --gas auto --gas-adjustment 1.3 -b block -y --no-admin
Note that the instantiation message will not be the exact same for you since you would be using a different address.
Upon instantiation, we will get a txhash.
We can search for the transaction in the JUNO Testnet explorer to get the transaction details. This transaction can be found here.
As you can see from the explorer, the instantiation was a success. Six contracts were instantiated in total which are the App and the 5 components we used.

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 the execute:
junod tx wasm execute juno13nasvrktd9zmfmscaaxjq9ukrdgvzj0026tz7s30z4tdml2ktumqzzzu8c '{"claim_ownership":{}}' --from juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec --gas-prices 0.1ujunox --gas auto --gas-adjustment 1.3 -b block -y
Again using the txhash along with the testnet explorer we can find the transaction details here.

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:
junod tx wasm execute juno1y9zujn05f0cwtdr6tuqzx0xrhdr7j6ncqlqexkhn2s73v09sazeq20278r '{"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"}}]}' --from juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec --gas-prices 0.1ujunox --gas auto --gas-adjustment 1.3 -b block -y
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": 1119000
},
"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":{}}
Let us run the execute:
junod tx wasm execute juno1y9zujn05f0cwtdr6tuqzx0xrhdr7j6ncqlqexkhn2s73v09sazeq20278r '{"start_sale": {"expiration": {"at_height": 1179000},"price": {"amount": "1000","denom": "ujunox"},"min_tokens_sold": "0","recipient": {"a_d_o": {"address": {"identifier": "sale_splitter"},"msg": "eyJzZW5kIjp7fX0="}}}}' --from juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec --gas-prices 0.1ujunox --gas auto --gas-adjustment 1.3 -b block -y
This message commences a sale, listing each token for 1000ujunox with a minimum of 0 and an endpoint at block height 1179000. 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
}
}
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.
junod tx wasm execute juno1vh955hze2y3trx5f7kzak4g2avwvnq4s2v8az97txtlqvn5qjaeqqlsk29 '{"end_sale":{}}' --from juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec --gas-prices 0.1ujunox --gas auto --gas-adjustment 1.3 -b block -y
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":"juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec"
}
}
For the yield vault:
junod query wasm contract-state smart juno1jfw27n0m0nfz4wrz9t2plertrd8xp7eh9ryhtljxzlzevt83fxaq9jrdqr '{"balance":{"address":"juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec"}}'
Result:
data:
  • amount: "2700"
  • denom: ujunox
For the reserve vault:
junod query wasm contract-state smart juno12dv3wfrypg82mc0xh8ms52jpx0jx24yk5p0pp0jz6k0cp9yjjjuq4degc8 '{"balance":{"address":"juno197y44w58djquqkpu8v56pgwvm058f6jswm0jec"}}'
Result:
data:
  • amount: "300"
  • denom: ujunox
The results check out.
Conclusion
This example serves as a small tutorial for building an Andromeda app. 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.
Copy link
Outline
The steps of deploying an app.
Deploying the Contracts
Defining our App
Instantiating the App
Interacting with the App