> For the complete documentation index, see [llms.txt](https://hinkal-team.gitbook.io/hinkal/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://hinkal-team.gitbook.io/hinkal/hinkal-mobile-sdk/ios/guides/withdrawals.md).

# Withdrawals

A withdrawal moves tokens from the shielded balance to a public address (`private → public`). It runs server-side through a Hinkal relayer, so no on-chain signature is needed from the user.

### Basic Withdrawal

SDK calls take an `NSError` out-parameter, wrapped here with the `call` helper from Getting Started.

```swift
let wei = try call { MobileAmountToWei(chainId, tokenAddress, "0.25", &$0) }
let txHash = try call {
    hinkal.withdraw(
        chainId,
        tokenAddrsJSON: "[\"\(tokenAddress)\"]",
        amountsWeiJSON: "[\"\(wei)\"]",
        recipient: recipientPublicAddress,
        relayerOff: false,
        feeToken: tokenAddress,
        feeStructureJSON: "",
        error: &$0
    )
}
print("Withdraw tx:", txHash)
```

### Parameters

<table data-search="false"><thead><tr><th>Parameter</th><th>Type</th><th>Description</th></tr></thead><tbody><tr><td><code>chainID</code></td><td><code>Int64</code></td><td>Target chain id.</td></tr><tr><td><code>tokenAddrsJSON</code></td><td><code>String</code></td><td>JSON array of token addresses.</td></tr><tr><td><code>amountsWeiJSON</code></td><td><code>String</code></td><td>JSON array of amounts in wei.</td></tr><tr><td><code>recipient</code></td><td><code>String</code></td><td>Public address that receives the funds.</td></tr><tr><td><code>relayerOff</code></td><td><code>Bool</code></td><td>Bypass the relayer and broadcast yourself.</td></tr><tr><td><code>feeToken</code></td><td><code>String</code></td><td>Token the relayer fee is paid in.</td></tr><tr><td><code>feeStructureJSON</code></td><td><code>String</code></td><td>Fee quote, or empty for defaults.</td></tr></tbody></table>

### Fees

The relayer fee is charged on top of the amount, so the shielded balance must cover `amount + fee`. Leave `feeStructureJSON` empty for defaults, or pass a quote obtained from the fee helpers on `MobileClient` (`calculateTotalFee`, `getFeeStructureJSON`).

### Error Handling

```swift
do {
    let txHash = try call {
        hinkal.withdraw(chainId, tokenAddrsJSON: "[\"\(tokenAddress)\"]", amountsWeiJSON: "[\"\(wei)\"]",
                        recipient: recipientPublicAddress, relayerOff: false, feeToken: tokenAddress,
                        feeStructureJSON: "", error: &$0)
    }
} catch {
    print("Withdraw failed:", error.localizedDescription)
}
```

### Best Practices

#### 1. Validate the amount before submitting

`MobileAmountToWei` throws if the token has fewer decimals than provided - convert first and confirm the shielded balance covers `amount + fee`.

```swift
let wei = try call { MobileAmountToWei(chainId, tokenAddress, amountText, &$0) }
```

#### 2. Validate the recipient

```swift
guard !MobileIsValidPrivateAddress(recipient) else { return }  // withdraw recipient must be a public address
// or check it is non-empty and correctly formatted for the chain
```

#### 3. Handle long operations

Withdrawals run server-side and can take time. Show a loading indicator and refresh the balance afterward.

```swift
statusLabel.text = "Processing withdrawal..."
let txHash = try call { hinkal.withdraw(chainId, tokenAddrsJSON: "[\"\(tokenAddress)\"]",
    amountsWeiJSON: "[\"\(wei)\"]", recipient: recipient, relayerOff: false,
    feeToken: tokenAddress, feeStructureJSON: "", error: &$0) }
statusLabel.text = "Withdrawal complete"
 = try call { hinkal.getTotalBalance(chainId, userKeysSignature: signature,
    ethAddress: address, resetCache: true, useBlockedUtxos: false, error: &$0) }
```
