Documentation Index
Fetch the complete documentation index at: https://developers.circle.com/llms.txt
Use this file to discover all available pages before exploring further.
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 USYC 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:
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:
// 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:
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:
// 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:
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:
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,
);
};