SEP-6: Deposit (GHS → USDC)
On-ramp Ghana Cedis to USDC via Mobile Money using the SEP-6 deposit API.
A deposit converts GHS (Ghana Cedis) to USDC on the Stellar network. The customer pays via Mobile Money and receives USDC in their Stellar wallet. This page covers the full programmatic SEP-6 deposit flow.
Flow overview
Client Anchor MoMo Gateway Stellar
│ │ │ │
│ GET /sep6/deposit │ │ │
│────────────────────────►│ │ │
│ { id, extra_info } │ │ │
│◄────────────────────────│ │ │
│ │ │ │
│ │ Collect GHS (auto) │ │
│ │──────────────────────────►│ │
│ │ Payment confirmed │ │
│ │◄──────────────────────────│ │
│ │ │ │
│ │ Send USDC │ │
│ │──────────────────────────────────────────►│
│ │ │ │
│ on_change_callback │ │ │
│◄────────────────────────│ │ │Prerequisites
- Valid SEP-10 JWT token (Authentication)
- KYC status is
ACCEPTED(KYC) - Stellar account has a USDC trustline established
Step 1: Check anchor capabilities
curl -X GET "https://<your-anchor-domain>/sep6/info" \
-H "Authorization: Bearer <sep10_jwt_token>"import { Wallet, Keypair } from '@stellar/typescript-wallet-sdk';
const wallet = Wallet.TestNet();
const ANCHOR_HOME_DOMAIN = '<your-anchor-domain>';
const anchor = wallet.anchor({ homeDomain: ANCHOR_HOME_DOMAIN });
const accountKp = Keypair.fromSecret('SCZANGBA5YHTNYVVV3C7CAZMCLXPJLNS2YFGXDNASLFTGBPFMRKCY6ML');
// Authenticate
const sep10 = await anchor.sep10();
const authToken = await sep10.authenticate({ accountKp });
// Get SEP-6 info
const sep6 = anchor.sep6();
const info = await sep6.info();
console.log("Deposit assets:", info.deposit);
console.log("USDC min:", info.deposit.USDC.min_amount);
console.log("USDC max:", info.deposit.USDC.max_amount);Response:
{
"deposit": {
"USDC": {
"enabled": true,
"min_amount": 1,
"max_amount": 10000,
"funding_methods": ["mobile"],
"fields": {
"funding_method": {
"description": "Payment method",
"choices": ["mobile"]
}
}
}
},
"withdraw": {
"USDC": {
"enabled": true,
"min_amount": 1,
"max_amount": 10000
}
},
"fee": {
"enabled": true
},
"features": {
"account_creation": false,
"claimable_balances": false
}
}Step 2: Initiate a deposit
The amount parameter represents the USDC amount you want to receive. The anchor calculates the equivalent GHS the customer needs to pay based on the current exchange rate.
curl -X GET "https://<your-anchor-domain>/sep6/deposit?\
asset_code=USDC&\
account=GCEXAMPLE4KEYPAIR7HERE2REPLACE5WITH5YOUR5ACTUAL5STELLAR5KEY&\
amount=6.5&\
funding_method=mobile&\
on_change_callback=https://yourapp.com/webhooks/anchor" \
-H "Authorization: Bearer <sep10_jwt_token>"import { Wallet, Keypair } from '@stellar/typescript-wallet-sdk';
const wallet = Wallet.TestNet();
const ANCHOR_HOME_DOMAIN = '<your-anchor-domain>';
const anchor = wallet.anchor({ homeDomain: ANCHOR_HOME_DOMAIN });
const accountKp = Keypair.fromSecret('SCZANGBA5YHTNYVVV3C7CAZMCLXPJLNS2YFGXDNASLFTGBPFMRKCY6ML');
// Authenticate
const sep10 = await anchor.sep10();
const authToken = await sep10.authenticate({ accountKp });
// Initiate deposit
const sep6 = anchor.sep6();
const deposit = await sep6.deposit({
authToken,
params: {
asset_code: 'USDC',
account: accountKp.publicKey,
amount: '6.5',
funding_method: 'mobile',
},
});
console.log("Deposit ID:", deposit.id);Parameters
| Parameter | Required | Description |
|---|---|---|
asset_code | Yes | Asset to deposit — USDC |
account | Yes | Stellar public key to receive USDC |
amount | No | Amount of USDC to receive (anchor converts to GHS) |
funding_method | No | Payment method — mobile for Mobile Money |
on_change_callback | No | URL to receive transaction status updates |
memo | No | Memo to attach to the Stellar payment |
memo_type | No | Memo type: text, id, or hash |
Auto vs Manual collection modes
Auto collection
When the anchor can automatically collect via STK push:
{
"id": "txn_8f3a4b2c-1d5e-6f7a-8b9c-0d1e2f3a4b5c",
"how": "mobile_money",
"extra_info": {
"message": "A Mobile Money prompt will be sent to +233241234567. Approve the payment of GHS 100.00 to complete your deposit of 6.50 USDC."
},
"min_amount": "1",
"max_amount": "10000",
"eta": 300
}In auto mode, the anchor sends an STK push (payment prompt) directly to the customer's registered Mobile Money number. The customer approves the prompt on their phone.
Manual collection
When auto-collection is not available:
{
"id": "txn_8f3a4b2c-1d5e-6f7a-8b9c-0d1e2f3a4b5c",
"how": "mobile_money",
"instructions": {
"network": "MTN",
"short_code": "*170#",
"recipient": "0241000000",
"reference": "SEEV-DEP-8F3A4B",
"amount": "GHS 100.00"
},
"min_amount": "1",
"max_amount": "10000",
"eta": 600
}In manual mode, the customer must initiate the payment themselves using the provided instructions.
Amount handling
| You send | Meaning |
|---|---|
amount=6.5 | You want to receive 6.5 USDC. Anchor calculates GHS equivalent (~100 GHS at rate 15.38 GHS/USDC). |
No amount | Anchor returns min/max. Customer decides how much GHS to send. |
The exchange rate includes the anchor's fee. Use SEP-38 Quotes to lock in a rate before initiating the deposit.
Step 3: Poll transaction status
curl -X GET "https://<your-anchor-domain>/sep6/transaction?id=txn_8f3a4b2c-1d5e-6f7a-8b9c-0d1e2f3a4b5c" \
-H "Authorization: Bearer <sep10_jwt_token>"import { Wallet, Keypair } from '@stellar/typescript-wallet-sdk';
const wallet = Wallet.TestNet();
const anchor = wallet.anchor({ homeDomain: '<your-anchor-domain>' });
const accountKp = Keypair.fromSecret('SCZANGBA5YHTNYVVV3C7CAZMCLXPJLNS2YFGXDNASLFTGBPFMRKCY6ML');
const sep10 = await anchor.sep10();
const authToken = await sep10.authenticate({ accountKp });
// Use the watcher for real-time transaction tracking
const sep6 = anchor.sep6();
const watcher = sep6.watcher();
const { stop, refresh } = watcher.watchOneTransaction({
authToken,
assetCode: 'USDC',
id: 'txn_8f3a4b2c-1d5e-6f7a-8b9c-0d1e2f3a4b5c',
onMessage: (txn) => console.log(`Status: ${txn.status}`),
onSuccess: (txn) => {
console.log(`Received ${txn.amount_out} USDC`);
console.log(`Stellar tx: ${txn.stellar_transaction_id}`);
},
onError: (err) => console.error('Error:', err),
});
// Or get a single transaction
const { transaction } = await sep6.getTransactionBy({
authToken,
id: 'txn_8f3a4b2c-1d5e-6f7a-8b9c-0d1e2f3a4b5c',
});
console.log(`Status: ${transaction.status}`);Response (pending):
{
"transaction": {
"id": "txn_8f3a4b2c-1d5e-6f7a-8b9c-0d1e2f3a4b5c",
"kind": "deposit",
"status": "pending_user_transfer_start",
"status_eta": 300,
"amount_in": "100.00",
"amount_in_asset": "iso4217:GHS",
"amount_out": "6.50",
"amount_out_asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
"amount_fee": "0.10",
"amount_fee_asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
"started_at": "2026-08-05T01:15:00Z",
"more_info_url": "https://<your-anchor-domain>/more-info/txn_8f3a4b2c"
}
}Response (completed):
{
"transaction": {
"id": "txn_8f3a4b2c-1d5e-6f7a-8b9c-0d1e2f3a4b5c",
"kind": "deposit",
"status": "completed",
"amount_in": "100.00",
"amount_in_asset": "iso4217:GHS",
"amount_out": "6.50",
"amount_out_asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
"amount_fee": "0.10",
"amount_fee_asset": "stellar:USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
"started_at": "2026-08-05T01:15:00Z",
"completed_at": "2026-08-05T01:19:30Z",
"stellar_transaction_id": "abc123def456...",
"more_info_url": "https://<your-anchor-domain>/more-info/txn_8f3a4b2c"
}
}on_change_callback
If you provided an on_change_callback URL, the anchor POSTs status updates to it:
{
"transaction": {
"id": "txn_8f3a4b2c-1d5e-6f7a-8b9c-0d1e2f3a4b5c",
"status": "completed",
"stellar_transaction_id": "abc123def456..."
}
}Do not rely solely on callbacks — always verify transaction status by polling. Callbacks are best-effort and may be delayed or missed.
Deposit status flow
pending_user_transfer_start → User needs to approve/send MoMo payment
pending_anchor → Anchor received GHS, processing USDC send
completed → USDC sent to customer's Stellar address
error → Something went wrong (check message)
expired → Deposit window expired without paymentSee Transactions for the full status lifecycle.
Full TypeScript example
import { Wallet, Keypair } from '@stellar/typescript-wallet-sdk';
const wallet = Wallet.TestNet();
const ANCHOR_HOME_DOMAIN = '<your-anchor-domain>';
const anchor = wallet.anchor({ homeDomain: ANCHOR_HOME_DOMAIN });
const accountKp = Keypair.fromSecret('SCZANGBA5YHTNYVVV3C7CAZMCLXPJLNS2YFGXDNASLFTGBPFMRKCY6ML');
async function deposit() {
// Step 1: Authenticate
const sep10 = await anchor.sep10();
const authToken = await sep10.authenticate({ accountKp });
// Step 2: Initiate deposit of 6.5 USDC (≈ GHS 100)
const sep6 = anchor.sep6();
const deposit = await sep6.deposit({
authToken,
params: {
asset_code: 'USDC',
account: accountKp.publicKey,
amount: '6.5',
funding_method: 'mobile',
},
});
console.log("Deposit initiated:", deposit.id);
// Step 3: Watch for completion using the watcher
const watcher = sep6.watcher();
const { stop } = watcher.watchOneTransaction({
authToken,
assetCode: 'USDC',
id: deposit.id,
onMessage: (txn) => {
console.log(`Status: ${txn.status}`);
if (txn.status === 'pending_user_transfer_start') {
console.log('Approve the Mobile Money prompt on your phone.');
}
},
onSuccess: (txn) => {
console.log(`Received ${txn.amount_out} USDC`);
console.log(`Stellar tx: ${txn.stellar_transaction_id}`);
stop();
},
onError: (err) => {
console.error('Deposit error:', err);
stop();
},
});
}
await deposit();Establishing a USDC trustline
Before the anchor can send USDC to your account, you must trust the USDC asset:
import { Wallet, Keypair } from '@stellar/typescript-wallet-sdk';
const wallet = Wallet.TestNet();
const accountKp = Keypair.fromSecret('SCZANGBA5YHTNYVVV3C7CAZMCLXPJLNS2YFGXDNASLFTGBPFMRKCY6ML');
// The wallet SDK's stellar module handles trustline operations
const stellar = wallet.stellar();
const txBuilder = await stellar.transaction({ sourceAddress: accountKp });
// Add USDC trustline
const USDC_ISSUER = 'GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5';
txBuilder.addAssetSupport(new IssuedAssetId('USDC', USDC_ISSUER));
const tx = txBuilder.build();
tx.sign(accountKp);
await stellar.submitTransaction(tx);
console.log("USDC trustline established");Error responses
| Status | Error | Meaning |
|---|---|---|
| 400 | invalid_amount | Amount outside min/max range |
| 400 | invalid_asset_code | Asset not supported |
| 403 | kyc_required | KYC not yet approved |
| 403 | customer_info_needed | Additional KYC fields required |
| 429 | rate_limit | Too many requests |
| 500 | internal_error | Anchor-side error — retry later |