Skip to main content
The TON blockchain stores its configuration parameters on-chain. Changing one requires a majority of validators to vote in favor through the configuration smart contract.

Prerequisites

  • The fift and lite-client binaries from the latest TON release.
  • A masterchain wallet holding at least 4 Toncoin to cover the proposal fee.
The guide assumes familiarity with Fift, the Lite Client, and the structure of TON configuration parameters.

Create a configuration proposal

A configuration proposal carries:
  • The index of the parameter to change.
  • The new value, or Null to delete the parameter.
  • The proposal’s expiration Unix time.
  • A flag marking the proposal as critical.
  • An optional old value hash. When set, the proposal applies only if the cell hash of the current value matches.
Any masterchain wallet can submit a proposal after paying the required fee. Only validators may vote on existing proposals. Proposals come in two kinds: ordinary and critical. A critical proposal can change any parameter, including the ones listed as critical in parameter #10. Submitting a critical proposal costs more and usually requires more votes across more rounds. The thresholds for both kinds are stored in parameter #11. Ordinary proposals are cheaper but cannot touch critical parameters. Creating a proposal starts with a BoC file holding the new value. The exact procedure depends on the target parameter. As a trivial example, parameter -239 containing the UTF-8 string "TEST", namely 0x54455354, is produced in Fift by:
The result is a 21-byte file, config-param-239.boc, holding the serialization. Non-negative parameter indices need more care, since validators reject values that do not parse against the expected TL-B type. For these, prefer crypto/create-state from the build directory over plain fift, and adapt the relevant fragments from gen-zerostate.fif and CreateState.fif. Both files build the zero state, TON’s equivalent of a genesis block. Take parameter #8, which encodes the active global blockchain version and capabilities:
Read the active value with the Lite Client:
lite-client
Expected output:
Enabling bit #3, weight +8, turns on capReportVersion. That capability makes every collator record its supported versions and capabilities in the block header it produces. The new value is therefore version=1, capabilities=14. The serialization fits on one line of Fift:
The resulting config-param8.boc is 30 bytes long. Hand-writing the hex is impractical for richer parameters. Reuse the relevant fragments of gen-zerostate.fif and CreateState.fif:
and
Dropping the trailing 8 config! from config.version! leaves exactly the builder needed for the BoC. Save the following as create-param8.fif:
Run it with create-state, a Fift variant that performs extra blockchain validity checks:
Expected output:
The file is again 30 bytes long and holds the same bits as the hand-written version. With the value file ready, build the proposal by running create-config-proposal.fif from crypto/smartcont:
Expected output:
config-msg-body.boc now holds the internal message body for the configuration smart contract. Send it from a masterchain wallet attached to enough Toncoin to cover the fee. Find the address of the configuration smart contract with getconfig 0:
lite-client
Expected output:
The configuration smart contract’s address is -1:5555…5555. Its proposal_storage_price get-method returns the fee required to submit the proposal:
lite-client
Expected output:
The arguments are: critical? flag = 0, lifetime = 1100000 seconds (about 12.7 days), bits = 104, refs = 0. The bit and reference counts come from the earlier create-config-proposal.fif output. The storage fee is 2.3408 Toncoin. Add at least 1.5 Toncoin for processing and send 4 Toncoin in total; any surplus is refunded. Sign the transfer with wallet.fif, or the appropriate Fift script for the controlling wallet:
Expected output:
Broadcast the external message with the Lite Client:
lite-client
Expected output:
After a short wait, check the wallet for replies from the configuration smart contract, or query the contract’s list_proposals get-method directly:
lite-client
Expected output:
The list holds one pair:
The first element, 6465…6321, is the 256-bit proposal hash that uniquely identifies it. The second is a status tuple. It opens with the expiration Unix time 1586779536 and the criticality flag 0. The proposal payload follows as the triple [8 C{FDCD...} -1]. The fields are the parameter index 8, the new value cell C{FDCD…} shown by a prefix of its hash, and the required old-value hash. The value -1 means the proposal places no constraint on the prior value. The next fields hold the voting state. 1124…2998 identifies the active validator set, () is the list of validator indices that have voted so far, and weight_remaining is 8646…209. A positive weight_remaining means the proposal has not yet reached 3/4 of the validator weight this round; a negative value means it has. The trailing 3 0 0 is rounds_remaining, wins, and losses. rounds_remaining is the number of future validator sets that can still carry the proposal. wins counts rounds whose votes exceeded 3/4 of the validator weight, and losses counts rounds that fell short. Expand the value cell with dumpcell, passing the hash or any unique prefix:
lite-client
Expected output:
The contents match the value placed in the proposal. The Lite Client can also render the cell as a ConfigParam 8 TL-B value:
lite-client
Expected output:
This helps when reviewing proposals submitted by other validators. To inspect a single proposal by its 256-bit hash, call get_proposal with that hash as the only argument:
lite-client
Expected output:
The output matches the list_proposals entry for this proposal, with the leading identifier omitted.

