This guide explains the programmatic lifecycle for integrating with the Hastra AUTO pool on Solana.
For partners, this process converts liquid capital (USDC) into wYLDS, which can then be staked into
AUTO shares.
The Hastra Solana protocol consists of two interconnected Anchor programs:
- vault-mint (
9WUyNREiPDMgwMh5Gt81Fd3JpiCKxpjZ5Dpq9Bo1RhMV) β accepts USDC and issues wYLDS at a 1:1 ratio. Shared across all Hastra Solana pools.
- vault-stake-auto (
5uJgCDrQHfA58fPqLsuU14Srg9quxXNHz91cZ54cq4pK) β accepts wYLDS and issues AUTO shares based on a Chainlink-sourced exchange rate.
Other pools: Seehastra-sol-programmatic-integration-guide.md(PRIME pool) andhastra-sol-programmatic-integration-guide-smb.md(SMB pool).
1. The Asset Lifecycle: USDC β wYLDS
When a team "mints" on Hastra Solana, they deposit USDC into the vault-mint program to receive
wYLDS (Wrapped YLDS).
wYLDS is the base-layer wrapped token on Solana, acting as a custodial wrapper for YLDS, the
underlying SEC-registered stablecoin. It acts as a 1:1 receipt for the USDC deposited into the
vault token account.
Context: Unlike a simple swap, this "Deposit" moves USDC into a program-controlled vault token
account (PDA-owned), and the program mints wYLDS to the user.
PDA Derivations (vault-mint)
typescriptconst [configPda] = PublicKey.findProgramAddressSync( [Buffer.from("config")], vaultMintProgramId ); const [vaultTokenAccountConfigPda] = PublicKey.findProgramAddressSync( [Buffer.from("vault_token_account_config"), configPda.toBuffer()], vaultMintProgramId ); const [mintAuthorityPda] = PublicKey.findProgramAddressSync( [Buffer.from("mint_authority")], vaultMintProgramId );
Programmatic Mint (USDC β wYLDS)
typescriptconst vaultMint = new Program<HastraSolVaultMint>(idl, provider); // Deposit USDC to receive wYLDS. // The program transfers USDC from the user's ATA into the vault token account // and mints wYLDS into the user's wYLDS ATA. await vaultMint.methods .deposit(new BN(amount)) .accountsStrict({ config: configPda, vaultTokenAccount: vaultUsdcAta, // program-owned USDC vault vaultTokenAccountConfig: vaultTokenAccountConfigPda, mint: wyldsMint, // wYLDS mint mintAuthority: mintAuthorityPda, signer: userPublicKey, userVaultTokenAccount: userUsdcAta, // user's USDC ATA userMintTokenAccount: userWyldsAta, // user's wYLDS ATA tokenProgram: TOKEN_PROGRAM_ID, }) .rpc();
2. The Staking Lifecycle: wYLDS β AUTO
AUTO represents a share-based position in the AUTO pool. Unlike the ETH flow, on Solana yield does
not require a manual claim β it accrues through an appreciating Chainlink-sourced exchange rate.
Context: Yield is generated off-chain on the Provenance side and bridged back to Solana as
YLDS/wYLDS. Periodically, the vault-stake-auto program receives newly minted wYLDS via a CPI into
vault-mint's
external_program_mint instruction (publish_rewards). The on-chain AUTO/wYLDS
exchange rate is maintained by Chainlink Data Streams and updated periodically by rewards
administrators via verify_price.AUTO Exchange Rate:
plain textshares_to_mint = deposit_wYLDS * price_scale / price wYLDS_returned = shares_burned * price / price_scale where: price = (wYLDS per 1 AUTO) * price_scale [from Chainlink Data Streams]
As yield flows into the stake vault, the Chainlink price appreciates β 1 AUTO becomes redeemable
for more wYLDS over time.
PDA Derivations (vault-stake-auto)
typescriptconst vaultStakeAutoProgramId = new PublicKey("5uJgCDrQHfA58fPqLsuU14Srg9quxXNHz91cZ54cq4pK"); const [stakeConfigPda] = PublicKey.findProgramAddressSync( [Buffer.from("stake_config")], vaultStakeAutoProgramId ); const [stakeVaultTokenAccountConfigPda] = PublicKey.findProgramAddressSync( [Buffer.from("stake_vault_token_account_config"), stakeConfigPda.toBuffer()], vaultStakeAutoProgramId ); const [vaultAuthorityPda] = PublicKey.findProgramAddressSync( [Buffer.from("vault_authority")], vaultStakeAutoProgramId ); const [mintAuthorityPda] = PublicKey.findProgramAddressSync( [Buffer.from("mint_authority")], vaultStakeAutoProgramId ); const [stakePriceConfigPda] = PublicKey.findProgramAddressSync( [Buffer.from("stake_price_config"), stakeConfigPda.toBuffer()], vaultStakeAutoProgramId );
Programmatic Stake (wYLDS β AUTO)
typescriptconst vaultStakeAuto = new Program<HastraSolVaultStake>(idl, provider); // Deposit wYLDS to receive AUTO shares at the current Chainlink exchange rate. await vaultStakeAuto.methods .deposit(new BN(amount)) .accountsStrict({ stakeConfig: stakeConfigPda, vaultTokenAccount: stakeVaultWyldsAta, // program-owned wYLDS vault stakeVaultTokenAccountConfig: stakeVaultTokenAccountConfigPda, vaultAuthority: vaultAuthorityPda, mint: autoMint, // AUTO share token mint vaultMint: wyldsMint, // wYLDS mint mintAuthority: mintAuthorityPda, signer: userPublicKey, userVaultTokenAccount: userWyldsAta, // user's wYLDS ATA userMintTokenAccount: userAutoAta, // user's AUTO ATA stakePriceConfig: stakePriceConfigPda, tokenProgram: TOKEN_PROGRAM_ID, }) .rpc();
Optional: Merkle-Based wYLDS Reward Claims (vault-mint)
In addition to exchange-rate appreciation, the protocol supports discrete reward epochs distributed
via merkle proofs (used for incentive programs and supplemental allocations).
Important: These claims live on the vault-mint program and mint wYLDS (not AUTO shares)
directly to the user. If a partner wants AUTO exposure on the claimed wYLDS, they must subsequently
stake it via vault-stake-auto.
Context: Each epoch contains a merkle root summarizing user rewards for the period. Rewards are
minted as wYLDS directly to the user's wYLDS ATA.
Leaf format:
sha256(user_pubkey || reward_amount_le_bytes || epoch_index_le_bytes)typescriptconst indexLe = new BN(epochIndex).toArrayLike(Buffer, "le", 8); // V2 epoch PDAs (seed "epoch_v2" distinguishes from legacy V1 "epoch" namespace). const [epochPda] = PublicKey.findProgramAddressSync( [Buffer.from("epoch_v2"), indexLe], vaultMintProgramId ); const [epochCapPda] = PublicKey.findProgramAddressSync( [Buffer.from("epoch_cap"), indexLe], vaultMintProgramId ); const [epochRewardsPoolPda] = PublicKey.findProgramAddressSync( [Buffer.from("epoch_rewards_pool"), indexLe], vaultMintProgramId ); const [epochRewardsPoolAuthorityPda] = PublicKey.findProgramAddressSync( [Buffer.from("epoch_rewards_pool_authority"), indexLe], vaultMintProgramId ); const [claimRecordPda] = PublicKey.findProgramAddressSync( [Buffer.from("claim"), epochPda.toBuffer(), userPublicKey.toBuffer()], vaultMintProgramId ); // Claim rewards from a V2 epoch. Transfers wYLDS from the pre-funded pool rather than minting; // the on-chain cap tracker ensures claimed_total never exceeds the epoch total. // Note: this is called on vaultMint, not vaultStakeAuto. await vaultMint.methods .claimRewardsV2(new BN(rewardAmount), merkleProof) .accountsStrict({ config: configPda, // vault-mint config PDA user: userPublicKey, epoch: epochPda, epochCap: epochCapPda, // aggregate cap tracker claimRecord: claimRecordPda, // PDA: ["claim", epoch, user] β prevents double-claim epochRewardsPool: epochRewardsPoolPda, // pre-funded wYLDS escrow epochRewardsPoolAuthority: epochRewardsPoolAuthorityPda, userMintTokenAccount: userWyldsAta, // user's wYLDS ATA systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, }) .rpc();
3. The Redemption Lifecycle: AUTO β wYLDS β USDC
Returning to USDC is a multi-step process across both programs.
Phase 1: Redeem AUTO for wYLDS (vault-stake-auto)
The vault-stake-auto program supports a single-step direct redemption. There is no unbonding
period or queuing β
redeem atomically burns the specified AUTO shares and transfers the
proportional wYLDS from the stake vault to the user.Context: The amount of wYLDS returned is calculated using the live Chainlink price at the time
of the call:
wYLDS_returned = auto_amount * price / price_scale. The call will fail if the stored
Chainlink price is stale (past price_max_staleness) or uninitialised.typescript// Atomically burn AUTO shares and receive wYLDS in a single call. await vaultStakeAuto.methods .redeem(new BN(autoAmount)) .accountsStrict({ stakeConfig: stakeConfigPda, vaultTokenAccount: stakeVaultWyldsAta, // program-owned wYLDS vault stakeVaultTokenAccountConfig: stakeVaultTokenAccountConfigPda, vaultAuthority: vaultAuthorityPda, signer: userPublicKey, ticket: vaultStakeAutoProgramId, // pass program ID if no legacy v1 ticket exists userVaultTokenAccount: userWyldsAta, // user's wYLDS ATA (destination) userMintTokenAccount: userAutoAta, // user's AUTO ATA (source to burn) mint: autoMint, // AUTO share token mint vaultMint: wyldsMint, // wYLDS mint stakePriceConfig: stakePriceConfigPda, tokenProgram: TOKEN_PROGRAM_ID, }) .rpc();
Legacy note: An optionalticketaccount exists for users who have anUnbondingTicketfrom the deprecated v1 two-step flow. If no ticket exists, pass the vault-stake-auto program ID for that account; Anchor treats it asNoneand skips all ticket-related constraints. The on-chainunbonding_periodfield is permanently set to 0 and theunbondinstruction no longer exists.
Phase 2: wYLDS β USDC (Operator-Mediated, vault-mint)
To convert wYLDS back to USDC, the vault-mint program uses an admin-mediated two-step
request_redeem / complete_redeem flow. This leg is operator-managed and includes a batching
minimum (currently $2k) and may be subject to banking-hours delays during the YLDS β USDC
conversion via Circle's CCTP.For most institutional integrators, the recommended path is to stop at wYLDS unless USDC
settlement is strictly required.
Phase 2a: User submits a redemption request
typescriptconst [redemptionRequestPda] = PublicKey.findProgramAddressSync( [Buffer.from("redemption_request"), userPublicKey.toBuffer()], vaultMintProgramId ); const [redeemVaultAuthorityPda] = PublicKey.findProgramAddressSync( [Buffer.from("redeem_vault_authority")], vaultMintProgramId ); // Burns wYLDS from the user and creates a RedemptionRequest PDA. await vaultMint.methods .requestRedeem(new BN(wyldsAmount)) .accountsStrict({ signer: userPublicKey, userMintTokenAccount: userWyldsAta, // user's wYLDS ATA (to burn from) redemptionRequest: redemptionRequestPda, mint: wyldsMint, config: configPda, redeemVaultAuthority: redeemVaultAuthorityPda, systemProgram: SystemProgram.programId, tokenProgram: TOKEN_PROGRAM_ID, }) .rpc();
Phase 2b: Operator fulfills the redemption
The operator calls
complete_redeem once USDC has been bridged via Circle's CCTP. The
RedemptionRequest PDA is closed and rent is returned to the user.typescript// Operator-only: transfers USDC from the redeem vault to the user's wallet. await vaultMint.methods .completeRedeem() .accountsStrict({ admin: operatorPublicKey, user: userPublicKey, userMintTokenAccount: userWyldsAta, // wYLDS ATA (for constraint validation) userVaultTokenAccount: userUsd...