For the complete documentation index, see llms.txt. This page is also available as Markdown.

Contract Reference

This chapter is the authoritative low-level reference for the KTON pool contract: the on-chain constants baked into the FunC bytecode, the message op-code list, the get-method interface (including the exact get_pool_full_data_raw stack order), and a warning about stale TypeScript defaults in the repository.

Everything here is taken from the contract source in the liquid-staking-contract repo (contracts/pool.func, contracts/op-codes.func, contracts/types.func) and cross-checked against live on-chain state. Where the repository's TypeScript constants disagree with the deployed bytecode, the FunC source and the on-chain reads win. Always verify against the live pool before relying on any number.

Public KTON pool: EQA9HwEZD_tONfVz6lJS0PVKR5viEiEGyj9AuQewGQVnXPg0 KTON jetton master: EQBuIhXNNkWf9AW9miNGNTSO_uFZ23ejfIWrieXge5f733mw (9 decimals)

Pool constants (FunC, authoritative for the bytecode)

These are compile-time constants in contracts/pool.func and contracts/types.func. They are part of the deployed code and cannot drift; the only way to change them is a code upgrade. ONE_TON = 1_000_000_000 nanograms (9 decimals).

Constant
Value
Source line
Meaning

SHARE_BASIS

256 * 256 * 256 = 16,777,216 (2^24)

types.func

Fixed-point divisor for interest_rate, governance_fee_share, and instant_withdrawal_fee. A raw value r means r / 2^24.

DEPOSIT_FEE

ONE_TON = 1 Gram

pool.func

Reserved from a deposit's msg_value to fund processing (mint to the depositor, notifications); the remainder is the staked amount. It is forwarded through the mint with the depositor as response address, so the unused part is refunded. Not retained by the protocol.

WITHDRAWAL_FEE

ONE_TON / 2 = 0.5 Gram

pool.func

Minimum message value a withdrawal request must carry (msg_value > WITHDRAWAL_FEE). Not a flat deduction.

FINALIZE_ROUND_FEE

ONE_TON = 1 Gram

pool.func

Subtracted from each round's profit before the fee and rate are computed. If profit is below 1 Gram, the shortfall is socialized over holders.

MIN_TONS_FOR_STORAGE

10 * ONE_TON = 10 Gram

pool.func

Reserve the pool always keeps for its own storage/operation. Liquidity available for instant withdrawals and distributions is computed net of this.

SERVICE_NOTIFICATION_AMOUNT

2 * ONE_TON / 100 = 0.02 Gram

pool.func

Value attached to the interest-manager notification. A governance fee smaller than this is accrued but not sent (silently absorbed into accounting).

TRANSFER_NOTIFICATION_AMOUNT

ONE_TON / 10 = 0.1 Gram

pool.func

Value attached when starting a distribution to a payout collection.

PAYOUT_DISTRIBUTION_AMOUNT

ONE_TON / 5 = 0.2 Gram

pool.func

Gas budget sent to kick off a payout distribution (excess is returned).

MINTER_DEPLOY_FEE

ONE_TON / 10 = 0.1 Gram

pool.func

Budget to deploy a payout minter.

MAX_POOL_GAS_ON_USER_ACTION

ONE_TON / 3

pool.func

Cap on gas spent processing a single user action.

GOVERNOR_QUARANTINE

86,400 s (1 day)

pool.func

Timelock before a prepared governance migration can execute.

SUDOER_QUARANTINE

2 * 24 * 3600 = 172,800 s (2 days)

sudoer_requests.func

Timelock before a newly set sudoer can act; re-setting the sudoer restarts the clock.

DISBALANCE_TOLERANCE_BASIS

2 << 8 = 512

pool.func

Denominator for the per-validator disbalance cap. Base draw is 256/512 (50%); disbalance_tolerance is added to the numerator.

MAX_LOAN_DICT_DEPTH

12

pool.func

Maximum depth of the borrowers dictionary.

state::NORMAL

0

pool.func

Pool operating state. REPAYMENT_ONLY = 1.

The controller contract (contracts/controller.func) defines its own, separately-named constants that should not be confused with the pool's: controller MIN_TONS_FOR_STORAGE = 2 Gram, controller DEPOSIT_FEE = WITHDRAWAL_FEE = 0.25 Gram, MIN_STAKE_TO_SEND = 50,000 Gram (a validator-stake floor for the Elector, not a user minimum), ELECTOR_OPERATION_VALUE ≈ 1.03 Gram, GRACE_PERIOD = 600 s, HASH_UPDATE_FINE = STAKE_RECOVER_FINE = 10 Gram. When you see "0.25" or "50000" in tooling, check whether it is a controller value being mislabeled as a pool value.

