Permissioned Alpha Vault has whitelist and individual deposit cap, so only whitelisted addresses can deposit into the vault, up to the deposit cap defined for the wallet.
To use the following Alpha Vault SDK examples, please complete the steps listed in the Alpha Vault SDK section first.
This example demonstrates how to use the permissioned Alpha Vault to achieve authorization of user deposit with whitelisted wallet and deposit cap.
Update address and deposit cap. Check whitelist_wallet.csv.
Create a pool with permissioned vault. Check createDynamicPoolWithPermissionedVault.ts for more details.
Generate merkle tree, and create merkle root config. Check createMerkleRootConfig.ts for more details.
Deposit with merkle proof. Check depositWithProof.ts for more details.
2. Create Permissioned Alpha Vault with Whitelist and Individual Deposit Cap
This example shows how to create a Dynamic AMM Pool with permissioned Alpha Vault (that has a whitelist and deposit cap)
Please contact the Meteora team if the existing pool and Alpha Vault config don't meet your requirements.
Currently only Dynamic AMM Pool support permissioned Alpha Vault with whitelist and deposit cap.
import { clusterApiUrl, Connection, Keypair, PublicKey, sendAndConfirmTransaction, SYSVAR_CLOCK_PUBKEY,} from"@solana/web3.js";import AlphaVault, { DYNAMIC_AMM_PROGRAM_ID, PoolType, VaultMode } from"../..";import DynamicAmm from"@mercurial-finance/dynamic-amm-sdk";import { ActivationType, Clock, ClockLayout, createTokenAndMint, loadKeypairFromFile,} from"../utils";import dotenv from"dotenv";import { BN } from"bn.js";import { derivePoolAddressWithConfig } from"@mercurial-finance/dynamic-amm-sdk/dist/cjs/src/amm/utils";dotenv.config();asyncfunctioncreateDummyMints(connection:Connection, payer:Keypair) {console.log("Creating mint A");constmintAInfo=awaitcreateTokenAndMint( connection, payer,6,100_000_000_000 );console.log("Creating mint B");constmintBInfo=awaitcreateTokenAndMint( connection, payer,6,100_000_000_000 );return { mintAInfo, mintBInfo, };}/** * * @param connection * @param creator * @param activationType Pool activation based on time or slot * @param maximumActivationDuration Maximum duration for pool to be activation after creation * @param minimumLockDuration Minimum duration for bought token be locked * @param maximumLockDuration Maximum duration for bought token to be locked * @param minimumVestingDuration Minimum duration for bought token to be vested * @param maximumVestingDuration Maximum duration for bought token be vested * @param vaultMode Vault mode: fcfs or prorata */asyncfunctiongetPoolConfigByRequirement( connection:Connection, creator:PublicKey, activationType:ActivationType, maximumActivationDuration:number, minimumLockDuration:number, maximumLockDuration:number, minimumVestingDuration:number, maximumVestingDuration:number, vaultMode:VaultMode) {// 1. Pool configs that can be used by anyone to create poolconstpublicPoolConfigs=awaitDynamicAmm.getPoolConfigsWithPoolCreatorAuthority( connection,PublicKey.default );// 2. Pool configs that can only be used by specific pubkey to create poolconstcreatorPoolConfigs=awaitDynamicAmm.getPoolConfigsWithPoolCreatorAuthority( connection, creator );// 3. Look for pool configs that have vault supportconstpoolConfigsWithVaultSupport= [...publicPoolConfigs,...creatorPoolConfigs, ].filter((config) =>config.account.vaultConfigKey !=PublicKey.default);console.log(`Gotten ${poolConfigsWithVaultSupport.length} usable pool configs with vault support` );constconfigFuture= vaultMode ===VaultMode.FCFS?AlphaVault.getFcfsConfigs(connection):AlphaVault.getProrataConfigs(connection);constconfigs=await configFuture;for (constprogramAccountof poolConfigsWithVaultSupport) {const { account } = programAccount;// 3.1 Pool activation type and duration matchesif (account.activationType == activationType &&account.activationDuration.toNumber() >= maximumActivationDuration ) {constvaultConfig=configs.find((c) =>c.publicKey.equals(account.vaultConfigKey) );if (!vaultConfig) {continue; }conststartVestingDuration=vaultConfig.account.startVestingDuration.toNumber();constendVestingDuration=vaultConfig.account.endVestingDuration.toNumber();constvestingDuration= endVestingDuration - startVestingDuration;// 3.2 Vault activation type, lock and vesting duration matchesif ( startVestingDuration >= minimumLockDuration && startVestingDuration <= maximumLockDuration && vestingDuration >= minimumVestingDuration && vestingDuration <= maximumVestingDuration &&vaultConfig.account.activationType == activationType ) {return programAccount; } } }returnnull;}asyncfunctioncreateDynamicPoolWithPermissionedVault( connection:Connection, payer:Keypair) {const { mintAInfo,mintBInfo } =awaitcreateDummyMints(connection, payer);// Pool and vault requirementconstmaximumActivationDuration=86400; // 1 dayconstminimumLockDuration=60*30; // 30 minutesconstmaximumLockDuration=86400; // 1 dayconstminimumVestingDuration=60*60; // 1 hourconstmaximumVestingDuration=60*60*24*7; // 1 weekconstvaultMode=VaultMode.PRORATA; // 1.Find pool config where it allow user to create pool with customizable start trading time which start from NOW -> NOW + 24H
// With lock duration fall between 30 minutes -> 1 days (non customizable)// And vesting duration fall between 1 hour -> 1 week (non customizable)// And prorata vaultconstpoolConfig=awaitgetPoolConfigByRequirement( connection,payer.publicKey,ActivationType.Timestamp, maximumActivationDuration, minimumLockDuration, maximumLockDuration, minimumVestingDuration, maximumVestingDuration, vaultMode );console.log("Got pool config");console.log(poolConfig);constclockAccount=awaitconnection.getAccountInfo(SYSVAR_CLOCK_PUBKEY);constclock:Clock=ClockLayout.decode(clockAccount.data);// Pool start trade after created for 5 hourconstactivationPoint=clock.unixTimestamp.add(newBN(3600*5));// 2. Create the poolconsttransactions=awaitDynamicAmm.createPermissionlessConstantProductPoolWithConfig2( connection,payer.publicKey,mintAInfo.mint,mintBInfo.mint,newBN(100_000_000),newBN(100_000_000),poolConfig.publicKey, { activationPoint, } );console.log("Creating pool");for (consttxof transactions) {consttxHash=awaitsendAndConfirmTransaction(connection, tx, [payer]);console.log(txHash); }constpoolPubkey=derivePoolAddressWithConfig(mintAInfo.mint,mintBInfo.mint,poolConfig.publicKey,DYNAMIC_AMM_PROGRAM_ID );console.log("Pool created",poolPubkey.toBase58());// 3. Create the alpha vaultconstinitPermissionlessVaultTx=awaitAlphaVault.createPermissionedVault( connection, { baseMint:mintAInfo.mint, quoteMint:mintBInfo.mint, poolType:PoolType.DYNAMIC, vaultMode:VaultMode.PRORATA, poolAddress: poolPubkey, config:poolConfig.account.vaultConfigKey, },payer.publicKey );console.log("Creating vault");consttxHash=awaitsendAndConfirmTransaction( connection, initPermissionlessVaultTx, [payer] );console.log(txHash);}constconnection=newConnection(clusterApiUrl("devnet"));constpayer=loadKeypairFromFile(process.env.KEYPAIR_PATH);/** * This example shows how to create dynamic pool with permissioned vault * Please contact meteora team if the existing pool and vault config doesn't meet the requirement * Currently only dynamic pool support permissioned vault */createDynamicPoolWithPermissionedVault(connection, payer).then(() => {console.log("Done"); }).catch(console.error);
This example shows how to create a merkle root config after generating a merkle tree, which is required to verify the whitelisted wallet address data, and enable deposits by whitelisted wallets.
import { clusterApiUrl, Connection, Keypair, PublicKey, sendAndConfirmTransaction,} from"@solana/web3.js";import { loadKeypairFromFile } from"../utils";import { AlphaVault } from"../../alpha-vault";import dotenv from"dotenv";import { createMerkleTree, loadWhitelistWalletCsv } from"./utils";import { BN } from"bn.js";dotenv.config();asyncfunctioncreateMerkleRootConfig( vault:PublicKey, csvPath:string, payer:Keypair) {constconnection=newConnection(clusterApiUrl("devnet"),"confirmed");constalphaVault=awaitAlphaVault.create(connection, vault);// 1. Load whitelist wallet from csv fileconstwhitelistWallets=awaitloadWhitelistWalletCsv(csvPath);console.log("Loaded whitelist wallets");console.log(whitelistWallets);// 2. Create merkle treeconsttree=awaitcreateMerkleTree(connection, alphaVault, whitelistWallets);// 3. Create merkle root config// If the tree grew too large, one can partition it into multiple tree by setting different versionconstversion=newBN(0);constcreateMerkleRootConfigTx=awaitalphaVault.createMerkleRootConfig(tree.getRoot(), version,payer.publicKey );// 4. Send transactionconsole.log("Sending transaction");consttxHash=awaitsendAndConfirmTransaction( connection, createMerkleRootConfigTx, [payer] );console.log(txHash);}// Permissioned alpha vaultconstvault=newPublicKey("ARGqVVUPPqtqH9UeBHvYsv7AtZv623YdEaEziZ1pdDUs");constpayer=loadKeypairFromFile(process.env.KEYPAIR_PATH);constwhitelistWalletCsvFilepath="src/examples/permissioned/whitelist_wallet.csv";/** * This example shows how to close an escrow account and get rental back. Close escrow can only happen after vesting is complete, and escrow have claimed all the bought token.
*/createMerkleRootConfig(vault, whitelistWalletCsvFilepath, payer).then(() => {console.log("Done"); }).catch(console.error);
4. Deposit SOL or USDC with Proof
This example shows how to deposit into a permissioned alpha vault using a whitelisted wallet. Deposit can only happen before the end of the deposit period.
import { clusterApiUrl, Connection, Keypair, PublicKey, sendAndConfirmTransaction,} from"@solana/web3.js";import { loadKeypairFromFile } from"../utils";import { AlphaVault } from"../../alpha-vault";import BN from"bn.js";import dotenv from"dotenv";import Decimal from"decimal.js";import { deriveMerkleRootConfig } from"../../alpha-vault/helper";import { createMerkleTree, loadWhitelistWalletCsv } from"./utils";import { PROGRAM_ID } from"../../alpha-vault/constant";dotenv.config();asyncfunctiondepositToPermissionedAlphaVault( vault:PublicKey, depositAmount:BN, csvPath:string, payer:Keypair) {constconnection=newConnection(clusterApiUrl("devnet"),"confirmed");constalphaVault=awaitAlphaVault.create(connection, vault);// 1. Load whitelisted walletconstwhitelistedWallets=awaitloadWhitelistWalletCsv(csvPath);// 2. Create merkle treeconsttree=awaitcreateMerkleTree( connection, alphaVault, whitelistedWallets );// 3. Get wallet proof infoconstdepositorWhitelistInfo=whitelistedWallets.find((w) =>w.wallet.equals(payer.publicKey) );constquoteMint=awaitconnection.getTokenSupply(alphaVault.vault.quoteMint);consttoNativeAmountMultiplier=newDecimal(10**quoteMint.value.decimals);constnativeDepositCap=newBN(depositorWhitelistInfo.depositCap.mul(toNativeAmountMultiplier).toString() );constdepositorProof= tree.getProof(payer.publicKey, nativeDepositCap).map((buffer) => {returnArray.from(newUint8Array(buffer)); });const [merkleRootConfig] =deriveMerkleRootConfig(alphaVault.pubkey,newBN(0),newPublicKey(PROGRAM_ID["mainnet-beta"]) );// 4. DepositconstdepositTx=awaitalphaVault.deposit(depositAmount,payer.publicKey, { merkleRootConfig, maxCap: nativeDepositCap, proof: depositorProof, });console.log(`Depositing ${depositAmount.toString()}`);consttxHash=awaitsendAndConfirmTransaction(connection, depositTx, [ payer, ]);console.log(txHash);constescrow=awaitalphaVault.getEscrow(payer.publicKey);console.log("Escrow info");console.log(escrow);}// Alpha vault to be deposited toconstvault=newPublicKey("ARGqVVUPPqtqH9UeBHvYsv7AtZv623YdEaEziZ1pdDUs");constdepositAmount=newBN(100_000);constpayer=loadKeypairFromFile(process.env.KEYPAIR_PATH);constwhitelistWalletCsvFilepath="src/examples/permissioned/whitelist_wallet.csv";/** * This example shows how to deposit to permissioned alpha vault. Deposit can only happen before the deposit close. */depositToPermissionedAlphaVault( vault, depositAmount, whitelistWalletCsvFilepath, payer).then(() => {console.log("Done"); }).catch(console.error);