Hosted Deposit and Withdrawal
This guide is available on three different programming languages: Typescript, Kotlin and Flutter (Dart). You can change the shown version on each page via the buttons above.
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 Interactive
class, responsible for all SEP-24 interactions:
- TypeScript
const sep24 = await 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.
- TypeScript
const getAnchorServices = async (): Promise<AnchorServiceInfo> => {
return await anchor.getServicesInfo();
};
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 token
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):
- TypeScript
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);
Before starting with the deposit flow, make sure that the user account has established a trustline for the asset you are working with.
Basic Flow​
Let's start with a basic deposit:
- TypeScript
const deposit = await sep24.deposit({
assetCode: asset.code,
authToken,
});
As a result, you will get an interactive response from the anchor.
Open the received URL in an iframe and deposit the transaction ID for future reference:
- TypeScript
const url = deposit.url;
const id = deposit.id;
Similarly to the deposit flow, a basic withdrawal flow has the same method signature and repose type:
- TypeScript
const withdrawal = await sep24.withdraw({
assetCode: asset.code,
authToken,
});
const url = withdrawal.url;
const 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.
While SEP-9 supports passing binary data, the current version of the SDK doesn't offer such functionality.
At the time, accepted SEP-9 is not strictly typed yet. Improved typing will be offered in future versions.
- TypeScript
const deposit = await sep24.deposit({
assetCode: asset.code,
authToken,
extraFields: { email_address: "[email protected]" },
});
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):
- TypeScript
import { Memo, MemoText } from "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,
});
};
Similarly, for a withdrawal, the origin account of the Stellar transaction could be changed:
- TypeScript
const originAccount = "G...";
const withdrawal = await sep24.withdraw({
withdrawalAccount: originAccount,
assetCode: asset.code,
authToken,
});
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.
- TypeScript
const watcher = sep24.watcher();
const { stop, refresh } = watcher.watchOneTransaction({
authToken,
assetCode: asset.code,
id: successfulTransaction.id,
onMessage,
onSuccess,
onError,
});
Alternatively, we can track multiple transactions for the same asset.
- TypeScript
const watcher = sep24.watcher();
const { stop, refresh } = watcher.watchAllTransactions({
authToken,
assetCode: asset.code,
onMessage,
onError,
});
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:
- TypeScript
const transaction = await sep24.getTransactionBy({
authToken,
id: transactionId,
});
It's also possible to fetch transaction by the asset
- TypeScript
const transactions = await sep24.getTransactionsForAsset({
authToken,
assetCode: asset.code,
});
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:
- TypeScript
const withdrawal = await sep24.withdraw({
assetCode: asset.code,
authToken,
});
Next, open an interactive url :
- TypeScript
const 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
- TypeScript
const watcher = sep24.watcher();
const onMessage = (transaction) => {
if (transaction.status === "pending_user_transfer_start") {
// begin transfer code
}
};
const { refresh, stop } = watcher.watchOneTransaction({
authToken,
assetCode: asset.code,
id: successfulTransaction.id,
onMessage,
onSuccess,
onError,
});
Next, sign and submit the Stellar transfer:
- TypeScript
const txBuilder = await stellar.transaction({
sourceAddress: keypair,
baseFee: 100,
});
const transfer = txBuilder
.transferWithdrawalTransaction(walletTransaction, asset)
.build();
keypair.sign(transfer);
stellar.submitTransaction(transfer);
Where keypair
is the SEP-10 authenticated account. If you want to transfer funds from a different address, refer to Changing Stellar Transfer Account section.
Finally, let's track transaction status updates. In this example we simply check if the transaction has been completed:
- TypeScript
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,
});