Op-code list

Message bodies begin with a 32-bit op-code (uint32). Below are the op-codes from contracts/op-codes.func, grouped by the contract that handles them. These are the values that matter for building or decoding messages.

Pool operations

Op
Code
Direction
Notes

pool::deposit

0x47d54391

in

Deposit Gram, receive KTON. update_round() runs first; DEPOSIT_FEE is reserved to fund processing (unused part refunded); the remainder must be > 0.

pool::withdraw

0x319b0cdc

in

Withdrawal handler. Identical to jetton::burn_notification on this contract, so a KTON burn lands directly here.

pool::withdrawal

0x0a77535c

out

Pool-side withdrawal-payout message.

pool::touch

0x4bc7c2df

in

Permissionless poke that runs update_round() / round bookkeeping.

pool::request_loan

0xe642c965

in

Loan request forwarded by a controller.

pool::loan_repayment

0xdfdca27b

in

Loan settlement from a controller.

pool::deploy_controller

0xb27edcad

in

Deploy a validator controller.

pool::get_conversion_rate_unsafe

0x4b7b42e6

in

Returns the projected rate; "unsafe" because state can change before the reply arrives.

pool::take_conversion_rate_unsafe

0x42e64b7b

out

Reply carrying the projected rate.

Governance, sudoer, halter, interest manager, treasury

Op
Code
Role

governor::set_governance_fee

0x2aaa96a0

Governor sets governance_fee_share (raw, /2^24).

governor::set_deposit_settings

0x9bf5561c

Governor sets optimistic flag / deposits_open / instant_withdrawal_fee.

governor::set_roles

0x5e517f36

Governor updates role addresses.

governor::set_sudoer

0x79e7c016

Governor sets the sudoer (starts the 2-day quarantine).

governor::unhalt

0x7247e7a5

Governor clears a halt (only the governor can unhalt).

governor::prepare_governance_migration

0x9971881c

Step 1 of governor migration (1-day quarantine).

governor::return_available_funds

0x55c26cd5

Governor returns idle funds.

sudo::send_message

0x270695fb

Sudoer sends an arbitrary message (behind the timelock).

sudo::upgrade

0x96e7f528

Sudoer replaces code + data (behind the timelock).

halter::halt

0x139a1b4e

Full emergency stop.

halter::partial_halt

0x77778888

Disable optimistic mode and/or deposits.

interest_manager::set_interest

0xc9f04485

Set interest_rate.

interest_manager::set_operational_params

0x4485c9f0

Set min/max loan, disbalance tolerance, credit timing.

interest_manager::request_notification

0xb1ebae06

Request round-stats notification.

interest_manager::stats

0xc1344900

Round-stats delivery.

treasury::operation_fee

0x54d37487

Fee payment to treasury.

Jetton (KTON) operations

Op
Code
Notes

jetton::transfer

0x0f8a7ea5

Standard TEP-74 transfer.

jetton::transfer_notification

0x7362d09c

Recipient notification.

jetton::internal_transfer

0x178d4519

Wallet-to-wallet internal step.

jetton::burn

0x595f07bc

Burn KTON (this is how you initiate unstaking).

jetton::burn_notification

0x7bdd97de

Standard burn notification op for jettons.

jetton::excesses

0xd53276db

Gas refund.

jetton::provide_wallet_address

0x2c76b973

Resolve a wallet address.

jetton::take_wallet_address

0xd1735400

Wallet-address reply.

jetton::change_content

0x5773d1f5

Update metadata.

Note on the burn path: in op-codes.func, payout::burn_notification is defined as pool::withdraw (0x319b0cdc), and the KTON minter is configured so the burn notification reaches the pool on that same op-code. The practical consequence is that burning KTON and a payout-NFT burn both arrive at the pool's withdraw handler.

Controller operations

Op
Code
Notes

controller::credit

0x1690c604

Pool credits a controller. Message value = cash lent; body = principal + interest (debt).

controller::recover_stake

0xeb373a05

Pull staked funds back from the Elector.

controller::send_request_loan

