Skip to main content
This quickstart shows you how to use the following Yieldcoin Manager program instructions:
  • Deposit: Convert underlying assets (USDC) into USYC shares
  • Redeem: Convert USYC shares back into underlying assets
The Yieldcoin Manager program is an audited smart contract that handles USYC subscriptions and redemptions on Solana. It includes the teller for processing deposits and redemptions, and a permissioning system for entitled addresses. The TypeScript examples in this quickstart use the Solana Kit library.
The code snippets in this quickstart use Solana devnet addresses. For mainnet addresses, see Smart Contract Addresses.

Prerequisites

Before you begin, ensure that you’ve:
  • Installed Node.js and npm
  • Obtained an entitled address to interact with the teller and hold USYC

Step 1: Set up the project

This step shows you how to install dependencies and generate the client code you need to interact with the Yieldcoin Manager program.

1.1. Install dependencies

Install the Solana Kit library and token program packages:
Shell
npm install @solana/kit @solana-program/token @solana-program/token-2022

1.2. Generate client code

Generate client code from the Yieldcoin Manager IDL using your preferred code generator, such as Codama or other Solana IDL tools. The generated code provides instruction builders similar to the following:
TypeScript
// Example of generated getDepositInstruction function
export function getDepositInstruction<
  TAccountPayer extends string,
  TAccountAssetMint extends string,
  TAccountSenderAssetAccount extends string,
  TAccountTreasuryAssetAccount extends string,
  TAccountFeeRecipientAssetAccount extends string,
  TAccountTeller extends string,
  TAccountShareMint extends string,
  TAccountReceiverShareAccount extends string,
  TAccountReceiverPermissions extends string,
  TAccountAssetTokenProgram extends string,
  TAccountShareTokenProgram extends string,
  TAccountEventAuthority extends string,
  TAccountProgram extends string,
  TProgramAddress extends Address
>(
  input: DepositInput</* ... type parameters ... */>,
  config?: { programAddress?: TProgramAddress }
): DepositInstruction</* ... return type parameters ... */>;

Step 2: Set up the script

This step shows you how to configure imports, constants, and helper functions for interacting with the Yieldcoin Manager program.

2.1. Define imports and constants

Create a new TypeScript file and add the following imports and constants:
TypeScript
import {
  address as toAddress,
  Address,
  appendTransactionMessageInstruction,
  createSolanaRpc,
  createTransactionMessage,
  getAddressEncoder,
  getProgramDerivedAddress,
  Instruction,
  pipe,
  sendAndConfirmTransaction,
  setTransactionMessageFeePayerSigner,
  setTransactionMessageLifetimeUsingBlockhash,
  signTransactionMessageWithSigners,
  TransactionSigner,
} from "@solana/kit";
import {
  fetchMaybeTeller,
  getDepositInstruction,
  getRedeemInstruction,
} from "./generated-client"; // Your generated client code
import { TOKEN_PROGRAM_ADDRESS } from "@solana-program/token";
import { TOKEN_2022_PROGRAM_ADDRESS } from "@solana-program/token-2022";

// Program address (devnet)
const YIELDCOIN_MANAGER_PROGRAM_ADDRESS =
  "8FZQeFy39fNK47ebUd7aUWoK1rLgJ7fsteWev3FPP1Aw" as Address;

// Program seeds for PDA derivation
const PROGRAM_SEEDS = {
  USER_PERMISSIONS: "user_permissions",
  EVENT_AUTHORITY: "__event_authority",
  TELLER: "teller",
};

const ONE_HUNDRED_USDC = 100_000000n;
const ONE_HUNDRED_USYC = 100_000000n;

2.2. Define helper functions

Add the following helper functions to derive PDAs, fetch teller data, and send transactions:
TypeScript
// Get all public PDAs
export const getPublicPDAs = async () => {
  const programAddress = YIELDCOIN_MANAGER_PROGRAM_ADDRESS;

  const [tellerPda] = await getProgramDerivedAddress({
    programAddress,
    seeds: [PROGRAM_SEEDS.TELLER],
  });

  const [eventAuthorityPda] = await getProgramDerivedAddress({
    programAddress,
    seeds: [PROGRAM_SEEDS.EVENT_AUTHORITY],
  });

  return { tellerPda, eventAuthorityPda };
};

// Get user permissions PDA - derived from wallet address
export const getUserPermissionsPDA = async (
  walletAddress: Address,
): Promise<Address> => {
  const addressEncoder = getAddressEncoder();
  const [userPermissionsPda] = await getProgramDerivedAddress({
    programAddress: YIELDCOIN_MANAGER_PROGRAM_ADDRESS,
    seeds: [
      PROGRAM_SEEDS.USER_PERMISSIONS,
      addressEncoder.encode(walletAddress),
    ],
  });
  return userPermissionsPda;
};

// Fetch teller account data
export const fetchTellerData = async (rpcEndpoint: string) => {
  const rpc = createSolanaRpc(rpcEndpoint);
  const { tellerPda } = await getPublicPDAs();
  const tellerAccount = await fetchMaybeTeller(rpc, tellerPda);
  if (!tellerAccount.exists || !tellerAccount.data) {
    throw new Error("Teller account not found");
  }
  return tellerAccount.data;
};

