Building with Internet Computer (ICP)
Step-by-Step Guide to Building an Application for Calimero with Internet Computer (ICP)
This tutorial will cover the following topics:
-
Starting a Local Devnet - Setting and configuring a local devnet using dfx through a script.
-
Configuring Nodes - Setting and configuring nodes, installing demo application, creating context and inviting nodes into the context.
-
Installing a Blockchain Demo Application - Setup demo application that contains all functionalities for interaction with smart contracts.
-
Creating a Proposal - Creating a proposal to execute cross-contract call to "Greet" smart contract deployed in step 3.
For easier guidance, we have named each terminal block by their purpose.
Requirements​
To follow this tutorial, you'll need the following:
- Calimero ICP Devnet - Repository
- Calimero Core - Installation instructions - Instructions
- Demo Blockchain Integrations - Repository
You also need following tools:
- Cargo: Version
1.86.0-nightly
used for tutorial - url - DFX (Dfinity SDK): Version
0.24.3
used for tutorial - url - Candid Extractor: Version
0.1.5
used for tutorial - url - Pnpm: Version
9.6.0
used for tutorial - url
Configuring a Local Devnet​
We have prepared a script that will deploy the contracts and create needed accounts on local environment. The script does the following:
- Creates accounts needed for ledger deployment.
- Deploys the context, ledger and example external contract.
- Funds the deployed context contract for initial usage.
- Calls required initial methods on deployed contracts.
Scripts are located in Calimero ICP Devnet repository. First clone the repository.
git clone https://github.com/calimero-network/icp-devnet
cd icp-devnet
chmod +x ./deploy_devnet_addon.sh && ./deploy_devnet_addon.sh
The repository contains two scripts:
- Create new dfx environment
- Install on existing dfx environment
deploy_devnet_fresh.sh
- Starts fresh dfx environment and deploys the contracts and needed accounts.
This script will delete the current dfx environment and create a new one.
deploy_devnet_addon.sh
- Deploys the contracts and creates needed account on already existing dfx environment.
dfx environment needs to be started before running this script.
For this tutorial we will be using deploy_devnet_fresh.sh
script.
./deploy_devnet_fresh.sh
=== Deployment Summary ===
Context Contract ID: bkyz2-fmaaa-aaaaa-qaaaq-cai
Ledger Contract ID: bd3sg-teaaa-aaaaa-qaaba-cai
Demo External Contract ID: be2us-64aaa-aaaaa-qaabq-cai
Account Information:
Minting Account: 8b768d662eeebfcbe55b180a7ac0ccb46e2ccd59cacd0b4ec3404f0c8d8b8086
Initial Account: 670183527b941adeae9e1552525853af7812d9441758c668b2e3b8553dead7a0
Archive Principal: kbwrg-ggsyr-4zl47-3z7by-owf4g-draak-xj2ni-mcvwv-6wqxc-nkjam-gae
Recipient Principal: mfefj-dsyoh-rb3b2-3yagk-rvb2p-wcb2v-fhu2n-fel2f-wqzjn-yhtxx-hqe
Deployment completed successfully!
Leave this terminal open as you will need values in later steps.
Context and Proxy Contract​
Context contract​
Calimero context contract is used to create and manage contexts and their members.
More information about context contract can be found here. To see the context contract implementation, refer to the calimero-network/core repository.
Proxy contract​
Proxy contract is used to handle blockchain operations such as cross-contract calls and trasnfers as well as storing variables.
More information about proxy contract can be found here. To see the proxy contract implementation, refer to the calimero-network/core repository. ## Setting up and configuring nodes
By deafult proposal needs 3 approvals before it is executed so we will setup 3 nodes.
- Initialize and start 3 nodes
- Install blockchain demo application
- Create context for blockchain demo application
- Invite all nodes in the same context
Initialize and Start Nodes​
Open 3 terminals side by side.
merod --node-name node1 init --server-port 2427 --swarm-port 2527
merod --node-name node2 init --server-port 2428 --swarm-port 2528
merod --node-name node3 init --server-port 2429 --swarm-port 2529
The output should look like this:
You can verify nodes initialization files by looking into ~/.calimero
folder.
More on node initialization can be found here.
Now we are going to start the nodes with the commands:
merod --node-name node1 run
merod --node-name node2 run
merod --node-name node3 run
After running the nodes you should see the similar to this one:
Install application​
Inside the node1 terminal we are going to install blockchain demo application.
- Clone the Demo Blockchain Integrations repository.
- Navigate to the
logic
directory. - Build the application using the
build.sh
script, script will compile wasm file in /res directory. - Copy the
blockchain.wasm
file path
Applications are installed with the following command:
application install file <PATH_TO_blockchain.wasm_FILE>
>Application installed <APPLICATION_ID>
After installing the application we can create context:
context create <APPLICATION_ID> --protocol icp
>Created context <CONTEXT_ID> with identity <CONTEXT_IDENTITY>
After creating context we need to add node2 and node3 to it.
To be able to do that we need to create identities for node2 and node3
with whom they will join to the created context.
identity new
>> Private key: <PRIVATE_KEY>
>> Public key: <PUBLIC_KEY>
identity new
>> Private key: <PRIVATE_KEY>
>> Public key: <PUBLIC_KEY>
The output should look like this:
After creating identities we can invite node2 and node3 to join the created context.
context invite <CONTEXT_ID> <CONTEXT_IDENTITY> <PUBLIC_KEY_OF_NODE2>
>> Invitation payload: <INVITATION_PAYLOAD>
This will generate a invitation payload which can node2 use to join the context. Copy the invitation payload and do the following command in node2 terminal:
context join <PRIVATE_KEY_OF_NODE2> <INVITATION_PAYLOAD>
>> Joined context <CONTEXT_ID>
Repeat the same steps for Node3.
context invite <CONTEXT_ID> <CONTEXT_IDENTITY> <PUBLIC_KEY_OF_NODE3>
context join <PRIVATE_KEY_OF_NODE3> <INVITATION_PAYLOAD>
>> Joined context <CONTEXT_ID>
The output in terminal after inviting and joining nodes should look like this:
More on invitations and joining the context can be found here
Lastly we can check if all nodes are connected in context with:
context ls
> <CONTEXT_ID>
identity ls <CONTEXT_ID>
We can see there are 3 nodes connected in context.
Fund Proxy Contract​
We have now installed the application and created context. To be able to fully use proxy contract we need to fund it. To get proxy contract address we need to use API GET call to retrieve it from the node.
The 3 running nodes are located on:
Any of them can be queried to get the value of proxy contract id.
API endpoint to fetch proxy contract id is:
http://localhost:2428/admin-api/contexts/CONTEXT_ID/proxy-contract
CONTEXT_ID can be copied from previous steps
And for our case it is:
http://localhost:2428/admin-api/contexts/7krojDziAKRWP8KrUD8aYGL3z5peScNxPXaBWTKCFr2h/proxy-contract
Example of request in postman:
With proxy contract id we can now fund it using dfx native commands. Reopen the terminal you used to execute devnet deployment script and do the following commands:
Switch to initial identity.
dfx identity use initial
>Using identity: "initial".
To fund the contract we are going to use command:
dfx canister call <LEDGER_CONTRACT_ID> icrc1_transfer "(record { to = record { owner = principal \"<PROXY_CONTRACT_ID> \"; subaccount = null}; amount = 1_000_000_000; fee = null; memo = null; from_subaccount = null; created_at_time = null})"
LEDGER_CONTRACT_ID - this value can be viewed when ./deploy_devnet.sh script finishes.
In our case value its:bd3sg-teaaa-aaaaa-qaaba-cai
PROXY_CONTRACT_ID - value we got in previous step.
In our case value its:b77ix-eeaaa-aaaaa-qaada-cai
dfx canister call bd3sg-teaaa-aaaaa-qaaba-cai icrc1_transfer "(record { to = record { owner = principal \"b77ix-eeaaa-aaaaa-qaada-cai"\"; subaccount = null}; amount = 1_000_000_000; fee = null; memo = null; from_subaccount = null; created_at_time = null})"
> (variant { Ok = 1 : nat })
And verify the contract balance:
dfx canister call bd3sg-teaaa-aaaaa-qaaba-cai icrc1_balance_of "(record { owner = principal \"b77ix-eeaaa-aaaaa-qaada-cai\"; subaccount = null })"
> (1_000_000_000 : nat)
Blockchain Demo Application​
This application is used to demonstrate how to interact with Calimero Proxy contract through creating, approving and executing proposals.
The proxy contract supports the following types of proposals:
- External function call - Enables cross-contract call execution.
- Transfer - Allows transferring funds.
- Set approval threshold - Specifies the number of approvals required for proposal execution.
- Set active proposal limit - Defines the maximum number of active proposals.
- Set context variables - Allows setting key-value pairs for context variables.
While the demo focuses on specific functionality, you can customize the application to suit your needs. Instead of building a complete frontend, this demo serves as a foundation that you can extend to create a fully-featured frontend application tailored to your requirements.
Navigate to frontend directory and install dependencies.
cd demo-blockchain-integrations/app && pnpm install
Since we have 3 nodes we will need to setup frontend for each of them. Open 3 terminals side by side and run the following commands in each of them from /app directory:
pnpm run dev
After running the frontend you should see the following output in each of the terminals:
Open the applications in by clicking on the link in each of the terminals and you will see form with input fields for node URL and application ID.
To get application ID you can use following command from any of node terminals:
application ls
> <APPLICATION_ID>
The node URL of the nodes are respectively:
After submitting the form you will be redirected to admin dashboard where you will see the login page with wallet selector.
Select a wallet you wish to login with and afterwards you will be prompted to select the context and context identity you wish to use.
After selecting both you will be logged in and automatically redirected to the application.
Change Approval Threshold Proposal​
First we are going to create an approval threshold change proposal so future proposals can be executed by only 1 node. As mentioned earlier each proposal by default needs 3 approvals to be executed.
- Click on "Create Proposal" button and select "Change number of approvals needed" from dropdown.
- In the input field set the number of approvals to 1 (this means that proposal will be executed on creation).
- Click on "Create Proposal" button to create the proposal.
After proposal is created you will get the alert that proposal is created and it will be shown in other frontend applications by selecting it in select field.
After selecting proposal on other frontend applications (node2 and node3) you can see that it is created and can be approved by clicking on "Approve Proposal" button.
Cross-Contract Call Proposal​
At this point proposal approval threshold is 1, meaning proposal will be executed on creation. This means when we create cross-contract call proposal it will insantly be executed.
We have created a demo external contract that contains following methods:
- test_method_no_transfer - This method does not require a deposit for execution, it sets sent arguments in its storage.
- test_method - This method requires a deposit for execution, it first transfers the funds to the contract from proxy contract and then sets sent arguments in its storage, if the transfer is completed successfully.
- get_calls - This method returns all calls stored in the contract.
- clear_state - This method clears the storage of the contract.
You can see the contract itself and its implementation here.
When creating cross-contract call proposal we need to provide following values:
- Contract ID: br5f7-7uaaa-aaaaa-qaaca-cai - Address of the external contract - shown in terminal output of dfx deployment script for demo external contract
- Method Name: test_method_no_transfer - Name of the method that will be called
- Deposit: 0 - Deposit for transfer, in this case we are not transferring any funds so we can set it to 0
- Arguments: someKey: someValue - Key and value pairs for arguments that are sent to the external contract
After we create proposal it will be executed, and the success of the execution can be checked by running following command in terminal:
dfx canister call <DEMO_EXTERNAL_CONTRACT_ID> get_calls
It's important to note that in this tutorial, we use a function call that does not require a deposit for execution.
On the other hand, there are examples where cross-contract calls require a deposit. For instance, in the case of an ERC20 token implementation, the following steps typically occur:
- The contract detects that an attached deposit is present.
- It calls the deployed ledger contract to enable the external contract (the contract being called) to withdraw funds. Before doing so, it sets the allowance to 0 to prevent vulnerabilities related to the Attack Vector.
- It then calls the ledger contract again to set the allowance equal to the deposit attached to the call.
- The external contract is called, and it must contain logic to withdraw the funds that have been allocated for it in the ledger contract.
- After the cross-contract call is completed, the proxy contract sets the allowance back to 0 to ensure security.
Conclusion​
This tutorial has covered all the essential steps developers need to build an application for Calimero and ICP, from setting up the environment to deploying a fully functional smart contract. By following this guide, you should now have a solid understanding of the development workflow and how to build your own ICP application for Calimero.
Feel free to use our demo applications as a starting point to build and customize your own application. If you have any questions or need assistance, don't hesitate to reach out to us on Socials. We're here to help!