0x6335b11a

Validator wallet asks the controller to request a loan.

controller::return_unused_loan

0xed7378a6

Return an unused loan.

controller::new_stake

0x4e73744b

Forward a stake to the Elector (same code as elector::new_stake).

controller::top_up

0xd372158c

Top up controller storage.

controller::update_validator_hash

0xf0fd2250

Refresh saved validator-set hash.

controller::approve

0x7b4b42e6

Approver approves the controller.

controller::disapprove

0xe8a0abfe

Approver revokes approval.

controller::withdraw_validator

0x8efed779

Validator withdrawal.

controller::validator_withdrawal

0x30026327

Out-bound validator withdrawal.

Payout NFT operations

Op
Code
Notes

payout::mint

0x1674b0a0

Mint a payout "bill" item.

payout::init

0xf5aa8943

Initialize a payout item.

payouts::start_distribution

0x1140a64f

Pool starts the round-end distribution cascade.

payout::burn_notification

0x319b0cdc

Aliased to pool::withdraw.

Elector (TON system contract)

elector::new_stake 0x4e73744b, elector::new_stake_ok 0xf374484c, elector::new_stake_error 0xee6f454c, elector::recover_stake 0x47657424, elector::recover_stake_ok 0xf96f7324, elector::recover_stake_error 0xfffffffe. These belong to the native TON Elector, not to KTON.

Selected error codes

From contracts/errors.func. Useful when decoding a reverted transaction:

Code
Name
Meaning

0x9283

wrong_sender

Message sender not authorized for this op.

0x9284

wrong_state

Operation not valid in the current state.

0x9285

halted

Pool (or controller) is halted.

0xa000

sudoer::quarantine

Sudoer acted before its timelock matured.

0xa001 / 0xa003

governor_update_too_soon / governor_update_not_matured

Governor migration timing violations.

0xf200

deposit_amount_too_low

Deposit remainder after DEPOSIT_FEE is not > 0.

0xf201

deposits_are_closed

deposits_open? is false.

0xf300

not_enough_TON_to_process

Withdrawal msg_value did not exceed WITHDRAWAL_FEE, or similar shortfall.

0xf800 / 0xf801

withdrawal_while_credited / incorrect_withdrawal_amount

Withdrawal-path guards.

Get-method interface

The pool exposes several get-methods (see docs/get-method-interface.md for the human-readable summary). The two full-data methods both call the same internal composer:

  • get_pool_full_data() calls update_round() first, so it returns state as if the current round were settled to "now".

  • get_pool_full_data_raw() skips update_round() and returns the stored state verbatim.

Other get-methods: get_controller_address(controller_id, validator), get_controller_address_legacy(controller_id, wc, addr_hash), get_loan(controller_id, validator_address, prev?, update?), get_controller_loan_position(controller_addr_hash, prev?), calculate_loan_amount(min_loan, max_loan, max_interest), and get_conversion_rate_unsafe() (returns the current total_balance/supply ratio used for optimistic withdrawals).

Corrected note on the published interface

The summary in docs/get-method-interface.md predates this V2 deployment and does not match the live stack. In particular the live V2 composer returns interest_rate early (4th item), returns instant_withdrawal_fee before disbalance_tolerance, and includes accrued_governance_fee (the doc omits it). It also lists supply twice. Use the order below, taken directly from compose_pool_full_data_internal in contracts/pool.func, not the order in the prose doc.

get_pool_full_data_raw stack order

The composer (pool.func) returns these values in this exact order. prev_round_borrowers and current_round_borrowers are each a 7-element tuple [borrowers_dict, round_id, active_borrowers, borrowed, expected, returned, profit]; loan_params_per_validator is unpacked into two fields; child_contract_codes is unpacked into three code cells. This is a V2-shape stack (the raw read returns roughly 34 top-level items once the tuples are flattened).

#
Field
Type
Meaning

1

state

uint8

Pool state; 0 = NORMAL, 1 = REPAYMENT_ONLY.

2

halted?

bool

True if operation is stopped.

3

total_balance

coins

Total Gram accounted by the pool (numerator of the rate).

4

interest_rate

int

Per-round loan interest, raw (/2^24).

5

optimistic_deposit_withdrawals

bool

Whether optimistic (instant) mode is on.

6

deposits_open?

bool

Whether deposits are accepted.

7

instant_withdrawal_fee

int

