> For the complete documentation index, see [llms.txt](https://sodex.com/documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sodex.com/documentation/for-developers/developers/wealth/vault-lifecycle/signed-deposits.md).

# Signed Deposits

Authorize a deposit with ERC-2612 and EIP-712 signatures, then submit it from a separate gas-paying wallet. See [Vault Lifecycle](/documentation/for-developers/developers/wealth/vault-lifecycle.md) for the settlement stages.

The underlying asset permit authorizes the vault as spender. A prior asset approval also works: if the allowance is sufficient, the permit signature can be `0x`. The vault's EIP-712 `RequestDeposit` signature is still required, using `vault.name()`, version `"1"`, and `vault.nonces(onBehalf)`.

## 1. Configure the client

Install dependencies with `npm install viem tsx`. Save the setup and the following function together in a `.ts` file. Configure `USER_PK` and `RELAYER_PK` locally; never send private keys to an API.

The addresses and amounts below are the CXMT/vUSDC example. Confirm current addresses with [Fund Information](/documentation/for-developers/api-reference/wealth-api/funds.md#get-fund-information), minimum amounts, balances, and the selected network before running. This example submits from a local gas-paying wallet (`RELAYER_PK`). To have SoDEX broadcast the signed `OnBehalf` call, use the [Transaction Relayer](/documentation/for-developers/developers/valuechain-evm/relayer.md).

Set `referral` from [Referrals](/documentation/for-developers/developers/wealth/referrals.md) before signing; the example `bytes32(0)` means no code.

```typescript
import {
  type Address,
  type Hex,
  createWalletClient,
  createPublicClient,
  defineChain,
  http,
  parseAbi,
  parseUnits,
} 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 VaultAbi = parseAbi([
  "function name() view returns (string)",
  "function nonces(address owner) view returns (uint256)",
  "function requestDepositOnBehalf(address onBehalf, uint256 assets, uint256 deadline, bytes permitSignature, bytes requestSignature, bytes32 referral)",
  "function requestRedeemOnBehalf(address onBehalf, uint256 shares, uint256 deadline, bytes32 referral, bytes signature)",
]);

const vault = "0x9876b381a94876eA8FeA56b625E15556fB450629" as Address; // CXMT Vault
const asset = "0xcb7F80Dff2727c791fA491722c428e6657f7e2c6" as Address; // vUSDC
const onBehalf = privateKeyToAccount(process.env.USER_PK as Hex);
const relayer = privateKeyToAccount(process.env.RELAYER_PK as Hex);

const publicClient = createPublicClient({ chain: valuechain, transport: http() });
const userClient = createWalletClient({ account: onBehalf, chain: valuechain, transport: http() });
const relayerClient = createWalletClient({ account: relayer, chain: valuechain, transport: http() });
```

## 2. Submit a deposit request

```typescript
async function exampleRequestDepositOnBehalf() {
  const assets = parseUnits("100", 6); // >= minDepositAmount
  const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600);
  const referral = "0x0000000000000000000000000000000000000000000000000000000000000000" as Hex;

  const [vaultName, vaultNonce, assetName, assetNonce, chainId] = await Promise.all([
    publicClient.readContract({ address: vault, abi: VaultAbi, functionName: "name" }),
    publicClient.readContract({ address: vault, abi: VaultAbi, functionName: "nonces", args: [onBehalf.address] }),
    publicClient.readContract({ address: asset, abi: VaultAbi, functionName: "name" }),
    publicClient.readContract({ address: asset, abi: VaultAbi, functionName: "nonces", args: [onBehalf.address] }),
    publicClient.getChainId(),
  ]);

  // 1) ERC-2612 permit: owner authorizes the vault to pull assets
  const permitSignature = await userClient.signTypedData({
    domain: { name: assetName, version: "1", chainId, verifyingContract: asset },
    types: {
      Permit: [
        { name: "owner", type: "address" },
        { name: "spender", type: "address" },
        { name: "value", type: "uint256" },
        { name: "nonce", type: "uint256" },
        { name: "deadline", type: "uint256" },
      ],
    },
    primaryType: "Permit",
    message: {
      owner: onBehalf.address,
      spender: vault,
      value: assets,
      nonce: assetNonce,
      deadline,
    },
  });

  // 2) Vault RequestDeposit: authorizes the on-behalf deposit
  const requestSignature = await userClient.signTypedData({
    domain: { name: vaultName, version: "1", chainId, verifyingContract: vault },
    types: {
      RequestDeposit: [
        { name: "owner", type: "address" },
        { name: "assets", type: "uint256" },
        { name: "nonce", type: "uint256" },
        { name: "deadline", type: "uint256" },
        { name: "referral", type: "bytes32" },
      ],
    },
    primaryType: "RequestDeposit",
    message: {
      owner: onBehalf.address,
      assets,
      nonce: vaultNonce,
      deadline,
      referral,
    },
  });

  // 3) Any relayer can submit; the request is owned by onBehalf
  const hash = await relayerClient.writeContract({
    address: vault,
    abi: VaultAbi,
    functionName: "requestDepositOnBehalf",
    args: [onBehalf.address, assets, deadline, permitSignature, requestSignature, referral],
  });

  return hash;
}
```

To submit, append `exampleRequestDepositOnBehalf().then(console.log).catch(console.error);` and run `npx tsx deposit.ts`.

## 3. Confirm submission and track settlement

Save the returned transaction hash before waiting. With the same client setup, append this after the selected function instead of the one-line invocation above:

```typescript
async function submitAndConfirm() {
  const hash = await exampleRequestDepositOnBehalf();
  console.log("Submitted transaction:", hash);
  const receipt = await publicClient.waitForTransactionReceipt({ hash });
  if (receipt.status !== "success") throw new Error("Vault request reverted");
  console.log("Request included in block:", receipt.blockNumber.toString());
}
submitAndConfirm().catch(console.error);
```

A successful receipt confirms execution of the deposit request, not completion of settlement. Query the user's fund timeline with `USER_ADDRESS` set to the request owner:

```bash
curl -sS "https://mainnet-gw.sodex.dev/api/v1/wealth/users/$USER_ADDRESS/fund-details?fundId=CXMT" \
  -H "Accept: application/json"
```

SoDEX normally claims shares for the user after settlement. Verify the owner's share balance; call `claimDeposit` only if the settled request was not auto-claimed. See [User Fund History](/documentation/for-developers/api-reference/wealth-api/user-fund-history.md) for history fields.

Continue with [Settlement and Claims](/documentation/for-developers/developers/wealth/vault-lifecycle/settlement-and-claims.md).
