Skip to main content
Each lending account is represented by one position NFT. The controller’s account_id matches the NFT’s token_id, and the NFT holder owns the account. The controller resolves ownership through owner_of when a call requires account authority.
Transferring the NFT transfers the entire account, including collateral and debt. There is no protocol-side acceptance or separate handover step.

Identifiers and lifecycle

The controller API uses u64 account ids, while NFT token ids use u32. The account-id space is therefore bounded by u32. Token id 0 is consumed during construction and cannot represent a lending account. Keep account ids as decimal strings in JavaScript. The SDK’s accountNonce is this lending id; sourceSequence is the Stellar transaction source sequence. They are independent. Convert to u32 with a range check when calling NFT views. Supported controller flows use account_id = 0 to create an account, including supply, multiply, flash_position, migrate_from_blend, and liquidation SeizeMode::Credit(0). Creation mints the NFT; account deletion burns it. Withdrawal and strategy paths can delete an account when no supply or debt remains. Liquidation and bad-debt cleanup have their own deletion paths. Repayment alone updates debt and does not delete an empty account. Reconcile account_exists(account_id) after an operation that can close it. Clear a stored id only after an explicit false; abort on a failed lookup. The controller owns the NFT contract. Governance deploys it through DeployPositionNft and upgrades it through UpgradePositionNft(hash). Account creation without a configured NFT fails with #53 PositionNftNotSet. Existing-account authority checks fail closed if NFT ownership cannot be resolved, usually with #24 AccountNotFound. Metadata-only views need no NFT lookup.

Protocol interface

The controller’s renew_account requires the account owner’s authorization and renews the controller instance, account records, and NFT owner/balance entries. Direct NFT renew remains permissionless and renews NFT state only. Neither call renews the integrating contract’s local account-id key. See Storage.

Transfer and approvals

The contract implements OpenZeppelin’s enumerable non-fungible token interface:
NFT approval permits token transfer. It does not grant controller authority to borrow or withdraw while the owner keeps the NFT. Controller delegation requires a separate owner grant to a governance-approved position manager.
A transfer preserves collateral, debt, spoke binding, position mode, and stored risk parameters. The previous owner’s delegate grants become inactive while another address owns the token. They can reactivate if it returns before those grants are replaced. See Delegates for grant lifecycle. The new owner can call remove_delegate to delete a stale grant. This can delete the previous owner’s stored grant without a delegate-change event, because the current owner’s delegate list did not change. Adding a delegate replaces stale grants with the new owner’s list. A reactivated delegate must also remain an active, registered position manager.

Enumeration and metadata

The URI format is {base_uri}{token_id}?isStatic=true&chain=STELLAR. token_uri fails with OpenZeppelin NonExistentToken for burned or never-minted ids. Use Addresses to resolve the NFT contract and Controller ABI for lending calls.