Send or Receive Funds
- Deposit: Monitor the funds account for USDC payments from MoneyGram, matching the memo provided during initiation.
- Withdrawal: Send USDC to MoneyGram’s specified Stellar account with the provided memo.
Send / Deposit
from stellar_sdk import (
Server, TransactionBuilder, Network, Asset, IdMemo
)
submit_payment(
destination=response_body["transaction"]["withdraw_anchor_account"],
memo=response_body["transaction"]["withdraw_memo"],
amount=response_body["transaction"]["amount_in"]
)
def submit_payment(destination: str, memo: str, amount: str):
server = Server()
account = server.load_account(FUNDS_STELLAR_KEYPAIR.public_key)
transaction = TransactionBuilder(
source_account=account,
network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE,
base_fee=10000 # this is 0.001 XLM
).append_payment_op(
destination=destination,
asset=Asset(ASSET_CODE, ASSET_ISSUER),
amount=amount,
).add_memo(
IdMemo(int(memo))
).build()
transaction.sign(FUNDS_STELLAR_KEYPAIR)
response = server.submit_transaction(transaction)
print(f"Stellar-generated transaction ID: {response['id']}")
import { Wallet, IssuedAssetId } from "@stellar/typescript-wallet-sdk";
import { Horizon } from "@stellar/stellar-sdk";
const wallet = Wallet.TestNet();
// This creates a Stellar instance to manage the connection with Horizon.
const stellar = wallet.stellar();
// Let's make sure Anchor supports the token we want to withdraw.
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`,
);
}
// This creates the Stellar asset object which we'll need while creating the
// transfer withdrawal transaction below.
const asset = new IssuedAssetId(currency.code, currency.issuer);
// 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: FUNDS_STELLAR_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 source (funds) account key pair
transferTransaction.sign(FUNDS_STELLAR_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/error-handling
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: FUNDS_STELLAR_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.
}
}
}
};
Receive / Withdrawal
from stellar_sdk import Server
from .queries import get_transaction_by_memo
def stream_payments(account: str, cursor: str):
s = Server()
payments = s.payments().for_account(account).join("transactions")
for payment in payments.cursor(cursor).stream():
if (
payment["type"] != "payment"
or payment["from"] == account
or payment["asset_type"] == "native"
or payment["asset_code"] != ASSET_CODE
or payment["asset_issuer"] != ASSET_ISSUER
):
continue
transaction = get_transaction_by_memo(
payment["transaction"]["memo"],
payment["transaction"]["memo_type"]
) # DB query
if not transaction:
continue
print(
f"Payment for deposit transaction {transaction.id} "
"matched with Stellar transaction "
f"{payment['transaction']['id']}"
)
// The Wallet SDK does not support payments streaming yet so let's build
// it using the underlying Horizon SDK
import { Horizon } from "@stellar/stellar-sdk";
import { getTransactionByMemo } from "./queries";
const streamPayments = (account: string, cursor: string) => {
const server = new Horizon.Server("https://horizon-testnet.stellar.org");
server
.payments()
.forAccount(account)
.join("transactions")
.cursor(cursor)
.stream({
onmessage: (payment) => {
if (
payment["type"] !== "payment" ||
payment["from"] === account ||
payment["asset_type"] === "native" ||
payment["asset_code"] !== ASSET_CODE ||
payment["asset_issuer"] !== ASSET_ISSUER
) {
return;
}
const transaction = getTransactionByMemo(
payment["transaction_attr"]["memo"],
payment["transaction_attr"]["memo_type"],
); // this is your own DB query function
if (!transaction) {
return;
}
console.log(
`Payment for deposit transaction ${transaction.id}`,
`matched with Stellar transaction `,
`${payment["transaction_attr"]["id"]}`,
);
},
onerror: (error) => {
// handle error
},
});
};
Updated 14 days ago
What’s Next