> ## 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.

# Sending Funds

> Send native assets and tokens from a Rain wallet, estimate network fees, and handle transaction results.


The SDK handles validation, signing, and broadcast. Specify the network, recipient, and amount, then use the returned transaction hash to track the transfer.

## Send native assets and tokens

Use `sendNative` for a network’s native asset and `sendToken` for tokens.

<CodeGroup>
  ```swift iOS theme={null}
  // Native currency (ETH on Base, POL on Polygon, SOL on Solana, …)
  let native = try await client.sendNative(
      chainId: RainChain.baseMainnet,
      to: "0x3cA8ac240F6ebeA8684b3E629A8e8C1f0E3bC0Ff",
      amount: 0.01
  )

  // ERC-20 on EVM chains, SPL on Solana; decimals resolved by the SDK
  let token = try await client.sendToken(
      chainId: RainChain.baseMainnet,
      contractAddress: "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",   // USDC on Base
      to: "0x3cA8ac240F6ebeA8684b3E629A8e8C1f0E3bC0Ff",
      amount: 25
  )
  print(token.transactionHash)
  ```

  ```kotlin Android theme={null}
  // Native currency (ETH on Base, POL on Polygon, SOL on Solana, …)
  val native = client.sendNative(
      chainId = RainChain.BASE_MAINNET,
      to = "0x3cA8ac240F6ebeA8684b3E629A8e8C1f0E3bC0Ff",
      amount = BigDecimal("0.01"),
  )

  // ERC-20 on EVM chains, SPL on Solana; decimals resolved by the SDK
  val token = client.sendToken(
      chainId = RainChain.BASE_MAINNET,
      contractAddress = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",   // USDC on Base
      to = "0x3cA8ac240F6ebeA8684b3E629A8e8C1f0E3bC0Ff",
      amount = BigDecimal("25"),
  )
  println(token.transactionHash)
  ```
</CodeGroup>

Amounts are passed in the token’s standard units. For example, `25` means 25 USDC. The SDK determines the token’s decimals and converts the amount before submitting the transaction.

For EVM tokens, decimals are resolved from Rain’s token registry, tokens registered by your application, or the token contract. If the SDK cannot determine the token, it returns `RAIN_102` with `tokenNotFound`.

The `chainId` determines which account and network are used. Solana chain IDs use the wallet’s Solana account. Supported EVM chain IDs use the wallet’s EVM account.

## What the SDK checks before signing

Before requesting a signature or submitting anything to the network, the SDK validates the transaction on the device and returns a typed error if a check fails.

| Check | Error | Recommended UI |
| - | - | - |
| The selected chain supports outbound transactions | `RAIN_104` `chainNotSupported` | Disable sending on that chain. See [Chain support](/sdks/embedded-wallets/features#blockchain-support) |
| The recipient is a valid address for the selected chain and is not the token contract or mint | `RAIN_102` `invalidRecipient` | Show the returned `reason` next to the recipient field |
| The amount is greater than zero and uses a supported number of decimal places | `RAIN_406` `invalidAmount` | Show the returned `reason` next to the amount field |
| The wallet has enough of the asset being sent | `RAIN_402` `insufficientTokenBalance` | Show the available balance and amount required |
| The wallet has enough native balance for required network costs | `RAIN_402` `insufficientFunds` | Show the shortfall returned by the SDK |
| For self paid transactions, the transaction simulation succeeds | `RAIN_403` `transactionSimulationFailed` | Show that the transaction is expected to fail and surface the underlying reason when available |

With [gas sponsorship](/docs/embedded-wallets/gas-sponsorship) enabled, the SDK skips the local simulation check. A transaction that later fails during processing can return `RAIN_403` or `RAIN_501` after signing.

## Fees

With gas sponsorship enabled, users do not pay network fees, so most send screens do not need to show a fee. If you want to display the network cost, or if sponsorship is disabled, use `estimateGas` to estimate the fee in the chain’s native asset.

<CodeGroup>
  ```swift iOS theme={null}
  let fee = try await client.estimateGas(
      chainId: RainChain.baseMainnet,
      from: try await client.getWalletAddress(),
      to: recipient,
      data: "0x"
  )   // Decimal, in ETH
  ```

  ```kotlin Android theme={null}
  val fee = client.estimateGas(
      chainId = RainChain.BASE_MAINNET,
      from = client.getWalletAddress(),
      to = recipient,
      data = "0x",
  )   // BigDecimal, in ETH
  ```
</CodeGroup>

`estimateGas` returns the estimated network cost even when Rain sponsors the transaction, so you can show what the user saves.

## Transaction results

A successful send returns a `RainTokenTransferResult` with a `transactionHash`. The presence of a transaction hash means the transaction was broadcast. It does not mean the transaction has been confirmed onchain. Use transaction history to reflect the final status in your UI.

Two additional outcomes require special handling:

* `RAIN_302 transactionPending(statusId)` means Rain accepted the transaction, but the transaction hash was not available before the SDK polling window ended. Show the transaction as pending, retain the `statusId`, refresh transaction history, and do not allow the user to resend immediately.
* `RAIN_301 networkError` after the user submits a send means the outcome is unknown: the request may have reached Rain and been broadcast even though the client did not receive a response. Do not treat it as a failure or offer an immediate retry. History lists confirmed transactions only, so an empty `getTransactions` result right after the error does not prove the send was lost. Keep the send pending, refresh history and the balance, and offer a retry only once the transaction has had time to confirm and neither reflects it.

## Solana considerations

Solana sends have a few additional requirements:

* **Token decimals are validated onchain.** The SDK reads the token’s decimals from the mint and uses `TransferChecked` for token transfers.
* **The sender may need SOL for account creation.** If the recipient has never held the token, the transfer may need to create an associated token account. That cost is paid by the sender and is not covered by gas sponsorship. If the sender does not have enough SOL, the SDK returns `RAIN_402` with the shortfall.
* **Outbound support is limited to mainnet and devnet.** Solana testnet is read only and returns `RAIN_104` for sends.

## Collateral withdrawals

Withdrawing card collateral is also a wallet signed transaction, but it follows a separate flow from a standard send.

The withdrawal targets the user’s collateral contract and also requires authorization from your backend. Use `withdrawCollateral`, `prepareWithdrawal`, and `estimateWithdrawalFee` for this flow. See [Card funding and settlement](/docs/embedded-wallets/card-funding-and-settlement#withdrawals) for implementation details.

## What's next

<Columns cols={2}>
  <Card title="Error reference" icon="circle-exclamation" href="/sdks/embedded-wallets/error-reference">
    Every code above, with handling patterns.
  </Card>

  <Card title="Gas sponsorship" icon="gas-pump" href="/docs/embedded-wallets/gas-sponsorship">
    Why the user doesn't need ETH, and where they still need SOL.
  </Card>
</Columns>