Fee on the instant-withdrawal path, raw (/2^24). On the live public pool this is ~100% (raw 0xffffff), which disables the instant path economically.

8

saved_validator_set_hash

int

Last accounted validator-set hash.

9

prev_round_borrowers

tuple(7)

Previous lending round: [borrowers_dict, round_id, active_borrowers, borrowed, expected, returned, profit].

10

current_round_borrowers

tuple(7)

Current lending round, same shape.

11

min_loan_per_validator

coins

From loan_params_per_validator.

12

max_loan_per_validator

coins

From loan_params_per_validator.

13

governance_fee_share

int

Governance fee on profit, raw (/2^24). Public pool live ≈ 16.00% (raw 2,684,355).

14

accrued_governance_fee

coins

Fees accrued but not yet swept to treasury.

15

disbalance_tolerance

int

Added to the 50% base draw cap (public live: 255).

16

credit_start_prior_elections_end

int

Earliest-credit guard (0 = disabled on live pools).

17

jetton_minter

slice

KTON jetton master address.

18

supply

coins

Issued KTON (denominator of the rate).

19

deposit_payout

slice / null

Current-round deposit payout collection (null if none).

20

requested_for_deposit

coins

Gram queued for end-of-round (pessimistic) deposit.

21

withdrawal_payout

slice / null

Current-round withdrawal payout collection (null if none).

22

requested_for_withdrawal

coins

KTON queued for end-of-round (pessimistic) withdrawal.

23

sudoer

slice / addr_none

Sudoer role (addr_none if unset).

24

sudoer_set_at

int

Timestamp the sudoer was set (quarantine start).

25

governor

slice

Governor role.

26

governor_update_after

int

Earliest time the governor may be updated.

27

interest_manager

slice

Interest-manager role.

28

halter

slice

Halter role.

29

approver

slice

Approver role.

30

controller_code

cell

From child_contract_codes.

31

pool_jetton_wallet_code

cell

KTON wallet code.

32

payout_minter_code

cell

Payout minter code.

33

projected_total_balance

int

Projected end-of-round total_balance (from _get_projected_conversion_ratio).

34

projected_supply

int

Projected end-of-round supply.

The current rate is total_balance / supply (item 3 over item 18). The projected rate, used for optimistic deposits, is projected_total_balance / projected_supply (item 33 over item 34). The projected balance pre-credits the previous round's expected profit minus the governance fee and FINALIZE_ROUND_FEE, which is why optimistic deposits convert at a slightly different rate than the spot total_balance/supply.

Stale TypeScript defaults: do not trust PoolConstants.ts / fees.ts

The repository ships TypeScript helpers (PoolConstants.ts, fees.ts) that are upstream and test-oriented defaults. Several of their values disagree with the deployed bytecode and the live on-chain state. Treat them as scaffolding, never as live parameters.

Known mismatches in PoolConstants.ts (the Conf class):

Conf.* value

Repo default

Reality

governanceFee

155n * 2^8 = 39,680 → ≈ 0.236%

Live public pool governance fee is 16.00% (raw 2,684,355). The repo default is not even close.

minStake

50000 Gram

This is the controller's validator-stake floor for the Elector, not a user minimum deposit. There is no 50,000-Gram user minimum.

depositFee / withdrawlFee

0.25 Gram each

These are controller fees. The pool's DEPOSIT_FEE is 1 Gram and WITHDRAWAL_FEE (minimum request value) is 0.5 Gram. PoolConstants.ts does carry the correct pool deposit fee separately as poolDepositFee = 1.

disbalanceTolerance

30

Stale default; the live public pool is 255.

testInterest

100 << 8

A test value, not the live interest_rate.

fees.ts is a different concern entirely: it is a port of TON's network message/forward-fee math (computeFwdFees, computeMessageForwardFees, getMsgPrices, and so on). It contains no KTON economic parameters and is not a source of fee rates for the protocol.

The values that are trustworthy from the repo are the compile-time FunC constants in contracts/ and the op-codes in op-codes.func, because they are part of the bytecode. For anything tunable by a role (governance_fee_share, interest_rate, instant_withdrawal_fee, disbalance_tolerance, min/max loan, the optimistic and deposits-open flags, role addresses), read it live from get_pool_full_data_raw on the public pool address. The authority order is: on-chain read > FunC source > TypeScript repo constants.

Next: Live On-chain State and Addresses

Last updated