The Vault is created as a token account with an address derived from a set of seeds which includes:
1.
the public address of a wallet key pair (this wallet would typically be the mint address of the asset the vault is associated with)
2.
The public address of the payment token (typically a stablecoin)
All instructions supported by the program require signatures from:
the wallet keypair - item 1 from above
a program vault authority - a signer PDA (Program Derived Address) tied to the Loan Remittance program; each vault has its own vault authority
As such, once stablecoins (or any other payment instrument that has been set for the vault) are deposited in the vault, they can only be paid out if ALL these 3 conditions are met:
1.
the payment (a debit) can be done only through the Debit Vault instruction of the Loan Remittance program
2.
the wallet signs the transaction
3.
the payment destination has been whitelisted
These restrictions ensure that the funds are well locked down and controlled, so payments are performed only for their intended uses.
Whitelisted destinations are represented onchain as marker PDAs - i.e. these accounts hold no data; their presence onchain signals the existence of a whitelisted entry.The whitelist PDA entry includes in its set of seeds:
At Alphaledger we are using the program to payout onchain loan remittance received from borrowers (typically through servicers) to loan investors who own loan assets onchain (e.g. auto or agricultural loans).A similar model can be used for the payout of cash disbursements associated with other assets too:
bonds (interest and maturity payments)
equities (dividends)
The whitelisting functionality allows the setup of only payment destinations that have been verified: KYC, investors' eligibility.
All interactions with the onchain program can be done through the setup of Loan Remittance API endpoints described here.Advantages of interacting with the program through the Vulcan Forge API:
built-in support for all main features supported by the Vulcan Forge in the introduction section: e.g. key management, flexible transaction configurations, offchain references, multiple environments support, offline signing, etc.
onchain whitelists are hard to work with as it is very hard to construct onchain queries to get a full list of marker whitelisted PDAs. Vulcan Forge through its twin offchain mirrors and database makes it easier to work these entities:
the Close Vault endpoint validates that all whitelisted entries have been removed (thus reclaiming the rent SOL associated with the marker PDAs) before allowing for the vault to be closed
there is no need to keep track if the payment instrument is an SPL or Token 2022 token: the system figures out the token type and interacts accordingly with the program
when execution an repayment operation, an optional memo can be added to the transaction
Custom program errors use the 7000+ range. The first custom error is VAULT_NOT_EMPTY_ERROR = 7000.Instructions that derive or verify program PDAs accept an optional svm_cluster_id after their fixed instruction data. When present, the value is a 16-byte UUID v4 byte array and is appended as the last PDA seed before the bump.Optional svm_cluster_id instruction parameter use caseAt Alphaledger we use the svm_cluster_id as we support multiple virtual environments, many of them running on Devnet. As such, for e.g. 2 virtual environments, both running on Devnet can have the same wallet (asset) and payment addresses loaded into each environment - when we create the various PDAs for the loan remittance program, in order to make sure that we don't generate PDAs w/ the same address for both environments, we use the v4 UUID identifier of the virtual environment to add the necessary entropy to the PDAs' seeds to get different address for each environment.Optional svm_cluster_id encoding:
no extra bytes no svm_cluster_id
[0] explicit no svm_cluster_id
[1, <16 bytes>] svm_cluster_id present
[0] CREATE_VAULT_DISCRIMINATOR = 0
[1] vault_bump
[2] token_program_kind (0 = SPL Token, 1 = Token 2022)
[3] optional svm_cluster_id flag (0 = none, 1 = present)
[4..20] svm_cluster_id bytes when flag is 1
Accounts:
0. [writable, signer] fee payer
1. [writable] vault PDA
2. [signer] wallet address seed
3. [] mint
4. [] system program
5. [] SPL Token Program or Token 2022 Program
The vault's Solana runtime owner is the selected token program, as required by SPL Token or Token 2022. Its token authority is the program_vault_authority PDA, which is controlled by this program.
The client should derive the vault PDA and bump off-chain, pass the PDA at account index 1, put the bump at instruction_data[1], and put the token program kind at instruction_data[2]. If the vault is scoped to an svm_cluster_id, append [1] plus the 16 UUID bytes to the instruction data and include the same bytes as the last PDA seed. The program verifies the PDA before creating it.
Transfers tokens out of the program-controlled vault.Instruction data:
[0] DEBIT_VAULT_DISCRIMINATOR = 1
[1] vault_bump
[2] token_program_kind (0 = SPL Token, 1 = Token 2022)
[3..11] amount as little-endian u64
[11] mint decimals
[12] optional svm_cluster_id flag (0 = none, 1 = present)
[13..29] svm_cluster_id bytes when flag is 1
Accounts:
0. [writable] source vault PDA
1. [writable] destination token account
2. [] allowed destination PDA
3. [] program_vault_authority PDA
4. [signer] wallet address seed
5. [] mint
6. [] SPL Token Program or Token 2022 Program
The program verifies the source vault PDA, the program vault authority PDA, and the allowed destination PDA before CPIs into the selected token program using TransferChecked. The allowed destination PDA must exist, be owned by this program, and be derived from ["allowed-destination", wallet, mint,
destination], or ["allowed-destination", wallet, mint, destination,
svm_cluster_id] when the cluster id is present. The program_vault_authority PDA is marked as the token-program signer in the CPI, and this program signs for it with ["program-vault-authority", wallet, mint, bump], or ["program-vault-authority", wallet, mint, svm_cluster_id, bump] when the cluster id is present.
Closes an empty vault PDA and returns its SOL rent to the fee payer. The wallet seed account must sign, mirroring the wallet authorization used by vault debit instructions.Instruction data:
[0] CLOSE_VAULT_DISCRIMINATOR = 4
[1] vault_bump
[2] token_program_kind (0 = SPL Token, 1 = Token 2022)
[3] optional svm_cluster_id flag (0 = none, 1 = present)
[4..20] svm_cluster_id bytes when flag is 1
Accounts:
0. [writable] fee payer rent recipient
1. [writable] vault PDA
2. [] program_vault_authority PDA
3. [signer] wallet address seed
4. [] mint
5. [] SPL Token Program or Token 2022 Program
The program verifies the vault PDA and program vault authority PDA for ["vault", wallet, mint] and ["program-vault-authority", wallet, mint], or the same seeds with svm_cluster_id appended when the cluster id is present. Before closing, it reads the SPL token account amount and fails with VAULT_NOT_EMPTY_ERROR if the vault still has a mint balance. That validation logs:
Vault is not empty; close requires zero token balance
When the balance is zero, it CPIs into the selected token program using CloseAccount; the program signs for the token authority PDA and the token program transfers the token account rent to the fee payer.