When to use this
- KYC’d-only depositor base — tie KYC completion to a wallet, then automate whitelisting via the API
- Accredited-investor restrictions — same pattern, gated by attestation
- Internal market for a specific app’s users — your app’s onboarding flow is the gate
- Permissioned borrowers — counterparty agreements that lock borrow access to named entities
How it works
Permissioning is enforced by a cosigner pattern. Every gated action on a permissioned market requires two on-chain signatures: the user’s, plus a co-signature from a kperm-controlled PDA called the Market Permissioner. klend will only execute the action if both are present. The mechanic has two on-chain pieces. The market holds two new fields.permissioning_authority is a pubkey that klend treats as a required co-signer on every gated action; the curator sets it to the kperm-derived Market Permissioner PDA (seeds: ["market", market_address]). permissioned_ops is a u64 bitfield naming which actions on this market are gated. Setting these two fields is what turns a market into a permissioned market.
The kperm program holds the per-user allowlist. For each (market, user) pair authorized to take gated actions, kperm stores a UserPermission account at PDA ["permission", market, user] recording which operations that user can take.
When a user takes a gated action, their wallet does not call klend directly. It builds the klend instruction it wants to execute, then wraps it inside kperm.permissioned_fwd_to_klend(ix_data) and submits the wrap. kperm reads the user’s UserPermission, confirms the requested action is in the allowlist, and CPIs into klend with its Market Permissioner PDA acting as the co-signer. klend sees the required co-signer present and executes the wrapped instruction. If the user has no UserPermission or the requested action is missing from the bitfield, kperm rejects and the entire transaction reverts.
For non-gated actions on the same market (actions whose bit is not set in permissioned_ops, plus withdraw, repay, refresh, and read paths), users call klend directly. Only the gated operations require the wrap.
The operations
The kperm bitfield supports four operation slots:
A user’s
UserPermission.permissioned_ops is the bitwise OR of the bits they’re allowed to act on (e.g. DEPOSIT | BORROW for a whitelisted depositor-borrower).
Withdraw and repay stay open. The on-chain bitfield doesn’t gate exits — once a user has been allowed to deposit or borrow, they can always close their position. Refresh and read paths are also always open. The design is to screen capital at the entry; not at the exit.
Curator workflow
1
Decide what to permission
Pick the operations to gate.
DEPOSIT is the most common (KYC’d depositor base). Add BORROW for fully gated lending venues. You can change this later.2
Switch the market into permissioned mode
Set
permissioning_authority to the Market Permissioner PDA and permissioned_ops to the chosen bitfield. From this point, klend requires the kperm cosigner for every gated action.3
Whitelist users
Use the SDK, CLI, or REST API (see tabs below). All three issue the same
init_user_permission (first grant) or update_user_permission (subsequent grants) instruction on-chain.4
Test the rejection path
Send a deposit transaction from an un-whitelisted wallet on staging and confirm it reverts. Add that wallet to the allowlist and retry — it should succeed.
- SDK
- API
- Kamino CLI
Configure permissioning via SDK
The kperm-aware methods live in@kamino-finance/klend-sdk alongside the standard KaminoManager.Curator: switch the market into permissioned mode
PermissionedOp.fromString accepts pipe-separated names: 'DEPOSIT', 'DEPOSIT|BORROW', 'DEPOSIT|BORROW|LIQUIDATE', etc.Whitelist a user (initial grant or update)
updatePermissionIx takes an overwrite flag: true replaces the user’s allowed ops with grantedOps; false bitwise-ORs grantedOps into the existing set.Revoke a user
UserPermission account stays on-chain with permissioned_ops = 0. Re-granting later avoids paying rent again.User flow: take a gated action
The standardKaminoAction builders detect when the target market is permissioned and automatically wrap the produced klend instructions with permissioned_fwd_to_klend. Builders integrating against a permissioned market continue to call KaminoAction.buildDepositTxns(...), buildBorrowTxns(...), etc. — the SDK handles the wrap.For instructions built outside of KaminoAction, wrap manually:On-chain accounts
Program ID for kperm:
KPermUZsf9tu4cSd9LNcMojCbiHfdbJXv9dr3pAUzz1.
Operational considerations
Reference
- Market config reference —
permissioning_authorityandpermissioned_opsfields - Market settings — broader market-level config flow
- Updating reserves — reserves work the same way under a permissioned market
update-lending-market-permission,add-permission,remove-permissionCLI — full flag detail