Send and Receive Money
Now that you have an account, you can send and receive funds through the XDBchain network. If you haven’t created an account yet, read step 2 of the Get Started guide.
Most of the time, you’ll be sending money to someone else who has their own account. For this interactive guide, however, you should make a second account to transact with using the same method you used to make your first account.
Send Payments
Actions that change things in XDBChain, like sending payments, changing your account, or making offers to trade various kinds of currencies, are called operations.1 In order to actually perform an operation, you create a transaction, which is just a group of operations accompanied by some extra information, like what account is making the transaction and a cryptographic signature to verify that the transaction is authentic.2
If any operation in the transaction fails, they all fail. For example, let’s say you have 100XDB and you make two payment operations of 60XDB each. If you make two transactions (each with one operation), the first will succeed and the second will fail because you don’t have enough XDB. You’ll be left with 40XDB. However, if you group the two payments into a single transaction, they will both fail and you’ll be left with the full 100XDB still in your account.
Finally, every transaction costs a small fee. Like the minimum balance on accounts, this fee helps stop people from overloading the system with lots of transactions. Known as the base fee, it is very small—100 nibbs per operation (that’s 0.00001 XDB; nibbs are easier to talk about than such tiny fractions of a XDB). A transaction with two operations would cost 200 nibbs.3
Building a Transaction
XDBChain stores and communicates transaction data in a binary format called XDR.4 Luckily, the Stellar SDKs provide tools that take care of all that. Here’s how you might send 10XDB to another account:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://horizon.futurenet.xdbchain.com");
// Note: You'll need to specify XDBchain's network passphrase explicitly
// instead of using Stellar's built-in constants like StellarSdk.Networks.TESTNET.
const FUTURENET = "Futurenet XDBChain Network ; October 2023"
var sourceKeys = StellarSdk.Keypair.fromSecret(
"SDSTGDEXYQCBI5NXCK6PO67TTQOSYL2KNX47VGEE2BRUBGM2DPPJCAU7",
);
var destinationId = "GDPK4I43LEHONGHQDVTAY5HRNMRZ2WW33OYO23XE4E4QX5KIYR55VUJ5";
// Transaction will hold a built transaction we can resubmit if the result is unknown.
var transaction;
// First, check to make sure that the destination account exists.
// You could skip this, but if the account does not exist, you will be charged
// the transaction fee when the transaction fails.
server
.loadAccount(destinationId)
// If the account is not found, surface a nicer error message for logging.
.catch(function (error) {
if (error instanceof StellarSdk.NotFoundError) {
throw new Error("The destination account does not exist!");
} else return error;
})
// If there was no error, load up-to-date information on your account.
.then(function () {
return server.loadAccount(sourceKeys.publicKey());
})
.then(function (sourceAccount) {
// Start building the transaction.
transaction = new StellarSdk.TransactionBuilder(sourceAccount, {
fee: StellarSdk.BASE_FEE,
networkPassphrase: FUTURENET,
})
.addOperation(
StellarSdk.Operation.payment({
destination: destinationId,
// Because XDBchain allows transaction in many currencies, you must
// specify the asset type. The special "native" asset represents XDB.
asset: StellarSdk.Asset.native(),
amount: "10",
}),
)
// A memo allows you to add your own metadata to a transaction. It's
// optional and does not affect how XDBchain treats the transaction.
.addMemo(StellarSdk.Memo.text("Test Transaction"))
// Wait a maximum of three minutes for the transaction
.setTimeout(180)
.build();
// Sign the transaction to prove you are actually the person sending it.
transaction.sign(sourceKeys);
// And finally, send it off to XDBchain!
return server.submitTransaction(transaction);
})
.then(function (result) {
console.log("Success! Results:", result);
})
.catch(function (error) {
console.error("Something went wrong!", error);
// If the result is unknown (no response body, timeout etc.) we simply resubmit
// already built transaction:
// server.submitTransaction(transaction);
});
What exactly happened there? Let’s break it down.
Confirm that the account ID you are sending to actually exists by loading the associated account data from the XDBchain network. Everything will actually be OK if you skip this step, but doing it gives you an opportunity to avoid making a transaction you know will fail. You can also use this call to perform any other verification you might want to do on a destination account. If you are writing banking software, for example, this is a good place to insert regulatory compliance checks and KYC verification.
1 2 3
server.loadAccount(destinationId).then(function (account) { /* validate the account */ });
Load data for the account you are sending from. An account can only perform one transaction at a time5 and has something called a sequence number, which helps XDBchain verify the order of transactions. A transaction’s sequence number needs to match the account’s sequence number, so you need to get the account’s current sequence number from the network.
1 2 3
.then(function() { return server.loadAccount(sourceKeys.publicKey()); })
The SDK will automatically increment the account’s sequence number when you build a transaction, so you won’t need to retrieve this information again if you want to perform a second transaction.
Start building a transaction. This requires an account object, not just an account ID, because it will increment the account’s sequence number.
1
var transaction = new StellarSdk.TransactionBuilder(sourceAccount)
Add the payment operation to the account. Note that you need to specify the type of asset you are sending — XDBchain’s “native” currency is XDB, but you can send any type of asset or currency you like, from dollars to bitcoin to any sort of asset you trust the issuer to redeem (more details below). For now, though, we’ll stick to XDB, which are called “native” assets in the SDK:
1 2 3 4 5
.addOperation(StellarSdk.Operation.payment({ destination: destinationId, asset: StellarSdk.Asset.native(), amount: "10" }))
You should also note that the amount is a string rather than a number. When working with extremely small fractions or large values, floating point math can introduce small inaccuracies. Since not all systems have a native way to accurately represent extremely small or large decimals, Stellar uses strings as a reliable way to represent the exact amount across any system.
Optionally, you can add your own metadata, called a memo, to a transaction. XDBchain doesn’t do anything with this data, but you can use it for any purpose you’d like. If you are a bank that is receiving or sending payments on behalf of other people, for example, you might include the actual person the payment is meant for here.
1
.addMemo(StellarSdk.Memo.text('Test Transaction'))
Now that the transaction has all the data it needs, you have to cryptographically sign it using your secret seed. This proves that the data actually came from you and not someone impersonating you.
1
transaction.sign(sourceKeys);
And finally, send it to the XDBchain network!
1
server.submitTransaction(transaction);
IMPORTANT It’s possible that you will not receive a response from Horizon server due to a bug, network conditions, etc. In such situation it’s impossible to determine the status of your transaction. That’s why you should always save a built transaction (or transaction encoded in XDR format) in a variable or a database and resubmit it if you don’t know it’s status. If the transaction has already been successfully applied to the ledger, Horizon will simply return the saved result and not attempt to submit the transaction again. Only in cases where a transaction’s status is unknown (and thus will have a chance of being included into a ledger) will a resubmission to the network occur.
Receive Payments
You don’t actually need to do anything to receive payments into a XDBchain account — if a payer makes a successful transaction to send assets to you, those assets will automatically be added to your account.
However, you’ll want to know that someone has actually paid you. If you are a bank accepting payments on behalf of others, you need to find out what was sent to you so you can disburse funds to the intended recipient. If you are operating a retail business, you need to know that your customer actually paid you before you hand them their merchandise. And if you are an automated rental car with a XDBchain account, you’ll probably want to verify that the customer in your front seat actually paid before that person can turn on your engine.
A simple program that watches the network for payments and prints each one might look like:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://horizon.futurenet.xdbchain.com");
var accountId = "GAH2PQR37FRRBHKWGY56P5W5MMRQNCVRJAMHJUBLMZA2O3LCECFAUVDW";
// Create an API call to query payments involving the account.
var payments = server.payments().forAccount(accountId);
// If some payments have already been handled, start the results from the
// last seen payment. (See below in `handlePayment` where it gets saved.)
var lastToken = loadLastPagingToken();
if (lastToken) {
payments.cursor(lastToken);
}
// `stream` will send each recorded payment, one by one, then keep the
// connection open and continue to send you new payments as they occur.
payments.stream({
onmessage: function (payment) {
// Record the paging token so we can start from here next time.
savePagingToken(payment.paging_token);
// The payments stream includes both sent and received payments. We only
// want to process received payments here.
if (payment.to !== accountId) {
return;
}
// In Horizon API, XDBs are referred to as the “native” type. Other
// asset types have more detailed information.
var asset;
if (payment.asset_type === "native") {
asset = "XDB";
} else {
asset = payment.asset_code + ":" + payment.asset_issuer;
}
console.log(payment.amount + " " + asset + " from " + payment.from);
},
onerror: function (error) {
console.error("Error in payment stream");
},
});
function savePagingToken(token) {
// In most cases, you should save this to a local database or file so that
// you can load it next time you stream new payments.
}
function loadLastPagingToken() {
// Get the last paging token from a local database or file
}
There are two main parts to this program. First, you create a query for payments involving a given account. Like most queries in XDBchain, this could return a huge number of items, so the API returns paging tokens, which you can use later to start your query from the same point where you previously left off. In the example above, the functions to save and load paging tokens are left blank, but in a real application, you’d want to save the paging tokens to a file or database so you can pick up where you left off in case the program crashes or the user closes it.
1
2
3
4
5
var payments = server.payments().forAccount(accountId);
var lastToken = loadLastPagingToken();
if (lastToken) {
payments.cursor(lastToken);
}
Second, the results of the query are streamed. This is the easiest way to watch for payments or other transactions. Each existing payment is sent through the stream, one by one. Once all existing payments have been sent, the stream stays open and new payments are sent as they are made.
Try it out: Run this program, and then, in another window, create and submit a payment. You should see this program log the payment.
1
2
3
4
5
payments.stream({
onmessage: function (payment) {
// handle a payment
},
});
You can also request payments in groups, or pages. Once you’ve processed each page of payments, you’ll need to request the next one until there are none left.
1
2
3
4
5
6
payments.call().then(function handlePage(paymentsPage) {
paymentsPage.records.forEach(function (payment) {
// handle a payment
});
return paymentsPage.next().then(handlePage);
});
Transacting in Other Currencies
One of the amazing things about the XDBchain network is that you can send and receive many types of assets, such as US dollars, Nigerian naira, digital currencies like bitcoin, or even your own new kind of asset.
While XDBchain’s native asset, XDB, is fairly simple, all other assets can be thought of like a credit issued by a particular account. In fact, when you trade US dollars on the XDBchain network, you don’t actually trade US dollars — you trade US dollars from a particular account. That’s why the assets in the example above had both a code
and an issuer
. The issuer
is the ID of the account that created the asset. Understanding what account issued the asset is important — you need to trust that, if you want to redeem your dollars on the XDBchain network for actual dollar bills, the issuer will be able to provide them to you. Because of this, you’ll usually only want to trust major financial institutions for assets that represent national currencies.
XDBchain also supports payments sent as one type of asset and received as another. You can send Nigerian naira to a friend in Germany and have them receive euros. These multi-currency transactions are made possible by a built-in market mechanism where people can make offers to buy and sell different types of assets. XDBchain will automatically find the best people to exchange currencies with in order to convert your naira to euros. This system is called distributed exchange.
You can read more about the details of assets in the assets overview.
What Next?
Now that you can send and receive payments using Horizon API, you’re on your way to writing all kinds of amazing financial software. Experiment with other parts of the API, then read up on more detailed topics:
A list of all the possible operations can be found on the operations page. ↩
The full details on transactions can be found on the transactions page. ↩
The 100 nibbs is called XDBchain’s base fee. The base fee can be changed, but a change in XDBchain’s fees isn’t likely to happen more than once every several years. ↩
Even though most responses from the Horizon REST API use JSON, most of the data in XDBchain is actually stored in a format called XDR, or External Data Representation. XDR is both more compact than JSON and stores data in a predictable way, which makes signing and verifying an XDR-encoded message easier. You can get more details on Stellar’s XDR page. ↩
In situations where you need to perform a high number of transactions in a short period of time (for example, a bank might perform transactions on behalf of many customers using one XDBchain account), you can create several XDBchain accounts that work simultaneously. Read more about this in the guide to channels. ↩