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).
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
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
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
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
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
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:
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()callsupdate_round()first, so it returns state as if the current round were settled to "now".get_pool_full_data_raw()skipsupdate_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
get_pool_full_data_raw stack orderThe 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).
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
PoolConstants.ts / fees.tsThe 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