This guide explains how to deploy smart contracts to TetraChain using Blueprint. We'll use the SimpleCounter contract as a practical example.
Overview
Blueprint provides a streamlined deployment workflow for TON smart contracts. The deployment process involves:
Configuring network settings
Creating a contract and deployment script
Executing the deployment
Verifying the contract on-chain // todo (?)
Step 1: Network Configuration
Create a blueprint.config.ts file in your project root to configure the deployment network:
import{Config}from'@ton/blueprint';exportconstconfig:Config={network:{endpoint:'https://tetra.tonapi.io',// API endpoint URLtype:'mainnet',// Network typeversion:'tonapi',// API provider},};
Step 2: Contract Implementation
Here's the FunC implementation of the SimpleCounter contract:
Step 3: Create Deployment Script
Create a deployment script at scripts/deploySimpleCounter.ts:
Step 4: Execute Deployment
Run the deployment script using Blueprint CLI:
Blueprint will prompt you to:
Select a script: Choose deploySimpleCounter
Choose a wallet: Select your deployment wallet
For all details about Smart Contracts, please use the links below
#include "imports/stdlib.fc";
const op::increase = "op::increase"c; ;; create an opcode from string using the "c" prefix, this results in 0x7e8764ef opcode in this case
;; storage variables
;; id is required to be able to create different instances of counters
;; since addresses in TON depend on the initial state of the contract
global int ctx_id;
global int ctx_counter;
;; load_data populates storage variables using stored data
() load_data() impure {
var ds = get_data().begin_parse();
ctx_id = ds~load_uint(32);
ctx_counter = ds~load_uint(32);
ds.end_parse();
}
;; save_data stores storage variables as a cell into persistent storage
() save_data() impure {
set_data(
begin_cell()
.store_uint(ctx_id, 32)
.store_uint(ctx_counter, 32)
.end_cell()
);
}
;; recv_internal is the main function of the contract and is called when it receives a message from other contracts
() recv_internal(int my_balance, int msg_value, cell in_msg_full, slice in_msg_body) impure {
if (in_msg_body.slice_empty?()) { ;; ignore all empty messages
return ();
}
slice cs = in_msg_full.begin_parse();
int flags = cs~load_uint(4);
if (flags & 1) { ;; ignore all bounced messages
return ();
}
load_data(); ;; here we populate the storage variables
int op = in_msg_body~load_uint(32); ;; by convention, the first 32 bits of incoming message is the op
int query_id = in_msg_body~load_uint(64); ;; also by convention, the next 64 bits contain the "query id", although this is not always the case
if (op == op::increase) {
int increase_by = in_msg_body~load_uint(32);
ctx_counter += increase_by;
save_data();
return ();
}
throw(0xffff); ;; if the message contains an op that is not known to this contract, we throw
}
;; get methods are a means to conveniently read contract data using, for example, HTTP APIs
;; they are marked with method_id
;; note that unlike in many other smart contract VMs, get methods cannot be called by other contracts
int get_counter() method_id {
load_data();
return ctx_counter;
}
int get_id() method_id {
load_data();
return ctx_id;
}