Direct Redemptions
Submit an escrow_vault redemption from the user's wallet. The wallet pays ValueChain gas. This is not a signed OnBehalf call and not a SoDEX RWA (clob) trade. See Vault Lifecycle for settlement. Encode referral first (Referrals).
Direct requestRedeem takes builderFeeData before referral. Pass bytes32(0) when no fee applies. Encoding and approval are in Builder Integration. Signed requestRedeemOnBehalf has no builder-fee argument.
The request spends vault shares the user already holds. No underlying-asset approval is required. Credit is the underlying asset on ValueChain, not Spot or Perps. A successful requestRedeem does not return assets in the same transaction. Immediate settlement is not delayed settlement. Claim only if a leg was not auto-delivered — that step is on Settlement and Claims.
1. Select the vault
Load contracts.vault from Fund Information for an escrow_vault fundId. Check fundStatus and chainConfig.paused. getMinRedeemShares() is raw vault-share units; RAW_AMOUNT uses that same unit.
2. Submit requestRedeem
Install viem and tsx. Set PRIVATE_KEY, VAULT_ADDRESS, RECEIVER, RAW_AMOUNT (share base units), and REFERRAL (the bytes32 from Referrals, or omit it for bytes32(0)). Set BUILDER_FEE_DATA to a packed bytes32 from Builder Integration, or omit it for bytes32(0). This broadcasts a redemption request.
Save as direct-redeem.mts and run npx tsx direct-redeem.mts.
import {
createPublicClient, createWalletClient, defineChain, http, parseAbi, zeroHash,
type Address, type Hex,
} from "viem";
import { privateKeyToAccount } from "viem/accounts";
const valuechain = defineChain({
id: 286623, name: "ValueChain",
nativeCurrency: { name: "SOSO", symbol: "SOSO", decimals: 18 },
rpcUrls: { default: { http: ["https://mainnet.valuechain.xyz"] } },
});
const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);
const publicClient = createPublicClient({ chain: valuechain, transport: http() });
const walletClient = createWalletClient({ account, chain: valuechain, transport: http() });
const vault = process.env.VAULT_ADDRESS as Address;
const receiver = process.env.RECEIVER as Address;
const shares = BigInt(process.env.RAW_AMOUNT!);
const referral = (process.env.REFERRAL as Hex | undefined) ?? zeroHash;
const builderFeeData = (process.env.BUILDER_FEE_DATA as Hex | undefined) ?? zeroHash;
const vaultAbi = parseAbi([
"function getMinRedeemShares() view returns (uint256)",
"function balanceOf(address account) view returns (uint256)",
"function requestRedeem(uint256 shares, address receiver, bytes32 builderFeeData, bytes32 referral)",
]);
const [minShares, balance] = await Promise.all([
publicClient.readContract({ address: vault, abi: vaultAbi, functionName: "getMinRedeemShares" }),
publicClient.readContract({ address: vault, abi: vaultAbi, functionName: "balanceOf", args: [account.address] }),
]);
if (shares < minShares) throw new Error(`Shares below min redeem: ${minShares}`);
if (balance < shares) throw new Error(`Insufficient vault shares: have ${balance}`);
const hash = await walletClient.writeContract({
address: vault, abi: vaultAbi, functionName: "requestRedeem",
args: [shares, receiver, builderFeeData, referral],
});
console.log({ requestTxHash: hash });
const receipt = await publicClient.waitForTransactionReceipt({ hash });
if (receipt.status !== "success") throw new Error("requestRedeem reverted");Save requestTxHash. Do not use getNextRedeemRequestId as the claim ID.
3. Track settlement
Query Settlement and Claims with that hash and KIND=redeem. Empty or processing records do not confirm assets. Keep tracking a delayed leg even if the immediate leg is done. Do not send a second redemption to retry a status query.
To submit with a user signature instead of this wallet paying gas, use Signed Redemptions and the Transaction Relayer.
Last updated