> ## Documentation Index
> Fetch the complete documentation index at: https://rain-sandbox-trial.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Card Funding and Settlement

> Learn how Rain embedded wallets fund card spend through collateral contracts, including funding, withdrawals, and settlement.

<Badge className="badge-rain-pink">Applies to: Rain-managed</Badge>

In Rain managed programs, each cardholder or company has a collateral contract owned by their registered wallet. Users transfer funds from their wallet into the contract to fund card spending. Rain manages transaction authorization and settlement, while users retain control of their wallets.

This guide covers the full process, from registering a wallet and creating a collateral contract to funding card spend and withdrawing collateral.

Implementation steps are labeled **App** for actions performed through the embedded wallet SDK and **Backend** for requests made to the Rain API using your API key.

For partner managed programs, see [Flow of funds](/docs/embedded-wallets/flow-of-funds).

## Card funding workflow

<Steps>
  <Step title="Create the wallet">
    <Badge className="badge-rain-pink">App</Badge> The user logs in for the first time and the wallet is created. Read its addresses and send them to your backend.

    ```swift theme={null}
    let evm = try await client.getWalletAddress()
    let sol = try await client.getWalletAddress(chainId: RainChain.solanaMainnet)   // if you fund on Solana
    ```

    See [Wallet creation](/docs/embedded-wallets/wallet-creation).
  </Step>

  <Step title="Create the application with the wallet address">
    <Badge className="badge-rain-pink">Backend</Badge> Create the user's consumer application with `walletAddress` (and `solanaAddress` where relevant), then run KYC as usual. For a corporate program, the wallet goes on `initialUser.walletAddress` of the company application.

    ```http theme={null}
    POST /v1/issuing/applications/user
    { "...": "...", "walletAddress": "0x3cA8…C0Ff", "solanaAddress": "7Np4…T4K2" }
    ```

    Full schemas: [Create a consumer application](/reference/applications/create-a-consumer-application-for-a-user), [Create a corporate application](/reference/applications/create-a-corporate-application-for-a-company).
  </Step>

  <Step title="Receive the collateral contract">
    <Badge className="badge-rain-pink">Backend</Badge> When the application is approved, Rain deploys the user's collateral contract on your program's chain and sends [`contract.created`](/docs/account-and-reports#contract-created). The wallet on the application is the contract's owner and appears in `adminAddresses`. You can read the contract at any time:

    ```http theme={null}
    GET /v1/issuing/users/{userId}/contracts
    ```

    ```json Response (abbreviated) theme={null}
    [{
      "id": "cc_abc123",
      "chainId": 8453,
      "proxyAddress": "0xabcd…ef12",
      "controllerAddress": "0x9876…5432",
      "depositAddress": "0xfedc…ba09",
      "adminAddresses": ["0x3cA8…C0Ff"],
      "tokens": [{ "address": "0x8335…2913", "balance": "0", "exchangeRate": 1, "advanceRate": 0.9 }]
    }]
    ```

    The `tokens` entries carry no symbol or decimals. Resolve them in the app with `rain.tokenMetadata(chainId:address:)`, which returns `nil` rather than guessing when the decimals can't be established.
  </Step>

  <Step title="Fund the contract from the wallet">
    <Badge className="badge-rain-pink">Backend</Badge> Hand the app the contract's `depositAddress` (or `proxyAddress` when no deposit address is returned) and the accepted token addresses.

    <Badge className="badge-rain-pink">App</Badge> Funding is an ordinary token send from the wallet to that address, so it inherits everything from [Sending funds](/docs/embedded-wallets/sending-funds), including gas sponsorship.

    <CodeGroup>
      ```swift iOS theme={null}
      let result = try await client.sendToken(
          chainId: contract.chainId,
          contractAddress: usdcOnBase,
          to: contract.depositAddress ?? contract.proxyAddress,
          amount: 250
      )
      ```

      ```kotlin Android theme={null}
      val result = client.sendToken(
          chainId = contract.chainId,
          contractAddress = usdcOnBase,
          to = contract.depositAddress ?: contract.proxyAddress,
          amount = BigDecimal("250"),
      )
      ```
    </CodeGroup>

    Users can also fund the contract from anywhere else (an exchange withdrawal straight to the deposit address, for example); the wallet is the convenient path, not the only one.

    <Warning>
      Only tokens listed on the contract count as collateral; send anything else there and it neither funds the card nor comes back through the withdrawal flow. Check `tokens` before offering a token in the funding UI.
    </Warning>
  </Step>

  <Step title="Watch spending power update">
    <Badge className="badge-rain-pink">Backend</Badge> Rain detects the deposit and sends a [`transaction.created` webhook of type `collateral`](/docs/transaction#collateral). Spending power updates within minutes; read it with `GET /v1/issuing/users/{userId}/balances` and show it in the app next to the wallet balance. Neither the wallet balance nor the on-chain contract balance is the spending limit; the balances endpoint is.
  </Step>

  <Step title="Issue the card and spend">
    <Badge className="badge-rain-pink">Backend</Badge> Create the card with [`POST /v1/issuing/users/{userId}/cards`](/reference/cards/create-a-card-for-a-user). From here the wallet isn't involved in a purchase: Rain authorizes each transaction against the user's spending power, settles with the network by liquidating collateral from the contract, and keeps the ledger. You consume `transaction.created`, `transaction.updated`, and `transaction.completed` webhooks to show card activity; there's no approval webhook to build. See [Transaction lifecycle](/docs/transaction-lifecycle) and [Managing collateral](/docs/managing-collateral).
  </Step>

  <Step title="Withdraw remaining collateral">
    <Badge className="badge-rain-pink">Backend</Badge> + <Badge className="badge-rain-pink">App</Badge> The user takes collateral back out by signing a withdrawal with the wallet, authorized by a signature your backend fetches from Rain. Details below.
  </Step>
</Steps>

## Withdrawals

Users can withdraw available collateral from their collateral contract to any recipient address. Each withdrawal requires two authorizations: Rain approves the amount available for withdrawal through an **admin signature**, and the contract's owner wallet signs the transaction.

Your backend retrieves Rain's admin signature, while your app uses the wallet SDK to sign and submit the withdrawal.

<div className="wf-diagram">
  <div className="diagram-shell">
    <svg id="wallet-withdraw-flow" role="img" aria-label="The withdrawal flow: the app asks your backend for a withdrawal, your backend requests an admin signature from the Rain API and returns it with the contract addresses, the app calls withdrawCollateral so the Rain wallet signs and broadcasts, and the chain returns a transaction hash." viewBox="0 0 1080 724" width="1080" height="724" style={{width: "100%", height: "auto"}}><defs><marker id="arrow" viewBox="0 0 10 10" refX="8" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0,0 L10,5 L0,10 z" className="marker-fill-default" /></marker></defs><line x1="150" y1="70" x2="150" y2="712" className="lane-line" stroke-width="1.5" stroke-dasharray="2 6" /><line x1="400" y1="70" x2="400" y2="712" className="lane-line" stroke-width="1.5" stroke-dasharray="2 6" /><line x1="650" y1="70" x2="650" y2="712" className="lane-line" stroke-width="1.5" stroke-dasharray="2 6" /><line x1="900" y1="70" x2="900" y2="712" className="lane-line" stroke-width="1.5" stroke-dasharray="2 6" /><path d="M 150 168 C 150 184, 400 184, 400 200" fill="none" className="conn-default" stroke-width="1.7" marker-end="url(#arrow)" opacity="0.92" /><path d="M 400 268 C 400 284, 650 284, 650 300" fill="none" className="conn-default" stroke-width="1.7" marker-end="url(#arrow)" opacity="0.92" /><path d="M 650 368 C 650 384, 400 384, 400 400" fill="none" className="conn-default" stroke-width="1.7" marker-end="url(#arrow)" opacity="0.92" /><path d="M 400 468 C 400 484, 150 484, 150 500" fill="none" className="conn-default" stroke-width="1.7" marker-end="url(#arrow)" opacity="0.92" /><path d="M 150 568 C 150 584, 900 584, 900 600" fill="none" className="conn-default" stroke-width="1.7" marker-end="url(#arrow)" opacity="0.92" /><foreignObject x="56" y="16" width="188" height="54"><div className="lane-head"><span className="ico">📱</span><span className="nm">Mobile App</span></div></foreignObject><foreignObject x="306" y="16" width="188" height="54"><div className="lane-head"><span className="ico">🖥️</span><span className="nm">Your Backend</span></div></foreignObject><foreignObject x="556" y="16" width="188" height="54"><div className="lane-head rain"><span className="ico">🌧️</span><span className="nm">Rain API</span></div></foreignObject><foreignObject x="806" y="16" width="188" height="54"><div className="lane-head"><span className="ico">⛓️</span><span className="nm">Blockchain</span></div></foreignObject><foreignObject x="56" y="100" width="188" height="68"><div className="card action"><span className="bn">1</span><span className="ct"><span className="tag">App → Backend</span><span className="lab">Withdraw: chain, token, amount, recipient</span></span></div></foreignObject><foreignObject x="306" y="200" width="188" height="68"><div className="card action"><span className="bn">2</span><span className="ct"><span className="tag">Backend → Rain API</span><span className="lab mono">GET …/signatures/withdrawals (Api-Key)</span></span></div></foreignObject><foreignObject x="556" y="300" width="188" height="68"><div className="card action"><span className="bn">3</span><span className="ct"><span className="tag">Rain API → Backend</span><span className="lab">status ready: signature, salt, expiresAt</span></span></div></foreignObject><foreignObject x="306" y="400" width="188" height="68"><div className="card action"><span className="bn">4</span><span className="ct"><span className="tag">Backend → App</span><span className="lab">Contract addresses + admin signature</span></span></div></foreignObject><foreignObject x="56" y="500" width="188" height="68"><div className="card action"><span className="bn">5</span><span className="ct"><span className="tag">App → Chain</span><span className="lab mono">withdrawCollateral(…) signs & broadcasts</span></span></div></foreignObject><foreignObject x="806" y="600" width="188" height="68"><div className="card action"><span className="bn">6</span><span className="ct"><span className="tag">Chain → App</span><span className="lab">Transaction hash</span></span></div></foreignObject></svg>
  </div>
</div>

<Steps>
  <Step title="Fetch the admin signature">
    <Badge className="badge-rain-pink">Backend</Badge> Request a signature for the exact withdrawal: chain, token, amount in the token's base units, the admin (the user's wallet address), and the recipient.

    ```http theme={null}
    GET /v1/issuing/users/{userId}/signatures/withdrawals
        ?chainId=8453
        &token=0x8335…2913
        &amount=100000000            # 100 USDC in base units
        &adminAddress=0x3cA8…C0Ff    # the user's wallet
        &recipientAddress=0x3cA8…C0Ff
        &isAmountNative=true
    ```

    ```json Response theme={null}
    {
      "status": "ready",
      "signature": { "salt": "0x…", "data": "0x…" },
      "expiresAt": "1727000000"
    }
    ```

    A `pending` status with `retryAfter` means Rain is still computing it; wait and retry. Treat anything other than `ready` with non-empty `signature.data` as not ready. Return the signature and the contract's `proxyAddress` and `controllerAddress` to the app. Full reference: [Get withdrawal signature for a user](/reference/signatures/get-withdrawal-signature-for-a-user).
  </Step>

  <Step title="Execute the withdrawal">
    <Badge className="badge-rain-pink">App</Badge> Assemble the addresses and the signature and call `withdrawCollateral`. The SDK checks that the wallet is an admin of the contract before signing (`RAIN_407` otherwise), signs, and broadcasts. On EVM chains it returns the transaction hash; on Solana, the transaction signature.

    <CodeGroup>
      ```swift iOS theme={null}
      let addresses = RainWithdrawAddresses(
          proxyAddress: contract.proxyAddress,
          controllerAddress: contract.controllerAddress,
          tokenAddress: usdcOnBase,
          recipientAddress: try await client.getWalletAddress()   // back to the wallet, or anywhere
      )
      let adminSignature = RainAdminSignature(
          salt: response.signature.salt,
          signature: response.signature.data,
          expiresAt: response.expiresAt
      )

      let txHash = try await client.withdrawCollateral(
          chainId: contract.chainId,
          addresses: addresses,
          amount: 100,
          decimals: 6,                     // from rain.tokenMetadata(chainId:address:)
          adminSignature: adminSignature
      )
      ```

      ```kotlin Android theme={null}
      val addresses = RainWithdrawAddresses(
          proxyAddress = contract.proxyAddress,
          controllerAddress = contract.controllerAddress,
          tokenAddress = usdcOnBase,
          recipientAddress = client.getWalletAddress(),   // back to the wallet, or anywhere
      )
      val adminSignature = RainAdminSignature(
          salt = response.signature.salt,
          signature = response.signature.data,
          expiresAt = response.expiresAt,
      )

      val txHash = client.withdrawCollateral(
          chainId = contract.chainId,
          addresses = addresses,
          amount = BigDecimal("100"),
          decimals = 6,                    // from rain.tokenMetadata(chainId, address)
          adminSignature = adminSignature,
      )
      ```
    </CodeGroup>

    `amount` and `decimals` must reproduce the exact base-unit amount the signature was issued for. Pass the token's real decimals; on Solana they aren't checked against the mint.
  </Step>
</Steps>

### Quoting the fee and preparing without broadcasting

`estimateWithdrawalFee` quotes the withdrawal's network cost in the chain's native currency (EVM only). Because a withdrawal is an EIP-712-signed call, estimating from scratch asks the wallet to sign; to quote without a second signature, prepare once and estimate on the result:

```swift theme={null}
let prepared = try await client.prepareWithdrawal(
    chainId: contract.chainId, addresses: addresses, amount: 100, decimals: 6, adminSignature: adminSignature
)
let fee = try await client.estimateWithdrawalFee(chainId: contract.chainId, prepared: prepared)
```

`prepareWithdrawal` also serves apps that broadcast themselves: `prepared.evmParameters` holds the signed transaction parameters, and `prepared.solanaTransfer` the unsigned Solana transfer, without anything being sent. Preparation works on every chain, including the chains the Rain wallet can't broadcast on, so it's the path for a program funding on Avalanche while sends there are pending. With gas sponsorship on, the fee estimate is informational; the user doesn't pay it.

### Withdrawal errors

| Error | Meaning | What to do |
| - | - | - |
| `RAIN_407` `walletNotAuthorized` | The wallet isn't an admin of the contract; the SDK checked before signing | The application was created with a different wallet, or the owner was changed. See below |
| `RAIN_405` `withdrawalRevertedByNetwork` | The contract rejected the withdrawal on-chain, usually a reused signature after the same amount was withdrawn recently | Fetch a fresh signature; if the user retries the same amount immediately, reuse the signature you already have rather than requesting another |
| `RAIN_104` `chainNotSupported` | The contract is on a chain the Rain wallet can't broadcast on | Use `prepareWithdrawal` and broadcast yourself, or fund on a send-capable chain |
| `RAIN_402` `insufficientFunds` | With sponsorship off, the wallet lacks native currency for gas | Show the shortfall |

A signature is bound to one (token, amount, recipient) and is consumed by a successful broadcast. Cache it with those inputs so a retry after a transient failure reuses it; a different amount needs a new one.

## Changing the owner wallet

The contract's owner is fixed at deployment from the address on the application. Changing it today is a two-step operation your backend runs against the Rain API, described in [Update a user's wallet address](/docs/rain-managed-programs#update-a-users-wallet-address): add the new wallet as an admin on every EVM collateral contract the user has, then `PATCH /v1/issuing/users/{userId}` with the new `walletAddress`. Rain verifies the new wallet is an admin on all of the user's EVM contracts and returns `423 Locked` when it isn't.

This matters for the Rain wallet in one scenario: a user who signs up again with a different contact gets a new account and therefore a new wallet, and withdrawals from their existing contract fail with `RAIN_407`. Avoid it by steering returning users to log in rather than sign up (see [Authentication options](/docs/embedded-wallets/authentication#accounts-and-identity)), and handle it, when it happens, by changing the owner to the new wallet.

<Info>
  Coming at GA: SDK calls to set and change the collateral contract's owner wallet from the app, so this no longer requires a manual admin step.
</Info>

## Corporate programs

The flow is the same with the company in place of the user. The initial user's wallet goes on the corporate application and becomes the owner of the company's contract; additional contracts on other chains can be created with [`POST /v1/issuing/companies/{companyId}/contracts`](/reference/contracts/create-a-smart-contract-for-a-company), which takes an explicit `ownerAddress`. Cards for the company's members all spend from the company contract, and the owner wallet funds and withdraws it exactly as above, using the company variants of the endpoints ([contracts](/reference/contracts/get-smart-contract-information-for-a-company), [withdrawal signature](/reference/signatures/get-withdrawal-signature-for-a-company)).

## Real-time funding from the wallet

Where your tenant has [Real-time funding](/docs/real-time-funding) enabled, pre-funding the contract becomes optional: the user approves Rain's operator once as a spender of the supported asset in their wallet, and at authorization time Rain pulls the purchase amount from the wallet into the contract before approving. It is available to Rain-managed programs only, on the assets and chains listed in the Real-time funding guide. A decline for insufficient spending power results when the wallet lacks the asset, the allowance is too low, or the pull fails within the authorization window.

Real-time funding is in beta; the wallet-side approval steps for the SDK are documented as it goes live. Follow the Real-time funding guide for the current prerequisites and operator addresses.

## What's next

<Columns cols={3}>
  <Card title="Flow of funds" icon="arrows-split-up-and-left" href="/docs/embedded-wallets/flow-of-funds">
    Rain-managed vs partner-managed, and what changes for the wallet.
  </Card>

  <Card title="Transaction webhooks and history" icon="webhook" href="/docs/embedded-wallets/transaction-webhooks">
    The events your backend receives along this flow.
  </Card>

  <Card title="Testing" icon="vial" href="/sdks/embedded-wallets/testing">
    Run the whole flow against the sandbox.
  </Card>
</Columns>
