@reflectmoney/whitelabel
v1.0.1
Published
Interact with the Reflect Whitelabel program — on-chain infrastructure for minting and redeeming whitelabel yield-bearing stablecoins backed by Reflect collateral. The SDK exposes high-level helpers for creating proxies, issuing branded stablecoins, depos
Readme
Reflect Whitelabel SDK
Interact with the Reflect Whitelabel program — on-chain infrastructure for minting and redeeming whitelabel yield-bearing stablecoins backed by Reflect collateral. The SDK exposes high-level helpers for creating proxies, issuing branded stablecoins, deposits/withdrawals, fee management, and asset registry maintenance.
Supported underlying assets. Any whitelisted stablecoin mint can be used. Whitelisting is on-chain via an Asset PDA (per stablecoin mint) holding the oracle and active flag. See Supported assets.
Installation
With npm:
npm install @reflectmoney/whitelabelWith yarn:
yarn add @reflectmoney/whitelabelQuick Start
import { Proxy, USDC_PLUS_MINT } from "@reflectmoney/whitelabel";
import { createSolanaRpc, address } from "@solana/kit";
const connection = createSolanaRpc("https://api.mainnet-beta.solana.com");
// Create a new proxy
const createProxyIx = await Proxy.initialize({
signer,
brandedMint: address("<branded mint address>"),
feeBps: 100, // 1%
stablecoinMint: USDC_PLUS_MINT,
});To work with an existing proxy, prefer loadFromMint — it derives the proxy state PDA from the branded mint and loads the on-chain state for you:
const proxy = await Proxy.loadFromMint({
connection,
brandedMint: address("<your branded mint address>"),
});If you already know the proxy state address, you can use the constructor and call load() yourself:
const proxy = new Proxy({
connection,
proxyStateAddress: address("<proxy state pda>"),
});
await proxy.load();The SDK's instruction builders return raw
Instructionobjects. Sign and send them with your preferred transaction layer (@solana/kit,@solana/web3.js, etc).
Proxy
Construction & loading
Proxy.initialize(params)
Build a CreateProxy instruction.
signer: TransactionSigner— pays for the transaction.authority?: TransactionSigner— proxy authority. Defaults tosigner.brandedMint: Address— branded token mint (must already exist; mint authority must be the derived proxy state PDA).stablecoinMint: Address— underlying stablecoin (e.g.USDC_PLUS_MINT).feeBps: number— integrator fee in basis points (100= 1%).
Returns the CreateProxy instruction.
Proxy.loadFromMint(params)
Derives the proxy state PDA from a branded mint and returns a fully-loaded Proxy instance.
connection: Rpc<SolanaRpcApi>brandedMint: Address
Returns Promise<Proxy>.
new Proxy({ connection, proxyStateAddress })
Constructs a wrapper for a known proxy state account. Call load() before using any instance method that depends on the proxy state.
load()
Fetches and caches the on-chain ProxyState. Call again to refresh after a state-mutating transaction.
Deposits & withdrawals
proxy.deposit(params)
Wrap stablecoin into branded tokens.
signer: TransactionSigner— token owner.amount: number— stablecoin amount (raw units).minimumReceived: number— slippage protection. Set to0to disable.
Returns the Wrap instruction. The user's branded ATA must already exist — initialize it with proxy.initializeUserBrandedTokenAccount(...) if needed.
proxy.withdraw(params)
Unwrap branded tokens back into stablecoin.
signer: TransactionSigneramount: number— branded amount (raw units).minimumReceived: number
Returns the Unwrap instruction.
proxy.claimFees({ authority })
Authority-only. Drains accrued integrator commission to the authority's stablecoin ATA.
authority: TransactionSigner— must be the proxy authority.
Simulation
Both methods replicate on-chain math without sending a transaction.
proxy.simulateWrap({ inputAmount, oraclePrice, oraclePrecision, vaultBalance })
Returns { outputAmount, newPrincipal, newIntegratorsCommission } (all bigint).
proxy.simulateUnwrap({ inputAmount, oraclePrice, oraclePrecision, vaultBalance })
Same return shape, for the reverse direction.
Use proxy.getOraclePrice() and the vault's token balance to source the inputs.
State accessors
| Method | Description |
| --- | --- |
| getProxyState() | Cached ProxyState struct from the last load(). |
| getProxyStateAddress() | Proxy state PDA. |
| getBrandedMint() | Branded token mint address. |
| getStablecoinMint() | Underlying stablecoin mint. |
| getBrandedMintData() | Live Mint account fetch for the branded token. |
| getStablecoinMintData() | Live Mint account fetch for the stablecoin. |
| getOraclePrice() | { price, precision } from the asset's oracle (Doppler / Pyth / Pyth Lazer auto-detected). |
| getExchangeRateToUnderlying() | Branded → stablecoin exchange rate scaled by EXCHANGE_RATE_PRECISION. |
Token-account helpers
proxy.initializeStablecoinProxyStateVault(payer)
Creates the proxy's stablecoin vault ATA. Required once per proxy before the first deposit.
Proxy.initializeStablecoinProxyStateVaultStateless(payer, brandedMint, stablecoinMint)
Same as above without needing a loaded Proxy instance.
proxy.initializeUserBrandedTokenAccount({ user, payer? })
Creates a user's branded-token ATA. Useful to bundle ahead of deposit/withdraw.
Authority & configuration
All authority-mutating instructions require the proxy authority as signer.
| Method | Effect |
| --- | --- |
| proxy.updateAuthority({ authority, newAuthority }) | Transfers proxy authority. |
| proxy.updateFee({ authority, feeBps }) | Updates the integrator fee. Fails if fee has been frozen. |
| proxy.updateDepositCap({ authority, depositCap }) | Sets max principal allowed. 0 = unlimited. |
| proxy.freezeFee({ authority }) | Permanent: locks the current fee value. Cannot be undone. |
| proxy.manageFreezeTokenAccount({ freeze, tokenAccount, authority }) | Freeze/thaw a branded token account. freeze: true → FreezeTokenAccount; freeze: false → ThawTokenAccount. |
Asset registry (protocol-authority only)
Asset accounts are protocol-wide PDAs that bind a stablecoin mint to its oracle. All proxies sharing a stablecoin share the same Asset.
| Method | Effect |
| --- | --- |
| Proxy.createAsset({ authority, payer, stablecoinMint, oracle }) | Registers a new asset. |
| Proxy.deactivateAsset({ authority, stablecoinMint }) | Marks the asset inactive (blocks future wraps). |
| Proxy.updateOracle({ authority, stablecoinMint, newOracle }) | Points the asset at a new oracle. |
Migration
Proxy.migrateProxy({ payer, brandedMint })
Permissionless. Migrates a legacy ProxyState (pre-frozen or pre-deposit_cap layout) to the current layout. Anybody can pay the additional rent. The instruction rejects accounts already at the current size.
Discovery
These static helpers query the program for accounts matching the current layout sizes.
| Method | Returns |
| --- | --- |
| Proxy.getAllIntegrations(connection) | All ProxyState accounts at the current size. |
| Proxy.getAllIntegrationsByAuthority(connection, authority) | All ProxyState accounts owned by a given authority (memcmp on the authority field). |
| Proxy.getAllAssets(connection) | All Asset registry entries. |
Each entry has shape { pubkey: Address, data: ProxyState | Asset }.
PdaClient
import { PdaClient } from "@reflectmoney/whitelabel";| Method | Returns [Address, number] for |
| --- | --- |
| PdaClient.deriveProxyState(brandedMint) | ("proxy", brandedMint) |
| PdaClient.deriveAuthority() | ("auth") — protocol authority PDA |
| PdaClient.deriveAsset(stablecoinMint) | ("asset", stablecoinMint) |
Constants
Exported from the package root:
| Name | Value |
| --- | --- |
| REFLECT_PROXY_PROGRAM_PROGRAM_ADDRESS | Program ID. |
| USDC_MINT | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
| USDC_PLUS_MINT | usd63SVWcKqLeyNHpmVhZGYAqfE5RHE8jwqjRA2ida2 |
| EXCHANGE_RATE_PRECISION | 10_000 — scale used by getExchangeRateToUnderlying. |
| PROXY_STATE_SEED, AUTHORITY_SEED, ASSET_SEED | PDA seed prefixes. |
Examples
End-to-end deposit (with ATA bootstrap)
import { Proxy } from "@reflectmoney/whitelabel";
import { findAssociatedTokenPda, TOKEN_PROGRAM_ADDRESS } from "@solana-program/token";
import { fetchEncodedAccount } from "@solana/kit";
const proxy = await Proxy.loadFromMint({ connection, brandedMint });
const instructions = [];
// Make sure the user has a branded-token ATA
const [brandedAta] = await findAssociatedTokenPda({
mint: brandedMint,
owner: signer.address,
tokenProgram: TOKEN_PROGRAM_ADDRESS,
});
if (!(await fetchEncodedAccount(connection, brandedAta)).exists) {
instructions.push(
await proxy.initializeUserBrandedTokenAccount({ user: signer })
);
}
instructions.push(
await proxy.deposit({
signer,
amount: 1_000_000, // 1 USDC+ (6 decimals)
minimumReceived: 990_000, // accept up to ~1% slippage
})
);Reading the live exchange rate
const proxy = await Proxy.loadFromMint({ connection, brandedMint });
const rate = await proxy.getExchangeRateToUnderlying();
// rate / EXCHANGE_RATE_PRECISION = branded → stablecoin ratioListing every proxy on the cluster
const integrations = await Proxy.getAllIntegrations(connection);
for (const { pubkey, data } of integrations) {
console.log(pubkey, data.brandedMint, data.fee);
}Supported assets
| Name | Stablecoin mint | Asset PDA | Oracle |
| --- | --- | --- | --- |
| USDC+ | usd63SVWcKqLeyNHpmVhZGYAqfE5RHE8jwqjRA2ida2 | <asset pda> | <oracle address> |
| | | | |
| | | | |
Support
- Repository: https://github.com/palindrome-eng/reflect-proxy-program
- Authors: L0STE, stablecoinjesus @ Palindrome Engineering
License
MIT
