Router
Description
The Router is the system's central dispatcher and the custodian of all LP jetton wallets. Users reach the protocol by transferring jettons to the router with a payload (see router payloads); the router parses it and routes the funds to the right Pool for a swap or for funding a mint. It also deploys new pools (from the Pool Factory or the admin), pays funds back out through its own jetton wallets (PAY_TO), and supports a multihop shortcut. The router cannot cryptographically verify that a transfer notification came from a real jetton wallet, so it derives the target pool from the sender — a fake sender simply maps to a pool that does not exist. Critical parameters (admin, proxyTON wallet, flags, router/pool code) change only through a two-phase timelock; the admin is expected to be a multisig.
Interface
getRouterState
(address, address, address, address?, int, int, int) getRouterState ()
returns the router state and configuration
Return Values
| # | Type | Description |
|---|---|---|
| 0 | address | router admin address |
| 1 | address | pool admin address (passed to pools on creation; has privileged pool rights) |
| 2 | address | pool factory address (allowed to request pool creation) |
| 3 | address? | proxy TON wallet address (critial to multihop shortcut) |
| 4 | int | router flags |
| 5 | int | router seqno (monotonic counter for indexing) |
| 6 | int | router version (e.g. 16000 = v1.6.000) |
getPoolAddress
(address) getPoolAddress (jettonWallet0: address, jettonWallet1: address)
returns pool address for two given jetton_wallets belonging to the router
Parameters
| # | Type | Name | Description |
|---|---|---|---|
| 0 | address | jettonWallet0 | Address of the jetton 0 wallet belonging to router |
| 1 | address | jettonWallet1 | Address of the jetton 1 wallet belonging to router |
Return Values
| # | Type | Description |
|---|---|---|
| 0 | address | pool address |
getChildContracts
(cell, cell, cell, cell) getChildContracts ()
returns code of the child contracts deprecated
Return Values
| # | Type | Description |
|---|---|---|
| 0 | cell | code of the pool contract |
| 1 | cell | code of the account contract |
| 2 | cell | code of the nft position contract |
| 3 | cell | code of the pool prototype contract |
get_pending_timelocks
( int, int, address?, int, address?, int, int, int, cell?, int, cell?,) get_pending_timelocks ()
returns active router timelocks to monitor system state. Timestamp 0xFFFFFFFFFFFFFFFF means - never/timelock not active
Return Values
| # | Type | Description |
|---|---|---|
| 0 | int | Timelock delay |
| 1 | int | Timestamp when new Admin Address can be committed |
| 2 | address? | New Admin Address |
| 3 | int | Timestamp when new Proxy Ton Address can be committed |
| 4 | address? | New Proxy Ton Address |
| 5 | int | Timestamp when new flags can be committed |
| 6 | int | New Flags |
| 7 | int | Timestamp when new code can be committed |
| 8 | cell? | Cell with new code |
| 9 | int | Timestamp when new pool code can be committed |
| 10 | cell? | Cell with new pool code |
Messages
JETTON_TRANSFER_NOTIFICATION
Opcode : 0x7362d09c
Process router funding, payload determines if it is mint or swap Access Rights: Sent by a jetton wallet when a user transfers tokens to the router. The router cannot verify that the notification truly comes from a real jetton wallet, so it does not authorize the sender directly: the target pool is derived from the sender, and if the sender is not a genuine wallet the derived pool simply does not exist. Funds that fail validation are returned to the user.
| Path | Mnemonic | Type | Description |
|---|---|---|---|
| op | Uint(32),op | ||
| query_id | Uint(64) | queryid as of the TON documentation | |
| jetton_amount | Coins(124) | Amount of coins sent to the router | |
| from_user | Address(267) | User that originated the transfer | |
| forward_payload | Cell(0),Either, Payload | Payload for processing |
TL-B Description
This is a preliminary tl-b - subject to change **Tlb for JETTON_TRANSFER_NOTIFICATION**JETTON_TRANSFER_NOTIFICATION#7362d09c
query_id:uint64
jetton_amount:(VarUInteger 16)
from_user:MsgAddress
forward_payload:(Either ^Cell Cell)
= ContractMessages;
ROUTER_CREATE_POOL
Opcode : 0x2e3034ef
Operation that deploys and inits new Pool contract for two given jettons identified by their wallets. New pool would reorder the jettons to match the invariant slice_hash(jetton0_address) > slice_hash(jetton1_address).
Access Rights:
Allowed for the Pool Factory and the Router Admin (Superadmin). On a repeated CREATE_POOL for an existing pool the router forwards an initialization message, which lets the admin change tick spacing, NFT metadata, the initial price (while no positions exist), the Pool Admin and Controller, and the lock state; a request coming through the Pool Factory cannot assign the ALM Vault or Arbiter roles.
| Path | Mnemonic | Type | Description |
|---|---|---|---|
| op | Uint(32),op | ||
| query_id | Uint(64) | queryid as of the TON documentation | |
| jetton_wallet0 | Address(267) | Address of the jetton0 wallet. Used to compute pool address | |
| jetton_wallet1 | Address(267) | Address of the jetton1 wallet. Used to compute pool address | |
| tick_spacing | Uint(24) | Tick spacing to be used in the pool | |
| initial_priceX96 | Uint(160),PriceX96 | Initial price for the pool | |
| pool_active | Uint(1) | Should pool be created as active | |
| protocol_fee | Uint(16),Fee | Protocol fee in FEE_DENOMINATOR parts. If value is more than 10000 value would be default | |
| lp_fee_base | Uint(16),Fee | Liquidity provider base fee in FEE_DENOMINATOR. If value is more than 10000 value would be default | |
| lp_fee_current | Uint(16),Fee | Current value of the pool fee, in case of dynamic adjustment. If value is more than 10000 value would be default | |
| 0 | Cell(0) roles_cell | Cell With Role | |
| 0 | has_controller | Uint(1),Bool | Flag that shows if this message have a new controller address |
| 0 | controller_addr | Address(267) | Address that is allowed to change the fee. Can always be updated by admin. |
| 0 | has_creator | Uint(1),Bool | Flag that shows if this message have a new creator address |
| 0 | creator_addr | Address(267) | Address that is allowed to change the fee. Can always be updated by admin. If has_controller is false could be 00b |
| 0 | 0 | Cell(0) privilege_roles_cell | Cell With Privilege Roles |
| 0->0 | has_arbiter | Uint(1),Bool | Flag that shows if this message have a new Arbiter address |
| 0->0 | arbiter_addr | Address(267) | Address that is allowed to make privileged swaps |
| 0->0 | has_alm | Uint(1),Bool | Flag that shows if this message have a new ALM address |
| 0->0 | alm_addr | Address(267) | Address that is used to identify ALM contract |
| 0 | 1 | Cell(0) roles_add | Cell With Additional Roles |
| 0->1 | has_oracle | Uint(1),Bool | Flag that shows if this message have a new oracle address |
| 0->1 | oracle_addr | Address(267) | Address that is allowed to change the fee. Can always be updated by admin. Could be 00b |
| 1 | Cell(0) metadata_cell | Cell With Metainfo | |
| 1 | nftv3_content | Cell(0),Metadata | Metadata for the NFT Collection |
| 1 | nftv3item_content | Cell(0),Metadata | Metadata for the NFT Item |
| 2 | Cell(0) minter_cell | Cell With Minters | |
| 2 | jetton0_minter | Address(267) | Address of the jetton0 minter, used by indexer and frontend |
| 2 | jetton1_minter | Address(267) | Address of the jetton1 minter, used by indexer and frontend |
TL-B Description
This is a preliminary tl-b - subject to change **Tlb for ROUTER_CREATE_POOL**ROUTER_CREATE_POOL#2e3034ef
query_id:uint64
jetton_wallet0:MsgAddress
jetton_wallet1:MsgAddress
tick_spacing:uint24
initial_priceX96:uint160
pool_active:uint1
protocol_fee:uint16
lp_fee_base:uint16
lp_fee_current:uint16
roles_cell:^Roles_cellType
metadata_cell:^Metadata_cellType
minter_cell:^Minter_cellType
= ContractMessages;
_
has_arbiter:uint1
arbiter_addr:MsgAddress
has_alm:uint1
alm_addr:MsgAddress
= Privilege_roles_cellType;
_
has_oracle:uint1
oracle_addr:MsgAddress
= Roles_addType;
_
has_controller:uint1
controller_addr:MsgAddress
has_creator:uint1
creator_addr:MsgAddress
privilege_roles_cell:^Privilege_roles_cellType // optional ref (present only if a ref is left)
roles_add:^Roles_addType // optional ref (present only if a ref is left)
= Roles_cellType;
_
nftv3_content:^Cell
nftv3item_content:^Cell
= Metadata_cellType;
_
jetton0_minter:MsgAddress
jetton1_minter:MsgAddress
= Minter_cellType;
ROUTER_PAY_TO
Opcode : 0xa1daa96d
This message is the message that is sent to Router by the pool, an it would be accepted only from the pool:
Possible error codes
| Operation | Error Core | Value | Meaning |
|---|---|---|---|
| SWAP | RESULT_SWAP_OK | 200 | Swap went fine |
| SWAP | RESULT_SWAP_OUTPUT_TOO_SMALL | 230 | minAmount was not reached, swap reverted |
| SWAP | RESULT_SWAP_INTERNAL_ERROR | 231 | Internal error, generally this should not happen |
| SWAP | RESULT_SWAP_POOL_LOCKED | 232 | Attempt to swap in the locked pool |
| SWAP | RESULT_SWAP_POOL_EMPTY | 233 | Attempt to swap in the empty pool |
| MINT | RESULT_MINT_OK | 202 | Mint went fine |
| MINT | RESULT_NO_LIQUIDITY | 226 | Minting Zero liquidity was asked (not always unintended) |
| MINT | RESULT_TICK_IMPOSSIBLE_LOW | 220 | One of the ticks is too low |
| MINT | RESULT_TICK_IMPOSSIBLE_HIGH | 221 | One of the ticks is too high |
| MINT | RESULT_WRONG_TICK_SPACING | 222 | One of ticks is not divisible by tick_spacing |
| MINT | RESULT_TOO_MANY_TICKS | 223 | Pool is full |
| MINT | RESULT_TOO_MUCH_LIQUIDITY | 224 | Too much liquidity in one tick |
| MINT | RESULT_NOT_ENOUGH_COINS | 225 | Not enough coins to mint |
| BURN | RESULT_BURN_OK | 201 | Burn went fine |
| Path | Mnemonic | Type | Description |
|---|---|---|---|
| op | Uint(32),op | ||
| query_id | Uint(64) | queryid as of the TON documentation | |
| reciever0 | Address(267) | Address of the first receiver of the funds | |
| reciever1 | Address(267) | Address of the second receiver of the funds | |
| exit_code0 | Uint(8),ErrorCode | Most significant byte. Reforge: result of the first processed order | |
| exit_code1 | Uint(8),ErrorCode | Reforge: result of the second processed order | |
| exit_code2 | Uint(8),ErrorCode | Reforge: result of the third processed order | |
| exit_code3 | Uint(8),ErrorCode | Least significant byte. Swap/burn/collect result is here; reforge: fourth order | |
| seqno | Uint(64),Indexer | Internal pool sequence number | |
| 0 | Maybe Cell(1) coinsinfo_cell | Cell with info about the coins | |
| 0 | jetton0_address | Address(267) | Jetton to be sent to receiver0 identified by the wallet that belongs to router |
| 0 | jetton1_address | Address(267) | Jetton to be sent to receiver1 identified by the wallet that belongs to router |
| 0 | amount0 | Coins(124) | Amount of coins to be payed to receiver0 |
| 0 | amount1 | Coins(124) | Amount of coins to be payed to receiver1 |
| 0 | 0 | Cell(0) payload_cell | Information about the payload |
| 0->0 | payload_amount0 | Coins(124) | Amount of GRAM to forward to receiver0 together with payload_0 |
| 0->0 | payload_0 | Cell(1),Maybe, Payload | Forward payload delivered to receiver0 with the jetton transfer |
| 0->0 | payload_amount1 | Coins(124) | Amount of GRAM to forward to receiver1 together with payload_1 |
| 0->0 | payload_1 | Cell(1),Maybe, Payload | Forward payload delivered to receiver1 with the jetton transfer |
| 0->0 | excess_address | Address(267) | Address to receive the excess gas |
| 1 | Maybe Cell(1) indexer_swap_info_cell | Indexer-only. Post-swap pool state (null for non-swap) | |
| 1 | liquidity | Uint(128),Indexer | Post-swap concentrated liquidity at current tick |
| 1 | price_sqrt | Uint(160),Indexer,PriceX96 | Post-swap square root of the price stored as fixed point 64.96 |
| 1 | tick | Int(24),Indexer | Post-swap current tick |
| 1 | feeGrowthGlobal0X128 | Int(256),Indexer | Current range fee per unit of the liquidity for jetton0 |
| 1 | feeGrowthGlobal1X128 | Int(256),Indexer | Current range fee per unit of the liquidity for jetton1 |
| 2 | Maybe Cell(1) positions_cell | Cell with positions (null for swap/collect) | |
| 2 | 0 | Maybe Cell(1) burn0 | Cell with burn number 0 |
| 2->0 | index[0] | Uint(64) | Index of the burned position NFT |
| 2->0 | subindex[0] | Uint(4) | Subindex |
| 2->0 | liquidity[0] | Uint(128) | Position liquidity before this burn |
| 2->0 | tickLower[0] | Int(24) | lower bound of the range in which to mint |
| 2->0 | tickUpper[0] | Int(24) | upper bound of the range in which to mint |
| 2->0 | feeGrowthInside0LastX128[0] | Int(256),x128 | Fee counter inside position range for jetton0, per unit of liquidity, in 128.128 fixed point |
| 2->0 | feeGrowthInside1LastX128[0] | Int(256),x128 | Fee counter inside position range for jetton1, per unit of liquidity, in 128.128 fixed point |
| 2,0 | 0 | Maybe Cell(1) new_fee_cell | Fee counters to collect to (Used by indexer) |
| 2->0->0 | feeGrowthInside0CurrentX128[0] | Int(256),x128 | Fee counter inside position range for jetton0, per unit of liquidity, in 128.128 fixed point |
| 2->0->0 | feeGrowthInside1CurrentX128[0] | Int(256),x128 | Fee counter inside position range for jetton1, per unit of liquidity, in 128.128 fixed point |
| 2->0 | liquidity2Burn[0] | Uint(128) | Amount of liquidity that was burned from this position |
| 2 | 1 | Maybe Cell(1) burn1 | Cell with burn number 1 |
| 2->1 | index[1] | Uint(64) | Index of the burned position NFT |
| 2->1 | subindex[1] | Uint(4) | Subindex |
| 2->1 | liquidity[1] | Uint(128) | Position liquidity before this burn |
| 2->1 | tickLower[1] | Int(24) | lower bound of the range in which to mint |
| 2->1 | tickUpper[1] | Int(24) | upper bound of the range in which to mint |
| 2->1 | feeGrowthInside0LastX128[1] | Int(256),x128 | Fee counter inside position range for jetton0, per unit of liquidity, in 128.128 fixed point |
| 2->1 | feeGrowthInside1LastX128[1] | Int(256),x128 | Fee counter inside position range for jetton1, per unit of liquidity, in 128.128 fixed point |
| 2,1 | 0 | Maybe Cell(1) new_fee_cell | Fee counters to collect to (Used by indexer) |
| 2->1->0 | feeGrowthInside0CurrentX128[1] | Int(256),x128 | Fee counter inside position range for jetton0, per unit of liquidity, in 128.128 fixed point |
| 2->1->0 | feeGrowthInside1CurrentX128[1] | Int(256),x128 | Fee counter inside position range for jetton1, per unit of liquidity, in 128.128 fixed point |
| 2->1 | liquidity2Burn[1] | Uint(128) | Amount of liquidity that was burned from this position |
| 2 | 2 | Maybe Cell(1) burn2 | Cell with burn number 2 |
| 2->2 | index[2] | Uint(64) | Index of the burned position NFT |
| 2->2 | subindex[2] | Uint(4) | Subindex |
| 2->2 | liquidity[2] | Uint(128) | Position liquidity before this burn |
| 2->2 | tickLower[2] | Int(24) | lower bound of the range in which to mint |
| 2->2 | tickUpper[2] | Int(24) | upper bound of the range in which to mint |
| 2->2 | feeGrowthInside0LastX128[2] | Int(256),x128 | Fee counter inside position range for jetton0, per unit of liquidity, in 128.128 fixed point |
| 2->2 | feeGrowthInside1LastX128[2] | Int(256),x128 | Fee counter inside position range for jetton1, per unit of liquidity, in 128.128 fixed point |
| 2,2 | 0 | Maybe Cell(1) new_fee_cell | Fee counters to collect to (Used by indexer) |
| 2->2->0 | feeGrowthInside0CurrentX128[2] | Int(256),x128 | Fee counter inside position range for jetton0, per unit of liquidity, in 128.128 fixed point |
| 2->2->0 | feeGrowthInside1CurrentX128[2] | Int(256),x128 | Fee counter inside position range for jetton1, per unit of liquidity, in 128.128 fixed point |
| 2->2 | liquidity2Burn[2] | Uint(128) | Amount of liquidity that was burned from this position |
| 2 | 3 | Maybe Cell(1) burn3 | Cell with burn number 3 |
| 2->3 | index[3] | Uint(64) | Index of the burned position NFT |
| 2->3 | subindex[3] | Uint(4) | Subindex |
| 2->3 | liquidity[3] | Uint(128) | Position liquidity before this burn |
| 2->3 | tickLower[3] | Int(24) | lower bound of the range in which to mint |
| 2->3 | tickUpper[3] | Int(24) | upper bound of the range in which to mint |
| 2->3 | feeGrowthInside0LastX128[3] | Int(256),x128 | Fee counter inside position range for jetton0, per unit of liquidity, in 128.128 fixed point |
| 2->3 | feeGrowthInside1LastX128[3] | Int(256),x128 | Fee counter inside position range for jetton1, per unit of liquidity, in 128.128 fixed point |
| 2,3 | 0 | Maybe Cell(1) new_fee_cell | Fee counters to collect to (Used by indexer) |
| 2->3->0 | feeGrowthInside0CurrentX128[3] | Int(256),x128 | Fee counter inside position range for jetton0, per unit of liquidity, in 128.128 fixed point |
| 2->3->0 | feeGrowthInside1CurrentX128[3] | Int(256),x128 | Fee counter inside position range for jetton1, per unit of liquidity, in 128.128 fixed point |
| 2->3 | liquidity2Burn[3] | Uint(128) | Amount of liquidity that was burned from this position |
TL-B Description
This is a preliminary tl-b - subject to change **Tlb for ROUTER_PAY_TO**ROUTER_PAY_TO#a1daa96d
query_id:uint64
reciever0:MsgAddress
reciever1:MsgAddress
exit_code0:uint8
exit_code1:uint8
exit_code2:uint8
exit_code3:uint8
seqno:uint64
coinsinfo_cell:(Maybe ^Coinsinfo_cellType)
indexer_swap_info_cell:(Maybe ^Indexer_swap_info_cellType)
positions_cell:(Maybe ^Positions_cellType)
= ContractMessages;
_
payload_amount0:(VarUInteger 16)
payload_0:(Maybe ^Cell)
payload_amount1:(VarUInteger 16)
payload_1:(Maybe ^Cell)
excess_address:MsgAddress
= Payload_cellType;
_
jetton0_address:MsgAddress
jetton1_address:MsgAddress
amount0:(VarUInteger 16)
amount1:(VarUInteger 16)
payload_cell:^Payload_cellType // optional ref (present only if a ref is left)
= Coinsinfo_cellType;
_
liquidity:uint128
price_sqrt:uint160
tick:int24
feeGrowthGlobal0X128:int256
feeGrowthGlobal1X128:int256
= Indexer_swap_info_cellType;
_
feeGrowthInside0CurrentX128_0:int256
feeGrowthInside1CurrentX128_0:int256
= New_fee_cellType;
_
index_0:uint64
subindex_0:uint4
liquidity_0:uint128
tickLower_0:int24
tickUpper_0:int24
feeGrowthInside0LastX128_0:int256
feeGrowthInside1LastX128_0:int256
new_fee_cell:(Maybe ^New_fee_cellType)
liquidity2Burn_0:uint128
= Burn0Type;
_
feeGrowthInside0CurrentX128_1:int256
feeGrowthInside1CurrentX128_1:int256
= New_fee_cellType1;
_
index_1:uint64
subindex_1:uint4
liquidity_1:uint128
tickLower_1:int24
tickUpper_1:int24
feeGrowthInside0LastX128_1:int256
feeGrowthInside1LastX128_1:int256
new_fee_cell:(Maybe ^New_fee_cellType1)
liquidity2Burn_1:uint128
= Burn1Type;
_
feeGrowthInside0CurrentX128_2:int256
feeGrowthInside1CurrentX128_2:int256
= New_fee_cellType2;
_
index_2:uint64
subindex_2:uint4
liquidity_2:uint128
tickLower_2:int24
tickUpper_2:int24
feeGrowthInside0LastX128_2:int256
feeGrowthInside1LastX128_2:int256
new_fee_cell:(Maybe ^New_fee_cellType2)
liquidity2Burn_2:uint128
= Burn2Type;
_
feeGrowthInside0CurrentX128_3:int256
feeGrowthInside1CurrentX128_3:int256
= New_fee_cellType3;
_
index_3:uint64
subindex_3:uint4
liquidity_3:uint128
tickLower_3:int24
tickUpper_3:int24
feeGrowthInside0LastX128_3:int256
feeGrowthInside1LastX128_3:int256
new_fee_cell:(Maybe ^New_fee_cellType3)
liquidity2Burn_3:uint128
= Burn3Type;
_
burn0:(Maybe ^Burn0Type)
burn1:(Maybe ^Burn1Type)
burn2:(Maybe ^Burn2Type)
burn3:(Maybe ^Burn3Type)
= Positions_cellType;
ROUTERV3_RESET_GAS
Opcode : 0x42a0fb43
This operation allows router owners the gas if too much accumulated on the contract Access Rights: Allowed only for the Router Admin. Withdraws excess TON that accumulated on the router, leaving enough to keep it funded.
| Path | Mnemonic | Type | Description |
|---|---|---|---|
| op | Uint(32),op | ||
| query_id | Uint(64) | queryid as of the TON documentation |
TL-B Description
This is a preliminary tl-b - subject to change **Tlb for ROUTERV3_RESET_GAS**ROUTERV3_RESET_GAS#42a0fb43
query_id:uint64
= ContractMessages;