// Send a transaction with a single instruction
export const sendTransaction = async (
  rpcEndpoint: string,
  instruction: Instruction,
  feePayerSigner: TransactionSigner,
) => {
  const rpc = createSolanaRpc(rpcEndpoint);

  const { value: latestBlockhash } = await rpc
    .getLatestBlockhash({ commitment: "confirmed" })
    .send();

  const transactionMessage = pipe(
    createTransactionMessage({ version: 0 }),
    (m) => setTransactionMessageFeePayerSigner(feePayerSigner, m),
    (m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),
    (m) => appendTransactionMessageInstruction(instruction, m),
  );

  const signedTx = await signTransactionMessageWithSigners(transactionMessage);

  return await sendAndConfirmTransaction(rpc, signedTx, {
    commitment: "confirmed",
  });
};

Step 3: Subscribe and redeem

This step shows you how to subscribe to USYC (deposit USDC) and redeem USYC (withdraw USDC) using the generated contract functions.

3.1. Subscribe to USYC (deposit)

The deposit operation converts USDC into USYC shares. Add the following code to create a deposit instruction and execute it:
TypeScript
export const createUSYCSubscribeInstruction = async (
  walletAddress: Address,
  senderAssetAccount: Address,
  receiverShareAccount: Address,
  assetAmount: bigint,
  tellerData: any, // Fetched from teller account
) => {
  const { tellerPda, eventAuthorityPda } = await getPublicPDAs();
  // Permissions PDA is derived from the wallet address
  const receiverPermissions = await getUserPermissionsPDA(walletAddress);
  const programAddress = YIELDCOIN_MANAGER_PROGRAM_ADDRESS;

  const depositInstruction = getDepositInstruction(
    {
      payer: walletAddress,
      assetMint: tellerData.assetMint,
      senderAssetAccount,
      treasuryAssetAccount: tellerData.treasuryAssetAccount,
      feeRecipientAssetAccount: tellerData.feeRecipientAssetAccount,
      teller: tellerPda,
      shareMint: tellerData.shareMint,
      receiverShareAccount,
      receiverPermissions,
      assetTokenProgram: TOKEN_PROGRAM_ADDRESS,
      shareTokenProgram: TOKEN_2022_PROGRAM_ADDRESS,
      eventAuthority: eventAuthorityPda,
      program: programAddress,
      assets: assetAmount,
      receiver: walletAddress,
    },
    { programAddress },
  );

  return depositInstruction;
};

// Example usage
export const subscribeExample = async (signer: TransactionSigner) => {
  const walletAddress = "YourWalletAddressHere";
  const senderAssetAccount: Address = "YourUSDCTokenAccountHere";
  const receiverShareAccount: Address = "YourUSYCTokenAccountHere";

  // Fetch teller data first to get mints and treasury addresses
  const tellerData = await fetchTellerData("https://api.devnet.solana.com");

  const instruction = await createUSYCSubscribeInstruction(
    walletAddress,
    senderAssetAccount,
    receiverShareAccount,
    ONE_HUNDRED_USDC,
    tellerData,
  );

  return await sendTransaction(
    "https://api.devnet.solana.com",
    instruction,
    signer,
  );
};

3.2. Redeem USYC

The redeem operation converts USYC shares back into USDC. Add the following code to create a redeem instruction and execute it:
TypeScript
export const createUSYCRedeemInstruction = async (
  walletAddress: Address,
  senderShareAccount: Address,
  receiverAssetAccount: Address,
  shareAmount: bigint,
  tellerData: any, // Fetched from teller account
) => {
  const { tellerPda, eventAuthorityPda } = await getPublicPDAs();
  // Permissions PDA is derived from the wallet address
  const senderPermissions = await getUserPermissionsPDA(walletAddress);
  const programAddress = YIELDCOIN_MANAGER_PROGRAM_ADDRESS;

  const redeemInstruction = getRedeemInstruction(
    {
      payer: walletAddress,
      assetMint: tellerData.assetMint,
      receiverAssetAccount,
      treasuryAssetAccount: tellerData.treasuryAssetAccount,
      feeRecipientAssetAccount: tellerData.feeRecipientAssetAccount,
      teller: tellerPda,
      shareMint: tellerData.shareMint,
      senderShareAccount,
      senderPermissions,
      assetTokenProgram: TOKEN_PROGRAM_ADDRESS,
      shareTokenProgram: TOKEN_2022_PROGRAM_ADDRESS,
      eventAuthority: eventAuthorityPda,
      program: programAddress,
      shares: shareAmount,
      receiver: walletAddress,
    },
    { programAddress },
  );

  return redeemInstruction;
};

// Example usage
export const redeemExample = async (signer: TransactionSigner) => {
  const walletAddress = "YourWalletAddressHere";
  const senderShareAccount: Address = "YourUSYCTokenAccountHere";
  const receiverAssetAccount: Address = "YourUSDCTokenAccountHere";

  // Fetch teller data first to get mints and treasury addresses
  const tellerData = await fetchTellerData("https://api.devnet.solana.com");

  const instruction = await createUSYCRedeemInstruction(
    walletAddress,
    senderShareAccount,
    receiverAssetAccount,
    ONE_HUNDRED_USYC,
    tellerData,
  );

  return await sendTransaction(
    "https://api.devnet.solana.com",
    instruction,
    signer,
  );
};