Vote on a configuration proposal

After submission, a proposal must collect votes from more than 75% of the current validator set by stake, possibly across several consecutive rounds with new sets elected in between. This raises the bar so that a configuration change reflects the agreement of multiple validator sets, not only the present one. Only validators listed in parameter #34 may vote, identified there by their permanent public keys. The procedure is:
  • Look up val-idx, the zero-based index of the validator inside parameter #34.
  • Run config-proposal-vote-req.fif from crypto/smartcont, passing val-idx and config-proposal-id:
    Expected output:
  • Sign the request with the validator’s private key by running sign <validator-key-id> 566F744…28A1 in the validator-engine-console connected to that validator.
  • Run config-proposal-vote-signed.fif with the same arguments as config-proposal-vote-req.fif, followed by the base64 validator public key and the base64 signature.
  • The script writes vote-msg-body.boc, the body of an internal message that carries the signed vote.
  • Send vote-msg-body.boc from any masterchain smart contract, typically the validator’s controlling smart contract, attaching a small amount of Toncoin for processing. Around 1.5 Toncoin is enough. This step mirrors the procedure used during validator elections:
    The example above assumes a simple wallet controls the validator. Send the resulting wallet-query.boc from the Lite Client:
    lite-client
  • Track the queries by watching replies from the configuration smart contract to the controlling smart contract, or by reading the proposal with get_proposal:
    lite-client
    Expected output:
    The list of voter indices, (0) here, must include the validator’s index from parameter #34. Each round whose votes exceed 3/4 of the validator weight increments wins, the first zero in 3 0 0. Once wins reaches the threshold in parameter #11, the proposal is accepted and the change takes effect in the next masterchain block. When the validator set rotates, the voter list resets to empty and rounds_remaining, initially 3, drops by one. If it falls below zero, the proposal is discarded. A round that ends without crossing the threshold adds to losses, the second zero in 3 0 0; once losses exceeds the limit in parameter #11, the proposal is rejected.

Automated vote generation

Just as create-election-bid automates election bids, validator-engine and validator-engine-console automate most of the steps above and produce a vote-msg-body.boc ready to send from the controlling wallet. Place config-proposal-vote-req.fif and config-proposal-vote-signed.fif next to validator-elect-req.fif and validator-elect-signed.fif, in the directory where the validator-engine looks for them. Then run, in the validator-engine-console:
validator-engine-console
The command writes vote-msg-body.boc, the internal message body to send to the configuration smart contract.

Upgrade the configuration or elector contract

Upgrading the configuration smart contract or the elector smart contract uses the same proposal mechanism. Wrap the new code as the only reference of a value cell and propose that cell as the new value of parameter -1000 for the configuration contract, or parameter -1001 for the elector contract. Both parameters are critical, so an upgrade demands the same heightened voting threshold as a constitutional change. Stage each upgrade on testnet and discuss it publicly before validator operators cast their votes. An alternative is to point the parameter 0, the configuration contract address, or parameter 1 (elector contract address), to a different and already initialized contract. The replacement configuration contract must hold a valid configuration dictionary in the first reference of its persistent data. Migrating mutable state across contracts is hard, since that state includes the active proposals and the previous and current validator participant lists. Upgrading code in place is therefore preferable to swapping addresses. Two helper scripts build these upgrade proposals. create-config-upgrade-proposal.fif loads a Fift assembler source, auto/config-code.fif by default, which is the FunC compiler output for config-code.fc, and emits a proposal for parameter -1000. create-elector-upgrade-proposal.fif does the same for the elector code, loading auto/elector-code.fif and emitting a proposal for parameter -1001. Publish the modified FunC source alongside the exact FunC compiler version used to build it. Validators and their operators can then reproduce the compiled cell, compare hashes, and audit the changes before voting.