> For the complete documentation index, see [llms.txt](https://docs.kton.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.kton.io/protocol-internals/12-contract-reference.md).

# 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**
