> ## Documentation Index
> Fetch the complete documentation index at: https://developers.stellar.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Hosted Deposit and Withdrawal

> The [SEP-24] standard defines the standard way for anchors and wallets to interact on behalf of users.

> [!NOTE]
>
> This guide is available on four different programming languages: Typescript, Kotlin, Flutter (Dart) and Swift. You can change the shown version on each page via the buttons above.

> [!CAUTION]
>
> **Important**
>
> Documentation for this language is currently work in progress. Some of information may not be applicable for this language, or missing. Code samples may be incomplete.

The [SEP-24] standard defines the standard way for anchors and wallets to interact on behalf of users. Wallets use this standard to facilitate exchanges between on-chain assets (such as stablecoins) and off-chain assets (such as fiat, or other network assets such as BTC).

During the flow, a wallet makes several requests to the anchor, and finally receives an interactive URL to open in iframe. This URL is used by the user to provide an input (such as KYC) directly to the anchor. Finally, the wallet can fetch transaction information using query endpoints.

## Get Anchor Information

Let's start with getting an instance of `Sep24` class, responsible for all SEP-24 interactions:

> [tab] Kotlin
>
> ```kotlin theme={"theme":{"light":"github-light","dark":"github-dark"}}
> val sep24 = anchor.sep24()
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const sep24 = await anchor.sep24();
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final sep24 = await anchor.sep24();
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let sep24 = anchor.sep24
> ```

First, let's get the information about the anchor's support for [SEP-24]. This request doesn't require authentication, and will return generic info, such as supported currencies, and features supported by the anchor. You can get a full list of returned fields in the [SEP-24 specification](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0024.md#info).

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> suspend fun getAnchorServices(): AnchorServiceInfo {
>   return sep24.getServicesInfo()
> }
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const getAnchorServices = async (): Promise<AnchorServiceInfo> => {
>   return await anchor.getServicesInfo();
> };
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final servicesInfo = await sep24.getServiceInfo();
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let servicesInfo = try await sep24.info
> ```

## Interactive Flows

Before getting started, make sure you have connected to the anchor and received an authentication token, as described in the [Stellar Authentication] wallet guide. We will use the `authToken` object in the examples below as the [SEP-10] authentication token, obtained earlier.

To initiate an operation, we need to know an asset. You may want to hard-code it, or get it dynamically from the anchor's info file, like shown below (for USDC):

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> val asset = info.currencies.first { it.code == "USDC" }.assetId
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> import { IssuedAssetId } from "@stellar/typescript-wallet-sdk";
>
> const assetCode = "USDC";
>
> const info = await anchor.getInfo();
>
> const currency = info.currencies.find(({ code }) => code === assetCode);
>
> if (!currency?.code || !currency?.issuer) {
>   throw new Error(
>     `Anchor does not support ${assetCode} asset or is not correctly configured on TOML file`,
>   );
> }
>
> const asset = new IssuedAssetId(currency.code, currency.issuer);
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final asset = info.currencies.firstWhere((it)=>it.code=='USDC').assetId;
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let info = try await anchor.info
> let asset = try info.currencies?.first(where: {$0.code == "USDC"})?.assetId
> ```

> [!NOTE]
>
> Before starting with the deposit flow, make sure that the user account has [established a trustline](https://developers.stellar.org/docs/build/apps/wallet/stellar.md#modify-assets-trustlines) for the asset you are working with.

### Basic Flow

Let's start with a basic deposit:

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> val deposit = sep24.deposit(asset, authToken)
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const deposit = await sep24.deposit({
>   assetCode: asset.code,
>   authToken,
> });
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final deposit = await sep24.deposit(asset, authToken);
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let deposit = try await sep24.deposit(assetId: asset, authToken: authToken)
> ```

As a result, you will get an [interactive response](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0024.md#deposit-and-withdraw-shared-responses) from the anchor.

Open the received URL in an iframe and save the transaction ID for future reference:

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> val url = deposit.url
> val id = deposit.id
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const url = deposit.url;
> const id = deposit.id;
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final url = deposit.url;
> final id = deposit.id;
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let url = deposit.url
> let id = deposit.id
> ```

Similarly to the deposit flow, a basic withdrawal flow has the same method signature and response type:

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> val withdrawal = sep24.withdraw(asset, authToken)
> val url = withdrawal.url
> val id = withdrawal.id
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const withdrawal = await sep24.withdraw({
>   assetCode: asset.code,
>   authToken,
> });
> const url = withdrawal.url;
> const id = withdrawal.id;
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final withdrawal = await sep24.withdraw(asset, authToken);
> final url = withdrawal.url;
> final id = withdrawal.id;
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let withdrawal = try await sep24.withdraw(assetId: asset, authToken: authToken)
> let url = withdrawal.url
> let id = withdrawal.id
> ```

### Providing KYC Info

To improve the user experience, the [SEP-24] standard supports passing user KYC to the anchor via [SEP-9]. In turn, the anchor will pre-fill this information in the interactive popup.

> [!NOTE]
>
> While \[SEP-9] supports passing binary data, the current version of the SDK doesn't offer such functionality.

> [!NOTE]
>
> At the time, accepted \[SEP-9] is not strictly typed yet. Improved typing will be offered in future versions.

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> val sep9 = mapOf("email_address" to "mail@example.com")
>
> val deposit = sep24.deposit(asset, authToken, sep9)
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const deposit = await sep24.deposit({
>   assetCode: asset.code,
>   authToken,
>   extraFields: { email_address: "mail@example.com" },
> });
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final deposit = await sep24.deposit(asset, authToken,
>   extraFields: {"email_address": "mail@example.com"});
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let deposit = try await sep24.deposit(assetId: asset,
>                                       authToken: authToken,
>                                       extraFields: ["email_address" : "mail@example.com"])
> ```

### Changing Stellar Transfer Account

By default, the Stellar transfer will be sent to the authenticated account (with a memo) that initiated the deposit.

While in most cases it's acceptable, some wallets may split their accounts. To do so, pass additional account (and optionally a memo):

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> suspend fun depositDifferentAccount(): InteractiveFlowResponse {
>   val recipientAccount = "G..."
>   val memo = "my memo" to MemoType.TEXT
>   return sep24.deposit(asset, authToken, destinationAccount = recipientAccount, destinationMemo = memo)
> }
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> import { Memo, MemoText } from "@stellar/stellar-sdk";
>
> const recipientAccount = "G...";
> const depositDifferentAccount = async (): Promise<InteractiveFlowResponse> => {
>   return await sep24.deposit({
>     destinationAccount: recipientAccount,
>     destinationMemo: new Memo(MemoText, "some memo"),
>     assetCode: asset.code,
>     authToken,
>   });
> };
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final recipientAccount = "G...";
> final deposit = await sep24.deposit(asset, authToken, destinationAccount: recipientAccount,
>   destinationMemo: "my memo", destinationMemoType: MemoType.text);
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let recipientAccount = "G..."
> let deposit = try await sep24.deposit(assetId: asset,
>                                       authToken: authToken,
>                                       destinationAccount: recipientAccount,
>                                       destinationMemo: "my memo",
>                                       destinationMemoType: MemoType.text)
> ```

Similarly, for a withdrawal, the origin account of the Stellar transaction could be changed:

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> val originAccount = "G..."
> val withdrawal = sep24.withdraw(asset, authToken, withdrawalAccount = originAccount)
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const originAccount = "G...";
> const withdrawal = await sep24.withdraw({
>   withdrawalAccount: originAccount,
>   assetCode: asset.code,
>   authToken,
> });
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final originAccount = "G...";
> final withdrawal = await sep24.withdraw(asset, authToken, withdrawalAccount: originAccount);
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let originAccount = "G..."
> let withdrawal = try await sep24.withdraw(assetId: asset,
>                                           authToken: authToken,
>                                           withdrawalAccount: originAccount)
> ```

## Getting Transaction Info

On the typical flow, the wallet would get transaction data to notify users about status updates. This is done via the [SEP-24] `GET /transaction` and `GET /transactions` endpoint.

Alternatively, some anchors support webhooks for notifications. Note that this feature is not widely adopted yet.

### Tracking Transaction

Let's look into how to use the wallet SDK to track transaction status changes. We will use `Watcher` class for this purpose. First, let's initialize watcher and start tracking a transaction.

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> val watcher = sep24.watcher()
> val result = watcher.watchOneTransaction(authToken, "transaction id")
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const watcher = sep24.watcher();
>
> const { stop, refresh } = watcher.watchOneTransaction({
>   authToken,
>   assetCode: asset.code,
>   id: successfulTransaction.id,
>   onMessage,
>   onSuccess,
>   onError,
> });
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final watcher = sep24.watcher();
> final result = watcher.watchOneTransaction(authToken, "transaction id");
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let watcher = sep24.watcher()
> let result = watcher.watchOneTransaction(authToken: authToken,
>                                          id: "transaction id")
> ```

Alternatively, we can track multiple transactions for the same asset.

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> val watcher = sep24.watcher()
> val result = watcher.watchAsset(getAuthToken(), asset)
> ```
>
> Next, let's get the channel provided by `WatcherResult` to receive events.
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> do {
>   val event = result.channel.receive()
>   when (event) {
>     is StatusChange ->
>       println("Status changed to ${event.status}. Transaction: ${event.transaction}")
>     is ExceptionHandlerExit -> println("Exception handler exited the job")
>     is ChannelClosed -> println("Channel closed. Job is done")
>   }
> } while (event !is ChannelClosed)
> ```
>
> This code example will consume all events coming from the channel until it's closed. There are three types of events:
>
> * `StatusChange`: indicates that transaction status has changed.
>
> * `ExceptionHandlerExit`: indicates that the exception handler exited the processing loop. With default retry handler it happens when retries are exhausted.
>
> * `ChannelClosed`: indicates that the channel is closed and no more events will be emitted. This event will always fire. If `ExceptionHandlerExit` happened, channel will close right after. Otherwise, (under normal circumstances) it will stop when all transactions reach terminal statuses.
>
> > [!NOTE]
> >
> > Events are stored in the channel until they are received, and calling the `receive()` method will block the channel until a message is received. You can read more about how channels work in the [channel documentation](https://kotlinlang.org/docs/coroutines-and-channels.html#channels).

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const watcher = sep24.watcher();
>
> const { stop, refresh } = watcher.watchAllTransactions({
>   authToken,
>   assetCode: asset.code,
>   onMessage,
>   onError,
> });
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final watcher = sep24.watcher();
> final result = watcher.watchAsset(authToken, asset);
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let watcher = sep24.watcher()
> let result = watcher.watchAsset(authToken: authToken, asset: asset)
> ```

### Fetching Transaction

While `Watcher` class offers powerful tracking capabilities, sometimes it's required to just fetch a transaction (or transactions) once. The `Anchor` class allows you to fetch a transaction by ID, Stellar transaction ID, or external transaction ID:

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> // "id" is the actual Anchor transaction id, all transactions should have it.
> val transaction = sep24.getTransactionBy(authToken, id = "transaction id")
>
> // "stellarTransactionId" (aka "stellar_transaction_id" on the SEP spec)
> // is the hash of the Stellar network transaction payment related to this
> // Anchor transaction.
> // The "stellarTransactionId" has a SHA256 hash format like the below:
> // - "a35135d8ed4b29b66d821444f6760f8ca1e77bea1fb49541bebeb2c3d844364a"
> // E.g. we'll only have this transaction id field AFTER the wallet sends funds
> // to Anchor on the withdrawal flow or receives funds from Anchor on the
> // deposit flow.
> val transaction = sep24.getTransactionBy(authToken, stellarTransactionId = "transaction id")
>
> // "externalTransactionId" (aka "external_transaction_id" on the SEP spec)
> // could refer to some ID of transaction on external network.
> // E.g. this could be some "reference number" displayed to the user on
> // the last step of the Interactive Flow UI which the user could use in some
> // other external place to complete the deposit or withdraw operation.
> val transaction = sep24.getTransactionBy(authToken, externalTransactionId = "transaction id")
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const transaction = await sep24.getTransactionBy({
>   authToken,
>   id: transactionId,
> });
>
> // "id" is the actual Anchor transaction id, all transactions should have it.
> const transaction = await anchor.sep24().getTransactionBy({
>   authToken,
>   id: transactionId,
> });
>
> // "stellarTransactionId" (aka "stellar_transaction_id" on the SEP spec)
> // is the hash of the Stellar network transaction payment related to this
> // Anchor transaction.
> // The "stellarTransactionId" has a SHA256 hash format like the below:
> // - "a35135d8ed4b29b66d821444f6760f8ca1e77bea1fb49541bebeb2c3d844364a"
> // E.g. we'll only have this transaction id field AFTER the wallet sends funds
> // to Anchor on the withdrawal flow or receives funds from Anchor on the
> // deposit flow.
> const transaction = await anchor.sep24().getTransactionBy({
>   authToken,
>   stellarTransactionId,
> });
>
> // "externalTransactionId" (aka "external_transaction_id" on the SEP spec)
> // could refer to some ID of transaction on external network.
> // E.g. this could be some "reference number" displayed to the user on
> // the last step of the Interactive Flow UI which the user could use in some
> // other external place to complete the deposit or withdraw operation.
> const transaction = await anchor.sep24().getTransactionBy({
>   authToken,
>   externalTransactionId,
> });
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final transaction = await sep24.getTransaction("transaction id", authToken);
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> // "transactionId" is the actual Anchor transaction id, all transactions should have it.
> let transaction = try await anchor.sep24.getTransactionBy(authToken: authToken,
>                                                           transactionId: "transaction id")
>
> // "stellarTransactionId" (aka "stellar_transaction_id" on the SEP spec)
> // is the hash of the Stellar network transaction payment related to this
> // Anchor transaction.
> // The "stellarTransactionId" has a SHA256 hash format like the below:
> // - "a35135d8ed4b29b66d821444f6760f8ca1e77bea1fb49541bebeb2c3d844364a"
> // E.g. we'll only have this transaction id field AFTER the wallet sends funds
> // to Anchor on the withdrawal flow or receives funds from Anchor on the
> // deposit flow.
> let transaction = try await anchor.sep24.getTransactionBy(authToken: authToken,
>                                                           stellarTransactionId: "stellar transaction id")
>
> // "externalTransactionId" (aka "external_transaction_id" on the SEP spec)
> // could refer to some ID of transaction on external network.
> // E.g. this could be some "reference number" displayed to the user on
> // the last step of the Interactive Flow UI which the user could use in some
> // other external place to complete the deposit or withdraw operation.
> let transaction = try await anchor.sep24.getTransactionBy(authToken: authToken,
>                                                           externalTransactionId: "external transaction id")
> ```

It's also possible to fetch transaction by the asset

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> val transactions = sep24.getTransactionsForAsset(asset, authToken)
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const transactions = await sep24.getTransactionsForAsset({
>   authToken,
>   assetCode: asset.code,
> });
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final transactions = await sep24.getTransactionsForAsset(asset, authToken);
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let transactions = try await sep24.getTransactionsForAsset(authToken: authToken, asset: asset)
> ```

## Submitting Withdrawal Transfer

Previously, we took a look at starting the withdrawal flow. Now, let's take a look at a full example.

First, start the withdrawal:

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> val withdrawal = sep24.withdraw(asset, authToken)
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const withdrawal = await sep24.withdraw({
>   assetCode: asset.code,
>   authToken,
> });
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final withdrawal = await sep24.withdraw(asset, authToken);
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let withdrawal = try await sep24.withdraw(assetId: asset, authToken: authToken)
> ```

Next, open an interactive url :

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> val url = withdrawal.url
> // open the url
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const url = withdrawal.url;
> // open the url
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final url = withdrawal.url
> // open the url
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let url = withdrawal.url
> // open the url
> ```

After that we need to wait until the anchor is ready to receive funds. To do so, we will be waiting until transaction reaches `pending_user_transfer_start` status. This code uses a simple watching (polling) mechanism with no bail-out condition. The application’s code should be more robust.

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> val withdrawalWatcher = sep24.watcher().watchOneTransaction(authToken, withdrawal.id)
> var statusChange: StatusUpdateEvent
>
> // Wait for user input
> do {
>   statusChange = withdrawalWatcher.channel.receive()
> } while (
>   ((statusChange as? StatusChange) ?: throw Exception("Channel unexpectedly closed"))
>     .status != TransactionStatus.PENDING_USER_TRANSFER_START
> )
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const watcher = sep24.watcher();
>
> const onMessage = (transaction) => {
>   if (transaction.status === "pending_user_transfer_start") {
>     // begin transfer code
>   }
> };
>
> const onSuccess = (transaction) => {
>   // transaction comes back as completed / refunded / expired
> };
>
> const onError = (transaction) => {
>   // runtime error, or the transaction comes back as
>   // no_market / too_small / too_large / error
> };
>
> // We can watch for a particular transaction.
> const { refresh, stop } = watcher.watchOneTransaction({
>   authToken,
>   assetCode: asset.code,
>   id: successfulTransaction.id,
>   onMessage,
>   onSuccess,
>   onError,
> });
>
> // Or watch for ALL transactions of a particular asset.
> const { refresh, stop } = watcher.watchAllTransactions({
>   authToken,
>   assetCode: asset.code,
>   onMessage,
>   onError,
> });
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final withdrawalWatcher = sep24.watcher().watchOneTransaction(authToken, withdrawal.id);
> withdrawalWatcher.controller.stream.listen(
>   (event) {
>     if (event is StatusChange && TransactionStatus.pendingUserTransferStart == event.status) {
>       // begin transfer
>     }
>   },
>   onError: (error) {
>     // handle error
>   },
> );
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let watcher = anchor.sep24.watcher()
> let result = watcher.watchOneTransaction(authToken: authToken,
>                                          id: withdrawal.id)
>
> NotificationCenter.default.addObserver(self,
>                                        selector: #selector(handleEvent(_:)),
>                                        name: result.notificationName,
>                                        object: nil)
>
> /// ...
> @objc public func handleEvent(_ notification: Notification) {
>     if let statusChange = notification.object as? StatusChange {
>         print("Status change to \(statusChange.status.rawValue). Transaction: \(statusChange.transaction.id)")
>         if statusChange.status == TransactionStatus.pendingUserTransferStart {
>           // begin transfer
>         }
>     } else if let _ = notification.object as? ExceptionHandlerExit {
>         print("Exception handler exited the job")
>     } else if let _ = notification.object as? NotificationsClosed {
>         print("Notifications closed. Job is done")
>     }
> }
> ```

Next, sign and submit the Stellar transfer:

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> val anchorTransaction = (statusChange.transaction as WithdrawalTransaction)
> val transfer = stellar.transaction(keypair).transferWithdrawalTransaction(anchorTransaction, asset).build()
>
> transfer.sign(keypair)
>
> stellar.submitTransaction(transfer)
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> // Import Horizon to get the result codes for error handling
> import { Horizon } from "@stellar/stellar-sdk";
>
> // This creates a transaction builder which we'll be using to assemble
> // our transfer withdrawal transaction as shown below.
> const txBuilder = await stellar.transaction({
>   sourceAddress: keypair,
>   baseFee: 10000, // this is 0.001 XLM
>   timebounds: 180, // in seconds
> });
>
> // We can use the transaction object received on the onMessage callback from
> // the watcher, or, we can also fetch the transaction object using either
> // getTransactionBy or getTransactionsForAsset as illustrated in previous step.
> onMessage: (transaction) => {
>   if (transaction.status === "pending_user_transfer_start") {
>     // Use the builder to assemble the transfer transaction. Behind the scenes
>     // it extracts the Stellar account (withdraw_anchor_account), memo (withdraw_memo)
>     // and amount (amount_in) to use in the Stellar payment transaction that will
>     // be submitted to the Stellar network.
>     const transferTransaction = txBuilder
>       .transferWithdrawalTransaction(transaction, asset)
>       .build();
>
>     // Signs it with the account key pair
>     transferTransaction.sign(keypair);
>
>     // Finally submits it to the Stellar network. This stellar.submitTransaction()
>     // function handles '504' status codes (timeout) by keep retrying it until
>     // submission succeeds or we get a different error.
>     try {
>       const response = await stellar.submitTransaction(transferTransaction);
>       console.log("Stellar-generated transaction ID: ", response.id);
>     } catch (error) {
>       /*
>         In case it's not a 504 (timeout) error, the application could try some
>         resolution strategy based on the error kind.
>
>         On Stellar docs you can find a page dedicated to error handling:
>         https://developers.stellar.org/docs/learn/encyclopedia/errors-and-debugging
>
>         And status/result codes:
>         https://developers.stellar.org/docs/data/apis/horizon/api-reference/errors
>       */
>
>       // Let's illustrate here how we could handle an 'invalid sequence number' error.
>
>       // We can access all possible result codes through Horizon's API.
>       const sdkResultCodes = Horizon.HorizonApi.TransactionFailedResultCodes;
>
>       // We can access error's response data to check for useful error details.
>       const errorData = error.response?.data;
>       /*
>         Sample of errorData object returned by the Wallet SDK:
>         {
>           type: 'https://stellar.org/horizon-errors/transaction_failed',
>           title: 'Transaction Failed',
>           status: 400,
>           detail: 'The transaction failed when submitted to the Stellar network.
>             The `extras.result_codes` field on this response contains further details.
>             Descriptions of each code can be found at:
>             https://developers.stellar.org/docs/data/apis/horizon/api-reference/errors/http-status-codes/horizon-specific/transaction-failed',
>           extras: {
>             envelope_xdr: 'AAAAAgAAAADBjF7n9gfByOwlnyaJH...k4BRagf/////////8AAAAAAAAAAA==',
>             result_codes: { transaction: 'tx_bad_seq' },
>             result_xdr: 'AAAAAAAAAGT////6AAAAAA=='
>           }
>         }
>       */
>
>       /*
>         Example scenario: invalid sequence numbers.
>
>         These errors typically occur when you have an outdated view of an account.
>         This could be because multiple devices are using this account, you have
>         concurrent submissions happening, or other reasons. The solution is relatively
>         simple: retrieve the account details and try again with an updated sequence number.
>       */
>       if (
>         errorData?.status == 400 &&
>         errorData?.extras?.result_codes?.transaction ===
>           sdkResultCodes.TX_BAD_SEQ
>       ) {
>         // Creating a new transaction builder means retrieving an updated sequence number.
>         const txBuilder2 = await stellar.transaction({
>           sourceAddress: keypair,
>           baseFee: 10000,
>           timebounds: 180,
>         });
>
>         // ...
>
>         // Repeat all the steps until submitting the transaction again.
>
>         // ...
>
>         const response2 = await stellar.submitTransaction(transferTransaction);
>         console.log(
>           "Stellar-generated transaction ID on retry: ",
>           response2.id,
>         );
>
>         // The application should take care to not resubmit the same transaction
>         // blindly with an updated sequence number as it could result in more than
>         // one payment being made when only one was intended.
>       }
>     }
>   }
> };
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final tx = event.transaction as WithdrawalTransaction;
>
> final paymentBuilder = flutter_sdk.PaymentOperationBuilder(
>   tx.withdrawAnchorAccount!,
>   flutter_sdk.Asset.createNonNativeAsset(asset.code, asset.issuer),
>   tx.amountIn!,
> );
>
> final transactionBuilder = flutter_sdk.TransactionBuilder(sourceAccount)
>   ..addOperation(paymentBuilder.build());
>
> flutter_sdk.Memo? memo;
> if ("text" == tx.withdrawalMemoType) {
>   memo = flutter_sdk.MemoText(tx.withdrawalMemo!);
> } else if ("hash" == tx.withdrawalMemoType) {
>   memo = flutter_sdk.MemoHash(base64Decode(tx.withdrawalMemo!));
> } // ... etc.
>
> if (memo != null) {
>   transactionBuilder.addMemo(memo);
> }
>
> flutter_sdk.KeyPair kp =
>   flutter_sdk.KeyPair.fromSecretSeed(userKeyPair.secretKey);
> final transaction = transactionBuilder.build()..sign(kp, network);
> final paymentResult = await sdk.submitTransaction(transaction);
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> if let tx = statusChange.transaction as? WithdrawalTransaction {
>     let paymentOperation = try PaymentOperation(sourceAccountId: sourceAccountId,
>                                                 destinationAccountId: tx.withdrawAnchorAccount!,
>                                                 asset: asset.toAsset(),
>                                                 amount: Decimal(string:tx.amountIn!)!)
>     var memo:Memo? = nil
>     if "text" == tx.withdrawalMemoType {
>         memo = Memo.text(tx.withdrawalMemo!)
>     } else if "hash" == tx.withdrawalMemoType {
>         memo = Memo.hash(Data(base64Encoded: tx.withdrawalMemo!)!)
>     } // ...etc
>
>     let transaction = try Transaction(sourceAccount: sourceAccount,
>                                       operations: [paymentOperation],
>                                       memo: memo)
>
>     let keypair = try KeyPair(secretSeed: userKeyPair.secretKey)
>     try transaction.sign(keyPair: keypair, network: Network.public)
>     let server = wallet.stellar.server
>     let paymentResult = await server.transactions.submitTransaction(transaction: transaction)
> }
> ```

Where `keypair` is the SEP-10 authenticated account. If you want to transfer funds from a different address, refer to [Changing Stellar Transfer Account](#changing-stellar-transfer-account) section.

Code for submitting transactions to Stellar should be developed thoughtfully. The SDF has a documentation page dedicated to [submitting transactions and handling errors gracefully]. Here are a few things you need to keep in mind:

* Offer a high fee. Your fee should be as high as you would offer before deciding the transaction is no longer worth sending. Stellar will only charge you the minimum necessary to be included in the ledger -- you won't be charged the amount you offer unless everyone else is offering the same amount or greater. Otherwise, you’ll pay the smallest fee offered in the set of transactions included in the ledger.
* Set a maximum timebound on the transaction. This ensures that if your transaction is not included in a ledger before the set time, you can reconstruct the transaction with a higher offered fee and submit it again with better chances of inclusion.
* Resubmit the transaction when you get 504 status codes. 504 status codes are just telling you that your transaction is still pending -- not that it has been canceled or that your request was invalid. You should simply make the request again with the same transaction to get a final status (either included or expired).

Finally, let's track transaction status updates. In this example we simply check if the transaction has been completed:

> [tab] Kotlin
>
> ```kt theme={"theme":{"light":"github-light","dark":"github-dark"}}
> var terminalStatus: TransactionStatus? = null
>
> do {
>   statusChange = withdrawalWatcher.channel.receive()
>
>   when (statusChange) {
>     is StatusChange -> {
>       if (statusChange.status.isTerminal()) {
>         terminalStatus = statusChange.status
>       }
>     }
>     is ChannelClosed -> println("Transaction tracking finished")
>     is ExceptionHandlerExit ->
>       println("Retries exhausted trying obtain transaction data, giving up.")
>   }
> } while (statusChange !is ChannelClosed)
>
> if (terminalStatus != TransactionStatus.COMPLETED) {
>   println("Transaction was not completed")
> }
> ```

> [tab] TypeScript
>
> ```typescript theme={"theme":{"light":"github-light","dark":"github-dark"}}
> const watcher = sep24.watcher();
>
> const onSuccess = (transaction) => {
>   // transaction came back as completed / refunded / expired
>   console.log("Transaction is completed");
> };
>
> const onError = (transaction) => {
>   // runtime error, or the transaction comes back as
>   // no_market / too_small / too_large / error
> };
>
> const { refresh, stop } = watcher.watchOneTransaction({
>   authToken,
>   assetCode: asset.code,
>   id: successfulTransaction.id,
>   onMessage,
>   onSuccess,
>   onError,
> });
> ```

> [tab] Flutter
>
> ```dart theme={"theme":{"light":"github-light","dark":"github-dark"}}
> final watcher = sep24.watcher().watchOneTransaction(authToken, withdrawal.id);
> watcher.controller.stream.listen(
>   (event) {
>     if (event is StatusChange && event.status.isTerminal()) {
>       if (TransactionStatus.completed != event.status) {
>         print("Transaction was not completed!");
>       } else {
>         print("Success");
>       }
>     } else if (event is ExceptionHandlerExit) {
>       print("Retries exhausted trying obtain transaction data, giving up.");
>     } else if (event is StreamControllerClosed) {
>       print("Transaction tracking finished");
>     }
>   },
>   onError: (error) {
>     // handle error
>   },
> );
> ```

> [tab] Swift
>
> ```swift theme={"theme":{"light":"github-light","dark":"github-dark"}}
> let watcher = anchor.sep24.watcher()
> let result = watcher.watchOneTransaction(authToken: authToken,
>                                          id: withdrawal.id)
>
> NotificationCenter.default.addObserver(self,
>                                        selector: #selector(handleEvent(_:)),
>                                        name: result.notificationName,
>                                        object: nil)
>
> /// ...
> @objc public func handleEvent(_ notification: Notification) {
>     if let statusChange = notification.object as? StatusChange {
>         if statusChange.status != TransactionStatus.completed {
>           print("Transaction was not completed!")
>         } else {
>           print("Success")
>         }
>     } else if let _ = notification.object as? ExceptionHandlerExit {
>         print("Exception handler exited the job")
>     } else if let _ = notification.object as? NotificationsClosed {
>         print("Notifications closed. Job is done")
>     }
> }
> ```

[sep-9]: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0009.md

[sep-10]: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0010.md

[sep-24]: https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0024.md

[stellar authentication]: https://developers.stellar.org/docs/build/apps/wallet/sep10.md

[submitting transactions and handling errors gracefully]: https://developers.stellar.org/docs/data/apis/horizon/api-reference/errors/error-handling.md
