# Welcome

**Getting Started**

Welcome to the Bantu Developer's Documentation. It contains everything required for developers to get started with Building and integrating their apps with Bantu.

**What is Bantu** The Bantu blockchain combines speed, security, reliability and includes human elements of fairness and sharing to empower participants on the network while lowering the entry barriers for participation. Many of the well-known blockchains, for all their popularity, have mostly benefited early adopters. We are changing that narrative and we are set to become a conduit for positive socio-economic impact in the emerging economies around the world.

**A quick rundown of what's here:**

**Tutorials:**

This section will show you how to do basic things like create an account and make payments. [Read More](https://bantu-foundation.gitbook.io/bantu-blockchain-foundation/tutorials)

**Issue Assets:**

You can issue assets easily on Bantu, and it can be done with few lines of code safely. You can also publish information about your asset for Wallets and consumers, control access to it by setting simple flags, and make it available for trade on the Bantu decentralized exchange. [Read More](https://bantu-foundation.gitbook.io/bantu-blockchain-foundation/issuing-assets/index)

**Anchor Assets**

&#x20;Organizations can connect assets issued on Bantu with external banking and payment systems, allowing users and businesses to transfer assets onto or through the Bantu network. Specifically, organizations can anchor assets issued on the Bantu network by facilitating 1-1 trades for the off-chain representation of the tokenized asset. [Read More](https://bantu-foundation.gitbook.io/bantu-blockchain-foundation/anchoring-assets)

**Build Apps**

&#x20;Bantu is a self-serve distributed ledger that you can use as a backend to power all kinds of apps and services. Any app built on Bantu relies on the same basic functions: key storage, account creation, transaction signing, and queries to the Bantu database. This section of the docs will walk you through the process of building a basic wallet that does all those things, and will show you how to add features to it like the support for in-app deposits and withdrawals from anchors. [Read More](https://bantu-foundation.gitbook.io/bantu-blockchain-foundation/building-apps/index)

**Run a Core Node**&#x20;

This section explains the technical and operational aspects of installing, configuring, and maintaining a Bantu Core node, which is a server that connects to the Bantu peer-to-peer network to keep a common distributed ledger. You don’t have to run a node to get started on Bantu, but you will likely want to if you're in production, need high-availability access network, or want to help increase network health and decentralization. [Read More](https://bantu-foundation.gitbook.io/bantu-blockchain-foundation/run-core-node/index)

**Run an API Server**

&#x20;Most developers access the network using Horizon, the Bantu API. It takes the performance-oriented data structures from Bantu Core and converts them into a friendlier format. If you're running your own Bantu Core node and using it to submit transactions or get network data, you will likely also want to run your own Horizon instance, and this section will show you how. If you're just looking to use Horizon (vs. setting up a Horizon server), consult the API Reference. [Read More](https://bantu-foundation.gitbook.io/bantu-blockchain-foundation/run-api-server/index)

**Software and SDKs**

&#x20;This is where you'll find all the Bantu SDKs. There are a lot of them, and they're all pretty well maintained and documented, so you should be able to build on Bantu in your language of choice. This section is also home to some tools and reference implementations created and maintained by the Bantu Development Foundation to kickstart development. [Read More](https://bantu-foundation.gitbook.io/bantu-blockchain-foundation/software-and-sdks/index)

**Glossary**&#x20;

This section defines all the terms and explains all the concepts germane to Bantu. Use it to look up a word, or to dig deeper into nitty-gritty details.[Read More](https://bantu-foundation.gitbook.io/bantu-blockchain-foundation/glossary)


# Where to Start


# Introduction

The goal of these docs is to explain key concepts and offer practical examples so developers can roll up their sleeves and get building on Bantu. Ideally, they answer crucial developer questions, and are thorough enough to guide a project from conception to production. Like the Bantu codebase, these docs are open source and constantly evolving, so if you can't find what you're looking for or have ideas for improvements, please contribute by filing a Github issue or pull request in this repository. This section outlines some of the basic concepts and channels for developers, so if you're already familiar with Bantu, you may want to skip ahead to whatever section applies to your product or use case. You can use the left-side menu of the docs to navigate to various sections, and the right-side menu to navigate to different parts of a given page. For a quick summary of each section, please visit the [Welcome Page](https://bantu-foundation.gitbook.io/bantu-blockchain-foundation/)

## Getting Started

Once you have a high-level understanding of Bantu, you can start building right away, and the subsequent sections of these docs will focus on how to do that. You don't have to run your own Bantu Core node to develop on Bantu — several organizations including the Bantu Development Foundation offer public-access API endpoints that allow you to submit transactions and query the ledger — so you can focus on building your product before committing to setting up the network infrastructure.

If you are new, you may want to start with the early [Tutorials](/tutorials/create-account) to familiarize yourself with some of the building blocks of Bantu. You should also investigate the [SDKs](/software-and-sdks/index) designed to make developing in your language of choice easy, and familiarize yourself with the canonical [List of Operations](/start/list-of-operations), which documents everything you can do with Bantu — along with parameters and error codes — and links to the relevant documentation for key SDKs. Finally, you may want to explore the [API Reference](/api/introduction/index) documentation, which details every resource, aggregation, and error provided by Horizon, the Bantu API.

## Developer Channels

Bantu has an active developer community, and it's often helpful to interact with other devs who are working on Bantu-based projects. They're great at answering questions, giving feedback, and sharing information about the best ways to use the network. For general information on our community channels, check out the Bantu's [Community Page](https://bantutalk.org/).

You can join our [dedicated developer channel ](https://bantutalk.org/c/bantu-developers/7)to get information on important plans, developments, and events


# list of Operations

This is the canonical list of Bantu operations, which lists every Bantu operation along with parameters, error codes, and links to the relevant documentation for key SDKs.

For a description of how operations work in Bantu, see [Operations](/glossary/operations).

* [Create Account](/start/list-of-operations#create-account)
* [Payment](/start/list-of-operations#payment)
* [Path Payment Strict Send](/start/list-of-operations#path-payment-strict-send)
* [Path Payment Strict Receive](/start/list-of-operations#path-payment-strict-receive)
* [Manage Buy Offer](/start/list-of-operations#manage-buy-offer)
* [Manage Sell Offer](/start/list-of-operations#manage-sell-offer)
* [Create Passive Sell Offer](/start/list-of-operations#create-passive-sell-offer)
* [Set Options](/start/list-of-operations#set-options)
* [Change Trust](/start/list-of-operations#change-trust)
* [Allow Trust](/start/list-of-operations#allow-trust)
* [Account Merge](/start/list-of-operations#account-merge)
* [Manage Data](/start/list-of-operations#manage-data)
* [Bump Sequence](/start/list-of-operations#bump-sequence)
* [Create Claimable Balance](/start/list-of-operations#create-claimable-balance)
* [Claim Claimable Balance](/start/list-of-operations#claim-claimable-balance)
* [Begin Sponsoring Future Reserves](/start/list-of-operations#begin-sponsoring-future-reserves)
* [End Sponsoring Future Reserves](/start/list-of-operations#end-sponsoring-future-reserves)
* [Revoke Sponsorship](/start/list-of-operations#revoke-sponsorship)

## Create Account

[JavaScript](http://stellar.github.io/js-stellar-sdk/Operation.html#.createAccount) | [Java](http://XBN.github.io/java-stellar-sdk/org/stellar/sdk/CreateAccountOperation.Builder.html) | [Go](https://godoc.org/github.com/stellar/go/txnbuild#CreateAccount)

Creates and funds a new account with the specified starting balance.

Threshold: Medium

Result: `CreateAccountResult`

Parameters:

| Parameter        | Type       | Description                                                                                 |
| ---------------- | ---------- | ------------------------------------------------------------------------------------------- |
| Destination      | account ID | Account address that is created and funded.                                                 |
| Starting Balance | integer    | Amount of XBN to send to the newly created account. This XBN comes from the source account. |

Possible errors:

| Error                           | Code | Description                                                                                                                                                                                                          |
| ------------------------------- | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| CREATE\_ACCOUNT\_MALFORMED      | -1   | The `destination` is invalid.                                                                                                                                                                                        |
| CREATE\_ACCOUNT\_UNDERFUNDED    | -2   | The source account performing the command does not have enough funds to give `destination` the `starting balance` amount of XBN and still maintain its minimum XBN reserve plus satisfy its XBN selling liabilities. |
| CREATE\_ACCOUNT\_LOW\_RESERVE   | -3   | This operation would create an account with fewer than the minimum number of XBN an account must hold.                                                                                                               |
| CREATE\_ACCOUNT\_ALREADY\_EXIST | -4   | The `destination` account already exists.                                                                                                                                                                            |

## Payment

[JavaScript](http://stellar.github.io/js-stellar-sdk/Operation.html#.payment) | [Java](http://stellar.github.io/java-stellar-sdk/org/stellar/sdk/PaymentOperation.Builder.html) | [Go](https://godoc.org/github.com/stellar/go/txnbuild#Payment)

Sends an amount in a specific asset to a destination account.

Threshold: Medium

Result: `PaymentResult`

Parameters:

| Parameters  | Type       | Description                                 |
| ----------- | ---------- | ------------------------------------------- |
| Destination | account ID | Account address that receives the payment.  |
| Asset       | asset      | Asset to send to the destination account.   |
| Amount      | integer    | Amount of the aforementioned asset to send. |

Possible errors:

| Error                         | Code | Description                                                                                                                                                                                                     |
| ----------------------------- | ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| PAYMENT\_MALFORMED            | -1   | The input to the payment is invalid.                                                                                                                                                                            |
| PAYMENT\_UNDERFUNDED          | -2   | The source account (sender) does not have enough funds to send `amount` and still satisfy its selling liabilities. Note that if sending XBN then the sender must additionally maintain its minimum XBN reserve. |
| PAYMENT\_SRC\_NO\_TRUST       | -3   | The source account does not trust the issuer of the asset it is trying to send.                                                                                                                                 |
| PAYMENT\_SRC\_NOT\_AUTHORIZED | -4   | The source account is not authorized to send this payment.                                                                                                                                                      |
| PAYMENT\_NO\_DESTINATION      | -5   | The receiving account does not exist.                                                                                                                                                                           |
| PAYMENT\_NO\_TRUST            | -6   | The receiver does not trust the issuer of the asset being sent. For more information, see the [assets doc](/glossary/assets).                                                                                   |
| PAYMENT\_NOT\_AUTHORIZED      | -7   | The destination account is not authorized by the asset's issuer to hold the asset.                                                                                                                              |
| PAYMENT\_LINE\_FULL           | -8   | The destination account (receiver) does not have sufficient limits to receive `amount` and still satisfy its buying liabilities.                                                                                |
| PAYMENT\_NO\_ISSUER           | -9   | The issuer of the asset does not exist.                                                                                                                                                                         |

## Path Payment Strict Send

[JavaScript](http://stellar.github.io/js-stellar-sdk/Operation.html#.pathPaymentStrictSend) | [Java](http://stellar.github.io/java-stellar-sdk/org/stellar/sdk/PathPaymentStrictSendOperation.Builder.html) | [Go](https://godoc.org/github.com/stellar/go/txnbuild#PathPaymentStrictSend)

A path payment sends an amount of a specific asset to a destination account through a path of offers. Since the asset sent (e.g., 450 XBN) can be different from the asset received (e.g, 6 BTC), path payments allow for the simultaneous transfer and conversion of currencies.

A Path Payment Strict Send allows a user to specify the *amount of the asset to send*. The amount received will vary based on offers in the order books. If you would like to instead specify the amount received, use the [Path Payment Strict Receive](/start/list-of-operations#path-payment-strict-receive) operation.

A few things to note:

* path payments don't allow intermediate offers to be from the source account as this would yield a worse exchange rate. You'll need to either split the path payment into two smaller path payments, or ensure that the source account's offers are not at the top of the order book.
* balances are settled at the very end of the operation
  * this is especially important when `(Destination, Destination Asset) == (Source, Send Asset)` as this provides a functionality equivalent to getting a no interest loan for the duration of the operation.
* `Destination min` is a protective measure: it allows you to specify a lower bound for an acceptable conversion. If offers in the order books are not favorable enough for the operation to deliver that amount, the operation will fail.

Threshold: Medium

Result: `PathPaymentStrictSendResult`

Parameters:

| Parameters        | Type           | Description                                                                                                                                                                                                                                                                  |
| ----------------- | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Send asset        | asset          | The asset deducted from the sender's account.                                                                                                                                                                                                                                |
| Send amount       | integer        | The amount of `send asset` to deduct (excluding fees).                                                                                                                                                                                                                       |
| Destination       | account ID     | Account ID of the recipient.                                                                                                                                                                                                                                                 |
| Destination asset | asset          | The asset the destination account receives.                                                                                                                                                                                                                                  |
| Destination min   | integer        | The minimum amount of `destination asset` the destination account can receive.                                                                                                                                                                                               |
| Path              | list of assets | The assets (other than `send asset` and `destination asset`) involved in the offers the path takes. For example, if you can only find a path from USD to EUR through XBN and BTC, the path would be USD -> XBN -> BTC -> EUR and the `path` field would contain XBN and BTC. |

Possible errors:

| Error                                             | Code | Description                                                                                                                                                                                            |
| ------------------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| PATH\_PAYMENT\_STRICT\_SEND\_MALFORMED            | -1   | The input to this path payment is invalid.                                                                                                                                                             |
| PATH\_PAYMENT\_STRICT\_SEND\_UNDERFUNDED          | -2   | The source account (sender) does not have enough funds to send and still satisfy its selling liabilities. Note that if sending XBN then the sender must additionally maintain its minimum XBN reserve. |
| PATH\_PAYMENT\_STRICT\_SEND\_SRC\_NO\_TRUST       | -3   | The source account does not trust the issuer of the asset it is trying to send.                                                                                                                        |
| PATH\_PAYMENT\_STRICT\_SEND\_SRC\_NOT\_AUTHORIZED | -4   | The source account is not authorized to send this payment.                                                                                                                                             |
| PATH\_PAYMENT\_STRICT\_SEND\_NO\_DESTINATION      | -5   | The destination account does not exist.                                                                                                                                                                |
| PATH\_PAYMENT\_STRICT\_SEND\_NO\_TRUST            | -6   | The destination account does not trust the issuer of the asset being sent. For more, see the [assets doc](/glossary/assets).                                                                           |
| PATH\_PAYMENT\_STRICT\_SEND\_NOT\_AUTHORIZED      | -7   | The destination account is not authorized by the asset's issuer to hold the asset.                                                                                                                     |
| PATH\_PAYMENT\_STRICT\_SEND\_LINE\_FULL           | -8   | The destination account does not have sufficient limits to receive `destination amount` and still satisfy its buying liabilities.                                                                      |
| PATH\_PAYMENT\_STRICT\_SEND\_NO\_ISSUER           | -9   | The issuer of one of the assets is missing.                                                                                                                                                            |
| PATH\_PAYMENT\_STRICT\_SEND\_TOO\_FEW\_OFFERS     | -10  | There is no path of offers connecting the `send asset` and `destination asset`. Bantu only considers paths of length 5 or shorter.                                                                     |
| PATH\_PAYMENT\_STRICT\_SEND\_OFFER\_CROSS\_SELF   | -11  | The payment would cross one of its own offers.                                                                                                                                                         |
| PATH\_PAYMENT\_STRICT\_SEND\_UNDER\_DESTMIN       | -12  | The paths that could send `destination amount` of `destination asset` would fall short of `destination min`.                                                                                           |

## Path Payment Strict Receive

[JavaScript](https://stellar.github.io/js-stellar-sdk/Operation.html#.pathPaymentStrictReceive) | [Java](https://stellar.github.io/java-stellar-sdk/org/stellar/sdk/PathPaymentStrictReceiveOperation.Builder.html) | [Go](https://godoc.org/github.com/stellar/go/txnbuild#PathPaymentStrictReceive)

A path payment sends an amount of a specific asset to a destination account through a path of offers. Since the asset sent (e.g., 450 XBN) can be different from the asset received (e.g, 6 BTC), path payments allow for the simultaneous transfer and conversion of currencies.

A Path Payment Strict Receive allows a user to specify the *amount of the asset received*. The amount sent varies based on offers in the order books. If you would like to instead specify the amount sent, use the [Path Payment Strict Send](/start/list-of-operations#path-payment-strict-send) operation.

A few things to note:

* path payment doesn't allow intermediate offers to be from the source account as this would yield a worse exchange rate. You'll need to either split the path payment into two smaller path payments, or ensure that the source account's offers are not at the top of the order book.
* balances are settled at the very end of the operation
  * this is especially important when `(Destination, Destination Asset) == (Source, Send Asset)` as this provides a functionality equivalent to getting a no interest loan for the duration of the operation.
* `Send max` is a protective measure: it allows you to specify an upper bound for an acceptable conversion. If offers in the order books are not favorable enough for the operation to succeed for less than `Send max`, the operation will fail.

Threshold: Medium

Result: `PathPaymentStrictReceiveResult`

Parameters:

| Parameters         | Type           | Description                                                                                                                                                                                                                                                                  |
| ------------------ | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Send asset         | asset          | The asset deducted from the sender's account.                                                                                                                                                                                                                                |
| Send max           | integer        | The maximum amount of `send asset` to deduct (excluding fees).                                                                                                                                                                                                               |
| Destination        | account ID     | Account ID of the recipient.                                                                                                                                                                                                                                                 |
| Destination asset  | asset          | The asset the destination account receives.                                                                                                                                                                                                                                  |
| Destination amount | integer        | The amount of `destination asset` the destination account receives.                                                                                                                                                                                                          |
| Path               | list of assets | The assets (other than `send asset` and `destination asset`) involved in the offers the path takes. For example, if you can only find a path from USD to EUR through XBN and BTC, the path would be USD -> XBN -> BTC -> EUR and the `path` field would contain XBN and BTC. |

Possible errors:

| Error                                                | Code | Description                                                                                                                                                                                            |
| ---------------------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| PATH\_PAYMENT\_STRICT\_RECEIVE\_MALFORMED            | -1   | The input to this path payment is invalid.                                                                                                                                                             |
| PATH\_PAYMENT\_STRICT\_RECEIVE\_UNDERFUNDED          | -2   | The source account (sender) does not have enough funds to send and still satisfy its selling liabilities. Note that if sending XBN then the sender must additionally maintain its minimum XBN reserve. |
| PATH\_PAYMENT\_STRICT\_RECEIVE\_SRC\_NO\_TRUST       | -3   | The source account does not trust the issuer of the asset it is trying to send.                                                                                                                        |
| PATH\_PAYMENT\_STRICT\_RECEIVE\_SRC\_NOT\_AUTHORIZED | -4   | The source account is not authorized to send this payment.                                                                                                                                             |
| PATH\_PAYMENT\_STRICT\_RECEIVE\_NO\_DESTINATION      | -5   | The destination account does not exist.                                                                                                                                                                |
| PATH\_PAYMENT\_STRICT\_RECEIVE\_NO\_TRUST            | -6   | The destination account does not trust the issuer of the asset being sent. For more, see the [assets doc](/glossary/assets).                                                                           |
| PATH\_PAYMENT\_STRICT\_RECEIVE\_NOT\_AUTHORIZED      | -7   | The destination account is not authorized by the asset's issuer to hold the asset.                                                                                                                     |
| PATH\_PAYMENT\_STRICT\_RECEIVE\_LINE\_FULL           | -8   | The destination account does not have sufficient limits to receive `destination amount` and still satisfy its buying liabilities.                                                                      |
| PATH\_PAYMENT\_STRICT\_RECEIVE\_NO\_ISSUER           | -9   | The issuer of one the of assets is missing.                                                                                                                                                            |
| PATH\_PAYMENT\_STRICT\_RECEIVE\_TOO\_FEW\_OFFERS     | -10  | There is no path of offers connecting the `send asset` and `destination asset`. Bantu only considers paths of length 5 or shorter.                                                                     |
| PATH\_PAYMENT\_STRICT\_RECEIVE\_OFFER\_CROSS\_SELF   | -11  | The payment would cross one of its own offers.                                                                                                                                                         |
| PATH\_PAYMENT\_STRICT\_RECEIVE\_OVER\_SENDMAX        | -12  | The paths that could send `destination amount` of `destination asset` would exceed `send max`.                                                                                                         |

## Manage Buy Offer

[JavaScript](http://stellar.github.io/js-stellar-sdk/Operation.html#.manageBuyOffer) | [Java](https://stellar.github.io/java-stellar-sdk/org/stellar/sdk/ManageBuyOfferOperation.Builder.html) | [Go](https://godoc.org/github.com/stellar/go/txnbuild#ManageBuyOffer)

Creates, updates, or deletes an offer to buy one asset for another, otherwise known as a "bid" order on a traditional orderbook.

If you want to create a new offer set Offer ID to `0`.

If you want to update an existing offer set Offer ID to existing offer ID.

If you want to delete an existing offer set Offer ID to existing offer ID and set Amount to `0`.

Threshold: Medium

Result: `ManageBuyOfferResult`

| Parameters | Type                     | Description                                                                                                                            |
| ---------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| Selling    | asset                    | Asset the offer creator is selling.                                                                                                    |
| Buying     | asset                    | Asset the offer creator is buying.                                                                                                     |
| Amount     | integer                  | Amount of `buying` being bought. Set to `0` if you want to delete an existing offer.                                                   |
| Price      | {numerator, denominator} | Price of 1 unit of `buying` in terms of `selling`. For example, if you wanted to buy 30 XBN and sell 5 BTC, the price would be {5,30}. |
| Offer ID   | unsigned integer         | The ID of the offer. `0` for new offer. Set to existing offer ID to update or delete.                                                  |

Possible errors:

| Error                                     | Code | Description                                                                                                                                                                                                                                                                                                |
| ----------------------------------------- | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| MANAGE\_BUY\_OFFER\_MALFORMED             | -1   | The input is incorrect and would result in an invalid offer.                                                                                                                                                                                                                                               |
| MANAGE\_BUY\_OFFER\_SELL\_NO\_TRUST       | -2   | The account creating the offer does not have a trustline for the asset it is selling.                                                                                                                                                                                                                      |
| MANAGE\_BUY\_OFFER\_BUY\_NO\_TRUST        | -3   | The account creating the offer does not have a trustline for the asset it is buying.                                                                                                                                                                                                                       |
| MANAGE\_BUY\_OFFER\_BUY\_NOT\_AUTHORIZED  | -4   | The account creating the offer is not authorized to sell this asset.                                                                                                                                                                                                                                       |
| MANAGE\_BUY\_OFFER\_SELL\_NOT\_AUTHORIZED | -5   | The account creating the offer is not authorized to buy this asset.                                                                                                                                                                                                                                        |
| MANAGE\_BUY\_OFFER\_LINE\_FULL            | -6   | The account creating the offer does not have sufficient limits to receive `buying` and still satisfy its buying liabilities.                                                                                                                                                                               |
| MANAGE\_BUY\_OFFER\_UNDERFUNDED           | -7   | The account creating the offer does not have sufficient limits to send `selling` and still satisfy its selling liabilities. Note that if selling XBN then the account must additionally maintain its minimum XBN reserve, which is calculated assuming this offer will not completely execute immediately. |
| MANAGE\_BUY\_OFFER\_CROSS\_SELF           | -8   | The account has opposite offer of equal or lesser price active, so the account creating this offer would immediately cross itself.                                                                                                                                                                         |
| MANAGE\_BUY\_OFFER\_SELL\_NO\_ISSUER      | -9   | The issuer of selling asset does not exist.                                                                                                                                                                                                                                                                |
| MANAGE\_BUY\_OFFER\_BUY\_NO\_ISSUER       | -10  | The issuer of buying asset does not exist.                                                                                                                                                                                                                                                                 |
| MANAGE\_BUY\_OFFER\_NOT\_FOUND            | -11  | An offer with that `offerID` cannot be found.                                                                                                                                                                                                                                                              |
| MANAGE\_BUY\_OFFER\_LOW\_RESERVE          | -12  | The account creating this offer does not have enough XBN to satisfy the minimum XBN reserve increase caused by adding a subentry and still satisfy its XBN selling liabilities. For every offer an account creates, the minimum amount of XBN that account must hold will increase.                        |

## Manage Sell Offer

[JavaScript](http://stellar.github.io/js-stellar-sdk/Operation.html#.manageSellOffer) | [Java](https://stellar.github.io/java-stellar-sdk/org/stellar/sdk/ManageSellOfferOperation.Builder.html) | [Go](https://godoc.org/github.com/stellar/go/txnbuild#ManageSellOffer)

Creates, updates, or deletes an offer to sell one asset for another, otherwise known as a "ask" order or "offer" on a traditional orderbook.

If you want to create a new offer set Offer ID to `0`.

If you want to update an existing offer set Offer ID to existing offer ID.

If you want to delete an existing offer set Offer ID to existing offer ID and set Amount to `0`.

Threshold: Medium

Result: `ManageSellOfferResult`

| Parameters | Type                     | Description                                                                                                                            |
| ---------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| Selling    | asset                    | Asset the offer creator is selling.                                                                                                    |
| Buying     | asset                    | Asset the offer creator is buying.                                                                                                     |
| Amount     | integer                  | Amount of `selling` being sold. Set to `0` if you want to delete an existing offer.                                                    |
| Price      | {numerator, denominator} | Price of 1 unit of `selling` in terms of `buying`. For example, if you wanted to sell 30 XBN and buy 5 BTC, the price would be {5,30}. |
| Offer ID   | unsigned integer         | The ID of the offer. `0` for new offer. Set to existing offer ID to update or delete.                                                  |

Possible errors:

| Error                                      | Code | Description                                                                                                                                                                                                                                                                                                |
| ------------------------------------------ | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| MANAGE\_SELL\_OFFER\_MALFORMED             | -1   | The input is incorrect and would result in an invalid offer.                                                                                                                                                                                                                                               |
| MANAGE\_SELL\_OFFER\_SELL\_NO\_TRUST       | -2   | The account creating the offer does not have a trustline for the asset it is selling.                                                                                                                                                                                                                      |
| MANAGE\_SELL\_OFFER\_BUY\_NO\_TRUST        | -3   | The account creating the offer does not have a trustline for the asset it is buying.                                                                                                                                                                                                                       |
| MANAGE\_SELL\_OFFER\_SELL\_NOT\_AUTHORIZED | -4   | The account creating the offer is not authorized to sell this asset.                                                                                                                                                                                                                                       |
| MANAGE\_SELL\_OFFER\_BUY\_NOT\_AUTHORIZED  | -5   | The account creating the offer is not authorized to buy this asset.                                                                                                                                                                                                                                        |
| MANAGE\_SELL\_OFFER\_LINE\_FULL            | -6   | The account creating the offer does not have sufficient limits to receive `buying` and still satisfy its buying liabilities.                                                                                                                                                                               |
| MANAGE\_SELL\_OFFER\_UNDERFUNDED           | -7   | The account creating the offer does not have sufficient limits to send `selling` and still satisfy its selling liabilities. Note that if selling XBN then the account must additionally maintain its minimum XBN reserve, which is calculated assuming this offer will not completely execute immediately. |
| MANAGE\_SELL\_OFFER\_CROSS\_SELF           | -8   | The account has opposite offer of equal or lesser price active, so the account creating this offer would immediately cross itself.                                                                                                                                                                         |
| MANAGE\_SELL\_OFFER\_SELL\_NO\_ISSUER      | -9   | The issuer of selling asset does not exist.                                                                                                                                                                                                                                                                |
| MANAGE\_SELL\_OFFER\_BUY\_NO\_ISSUER       | -10  | The issuer of buying asset does not exist.                                                                                                                                                                                                                                                                 |
| MANAGE\_SELL\_OFFER\_NOT\_FOUND            | -11  | An offer with that `offerID` cannot be found.                                                                                                                                                                                                                                                              |
| MANAGE\_SELL\_OFFER\_LOW\_RESERVE          | -12  | The account creating this offer does not have enough XBN to satisfy the minimum XBN reserve increase caused by adding a subentry and still satisfy its XBN selling liabilities. For every offer an account creates, the minimum amount of XBN that account must hold will increase.                        |

## Create Passive Sell Offer

[JavaScript](https://stellar.github.io/js-stellar-sdk/Operation.html#.createPassiveSellOffer) | [Java](https://stellar.github.io/java-stellar-sdk/org/stellar/sdk/CreatePassiveSellOfferOperation.Builder.html) | [Go](https://godoc.org/github.com/stellar/go/txnbuild#CreatePassiveSellOffer)

Creates, updates, or deletes an offer to sell one asset for another, otherwise known as a "ask" order or "offer" on a traditional orderbook, *without taking a reverse offer of equal price*.

A passive sell offer is an offer that does not act on and take a reverse offer of equal price. Instead, they only take offers of lesser price. For example, if an offer exists to buy 5 BTC for 30 XBN, and you make a passive offer to buy 30 XBN for 5 BTC, your passive offer *does not* take the first offer. Passive offers in Bantu are always expressed as "ask" or "offer" orders in a traditional orderbook.

Note that regular offers made later than your passive offer can act on and take your passive offer, even if the regular offer is of the same price as your passive offer.

Passive offers allow market makers to have zero spread. If you want to trade EUR for USD at 1:1 price and USD for EUR also at 1:1, you can create two passive offers so the two offers don't immediately act on each other.

Once the passive offer is created, you can manage it like any other offer using the [manage sell offer](/start/list-of-operations#manage-sell-offer) operation.

Threshold: Medium

Result: `ManageSellOfferResult`

| Parameters | Type                     | Description                                                                                                                            |
| ---------- | ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- |
| Selling    | asset                    | Asset the offer creator is selling.                                                                                                    |
| Buying     | asset                    | Asset the offer creator is buying.                                                                                                     |
| Amount     | integer                  | Amount of `selling` being sold. Set to `0` if you want to delete an existing offer.                                                    |
| Price      | {numerator, denominator} | Price of 1 unit of `selling` in terms of `buying`. For example, if you wanted to sell 30 XBN and buy 5 BTC, the price would be {5,30}. |

Possible errors:

| Error                                      | Code | Description                                                                                                                                                                                                                                                                                                |
| ------------------------------------------ | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| MANAGE\_SELL\_OFFER\_MALFORMED             | -1   | The input is incorrect and would result in an invalid offer.                                                                                                                                                                                                                                               |
| MANAGE\_SELL\_OFFER\_SELL\_NO\_TRUST       | -2   | The account creating the offer does not have a trustline for the asset it is selling.                                                                                                                                                                                                                      |
| MANAGE\_SELL\_OFFER\_BUY\_NO\_TRUST        | -3   | The account creating the offer does not have a trustline for the asset it is buying.                                                                                                                                                                                                                       |
| MANAGE\_SELL\_OFFER\_SELL\_NOT\_AUTHORIZED | -4   | The account creating the offer is not authorized to sell this asset.                                                                                                                                                                                                                                       |
| MANAGE\_SELL\_OFFER\_BUY\_NOT\_AUTHORIZED  | -5   | The account creating the offer is not authorized to buy this asset.                                                                                                                                                                                                                                        |
| MANAGE\_SELL\_OFFER\_LINE\_FULL            | -6   | The account creating the offer does not have sufficient limits to receive `buying` and still satisfy its buying liabilities.                                                                                                                                                                               |
| MANAGE\_SELL\_OFFER\_UNDERFUNDED           | -7   | The account creating the offer does not have sufficient limits to send `selling` and still satisfy its selling liabilities. Note that if selling XBN then the account must additionally maintain its minimum XBN reserve, which is calculated assuming this offer will not completely execute immediately. |
| MANAGE\_SELL\_OFFER\_CROSS\_SELF           | -8   | The account has opposite offer of equal or lesser price active, so the account creating this offer would immediately cross itself.                                                                                                                                                                         |
| MANAGE\_SELL\_OFFER\_SELL\_NO\_ISSUER      | -9   | The issuer of selling asset does not exist.                                                                                                                                                                                                                                                                |
| MANAGE\_SELL\_OFFER\_BUY\_NO\_ISSUER       | -10  | The issuer of buying asset does not exist.                                                                                                                                                                                                                                                                 |
| MANAGE\_SELL\_OFFER\_NOT\_FOUND            | -11  | An offer with that `offerID` cannot be found.                                                                                                                                                                                                                                                              |
| MANAGE\_SELL\_OFFER\_LOW\_RESERVE          | -12  | The account creating this offer does not have enough XBN to satisfy the minimum XBN reserve increase caused by adding a subentry and still satisfy its XBN selling liabilities. For every offer an account creates, the minimum amount of XBN that account must hold will increase.                        |

## Set Options

[JavaScript](http://stellar.github.io/js-stellar-sdk/Operation.html#.setOptions) | [Java](http://stellar.github.io/java-stellar-sdk/org/stellar/sdk/SetOptionsOperation.Builder.html) | [Go](https://godoc.org/github.com/stellar/go/txnbuild#SetOptions)

Sets options for an account, such as setting the inflation destination or adding an additional signer on an account.

Allows you to set multiple options on an account in a single operation, such as changing an operation threshold and setting the flags on an account at the same time.

For more information on the options related to signing, see our docs on [multi-sig](/glossary/multisig).

When updating signers or other thresholds, the threshold of this operation is High.

Threshold: Medium or High

Result: `SetOptionsResult`

Parameters:

| Parameters            | Type                 | Description                                                                                                                                                                                                                                                            |
| --------------------- | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Inflation Destination | account ID           | Account of the inflation destination.                                                                                                                                                                                                                                  |
| Clear flags           | integer              | Indicates which flags to clear. For details about the flags, please refer to the [accounts doc](/glossary/accounts). The bit mask integer subtracts from the existing flags of the account. This allows for setting specific bits without knowledge of existing flags. |
| Set flags             | integer              | Indicates which flags to set. For details about the flags, please refer to the [accounts doc](/glossary/accounts). The bit mask integer adds onto the existing flags of the account. This allows for setting specific bits without knowledge of existing flags.        |
| Master weight         | integer              | A number from 0-255 (inclusive) representing the weight of the master key. If the weight of the master key is updated to 0, it is effectively disabled.                                                                                                                |
| Low threshold         | integer              | A number from 0-255 (inclusive) representing the threshold this account sets on all operations it performs that have [a low threshold](/glossary/multisig).                                                                                                            |
| Medium threshold      | integer              | A number from 0-255 (inclusive) representing the threshold this account sets on all operations it performs that have [a medium threshold](/glossary/multisig).                                                                                                         |
| High threshold        | integer              | A number from 0-255 (inclusive) representing the threshold this account sets on all operations it performs that have [a high threshold](/glossary/multisig).                                                                                                           |
| Home domain           | string               | Sets the home domain of an account. See [Federation](/glossary/buds).                                                                                                                                                                                                  |
| Signer                | {Public Key, weight} | Add, update, or remove a signer from an account. Signer weight is a number from 0-255 (inclusive). The signer is deleted if the weight is 0.                                                                                                                           |

Possible errors:

| Error                                   | Code | Description                                                                                                                                                                                                                                                         |
| --------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| SET\_OPTIONS\_LOW\_RESERVE              | -1   | This account does not have enough XBN to satisfy the minimum XBN reserve increase caused by adding a subentry and still satisfy its XBN selling liabilities. For every new signer added to an account, the minimum reserve of XBN that account must hold increases. |
| SET\_OPTIONS\_TOO\_MANY\_SIGNERS        | -2   | 20 is the maximum number of signers an account can have, and adding another signer would exceed that.                                                                                                                                                               |
| SET\_OPTIONS\_BAD\_FLAGS                | -3   | The flags set and/or cleared are invalid by themselves or in combination.                                                                                                                                                                                           |
| SET\_OPTIONS\_INVALID\_INFLATION        | -4   | The destination account set in the `inflation` field does not exist.                                                                                                                                                                                                |
| SET\_OPTIONS\_CANT\_CHANGE              | -5   | This account can no longer change the option it wants to change.                                                                                                                                                                                                    |
| SET\_OPTIONS\_UNKNOWN\_FLAG             | -6   | The account is trying to set a flag that is unknown.                                                                                                                                                                                                                |
| SET\_OPTIONS\_THRESHOLD\_OUT\_OF\_RANGE | -7   | The value for a key weight or threshold is invalid.                                                                                                                                                                                                                 |
| SET\_OPTIONS\_BAD\_SIGNER               | -8   | Any additional signers added to the account cannot be the master key.                                                                                                                                                                                               |
| SET\_OPTIONS\_INVALID\_HOME\_DOMAIN     | -9   | Home domain is malformed.                                                                                                                                                                                                                                           |

## Change Trust

[JavaScript](http://stellar.github.io/js-stellar-sdk/Operation.html#.changeTrust) | [Java](http://stellar.github.io/java-stellar-sdk/org/stellar/sdk/ChangeTrustOperation.Builder.html) | [Go](https://godoc.org/github.com/stellar/go/txnbuild#ChangeTrust)

Creates, updates, or deletes a trustline. For more on trustlines, please refer to the [assets documentation](/glossary/assets).

To delete an existing trustline, set Line to the asset of the trustline, and Limit to `0`.

Threshold: Medium

Result: `ChangeTrustResult`

| Parameters | Type    | Description                                                                                                                     |
| ---------- | ------- | ------------------------------------------------------------------------------------------------------------------------------- |
| Line       | asset   | The asset of the trustline. For example, if a user extends a trustline of up to 200 USD to an anchor, the `line` is USD:anchor. |
| Limit      | integer | The limit of the trustline. In the previous example, the `limit` would be 200.                                                  |

Possible errors:

| Error                             | Code | Description                                                                                                                                                                                                                                                            |
| --------------------------------- | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| CHANGE\_TRUST\_MALFORMED          | -1   | The input to this operation is invalid.                                                                                                                                                                                                                                |
| CHANGE\_TRUST\_NO\_ISSUER         | -2   | The issuer of the asset cannot be found.                                                                                                                                                                                                                               |
| CHANGE\_TRUST\_INVALID\_LIMIT     | -3   | The `limit` is not sufficient to hold the current balance of the trustline and still satisfy its buying liabilities.                                                                                                                                                   |
| CHANGE\_TRUST\_LOW\_RESERVE       | -4   | This account does not have enough XBN to satisfy the minimum XBN reserve increase caused by adding a subentry and still satisfy its XBN selling liabilities. For every new trustline added to an account, the minimum reserve of XBN that account must hold increases. |
| CHANGE\_TRUST\_SELF\_NOT\_ALLOWED | -5   | The source account attempted to create a trustline for itself, which is not allowed.                                                                                                                                                                                   |

## Allow Trust

[JavaScript](http://stellar.github.io/js-stellar-sdk/Operation.html#.allowTrust) | [Java](http://stellar.github.io/java-stellar-sdk/org/stellar/sdk/AllowTrustOperation.Builder.html) | [Go](https://godoc.org/github.com/stellar/go/txnbuild#AllowTrust)

Updates the `authorized` flag of an existing trustline.

This can only be called by the issuer of a trustline's [asset](/glossary/assets), and only when `AUTHORIZATION REQUIRED` has been set on the issuer's account.

There are two different kinds of asset authorization: complete authorization, which allows an account to transact with an asset (by making payments, creating offers, etc.) and limited authorization, which allows an account to maintain and reduce current offers, but not to perform other operations with the asset.

The issuer can only change a flag from complete to limited authorization or clear the `authorized` flag if the issuer has the `AUTH_REVOCABLE_FLAG` set. Otherwise, the issuer can only set the `authorized` flag. For more on what toggling between authorization states allows an issuer to do, see the [Control Access to an Asset](/issuing-assets/control-asset-access) doc.

If the issuer clears the `authorized` flag, all offers owned by the `trustor` that are either selling `type` or buying `type` will be deleted.

Threshold: Low

Result: `AllowTrustResult`

| Parameters | Type       | Description                                                                                                                                                                                                  |
| ---------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Trustor    | account ID | The account of the recipient of the trustline.                                                                                                                                                               |
| Type       | asset code | The 4 or 12 character-maximum asset code of the trustline the source account is authorizing. For example, if an issuing account wants to allow another account to hold its USD credit, the `type` is `USD`.  |
| Authorize  | integer    | Flag indicating whether the trustline is authorized. `1` if the account is authorized to transact with the asset. `2` if the account is authorized to maintain offers, but not to perform other transactions |

Possible errors:

| Error                              | Code | Description                                                                                                                                       |
| ---------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| ALLOW\_TRUST\_MALFORMED            | -1   | The asset specified in `type` is invalid. In addition, this error happens when the native asset is specified.                                     |
| ALLOW\_TRUST\_NO\_TRUST\_LINE      | -2   | The `trustor` does not have a trustline with the issuer performing this operation.                                                                |
| ALLOW\_TRUST\_TRUST\_NOT\_REQUIRED | -3   | The source account (issuer performing this operation) does not require trust. In other words, it does not have the flag `AUTH_REQUIRED_FLAG` set. |
| ALLOW\_TRUST\_CANT\_REVOKE         | -4   | The source account is trying to revoke the trustline of the `trustor`, but it cannot do so.                                                       |
| ALLOW\_TRUST\_SELF\_NOT\_ALLOWED   | -5   | The source account attempted to allow a trustline for itself, which is not allowed because an account cannot create a trustline with itself.      |

## Account Merge

[JavaScript](http://stellar.github.io/js-stellar-sdk/Operation.html#.accountMerge) | [Java](http://stellar.github.io/java-stellar-sdk/org/stellar/sdk/AccountMergeOperation.Builder.html) | [Go](https://godoc.org/github.com/stellar/go/txnbuild#AccountMerge)

Transfers the native balance (the amount of XBN an account holds) to another account and removes the source account from the ledger.

Threshold: High

Result: `AccountMergeResult`

| Parameters  | Type       | Description                                                                |
| ----------- | ---------- | -------------------------------------------------------------------------- |
| Destination | account ID | The account that receives the remaining XBN balance of the source account. |

Possible errors:

| Error                             | Code | Description                                                                                                                    |
| --------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------ |
| ACCOUNT\_MERGE\_MALFORMED         | -1   | The operation is malformed because the source account cannot merge with itself. The `destination` must be a different account. |
| ACCOUNT\_MERGE\_NO\_ACCOUNT       | -2   | The `destination` account does not exist.                                                                                      |
| ACCOUNT\_MERGE\_IMMUTABLE\_SET    | -3   | The source account has `AUTH_IMMUTABLE` flag set.                                                                              |
| ACCOUNT\_MERGE\_HAS\_SUB\_ENTRIES | -4   | The source account has trust lines/offers.                                                                                     |
| ACCOUNT\_MERGE\_SEQNUM\_TOO\_FAR  | -5   | Source's account sequence number is too high. It must be less than `(ledgerSeq << 32) = (ledgerSeq * 0x100000000)`.            |
| ACCOUNT\_MERGE\_DEST\_FULL        | -6   | The `destination` account cannot receive the balance of the source account and still satisfy its spirit buying liabilities.    |
| ACCOUNT\_MERGE\_IS\_SPONSOR       | -7   | The source account is a sponsor.                                                                                               |

## Manage Data

[JavaScript](http://stellar.github.io/js-stellar-sdk/Operation.html#.manageData) | [Java](http://stellar.github.io/java-stellar-sdk/org/stellar/sdk/ManageDataOperation.Builder.html) | [Go](https://godoc.org/github.com/stellar/go/txnbuild#ManageData)

Sets, modifies, or deletes a data entry (name/value pair) that is attached to a particular account.

An account can have a large amount of data entries attached to it (subject to sub-entry limits for an account). Each data entry increases the minimum balance (via the base reserve) needed to be held by the account.

Data entries can be used for storing application-specific data on the Bantu Network. They are not used by the core Bantu Protocol.

Threshold: Medium

Result: `ManageDataResult`

| Parameters | Type        | Description                                                                                                                                                                         |
| ---------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Name       | string      | String up to 64 bytes long. If this is a new Name it will add the given name/value pair to the account. If this Name is already present then the associated value will be modified. |
| Value      | binary data | (optional) If not present then the existing Name will be deleted. If present then this value will be set in the DataEntry. Up to 64 bytes long.                                     |

Possible errors:

| Error                             | Code | Description                                                                                                                                                                                                                                                            |
| --------------------------------- | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| MANAGE\_DATA\_NOT\_SUPPORTED\_YET | -1   | The network hasn't moved to this protocol change yet. This failure means the network doesn't support this feature yet.                                                                                                                                                 |
| MANAGE\_DATA\_NAME\_NOT\_FOUND    | -2   | Trying to remove a Data Entry that isn't there. This will happen if Name is set (and Value isn't) but the Account doesn't have a DataEntry with that Name.                                                                                                             |
| MANAGE\_DATA\_LOW\_RESERVE        | -3   | This account does not have enough XBN to satisfy the minimum XBN reserve increase caused by adding a subentry and still satisfy its XBN selling liabilities. For every new DataEntry added to an account, the minimum reserve of XBN that account must hold increases. |
| MANAGE\_DATA\_INVALID\_NAME       | -4   | Name not a valid string.                                                                                                                                                                                                                                               |

## Bump Sequence

[JavaScript](http://stellar.github.io/js-stellar-sdk/Operation.html#.bumpSequence) | [Java](http://stellar.github.io/java-stellar-sdk/org/stellar/sdk/BumpSequenceOperation.Builder.html) | [Go](https://godoc.org/github.com/stellar/go/txnbuild#BumpSequence)

Bumps forward the sequence number of the source account to the given sequence number.

This operation invalidates any transactions with a smaller sequence number, and is often utilized in complex contracting scenarios.

If the specified `bumpTo` sequence number is greater than the source account's sequence number, the account's sequence number is updated with that value, otherwise it's not modified.

Threshold: Low

Result: `BumpSequenceResult`

| Parameters | Type           | Description                                                       |
| ---------- | -------------- | ----------------------------------------------------------------- |
| bumpTo     | SequenceNumber | desired value for the operation's source account sequence number. |

Possible errors:

| Error                    | Code | Description                                                                                                                                              |
| ------------------------ | ---- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BUMP\_SEQUENCE\_BAD\_SEQ | -1   | The specified `bumpTo` sequence number is not a valid sequence number. It must be between 0 and `INT64_MAX` (9223372036854775807 or 0x7fffffffffffffff). |

## Create Claimable Balance

Creates a ClaimableBalanceEntry. See [Claimable Balance](/glossary/claimable-balance) for more information on parameters and usage.

Threshold: Medium

Result: `CreateClaimableBalanceResult`

| Parameters | Type              | Description                                                                                                      |
| ---------- | ----------------- | ---------------------------------------------------------------------------------------------------------------- |
| Asset      | asset             | Asset that will be held in the ClaimableBalanceEntry in the form `asset_code:issuing_address` or `native` (XBN). |
| Amount     | integer           | Amount of `asset` stored in the ClaimableBalanceEntry.                                                           |
| Claimants  | list of claimants | List of Claimants (account address and ClaimPredicate pair) that can claim this ClaimableBalanceEntry.           |

Possible errors:

| Error                                       | Code | Description                                                                                                                                                                                                                                                   |
| ------------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| CREATE\_CLAIMABLE\_BALANCE\_MALFORMED       | -1   | The input to this operation is invalid.                                                                                                                                                                                                                       |
| CREATE\_CLAIMABLE\_BALANCE\_LOW\_RESERVE    | -2   | The account creating this entry does not have enough XBN to satisfy the minimum XBN reserve increase caused by adding a ClaimableBalanceEntry. For every claimant in the list, the minimum amount of XBN this account must hold will increase by baseReserve. |
| CREATE\_CLAIMABLE\_BALANCE\_NO\_TRUST       | -3   | The source account does not trust the issuer of the asset it is trying to include in the ClaimableBalanceEntry.                                                                                                                                               |
| CREATE\_CLAIMABLE\_BALANCE\_NOT\_AUTHORIZED | -4   | The source account is not authorized to transfer this asset.                                                                                                                                                                                                  |
| CREATE\_CLAIMABLE\_BALANCE\_UNDERFUNDED     | -5   | The source account does not have enough funds to transfer `amount` of this asset to the ClaimableBalanceEntry.                                                                                                                                                |

## Claim Claimable Balance

Claims a ClaimableBalanceEntry and adds the amount of asset on the entry to the source account.

Threshold: Low

Result: `ClaimClaimableBalanceResult`

| Parameters | Type               | Description                                                                                                                                                                                                                                                         |
| ---------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| BalanceID  | claimableBalanceID | BalanceID on the ClaimableBalanceEntry that the source account is claiming. The balanceID can be retrieved from a succesful `CreateClaimableBalanceResult`. See [ClaimableBalanceID](/glossary/miscellaneous-core-objects#ClaimableBalanceID) for more information. |

Possible errors:

| Error                                       | Code | Description                                                                                                                                             |
| ------------------------------------------- | ---- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
| CLAIM\_CLAIMABLE\_BALANCE\_DOES\_NOT\_EXIST | -1   | There is no existing ClaimableBalanceEntry that matches the input BalanceID.                                                                            |
| CLAIM\_CLAIMABLE\_BALANCE\_CANNOT\_CLAIM    | -2   | There is no claimant that matches the source account, or the claimants predicate is not satisfied.                                                      |
| CLAIM\_CLAIMABLE\_BALANCE\_LINE\_FULL       | -3   | The account claiming the ClaimableBalanceEntry does not have sufficient limits to receive amount of the asset and still satisfy its buying liabilities. |
| CLAIM\_CLAIMABLE\_BALANCE\_NO\_TRUST        | -4   | The source account does not trust the issuer of the asset it is trying to claim in the ClaimableBalanceEntry.                                           |
| CLAIM\_CLAIMABLE\_BALANCE\_NOT\_AUTHORIZED  | -5   | The source account is not authorized to claim the asset in the ClaimableBalanceEntry.                                                                   |

## Begin Sponsoring Future Reserves

Establishes the is-sponsoring-future-reserves-for relationship between the source account and sponsoredID. See [Sponsored Reserves](/glossary/sponsored-reserves) for more information.

There must be a corresponding [end sponsoring future reserves](/start/list-of-operations#end-sponsoring-future-reserves) operation in the same transaction to end the is-sponsoring-future-reserves-for relationship. The transaction will fail with `txBAD_SPONSORSHIP` otherwise.

Threshold: Medium

Result: `BeginSponsoringFutureReservesResult`

| Parameters  | Type       | Description                                     |
| ----------- | ---------- | ----------------------------------------------- |
| SponsoredID | account ID | Account that will have it's reserves sponsored. |

Possible errors:

| Error                                                   | Code | Description                                                                                       |
| ------------------------------------------------------- | ---- | ------------------------------------------------------------------------------------------------- |
| BEGIN\_SPONSORING\_FUTURE\_RESERVES\_MALFORMED          | -1   | Source account is equal to sponsoredID.                                                           |
| BEGIN\_SPONSORING\_FUTURE\_RESERVES\_ALREADY\_SPONSORED | -2   | Source account is already sponsoring sponsoredID.                                                 |
| BEGIN\_SPONSORING\_FUTURE\_RESERVES\_RECURSIVE          | -3   | Either source account is currently being sponsored, or sponsoredID is sponsoring another account. |

## End Sponsoring Future Reserves

Terminates the current is-sponsoring-future-reserves-for relationship in which the source account is sponsored.

Threshold: Medium

Result: `EndSponsoringFutureReservesResult`

| Error                                             | Code | Description                      |
| ------------------------------------------------- | ---- | -------------------------------- |
| END\_SPONSORING\_FUTURE\_RESERVES\_NOT\_SPONSORED | -1   | Source account is not sponsored. |

## Revoke Sponsorship

The logic of this operation depends on the state of the source account.

If the source account is not sponsored or is sponsored by the owner of the specified entry or sub-entry, then attempt to revoke the sponsorship. If the source account is sponsored, the next step depends on whether the entry is sponsored or not. If it is sponsored, attempt to transfer the sponsorship to the sponsor of the source account. If the entry is not sponsored, then establish the sponsorship.

Threshold: Medium

Result: `RevokeSponsorshipResult`

This operation is a union with **two** possible types -

| Union Type                         | Parameters | Type      | Description                                                                                                                                                                                         |
| ---------------------------------- | ---------- | --------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| REVOKE\_SPONSORSHIP\_LEDGER\_ENTRY | LedgerKey  | ledgerKey | Ledger key that holds information to identify a specific ledgerEntry that may have it's sponsorship modified. See [LedgerKey](/glossary/miscellaneous-core-objects#LedgerKey) for more information. |

Or

| Union Type                  | Parameters | Type                     | Description                                     |
| --------------------------- | ---------- | ------------------------ | ----------------------------------------------- |
| REVOKE\_SPONSORSHIP\_SIGNER | Signer     | {account ID, Signer Key} | Signer that may have it's sponsorship modified. |

Possible errors:

| Error                                   | Code | Description                                                                                                                                                                                                                                                                                                       |
| --------------------------------------- | ---- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| REVOKE\_SPONSORSHIP\_DOES\_NOT\_EXIST   | -1   | The ledgerEntry for LedgerKey doesn't exist, the account ID on signer doesn't exist, or the Signer Key doesn't exist on account ID's account.                                                                                                                                                                     |
| REVOKE\_SPONSORSHIP\_NOT\_SPONSOR       | -2   | If the ledgerEntry/signer is sponsored, then the source account must be the sponsor. If the ledgerEntry/signer is not sponsored, the source account must be the owner. This error will be thrown otherwise.                                                                                                       |
| REVOKE\_SPONSORSHIP\_LOW\_RESERVE       | -3   | The sponsored account does not have enough XBN to satisfy the minimum balance increase caused by revoking sponsorship on a ledgerEntry/signer it owns, or the sponsor of the source account doesn't have enough XBN to satisfy the minimum balance increase caused by sponsoring a transfered ledgerEntry/signer. |
| REVOKE\_SPONSORSHIP\_ONLY\_TRANSFERABLE | -4   | Sponsorship cannot be removed from this ledgerEntry. This error will happen if the user tries to remove the sponsorship from a ClaimableBalanceEntry.                                                                                                                                                             |


# Bantu Stack

Fundamentally, Bantu is a collection of Bantu Core nodes, which are computers that keep a common ledger of accounts and balances, listen for incoming transactions, and, using the Bantu Consensus Protocol, agree to apply a valid set of those transactions to update the ledger. Each transaction applied to the ledger incurs a small fee — which is necessary to prevent bad actors from spamming the network — and the ledger is generally updated every 3-5 seconds.

However, most developers don't interact directly with a Bantu Core node. Rather, the program using a Software Development Kit written in their preferred language, and those SDKs, in turn, interact with Expansion, the Bantu-network API. This three-tiered stack divides responsibilities so each piece of software can focus on a specific purpose. Bantu Core concentrates on transaction submission and consensus; Expansion handles queries and converts network data into a friendly format; SDKs abstract away complexity and offer ergonomic access in a variety of languages.

## Bantu SDKs

SDKs make it easy to craft code and handle network queries and transaction submissions. They're linked to in the [SDK section of the docs](/software-and-sdks/index), and each is robust and has its own documentation showing you how to request data and create and submit transactions. When you start developing on Bantu, the first step is usually to find the SDK in your language of choice and familiarize yourself with how it works.

## API: Expansion

[Expansion](/run-api-server/index) is a RESTful HTTP API server that provides a straightforward way to submit transactions, check accounts, and subscribe to events. Because it’s HTTP, you can communicate with Expansion using an SDK, but you can also use your web browser or simple command-line tools like cURL. Everything there is to know about Expansion is documented in the [API Reference](/api/introduction/index) section of the docs.

At the moment, Expansion requires access to Bantu Core's database to function properly — so every Expansion instance connects to a Bantu Core node — but we are increasing its independence from Bantu Core, and soon developers will be able to deploy the API without having to run their own node.

## Network Backbone: Bantu Core

The Bantu Core software does the hard work of validating and agreeing with other instances of Core on the status of every transaction through the [Bantu Consensus Protocol](/glossary/scp) (SCP). The ledger, transactions, results, history, and even the messages passed between computers running Bantu Core are encoded using XDR, which is incredibly efficient, but not human readable. Bantu Core nodes make up the network — and running a node is crucial if you want to ensure constant access or contribute to the health and decentralization of the network — but most developers don't work directly with Bantu Core. For more on how to set up a node, consult the [Run a Core Node](/run-core-node/index) section.

## The Public Network and the Test Network

There are two different versions of the Bantu network: one for testing and one for real-world deployments. The Bantu Development Foundation provides a free public Expansion instance for each, which you can use to submit transactions or query network data.

* <https://expansion.bantu.network/> is for interacting with the public network
* <https://expansion-testnet.bantu.network/> is for interacting with the testnet

Assets on the testnet don't represent anything in the real world, and when you're developing on the testnet, you can get free test XBN from a tool called Friendbot. On the testnet, you're free to experiment, create, and troubleshoot without risking the loss of funds. It generally upgrades a month before the public network — so if you're using it, you need to keep an eye out for major protocol releases — and unlike the public network — where data persists forever — the testnet gets reset every quarter. Additionally, it has a lower ledger limit than the public network: currently, the testnet tops out at 100 operations/ledger; the public network at 1,000 operations/ledger.

Other than that, the two networks are the same: they consist of Bantu Core nodes, support the Expansion API, and work with Bantu SDKs. Both support the same operations, process transactions in 3-5 seconds, and require the same fees and network minimum. In fact, if you build something on the testnet and decide you're ready to deploy it on the public network, all you need to do is change the [Network Passphrase](/glossary/network-passphrase). For more, check out our [guide to best practices for building on the testnet](/glossary/testnet).


# Tutorials


# Create Account

Before we get started with working with Bantu in code, consider going through the following examples using the [Bantu Laboratory](https://laboratory.bantu.network/). The lab allows you to create accounts, fund accounts on the Bantu test network, build transactions, run any operation, and inspect responses from Expansion via the Endpoint Explorer.

Accounts are a fundamental building block of Bantu: they hold all your balances, allow you to send and receive payments, and let you place offers to buy and sell assets. Since pretty much everything on Bantu is in some way tied to an account, the first thing you generally need to do when you start developing is to create one. This beginner-level tutorial will show you how to do that.

## Create a Keypair

Bantu uses public-key cryptography to ensure that every transaction is secure: every Bantu account has a key pair consisting of a public key and a secret key. The public key is always safe to share — other people need it to identify your account and verify that you authorized a transaction. It's like an email address. The secret key, however, is private information that proves you own — and gives you access to — your account. It's like a password, and you should never share it with anyone.

Before creating an account, you need to generate your own keypair:

```javascript
const pair = StellarSdk.Keypair.random();

pair.secret();
// SAV76USXIJOBMEQXPANUOQM6F5LIOTLPDIDVRJBFFE2MDJXG24TAPUU7
pair.publicKey();
// GCFXHS4GXL6BVUCXBWXGTITROWLVYXQKQLF4YH5O5JT3YZXCYPAFBJZB
```

## Create Account

A valid keypair, however, does not an account make: in order to prevent unused accounts from bloating the ledger, Bantu requires accounts to hold a [minumum balance](/glossary/minimum-balance) of 1 XBN before they actually exist. Until it gets a bit of funding, your keypair doesn't warrant space on the ledger.

On the [public network](/glossary/network-passphrase), where live users make live transactions, your next step would be to acquire XBN. Because this tutorial runs on the [test network](/glossary/testnet), you can get 10,000 test XBN from Friendbot, which is a friendly account funding tool.

To do that, send Friendbot the public key you created. It’ll create and fund a new account using that public key as the account ID.

```javascript
// The SDK does not have tools for creating test accounts, so you'll have to
// make your own HTTP request.

// if you're trying this on Node, install the `node-fetch` library and
// uncomment the next line:
// const fetch = require('node-fetch');

(async function main() {
  try {
    const response = await fetch(
      `https://friendbot.dev.bantu.network?addr=${encodeURIComponent(
        pair.publicKey(),
      )}`,
    );
    const responseJSON = await response.json();
    console.log("SUCCESS! You have a new account :)\n", responseJSON);
  } catch (e) {
    console.error("ERROR!", e);
  }
})();
```

Now for the last step: getting the account’s details and checking its balance. Accounts can carry multiple balances — one for each type of currency they hold.

```javascript
const server = new StellarSdk.Server("https://expansion-testnet.bantu.network");

// the JS SDK uses promises for most actions, such as retrieving an account
const account = await server.loadAccount(pair.publicKey());
console.log("Balances for account: " + pair.publicKey());
account.balances.forEach(function (balance) {
  console.log("Type:", balance.asset_type, ", Balance:", balance.balance);
});
```

Now that you’ve got an account, you can [start sending and receiving payments](/tutorials/send-and-receive-payments), or, if you're ready to hunker down, you can skip ahead and [build a wallet](/building-apps/index) or [issue a Bantu-network asset](/issuing-assets/index).


# Send and Receive Payments

Most of the time, you’ll be sending money to someone else who has their own account. For this tutorial, however, you'll need a second account to transact with. So before proceeding, follow the steps outlined in [Create an Account](/tutorials/create-account) to make *two* accounts: one for sending and one for receiving.

## About Operations and Transactions

Actions that do things on Bantu— like sending payments or making buy or sell offers — are called [operations](/glossary/operations). To submit an operation to the network, you bundle it into a [transaction](/glossary/transactions), which is a group of anywhere from 1 to 100 operations accompanied by some extra information, like which account is making the transaction and a cryptographic signature to verify that the transaction is authentic.

Transactions are atomic, meaning that if any operation in a transaction fails, they all fail. Let’s say you have 100 XBN and you make two payment operations of 60 XBN 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 XBN. You’ll be left with 40 XBN. However, if you group the two payments into a single transaction, they will both fail and you’ll be left with the full 100 XBN still in your account.

Every transaction also incurs a small fee. Like the minimum balance on accounts, this fee deters spam and prevents people from overloading the system. This [base fee](/glossary/fees) is very small — 100 spirits per operation where a stroop equals 1 \* 10 ^-7 XBN — and it's charged for each operation in a transaction. A transaction with two operations, for instance, would cost 200 spirits.

## Send a Payment

Bantustores and communicates transaction data in a binary format called [XDR](/glossary/xdr), which is optimized for network performance but unreadable to the human eye. Luckily, [Expansion](/api/introduction/index), the BantuAPI, and the [BantuSDKs](/software-and-sdks/index) convert XDRs into friendlier formats. Here’s how you might send 10 XBN to an account:

```javascript
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://horizon-testnet.stellar.org");
var sourceKeys = StellarSdk.Keypair.fromSecret(
  "SCZANGBA5YHTNYVVV4C3U252E2B6P6F5T3U6MM63WBSBZATAQI3EBTQ4",
);
var destinationId = "GA2C5RFPE6GCKMY3US5PAB6UZLKIGSPIUKSLRB6Q723BM2OARMDUYEJ5";
// 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: StellarSdk.Networks.TESTNET,
    })
      .addOperation(
        StellarSdk.Operation.payment({
          destination: destinationId,
          // Because Stellar allows transaction in many currencies, you must
          // specify the asset type. The special "native" asset represents Spirit.
          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 Stellar 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 Stellar!
    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 (aka the *public key*) you are sending to actually exists by loading the associated account data from the Bantu network. It's okay to skip this step, but it gives you an opportunity to avoid making a transaction that will inevitably fail.

```javascript
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 time and has something called a [sequence number](/glossary/accounts#sequence-number), which helps Bantu 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.

```javascript
.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.

```javascript
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: Bantu’s network currency is the [XBN](https://www.stellar.org/XBN), but you can send any asset issued on the network. We'll cover sending non-XBN assets [below](/tutorials/send-and-receive-payments#transacting-in-other-currencies). For now, though, we’ll stick to XBN, which are called “native” assets in the SDK:

```javascript
.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](https://en.wikipedia.org/wiki/Floating_point#Accuracy_problems). Since not all systems have a native way to accurately represent extremely small or large decimals, Bantu uses strings as a reliable way to represent the exact amount across any system.

Optionally, you can add your own metadata, called a [memo](/glossary/transactions#memo), to a transaction. Bantu doesn’t do anything with this data, but you can use it for any purpose you’d like. Many exchanges require memos for incoming transactions because they use a single Bantu account for all their users and rely on the memo to differentiate between internal user accounts.

```javascript
.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 key. This proves that the data actually came from you and not someone impersonating you.

```javascript
transaction.sign(sourceKeys);
```

And finally, submit it to the Bantu network!

```javascript
server.submitTransaction(transaction);
```

In this example, we're submitting the transaction to the Bantu-maintained public testnet instance of Expansion, the BantuAPI. When submitting transactions to an Expansion server — which is what most people do — it's possible that you will not receive a response from the server due to a bug, network conditions, etc. In such a 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 its status. If the transaction has already been successfully applied to the ledger, Expansion 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 a Payment

You don’t actually need to do anything to receive payments into a Bantu account: if a payer makes a successful transaction to send assets to you, those assets will automatically be added to your account.

However, you may want to keep an eye out for incoming payments. A simple program that watches the network for payments and prints each one might look like:

```javascript
var StellarSdk = require("stellar-sdk");

var server = new StellarSdk.Server("https://expansion-testnet.bantu.network");
var accountId = "GC2BKLYOOYPDEFJKLKY6FNNRQMGFLVHJKQRGNSSRRGSMPGF32LHCQVGF";

// 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 Bantu’s API, XBN are referred to as the “native” type. Other
    // asset types have more detailed information.
    var asset;
    if (payment.asset_type === "native") {
      asset = "XBN";
    } 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 Bantu, 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.

```javascript
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.

```javascript
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.

```javascript
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 Bantu network is that you can create, hold, send, receive, and trade any type of asset. Many organizations issue assets on Bantu that represent real-world currencies such as US dollars or Nigerian naira or other cryptocurrencies such as bitcoin or ether.

Each of these redeemable assets — *anchored* in the Bantu vernacular — is essentially a credit issued by a particular account that represents reserves those accounts hold outside the network. That's why the assets in the example above had both a `code` and an `issuer`: the `issuer` is the public key of the account that created the asset, an account owned by the organization that ultimately honors the credit that asset represents. To find out more about how that works, check out [Enable Deposits and Withdrawals](broken://pages/-MRydWt7s_wFT8_jRTPV).


# Follow Received Payments

This tutorial shows how easy it is to use Expansion to watch for incoming payments on an [account](/glossary/accounts) using JavaScript and `EventSource`. We will eschew using [`js-stellar-sdk`](https://github.com/stellar/js-stellar-sdk), the high-level helper library, to show that it is possible for you to perform this task on your own with whatever programming language you would like to use.

This tutorial assumes that you:

* Have node.js installed locally on your machine.
* Have curl installed locally on your machine.
* Are running on Linux, macOS, or any other system that has access to a bash-like shell.
* Are familiar with launching and running commands in a terminal.

In this tutorial we will learn:

* How to create a new account.
* How to fund your account using friendbot.
* How to follow payments to your account using curl and EventSource.

## Project Skeleton

Let's get started by building our project skeleton:

```bash
$ mkdir follow_tutorial
$ cd follow_tutorial
$ npm install --save bantu-base
$ npm install --save eventsource
```

This should have created a `package.json` in the `follow_tutorial` directory. You can check that everything went well by running the following command:

```bash
$ node -e "require('bantu-base')"
```

Everything was successful if no output was generated from the above command. Now let's write a script to create a new account.

## Creating an account

Create a new file named `make_account.js` and paste the following text into it:

```javascript
var Keypair = require("bantu-base").Keypair;

var newAccount = Keypair.random();

console.log("New key pair created!");
console.log("  Account ID: " + newAccount.publicKey());
console.log("  Secret: " + newAccount.secret());
```

Save the file and run it:

```bash
$ node make_account.js
New key pair created!
  Account ID: GB7JFK56QXQ4DVJRNPDBXABNG3IVKIXWWJJRJICHRU22Z5R5PI65GAK3
  Secret: SCU36VV2OYTUMDSSU4EIVX4UUHY3XC7N44VL4IJ26IOG6HVNC7DY5UJO
$
```

Before our account can do anything it must be funded. Indeed, before an account is funded it does not truly exist!

## Funding your account

The Bantu test network provides the Friendbot, a tool that developers can use to get testnet spirits for testing purposes. To fund your account, simply execute the following curl command:

```bash
$ curl "https://friendbot.bantu.network/?addr=GB7JFK56QXQ4DVJRNPDBXABNG3IVKIXWWJJRJICHRU22Z5R5PI65GAK3"
```

Don't forget to replace the account id above with your own. If the request succeeds, you should see a response like:

```javascript
{
  "hash": "ed9e96e136915103f5d8978cbb2036628e811f2c59c4c3d88534444cf504e360",
  "result": "received",
  "submission_result": "000000000000000a0000000000000001000000000000000000000000"
}
```

After a few seconds, the Bantu network will perform consensus, close the ledger, and your account will have been created. Next up we will write a command that watches for new payments to your account and outputs a message to the terminal.

## Following payments using `curl`

To follow new payments connected to your account you simply need to send the `Accept: text/event-stream` header to the [/payments](/api/resources/operations/object/payment) endpoint.

```bash
$ curl -H "Accept: text/event-stream" "https://expansion-testnet.bantu.network/accounts/GB7JFK56QXQ4DVJRNPDBXABNG3IVKIXWWJJRJICHRU22Z5R5PI65GAK3/payments"
```

As a result you will see something like:

```bash
retry: 1000
event: open
data: "hello"

id: 713226564145153
data: {"_links":{"effects":{"href":"/operations/713226564145153/effects/{?cursor,limit,order}","templated":true},
       "precedes":{"href":"/operations?cursor=713226564145153\u0026order=asc"},
       "self":{"href":"/operations/713226564145153"},
       "succeeds":{"href":"/operations?cursor=713226564145153\u0026order=desc"},
       "transactions":{"href":"/transactions/713226564145152"}},
       "account":"GB7JFK56QXQ4DVJRNPDBXABNG3IVKIXWWJJRJICHRU22Z5R5PI65GAK3",
       "funder":"GBS43BF24ENNS3KPACUZVKK2VYPOZVBQO2CISGZ777RYGOPYC2FT6S3K",
       "id":713226564145153,
       "paging_token":"713226564145153",
       "starting_balance":"10000",
       "type_i":0,
       "type":"create_account"}
```

Every time you receive a new payment you will get a new row of data. Payments is not the only endpoint that supports streaming. You can also stream transactions [/transactions](/api/resources/transactions/index) and operations [/operations](/api/resources/operations/index).

## Following payments using `EventStream`

> **Warning!** `EventSource` object does not reconnect for certain error types so it can stop working. If you need a reliable streaming connection please use our [SDK](https://github.com/stellar/js-stellar-sdk).

Another way to follow payments is writing a simple JS script that will stream payments and print them to console. Create `stream_payments.js` file and paste the following code into it:

```javascript
var EventSource = require("eventsource");
var es = new EventSource(
  "https://expansion-testnet.bantu.network/accounts/GB7JFK56QXQ4DVJRNPDBXABNG3IVKIXWWJJRJICHRU22Z5R5PI65GAK3/payments",
);
es.onmessage = function (message) {
  var result = message.data ? JSON.parse(message.data) : message;
  console.log("New payment:");
  console.log(result);
};
es.onerror = function (error) {
  console.log("An error occurred!");
};
```

Now, run our script: `node stream_payments.js`. You should see following output:

```bash
New payment:
{ _links:
   { effects:
      { href: '/operations/713226564145153/effects/{?cursor,limit,order}',
        templated: true },
     precedes: { href: '/operations?cursor=713226564145153&order=asc' },
     self: { href: '/operations/713226564145153' },
     succeeds: { href: '/operations?cursor=713226564145153&order=desc' },
     transactions: { href: '/transactions/713226564145152' } },
  account: 'GB7JFK56QXQ4DVJRNPDBXABNG3IVKIXWWJJRJICHRU22Z5R5PI65GAK3',
  funder: 'GBS43BF24ENNS3KPACUZVKK2VYPOZVBQO2CISGZ777RYGOPYC2FT6S3K',
  id: 713226564145153,
  paging_token: '713226564145153',
  starting_balance: '10000',
  type_i: 0,
  type: 'create_account' }
```

## Testing it out

We now know how to get a stream of transactions to an account. Let's check if our solution actually works and if new payments appear. Let's watch as we send a payment ([`create_account` operation](/start/list-of-operations#create-account)) from our account to another account.

We use the `create_account` operation because we are sending payment to a new, unfunded account. If we were sending payment to an account that is already funded, we would use the [`payment` operation](/start/list-of-operations#payment).

First, let's check our account sequence number so we can create a payment transaction. To do this we send a request to Expansion:

```bash
$ curl "https://expansion-testnet.bantu.network/accounts/GB7JFK56QXQ4DVJRNPDBXABNG3IVKIXWWJJRJICHRU22Z5R5PI65GAK3"
```

Sequence number can be found under the `sequence` field. For our example, the current sequence number is `713226564141056`. Save your value somewhere.

Now, create `make_payment.js` file and paste the following code into it, replacing the sequence number accordingly:

```javascript
var BantuBase = require("bantu-base");
BantuBase.Network.useTestNetwork();

var keypair = BantuBase.Keypair.fromSecret(
  "SCU36VV2OYTUMDSSU4EIVX4UUHY3XC7N44VL4IJ26IOG6HVNC7DY5UJO",
);
var account = new BantuBase.Account(keypair.publicKey(), "713226564141056");

var amount = "100";
var transaction = new BantuBase.TransactionBuilder(account)
  .addOperation(
    BantuBase.Operation.createAccount({
      destination: BantuBase.Keypair.random().publicKey(),
      startingBalance: amount,
    }),
  )
  .build();

transaction.sign(keypair);

console.log(transaction.toEnvelope().toXDR().toString("base64"));
```

After running this script you should see a signed transaction blob. To submit this transaction we send it to Expansion or Bantu-core. But before we do, let's open a new console and start our previous script by `node stream_payments.js`.

Now to send a transaction just use Expansion

```bash
curl -H "Content-Type: application/json" -X POST -d '{"tx":"AAAAAH6Sq76F4cHVMWvGG4AtNtFVIvayUxSgR401rPY9ej3TAAAD6AACiK0AAAABAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAKc1j3y10+nI+sxuXlmFz71JS35mp/RcPCP45Gw0obdAAAAAAAAAAAAExLQAAAAAAAAAAAT16PdMAAABAsJTBC5N5B9Q/9+ZKS7qkMd/wZHWlP6uCCFLzeD+JWT60/VgGFCpzQhZmMg2k4Vg+AwKJTwko3d7Jt3Y6WhjLCg=="}' "https://expansion-testnet.bantu.network/transactions"
```


# Securing Web-based Projects

It’s critical for any app managing the flow of cryptocurrency to architect their app to follow security best-practices. Cryptocurrency-enabled apps are significant targets for malicious actors in the sense they enable the attacker to realize immediate monetary gain from exploits. The following checklist offers guidance on the most common vulnerabilities. Even if every piece of advice is followed, security is not guaranteed. Web security is constantly evolving, which warrants a certain degree of paranoia.

## SSL/TLS

Ensure TLS enabled (Letsencrypt allows you to do this for free). Redirect http to https where necessary. This ensures that Man in the Middle attacks cannot occur, and sensitive data is securely transferred between the client and browser. Learn how to get an SSL certificate for free [here](https://letsencrypt.org/getting-started/).

*If you don’t have SSL/TLS enabled, stop everything and do this first.*

## Content Security Policy (CSP) Headers

CSP headers tell the browser where it can download static resources from. For example, if you astralwallet.io and it requests a JavaScript file from myevilsite.com, your browser would block it unless it was whitelisted with CSP headers. You may read about how to implement CSP headers [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP).

Most web frameworks have a configuration file or extensions where you may specify your CSP policy, and the headers will be auto-generated for you. For example, see [Helmet](https://www.npmjs.com/package/helmet) for Node.js.

This would have prevented the [Blackwallet Hack](https://www.ccn.com/yet-another-crypto-wallet-hack-causes-users-lose-400000/).

## HTTP Strict-Transport-Security Headers

This is an HTTP header that tells the browser that all future connections to a particular site should use HTTPS. To implement this, add the [header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security) to your website. Some web frameworks have this built in, like [Django](https://docs.djangoproject.com/en/2.0/topics/security/#ssl-https)!

This would have prevented the [MyEtherWallet DNS hack](https://bitcoinmagazine.com/articles/popular-ether-wallet-mew-hijacked-dns-attack/).

## Storing sensitive data

In an ideal world, you don’t have to store much sensitive data. If you must, tread carefully. There are many strategies in storing sensitive data; start by ensuring sensitive data is encrypted using a proven cipher like AES-256, and stored separately from application data and always pick an AEAD mode. Any communication between the application server and secret server should be in a private network and / or authenticated via HMAC. Your cipher strategy will change based on whether you will be sending the ciphertext over the wire multiple times. Finally, back up any encryption keys you may use offline, and store them only in-memory in your app.

Consult a good cryptographer and read up on best practices. A good place to start is looking into the documentation of your favorite web framework.

Rolling your own crypto is a bad idea. Always use tried and tested libraries. A good example is \[NaCl]\(<https://en.wikipedia.org/wiki/NaCl_(software)>).

## Monitoring

Attackers often need to spend some time exploring your website for unexpected or overlooked behavior. Examining logs defensively can help you catch on to what their trying to achieve and ensure you’re protected. At the least, you can block their IP, or automate blocking based on suspicious behavior you unearth.

Finally, it is worth setting up error reporting (e.g. [Sentry](https://sentry.io/welcome/)): Oftentimes, people trigger strange bugs when they are trying to hack things.

## Authentication weaknesses

If you have logins for users, it is critical that your authentication system is built securely. Of course, the best way to do this is to use something off the shelf. Both Ruby on Rails and Django have robust, built-in authentication schemes.

Many JSON web token implementations are poorly done, so ensure the library you use is audited.

Hash passwords with a time-tested scheme. The winner of the last password hashing contest was Argon2. Balloon Hashing is also worth looking into.

Strongly prefer 2FA, and require U2F or [TOTP](https://tools.ietf.org/html/rfc6238) 2FA for sensitive actions.

2FA is really important. Email accounts are usually not very secure. Having a second factor of authentication ensures that users who accidentally stay logged on, or have their password guessed are still protected.

Finally, require strong passwords. Common and short passwords can easily be brute forced. Dropbox has a [great open source tool](https://blogs.dropbox.com/tech/2012/04/zxcvbn-realistic-password-strength-estimation/) that gauges password strength fairly quickly, making it usable for user interactions.

## Denial of Service Attacks (DOS)

Denial of service attacks are usually accomplished by overloading your web servers with traffic. To mitigate this risk, rate limit traffic from IPs and browser fingerprints. Sometimes people will use proxies to bypass IP rate-limiting. In the end, malicious actors can always find ways to spoof their identity, so the surest way to block DOS attacks are to implement proof of work checks in your client, or use a managed service like [Cloudflare](https://www.cloudflare.com/ddos/).

## Lock down unused ports

Attackers will often scan your ports, and see if you were negligent and left any open. Services like Heroku do this for you. Read about how to enable this on AWS [here](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/authorizing-access-to-an-instance.html).

## Phishing and social engineering

Phishing attacks will thwart any well-formed security infrastructure. Have clear policies published on your website, and articulate them to users when they sign up (you will never ask for their password, etc.). Sign messages to your users. Prompt users to check the domain of the website they are on.

## Scan your website and libraries for vulnerabilities

Use a tool like [Snyk](https://snyk.io/) to scan your third party client libraries for vulnerabilities. Make sure you keep your third-party libraries up-to-date -- oftentimes upgrades are triggered by security exploits.

You can use [Mozilla Observatory](https://observatory.mozilla.org/) to check your HTTP security as well.

## Low hanging fruit: Cross-Site Request Forgery Protection (CSRF), SQL Injections

Most modern web and mobile frameworks handle both CSRF protection and SQL injections. Ensure CSRF protection enabled, and that you are using a database ORM instead of running raw SQL based on user input. For example, see what [Ruby on Rails documentation](http://guides.rubyonrails.org/security.html#sql-injection) says about SQL injections.

## Closing remarks

We hope this guide was useful! It is by no means comprehensive, but it's a good place to start if you haven’t paid attention to security yet. Remember, security is only as strong as its weakest link. All of this is pointless if you have bad passwords and hackers can guess your AWS password.


# Issue Assets


# Overview

The ability to issue assets is a core feature of Bantu. In a few simple operations, you can create Bantu-network tokens, and this section of the docs will show you how.

The possibilities are endless: any asset can be tokenized, and, once tokenized, transferred or traded over the Bantu network quickly and cheaply. Since any account can issue an asset on the Bantu network and anyone can set up a Bantu account, *anyone* can issue an asset: banks, payment processors, money service businesses of all stripes, for-profit enterprises, nonprofits, local communities, even individuals. It’s a self-serve process, no permission needed.

In addition to making it easy to issue an asset, Bantu also provides built-in mechanisms that allow you to tune your asset to specific use cases. You can — and should — [publish important identifying information](/issuing-assets/publishing-asset-info) about your asset so that wallets and consumers know what it represents, and it's easy to link that info to your asset in a single step. To comply with regulations, you can [control access](/issuing-assets/control-asset-access) to your asset using protocol-level flags. To take advantage of Bantu’s global reach, you can [list your asset](https://github.com/slideloft/new-docs/tree/046158a008b14dc6d54bdd6f4c48e078c303a05e/content/docs/issuing-assets/list-asset-on-dex.mdx) on the Bantu decentralized exchange, and use [market making bots](https://kelpbot.io/) to ensure necessary liquidity.

Currently, the biggest use case for Bantu is the tokenization of fiat currency to optimize processes like cross-border payments, so there’s also a whole subsequent section — [Enable Deposit and Withdrawal](broken://pages/-MRydWt7s_wFT8_jRTPV) — that focuses on how to connect Bantu tokens to existing rails to allow users to easily deposit real-world assets in exchange for them, and, on the flipside, to redeem them for real-world assets. Check that section if you're interested in creating an accessible on/off ramp that interoperates with wallets for seamless handling of user KYC, deposits, and withdrawals.

As more and more developers and businesses explore other possibilities, we’ll expand the docs to cover emerging ideas and applications.


# Anatomy of an Asset

Each Bantu asset has two characteristics: the asset code and the issuer. When you look up or interact with an asset on Bantu, you always use both to identify it.

Many Bantu tokens represent credits that can be redeemed for something outside the network—often fiat currency, but also bonds, carbon credits, gold, etc.—and since more than one organization can issue a credit representing the same underlying asset, asset codes often overlap. More than one company offers a USD token on Bantu, for instance.

However, the combination of asset code and issuer allows each asset to be uniquely identified: each USD token is offered by, and redeemable with, one specific issuer.

## Asset Code

When you issue an asset, the first thing you do is choose an identifying code. Currently, there are two supported formats.

* **Alphanumeric 4-character maximum**: Any characters from the set \[a-z]\[A-Z]\[0-9] are allowed. The code can be shorter than 4 characters, but the trailing characters must all be empty.
* **Alphanumeric 12-character maximum**: Any characters from the set \[a-z]\[A-Z]\[0-9] are allowed. The code can be any number of characters from 5 to 12, but the trailing characters must all be empty.

Provided it falls into one of those two buckets, you can choose any asset code you like. That said, if you’re issuing a currency, you should use the appropriate [ISO 4217 code](https://en.wikipedia.org/wiki/ISO_4217), and if you’re issuing a stock or bond, the appropriate [ISIN number](https://en.wikipedia.org/wiki/International_Securities_Identification_Number). Doing so makes it easier for Bantu interfaces to properly display and sort your token in their listings, and allows potential token holders to understand, at a glance, what your token represents.

## Issuer

There is no dedicated operation to create an asset on Bantu. Instead, assets are created with a payment operation: an issuing account makes a payment using the asset it’s issuing, and that payment actually creates the asset on the network.

The public key of the issuing account is linked on the ledger to the asset itself. Responsibility for and control over an asset resides with the issuing account, and since settings are stored at the account level on the ledger, the issuing account is where you use `set_options` operations to link to meta-information about an asset and set authorization flags.

## Trustlines

Before an account can hold an asset another account issues, it has to establish something called a trustline, which is a persistent account-level ledger entry created with a `change_trust` operation.

A trustline is an explicit opt-in to hold a particular token, so it specifies both asset code and issuer. Each trustline increases an account’s minimum lumen balance by one [base reserve](/glossary/minimum-balance) — currently 0.5 XLM — and tracks the balance of the asset the account holds. Trustlines can also limit the amount of an asset an account can hold, though more often than not, account holders don’t set that limit: they simply turn the trustline on, which by default allows the maximum.

A trustline also tracks liabilities. Buying liabilities equal the total amount of the asset offered to buy aggregated over all offers owned by an account, and selling liabilities equal the total amount of the asset offered to sell aggregated over all offers owned by an account. A trustline must always have balance sufficiently large to satisfy its selling liabilities, and a balance sufficiently below its limit to accommodate its buying liabilities.

## Representation

In Expansion, assets are represented in a JSON object:

```javascript
{
  "asset_code": "AstroDollar",
  "asset_issuer": "GC2BKLYOOYPDEFJKLKY6FNNRQMGFLVHJKQRGNSSRRGSMPGF32LHCQVGF",
  // `asset_type` is used to determine how asset data is stored.
  // It can be `native` (XBN), `credit_alphanum4`, or `credit_alphanum12`.
  "asset_type": "credit_alphanum12"
}
```

In the Bantu SDKs, they’re represented with the `Asset` class:

```javascript
var astroDollar = new StellarSdk.Asset(
  "AstroDollar",
  "GC2BKLYOOYPDEFJKLKY6FNNRQMGFLVHJKQRGNSSRRGSMPGF32LHCQVGF",
);
```

## Amount Precision

Each asset amount is encoded as a signed 64-bit integer in the [XDR structures](/glossary/xdr) that Bantu uses to encode transactions. The asset amount unit seen by end users is scaled down by a factor of ten million (10,000,000) to arrive at the native 64-bit integer representation.

For example, the integer amount value `25,123,456` equals `2.5123456` units of the asset. This scaling allows for seven decimal places of precision in human-friendly amount units.

The smallest non-zero amount unit is `0.0000001` (one ten-millionth) represented as an integer value of one. The largest amount unit possible is `((2^63)-1)/(10^7)` (derived from max int64 scaled down) which is `922,337,203,685.4775807`.

The numbers are represented as `int64`s. Amount values are stored as only signed integers to avoid bugs that arise from mixing signed and unsigned integers.

## Relevance in Expansion and Bantu Client Libraries

In Expansion and client-side libraries such as `js-stellar-sdk`, the integer encoded value is abstracted away. Many APIs expect an amount in unit value (the scaled-up amount displayed to end users). Some programming languages (such as JavaScript) have problems with maintaining precision on a number amount. It is recommended to use "big number" libraries that can record arbitrary precision decimal numbers without a loss of precision.

## XBN

XBN are the native currency of the Bantu network, and are the only asset that doesn't require an issuer or a trustline. Every account is required to hold a [minimum XBN balance](/glossary/minimum-balance), and all [transaction fees](/glossary/fees) are paid in XBN. The smallest unit of a XBN is a spirit, which is one ten-millionth of a XBN.


# Issue an Asset

There is no dedicated operation to create an asset on Bantu. Instead, assets are created with a [payment operation](/start/list-of-operations#payment): an issuing account makes a payment using the asset it’s issuing, and that payment actually creates the asset on the network.

It’s a pretty simple process that requires four operations: one to create an issuing account, one to create a distribution account, one to establish a trustline, and one to make a payment.

Note: you don't actually have to issue assets to a dedicated distribution account: you can issue them to any account with the requisite trustline. However, using a distribution account is the recommended practice, and it makes the process a lot easier to explain. So that's how we'll do it in this guide.

The code to create those operations and submit them as transactions is below. Here, we’ll walk through each step so that the process makes sense. You can breeze through to get a general understanding, or you can use the [Bantu laboratory](https://laboratory.bantu.network/), which is an interface that allows you to create and submit transactions, to actually follow along and issue a token right here right now.

One caveat: if you are creating a token on the public network, there is an additional prerequisite. You need a funded account to provide the XBN necessary to create the issuing and distribution accounts.

## Create the Issuing and Distribution Accounts

The issuing account is the origin of the asset, and will be forever linked to the asset’s identity. The distribution account is the first recipient of the asset. In the final step of this process, you’ll create the asset by sending a payment from the issuing account to the distribution account.

There are two steps to account creation:

1. Generate a keypair
2. Fund the account using a `create_account` operation

You can generate a keypair for free, but an account doesn’t exist on the Bantu ledger until it is funded with XBN to cover the minimum balance.

If you’re issuing an asset on the testnet, you can fund your accounts by getting free test XBN from Friendbot. If you’re issuing an asset in production, you will need to use an existing account to send enough live XBN to cover the minimum balance, transaction fees, and, in the case of the distribution account, a trustline.

Rule of thumb for production: don’t skirt too close to the minimum network balance. If you do, you may not have enough XBN to do what you need to do. Since the network minimum balance and transaction fees are low, it doesn’t require much to get started. 5 XBN should be sufficient. 100 XBN is even better.

### Why Have Separate Accounts for Issuing and Distribution?

Distributing assets through a distribution account is a design pattern. Functionally, you can do away with the distribution account and distribute directly from the issuer account. A less-known fact is that you can even create a market directly from the issuing account and issue by trading.

With that said, there are two main reasons to use a distribution account:

1. Security
2. Auditing

The account you use to distribute your asset from is going to be a *hot* account, meaning that some web service out there has direct access to sign its transactions.

If the account you use to distribute your asset *is also the issuing account* and is compromised by a malicious actor, *that actor can now issue as much of your asset as they want*. This is a hostile takeover of the asset and can increase your potential off-chain liabilities. If the malicous actor redeems these newly issued tokens with the anchor service, the anchor may not have the liquidity to support customers' withdrawals.

If the account you use to distribute your asset *is not the issuing account*, then the stakes are lower. Once discovered, the issuer account can effectively freeze the compromised account's asset balance and start fresh with a new distribution account. This is possible without changing the issuing account.

The second reason is bookkeeping or auditability. The issuing account can't actually hold a balance of its own asset. If you have standing inventory of your own asset in a separate account, it is easier to track. This is a common pattern in various ledgering solutions.

As an added bonus, distribution accounts decouple our ecosystem standards from issuance. This allows ecosystem participants to come up with interesting concepts like non-issuing anchors *without* actually changing protocols.

## Create a Trustline

Bantu requires accounts to explicitly opt-in to holding an asset by creating a trustline, and in this step the distribution account submits a `change_trust` operation to do just that. The trustline goes from the distribution account to the issuing account.

The distribution account specifies the issuer’s public key and the asset code in the `change_trust` operation, and since it’s the first time the asset code appears on the ledger, that means the *distribution* account actually names the asset, not the issuing account.

Currently, there are two supported formats for asset codes.

* **Alphanumeric 4-character maximum**: Any characters from the set \[a-z]\[a-z]\[0-9] are allowed. The code can be shorter than 4 characters, but the trailing characters must all be empty.
* **Alphanumeric 12-character maximum**: Any characters from the set \[a-z]\[a-z]\[0-9] are allowed. The code can be any number of characters from 5 to 12, but the trailing characters must all be empty.

Any asset code works provided it falls into one of those two buckets. That said, if you’re issuing a currency, you should use the appropriate [ISO 4217](https://en.wikipedia.org/wiki/ISO_4217) code, and if you’re issuing a stock or bond, the appropriate [ISIN number](https://en.wikipedia.org/wiki/International_Securities_Identification_Number). Doing so makes it easier for Bantu interfaces to properly display and sort your token in their listings, and allows potential token holders to understand, at a glance, what your token represents.

## Make a Payment

This is the step where the magic happens. The issuing account makes a payment to the distribution account using the newly named asset, and tokens exist where before there were none. Presto!

As long as the issuing account remains unlocked, it can continue to create new tokens by making payments to the distribution account, or to any other account with the requisite trustline.

If you’re planning to do, really, anything with your asset, your next step is to [complete a Bantu.toml file](/issuing-assets/publishing-asset-info) to provide wallets, exchanges, market listing services, and potential token holders with the information they need to understand what it represents.

Once you’ve done that, you can also [create a sell offer](https://github.com/slideloft/new-docs/tree/046158a008b14dc6d54bdd6f4c48e078c303a05e/content/docs/issuing-assets/list-asset-on-dex.mdx) to get your asset onto the Bantu decentralized exchange, and put some effort into market making to create liquidity for it.

## Sample Code

```javascript
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://expansion-testnet.bantu.network");

// Keys for accounts to issue and receive the new asset
var issuingKeys = StellarSdk.Keypair.fromSecret(
  "SCZANGBA5YHTNYVVV4C3U252E2B6P6F5T3U6MM63WBSBZATAQI3EBTQ4",
);
var receivingKeys = StellarSdk.Keypair.fromSecret(
  "SDSAVCRE5JRAI7UFAVLE5IMIZRD6N6WOJUWKY4GFN34LOBEEUS4W2T2D",
);

// Create an object to represent the new asset
var astroDollar = new StellarSdk.Asset("AstroDollar", issuingKeys.publicKey());

// First, the receiving account must trust the asset
server
  .loadAccount(receivingKeys.publicKey())
  .then(function (receiver) {
    var transaction = new StellarSdk.TransactionBuilder(receiver, {
      fee: 100,
      networkPassphrase: StellarSdk.Networks.TESTNET,
    })
      // The `changeTrust` operation creates (or alters) a trustline
      // The `limit` parameter below is optional
      .addOperation(
        StellarSdk.Operation.changeTrust({
          asset: astroDollar,
          limit: "1000",
        }),
      )
      // setTimeout is required for a transaction
      .setTimeout(100)
      .build();
    transaction.sign(receivingKeys);
    return server.submitTransaction(transaction);
  })
  .then(console.log)

  // Second, the issuing account actually sends a payment using the asset
  .then(function () {
    return server.loadAccount(issuingKeys.publicKey());
  })
  .then(function (issuer) {
    var transaction = new StellarSdk.TransactionBuilder(issuer, {
      fee: 100,
      networkPassphrase: StellarSdk.Networks.TESTNET,
    })
      .addOperation(
        StellarSdk.Operation.payment({
          destination: receivingKeys.publicKey(),
          asset: astroDollar,
          amount: "10",
        }),
      )
      // setTimeout is required for a transaction
      .setTimeout(100)
      .build();
    transaction.sign(issuingKeys);
    return server.submitTransaction(transaction);
  })
  .then(console.log)
  .catch(function (error) {
    console.error("Error!", error);
  });
```

Naturally, the balances for the distributor's account will now hold both XBN and our new Astrodollars.


# Publish Information About an Asset

When you issue an asset, it’s crucial to provide clear information about it represents. On Bantu, you do that by linking your issuing account to a home domain, publishing a `bantu.toml` file on that domain, and making sure that file is complete.

The most successful asset issuers give exchanges, wallets, and potential buyers lots of information about themselves in order to establish trust. More information in your `bantu.toml` will mean:

* Your asset gets *more* exposure, and is listed on *more* exchanges
* Your asset holders are *more* confident in you and the assets you issue.
* Your project will most likely be *more* successful!

The Bantu ticker, which is the source of market data for sites like CoinMarketCap, only includes assets with valid `bantu.toml` files. Trading interfaces use `bantu.toml` files to populate their listings, and to decide if and how to present assets to their users. Any and all ecosystem integrations that allow for interoperability — from buds to in-app deposit and withdrawal — rely on information in your `bantu.toml` detailing your Bantu setup.

Completing your `bantu.toml` is not a step you can skip.

## What is a bantu.toml?

The `bantu.toml` file is a common place where the Internet can find information about your organization’s Bantu integration. You write it in TOML, a simple and widely used configuration file format designed to be readable by both humans and machines, and publish it at `https://YOUR_DOMAIN/.well-known/bantu.toml`.

That way, everyone knows where to find it, anyone can look it up, and it *proves* that the owner of the HTTPS domain hosting the bantu.toml claims *responsibility* for the accounts and assets listed in it.

Using a [`set_options`](/start/list-of-operations#set-options) operation, you can link your Bantu account to the domain that hosts your `bantu.toml`, thereby creating a definitive on-chain connection between this information and that account.

## How to complete your bantu.toml

Bantu Ecosystem Proposals are open protocols for building on top of Bantu, and the very first SEP, aptly named [SEP-1](https://github.com/Bantu/Bantu-protocol/blob/master/ecosystem/sep-0001.md), specifies everything you could ever want to include in a `bantu.toml` file. This guide, which is targeted toward asset issuers, won’t cover the `Validator Information` section (that’s covered in the [Run a Core Node section](/run-core-node/index)), and may omit some details relevant to your use case.

The goal here is to walk through the sections of [SEP-1](https://github.com/Bantu/Bantu-protocol/blob/master/ecosystem/sep-0001.md) that directly relate to asset issuers, so you should use this guide in conjunction with that SEP to make sure you complete your `bantu.toml` correctly. The four sections we’ll cover:

* General Information
* Organization Documentation
* Point of Contact Documentation
* Currency Documentation

For each of those sections, we’ll let you know which fields are **required**, meaning all asset issuers *must* include them to be listed by exchanges and wallets, and which fields are **suggested**. Completing suggested fields is a good way to make your asset stand out.

Note: it's a good idea to keep the sections in the order presented in \[SEP-1]\([https://github.com/Bantu/Bantu-protocol/blob/master/ecosystem/sep-0001.md\\](https://github.com/Bantu/Bantu-protocol/blob/master/ecosystem/sep-0001.md%29\\), which is also the order they're presented here. TOML requires arrays to be at the end, so if you move scramble the order, you may cause errors for TOML parsers

### General Information

There is one field in the General Information section required for *all* token issuers:

* `ACCOUNTS`: A list of **public keys** for all the Bantu accounts associated with your asset.

Listing your public keys lets users confirm that you, in fact, own them. For example, when <https://google.com> hosts a bantu.toml file, users can be sure that *only* the accounts listed on it belong to Google. If someone then says, "You need to pay your Google bill this month, send payment to address GIAMGOOGLEIPROMISE", but that key is not listed on Google's bantu.toml, then users know to not trust it.

In addition, there are several fields where you list information about your Bantu integration to aid in discoverability. If you are an anchor service, and you have [set up infrastructure](/issuing-assets/publishing-asset-info) to interoperate with wallets and allow for in-app deposit and withdrawal of assets, make sure to include the locations of your servers on your bantu.toml file so those wallets know where to find relevant endpoints to query. In particular, list your:

* `TRANSFER_SERVER_SEP0024`, which is where wallets find endpoints to initiate interactive deposit and withdrawal based on the [SEP-24](https://github.com/Bantu/Bantu-protocol/blob/master/ecosystem/sep-0024.md) spec
* `WEB_AUTH_ENDPOINT`, which is where wallets initiate user authentication sessions based on the [SEP-10](https://github.com/Bantu/Bantu-protocol/blob/master/ecosystem/sep-0010.md) spec

If you support other Bantu Ecosystem Proposals — such as buds or delegated signing — or host a public Expansion instance that other people can use to query the ledger, you should also add the location of those resources to [General Information](https://github.com/Bantu/Bantu-protocol/blob/master/ecosystem/sep-0001.md#general-information) so they're discoverable.

### Organization Documentation

Basic information about your organization goes into a TOML **table** called `[DOCUMENTATION]`. Organization Documentation is your chance to inform exchanges and buyers about your business, and to demonstrate that your business is legitimate and trustworthy.

#### Required

* `ORG_NAME` The legal name of your organization, and if your business has one, its official `ORG_DBA`.
* `ORG_URL` The HTTPS URL of your organization's official website. In order to prove the website is yours, *you must host your bantu.toml on the same domain you list here.* That way, exchanges and buyers can view the SSL certificate on your website, and feel reasonably confident that you are who you say you are.
* `ORG_LOGO` A URL to a company logo, which will show up next to your organization on exchanges. This image should be a square aspect ratio transparent PNG, ideally of size 128x128. If you fail to provide a logo, the icon next to your organization will appear blank on many exchanges.
* `ORG_PHYSICAL_ADDRESS` The physical address of your organization. We understand you might want to keep your work address private. At the very least, you should put the *city* and *country* in which you operate. A street address is ideal and provides a higher level of trust and transparency to your potential asset holders.
* `ORG_OFFICIAL_EMAIL` The best contact email address for you organization. This should be hosted at the same domain as your official website.

#### Suggested

* `ORG_GITHUB` Your organization's official Github account.
* `ORG_KEYBASE` Your organization's official Keybase account. Your Keybase account should contain proof of ownership of any public online accounts you list here, including your organization's domain.
* `ORG_TWITTER` Your organization's official Twitter handle.
* `ORG_DESCRIPTION` A description of your organization. This is fairly open-ended, and you can write as much as you want. It's a great place to distinguish yourself by describing what it is that you do.

Issuers that list verified information including phone/address attestations and Keybase verifications are prioritized by Bantu clients.

### Point of Contact Documentation

Information about the primary point(s) of contact for your organization goes into a TOML [array of tables](https://github.com/toml-lang/toml#array-of-tables) called `[[PRINCIPALS]]`. You need to put contact information for *at least one person* at your organization. If you don't, exchanges can't verify your offering, and it is unlikely that buyers will be interested. Multiple principals can be added with additional `[[PRINCIPALS]]` entries.

#### Required

* `name` The name of the primary contact.
* `email` The primary contact's official email address. This should be hosted at the same domain as your organization's official website.

#### Suggested

* `github` The personal Github account of the point of contact.
* `twitter` The personal Twitter handle of the point of contact.
* `keybase` The personal Keybase account for the point of contact. This account should contain proof of ownership of any public online accounts listed here and may contain proof of ownership of your organization's domain.

### Currency Documentation

Information about the asset(s) you issue goes into a TOML [array of tables](https://github.com/toml-lang/toml#array-of-tables) called `[[CURRENCIES]]`. If you issue multiple assets, you can include them all in one bantu.toml. Each asset should have its own `[[CURRENCIES]]` entry.

#### Required

* `code` The asset code. This is one of two key pieces of information that identify your token. Without it, your token cannot be listed anywhere.
* `issuer` The Bantu public key of the issuing account. This is the second key piece of information that identifies your token. Without it, your token cannot be listed anywhere.
* `is_asset_anchored` An indication of whether your token is anchored or native: `true` if your token can be redeemed for an asset outside the Bantu network, `false` if it can’t. Exchanges use this information to sort tokens by type in listings. If you fail to provide it, your token is unlikely to show up in filtered market views.

If you're issuing anchored (tethered, stablecoin, asset-backed) tokens, there are several additional **required** fields:

* `anchor_asset_type` The type of asset your token represents. The possible categories are `fiat`, `crypto`, `stock`, `bond`, `commodity`, `realestate`, and `other`.
* `anchor_asset` The name of the asset that serves as the anchor for your token.
* `redemption_instructions` Instructions to redeem your token for the underlying asset.

#### Suggested

* `desc` A description of your token and what it represents. This is a good place to clarify what your token does, and why someone might want to own it.
* `conditions` Any conditions you place on the redemption of your token.
* `image` A URL to a PNG or GIF image with a transparent background representing your token. Without it, your token will appear blank on many exchanges.

## How to publish your bantu.toml

After you've followed the steps above to complete your bantu.toml, post it at the following location:

`https://YOUR_DOMAIN/.well-known/bantu.toml`

Enable [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS) so people can access this file from other sites, and set the following header for an HTTP response for a `/.well-known/bantu.toml` file request.

`Access-Control-Allow-Origin: *`

Set a `text/plain` content type so that browsers render the contents, rather than prompting for a download. ‘content-type: text/plain’

You should also use the `set_options` operation to set the home domain on your issueing account.

## Sample code to set the home domain of your issuing account

```javascript
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://expansion-testnet.bantu.network");

// Keys for issuing account
var issuingKeys = StellarSdk.Keypair.fromSecret(
  "SCZANGBA5YHTNYVVV4C3U252E2B6P6F5T3U6MM63WBSBZATAQI3EBTQ4",
);

server
  .loadAccount(issuingKeys.publicKey())
  .then(function (issuer) {
    var transaction = new StellarSdk.TransactionBuilder(issuer, {
      fee: 100,
      networkPassphrase: StellarSdk.Networks.TESTNET,
    })
      .addOperation(
        StellarSdk.Operation.setOptions({
          homeDomain: "yourdomain.com",
        }),
      )
      // setTimeout is required for a transaction
      .setTimeout(100)
      .build();
    transaction.sign(issuingKeys);
    return server.submitTransaction(transaction);
  })
  .then(console.log)
  .catch(function (error) {
    console.error("Error!", error);
  });
```

## Sample bantu.toml

```javascript
NETWORK_PASSPHRASE="Public Bantu Network ; 10-10-2020"
BUDS_SERVER="https://api.domain.com/buds"
AUTH_SERVER="https://api.domain.com/auth"
TRANSFER_SERVER="https://api.domain.com"
SIGNING_KEY="GBBHQ7H4V6RRORKYLHTCAWP6MOHNORRFJSDPXDFYDGJB2LPZUFPXUEW3"
EXPANSION_URL="https://expansion.domain.com"
ACCOUNTS=[
"GD5DJQDDBKGAYNEAXU562HYGOOSYAEOO6AS53PZXBOZGCP5M2OPGMZV3",
"GAENZLGHJGJRCMX5VCHOLHQXU3EMCU5XWDNU4BGGJFNLI2EL354IVBK7",
"GAOO3LWBC4XF6VWRP5ESJ6IBHAISVJMSBTALHOQM2EZG7Q477UWA6L7U"
]
VERSION="2.0.0"

[DOCUMENTATION]
ORG_NAME="Organization Name"
ORG_DBA="Organization DBA"
ORG_URL="https://www.domain.com"
ORG_LOGO="https://www.domain.com/awesomelogo.png"
ORG_DESCRIPTION="Description of issuer"
ORG_PHYSICAL_ADDRESS="123 Sesame Street, New York, NY 12345, United States"
ORG_PHYSICAL_ADDRESS_ATTESTATION="https://www.domain.com/address_attestation.jpg"
ORG_PHONE_NUMBER="1 (123)-456-7890"
ORG_PHONE_NUMBER_ATTESTATION="https://www.domain.com/phone_attestation.jpg"
ORG_KEYBASE="accountname"
ORG_TWITTER="orgtweet"
ORG_GITHUB="orgcode"
ORG_OFFICIAL_EMAIL="support@domain.com"

[[PRINCIPALS]]
name="Jane Jedidiah Johnson"
email="jane@domain.com"
keybase="crypto_jane"
twitter="crypto_jane"
github="crypto_jane"
id_photo_hash="be688838ca8686e5c90689bf2ab585cef1137c999b48c70b92f67a5c34dc15697b5d11c982ed6d71be1e1e7f7b4e0733884aa97c3f7a339a8ed03577cf74be09"
verification_photo_hash="016ba8c4cfde65af99cb5fa8b8a37e2eb73f481b3ae34991666df2e04feb6c038666ebd1ec2b6f623967756033c702dde5f423f7d47ab6ed1827ff53783731f7"

[[CURRENCIES]]
code="USD"
issuer="GCZJM35NKGVK47BB4SPBDV25477PZYIYPVVG453LPYFNXLS3FGHDXOCM"
display_decimals=2

[[CURRENCIES]]
code="BTC"
issuer="GAOO3LWBC4XF6VWRP5ESJ6IBHAISVJMSBTALHOQM2EZG7Q477UWA6L7U"
display_decimals=7
anchor_asset_type="crypto"
anchor_asset="BTC"
redemption_instructions="Use SEP6 with our buds server"
collateral_addresses=["2C1mCx3ukix1KfegAY5zgQJV7sanAciZpv"]
collateral_address_signatures=["304502206e21798a42fae0e854281abd38bacd1aeed3ee3738d9e1446618c4571d10"]

# asset with meta info
[[CURRENCIES]]
code="GOAT"
issuer="GD5T6IPRNCKFOHQWT264YPKOZAWUMMZOLZBJ6BNQMUGPWGRLBK3U7ZNP"
display_decimals=2
name="goat share"
desc="1 GOAT token entitles you to a share of revenue from Elkins Goat Farm."
conditions="There will only ever be 10,000 GOAT tokens in existence. We will distribute the revenue share annually on Jan. 15th"
image="https://static.thenounproject.com/png/2292360-200.png"
fixed_number=10000
```


# Control Access to an Asset

When you issue an asset on Bantu, anyone can hold it by default. In general, that’s a good thing: easy access means better reach and better liquidity, and most of the time, issuers with KYC requirements can handle those when an asset moves onto or off of the network. Most fiat-backed token issuers, for instance, use the transfer server protocol specified in SEP-24 to decide whether to honor deposits and withdrawals rather than setting account-level flags. You can read about how to do that in the [Enable Deposit and Withdrawal](broken://pages/-MRydWt7s_wFT8_jRTPV) section.

However, if you need to control access to an asset to comply with regulations (or for any other reason), you can easily do so by enabling flags on your issuing account.

Flags are created on the *account level* using a [`set_options`](/start/list-of-operations#set-options) operation. They can be set at any time in the life cycle of an asset, not just when you issue it:

## Authorization Required

When `AUTHORIZATION REQUIRED` is enabled, an issuer must approve an account before that account can hold its asset. This setting allows issuers to vet potential token holders using whatever means they see fit, and to approve trustlines if and only if the holders pass muster.

To allow access, the user creates a trustline, and the issuer approves it by changing the `AUTHORIZE` flag with the [`allow_trust`](/start/list-of-operations#allow-trust) operation.

There are two levels of authorization an asset issuer can grant using the `allow_trust` operation:

* `AUTHORIZED`: This flag signifies complete authorization allowing an account to transact freely with the asset to make and receive payments and place orders.
* `AUTHORIZED_TO_MAINTAIN_LIABILITIES`: This flag denotes limited authorization that allows an account to maintain current orders, but not to otherwise transact with the asset.  &#x20;

## Authorization Revocable

When `AUTHORIZATION_REVOCABLE` is enabled, an issuer can revoke an existing trustline's authorization, thereby freezing the asset held by an account. Doing so prevents that account from transfer or trading the asset, and cancels the account’s open orders for the asset.

`AUTHORIZATION_REVOCABLE` also allows an issuer to reduce authorization from complete to limited, which prevents the account from transferring or trading the asset, but does not cancel the account's open orders for the asset. This setting is useful for issuers of regulated assets who need to authorize transactions on a case-by-case basis to ensure each conforms to certain requirements.

All changes to asset authorization are performed with the [`allow_trust`](/start/list-of-operations#allow-trust) operation.

To use this setting, `AUTHORIZATION REQUIRED` must also be enabled.

## Authorization Immutable

With this setting, neither of the other authorization flags can be set, and the issuing account can’t be merged. You set this flag to signal to potential token holders that your are issuing account and its assets will persist on ledger in an open and accessible state.

## Example flow

To get a sense of how authorization flags work, let's look at how an issuer of a regulated asset might use the `AUTHORIZED_TO_MAINTAIN_LIABILITIES` flag.

If the issuer wants to approve transactions on a case-by-base basis while allowing accounts to maintain offers, they can leave an account in the `AUTHORIZED_TO_MAINTAIN_LIABILITIES` state. That account can own offers, but cannot otherwise do anything with the asset.

To initiate a new operation, the holding account requests that the issuer approve and sign a transaction. Once the issuer inspects the operation and decides to approve it, they sandwich it between a set of operations, first granting authorization, then reducing it.

Here's a payment from A to B sandwiched between [`allow_trust`](/start/list-of-operations#allow-trust) operations:

* Operation 1: Issuer uses `AllowTrust` to fully authorize account A, asset X
* Operation 2: Issuer uses `AllowTrust` to fully authorize account B, asset X
* Operation 3: Payment from A to B
* Operation 4: Issuer uses `AllowTrust` to set account B, asset X to `AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG` state
* Operation 5: Issuer uses `AllowTrust` to set account A, asset X to `AUTHORIZED_TO_MAINTAIN_LIABILITIES_FLAG` state

The authorization sandwich allows the issuer to inspect the specific payment, and to grant authorization for it and it alone. Since operations bundled in a transaction are simultaneous, A and B are only authorized for the specific, pre-approved payment operation. Complete authorization does not extend beyond the specific transaction.

## Sample code

The following example sets authorization to be both required and revocable:

```javascript
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://expansion-testnet.bantu.network");

// Keys for issuing account
var issuingKeys = StellarSdk.Keypair.fromSecret(
  "SCZANGBA5YHTNYVVV4C3U252E2B6P6F5T3U6MM63WBSBZATAQI3EBTQ4",
);

server
  .loadAccount(issuingKeys.publicKey())
  .then(function (issuer) {
    var transaction = new StellarSdk.TransactionBuilder(issuer, {
      fee: 100,
      networkPassphrase: StellarSdk.Networks.TESTNET,
    })
      .addOperation(
        StellarSdk.Operation.setOptions({
          setFlags: StellarSdk.AuthRevocableFlag | StellarSdk.AuthRequiredFlag,
        }),
      )
      // setTimeout is required for a transaction
      .setTimeout(100)
      .build();
    transaction.sign(issuingKeys);
    return server.submitTransaction(transaction);
  })
  .then(console.log)
  .catch(function (error) {
    console.error("Error!", error);
  });
```


# Building Apps


# Overview

Bantu is a self-serve distributed ledger that you can use as a backend to power all kinds of apps and services. It has built-in logic for creating accounts, signing transactions, and tracking balances, and anyone can use it to issue, store, transfer, and trade assets. Since many of those assets connect to real-world currencies, and since there are open protocols for integrating deposit and withdrawal of those assets, a Bantu-based app can take advantage of real banking rails and connect to real money.

Currently, developers use Bantu to power cross-border payment apps, currency exchanges, micropayment services, and platforms for in-game purchases, but what you build — and how you build it — is up to you. At its core, though, any app built on Bantu relies on the same basic functions: key storage, account creation, transaction signing, and queries to the Bantu database. To make things easy, and to stick with common parlance, we’ll call the part of an app that implements those core functions a *wallet*. This section of the docs will walk you through the process of building one.

Bantu wallets can do more than store and move assets, and they don't have to be the end product for your users. They can be front and center in an application, or function invisibly in the background without revealing anything about Bantu to the end user. However you decide to surface the UI of the wallet, there are several basic considerations and common implementations, and the goal of this section of the docs is to cover those by showing you how to build a basic wallet UI.

It’s a step-by-step tutorial explaining how to construct a robust Bantu wallet following best practices. If you follow it to the end, you'll have a fully functional Bantu interface, and a strong framework for continued development of your own use case.

## What’s in a Wallet?

Unlike real-world wallets, which hold real-world cash, Bantu wallets don’t hold digital cash. Not directly. Rather, they allow users to view the history and current state of the Bantu ledger, and to sign and submit transactions. Accounts, balances, and offers to buy and sell assets are kept on the ledger itself, which is shared by all the nodes that make up the network. A wallet may store references or caches to the Bantu database, but the actual record and the value it tracks are on chain.

What does this mean for the wallet we’re about to build? Good question. It means the wallet acts as a visual and interactive layer on top of Bantu rather than as an extension or storage mechanism. Our conversation moving forward will revolve around accessing Bantu and surfacing data in the network rather than "running" Bantu or storing something on our end.

A neat outflow of this is that most Bantu tools can operate almost entirely as client-side applications and services. There will certainly be server-side logic specific to your app, but the actual wallet interactions can operate openly on the client/browser.

## Securing Your Wallet

Even though wallets can operate client-side, they deal with a user’s secret keys, which give direct access to their account, and to any value they hold in it. That’s why it’s essential to require all web traffic to flow over strong TLS methods. Even when developing locally, use a self-signed localhost certificate to develop secure habits from the very beginning. Bantu may be incredibly easy and intuitive, but it’s also powerful money-moving software. Don’t skimp on security. Your future self and all your users thank you.


# Project Setup

Throughout this tutorial we'll be making use of a little toolchain called StencilJs. It takes the best of modern frontend frameworks and pares everything back to small, blazing fast, 100% standards-based Web Components that run in every browser. Don’t worry if you've never heard of it: it's just TS (JS), SCSS (CSS) and JSX (HTML). You should be able to follow along just fine if you've ever built something with modern web dev tools.

We chose Stencil so you can learn by doing: it’s an easy way to create a web-based application, which means you can see the ins and outs of building a Bantu wallet start to finish. Bantu also has a [suite of SDKs](/software-and-sdks/index) in various programming languages, so if Javascript isn’t your thing, you can follow along, and recreate the steps below using one of them.

To start the setup, open your terminal and initialize a new project.

```bash
npm init stencil
```

After running `init` you will be provided with a prompt to choose the type of project to start. While Stencil can be used to create entire apps, we’ll choose the component as we’ll just be dealing with modular components rather than building an entire application

```bash
? Pick a starter › - Use arrow-keys. Return to submit.

   ionic-pwa     Everything you need to build fast, production ready PWAs
   app           Minimal starter for building a Stencil app or website
❯  component     Collection of web components that can be used anywhere
```

We’ll walk through the prompt, `cd` into the project and run `npm i ; npm start`

```bash
✔ Pick a starter › component
✔ Project name › Bantu-wallet

✔ All setup  in 9 ms

  $ npm start
    Starts the development server.

  $ npm run build
    Builds your components/app in production mode.

  $ npm test
    Starts the test runner.

  We suggest that you begin by typing:

   $ cd bantu-wallet
   $ npm i
   $ npm start

  Further reading:

   - https://github.com/ionic-team/stencil-component-starter

  Happy coding!
```

Now that our project is initialized, let’s take a look at the directory structure and familiarize ourselves with where things are and what roles they play.

We’re mostly interested in the `src/` directory. The `dist/` and `www/` directories are outputs for compiled code. In the `src/components/` directory you’ll see a `my-component/` folder. We’re about to generate our own component, so go ahead and delete that folder. You can also nuke the `utils/` directory as we won’t be covering tests in this tutorial. Now run:

```bash
$ npm run generate
```

This will initialize a component generation script. Enter a name of `bantu-wallet`. Disable Spec Test and E2E Test as we’re not interested in those Stencil features today. Press Return and your component will generate and wire up.

```bash
% npm run generate

> bantu-wallet generate
> stencil generate

✔ Component tag name (dash-case): … bantu-wallet
✔ Which additional files do you want to generate? › Stylesheet

$ stencil generate bantu-wallet

The following files have been generated:
  - src/components/wallet/wallet.tsx
  - src/components/wallet/wallet.css
```

Amazing! Just a few more setup bits and we can get coding. I don’t know about you but I prefer to style in SCSS rather than CSS so let’s get some modern CSS dev tools setup.

```bash
npm i -D @stencil/postcss @stencil/sass autoprefixer @types/autoprefixer rollup-plugin-node-polyfills
```

Once those packages have successfully installed, hop over to the `stencil.config.ts` file at the root of the project and modify it to this:

```typescript
import { Config } from "@stencil/core";
import { sass } from "@stencil/sass";
import { postcss } from "@stencil/postcss";
import autoprefixer from "autoprefixer";
import nodePolyfills from "rollup-plugin-node-polyfills";

export const config: Config = {
  namespace: "bantu-wallet",
  outputTargets: [
    {
      type: "dist",
      esmLoaderPath: "../loader",
    },
    {
      type: "docs-readme",
    },
    {
      type: "www",
      serviceWorker: null, // disable service workers
    },
  ],
  globalStyle: "src/global/style.scss",
  commonjs: {
    namedExports: {
      "stellar-sdk": [
        "StrKey",
        "xdr",
        "Transaction",
        "Keypair",
        "Networks",
        "Account",
        "TransactionBuilder",
        "BASE_FEE",
        "Operation",
        "Asset",
        "Memo",
        "MemoHash",
      ],
      "@stellar/wallet-sdk": ["KeyManager", "KeyManagerPlugins", "KeyType"],
    },
  },
  plugins: [
    nodePolyfills(),
    sass(),
    postcss({
      plugins: [autoprefixer()],
    }),
  ],
  nodeResolve: {
    browser: true,
    preferBuiltins: true,
  },
};
```

With that file saved, pop over to the `src/components/wallet/` and rename the `wallet.css` to `wallet.scss`. While we’re here let’s go ahead and modify this new style file with some basic styling to put our project in a pretty place.

```css
@import "../../global/style.scss";

:host {
  display: block;
  font-family: $font-family;
  font-size: 15px;

  p {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    display: block;
  }
  button {
    margin-bottom: 10px;
  }

  .account-key {
    display: flex;
    align-items: center;
    width: 100%;
    margin-bottom: 10px;

    .small {
      margin: 0 0 0 10px;
      flex-shrink: 0;
    }
  }
  .account-state,
  .error {
    overflow: scroll;
    padding: 10px;
    font-size: 12px;
    line-height: 1.2;
    font-family: $font-mono;
    margin-bottom: 10px;
    width: 100%;
  }
  .account-state {
    background-color: whitesmoke;
  }
  .error {
    background-color: orangered;
    color: white;
  }

  stellar-loader {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%);
  }
}
```

Before we update our `wallet.tsx` with this new stylesheet, note that we’re also importing a global stylesheet. Go ahead and create that file at the root of the `src/` directory. So `src/global/style.scss`.

```css
html,
body,
div,
span,
applet,
object,
iframe,
h1,
h2,
h3,
h4,
h5,
h6,
p,
blockquote,
pre,
a,
abbr,
acronym,
address,
big,
cite,
code,
del,
dfn,
em,
img,
ins,
kbd,
q,
s,
samp,
small,
strike,
strong,
sub,
sup,
tt,
var,
b,
u,
i,
center,
dl,
dt,
dd,
ol,
ul,
li,
fieldset,
form,
label,
legend,
table,
caption,
tbody,
tfoot,
thead,
tr,
th,
td,
article,
aside,
canvas,
details,
embed,
figure,
figcaption,
footer,
header,
hgroup,
menu,
nav,
output,
ruby,
section,
summary,
time,
mark,
audio,
video {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
  display: block;
}
body {
  line-height: 1;
}
ol,
ul {
  list-style: none;
}
blockquote,
q {
  quotes: none;
}
blockquote:before,
blockquote:after,
q:before,
q:after {
  content: "";
  content: none;
}
table {
  border-collapse: collapse;
  border-spacing: 0;
}

* {
  box-sizing: border-box;
}
input,
button,
select,
textarea {
  font-family: $font-family;
  font-size: 15px;
  outline: none;
  appearance: none;
  border-radius: 0;
}
input,
select,
button {
  height: 30px;
}
button {
  border: none;
  appearance: none;
  position: relative;
  background-color: blue;
  color: white;
  margin: 0;
  display: flex;
  align-items: center;
  align-content: center;
  justify-content: center;
  justify-items: center;
  padding: 0 10px;
  cursor: pointer;

  &.loading {
    color: transparent;
    pointer-events: none;
  }
  &.small {
    font-size: 12px;
    height: 20px;
  }
}
```

Save those style files and update the `wallet.tsx` to point to our new styles like so:

```typescript
import { Component, h } from "@stencil/core";
import * as StellarSdk from "stellar-sdk";

@Component({
  tag: "Bantu-wallet",
  styleUrl: "wallet.scss",
  shadow: true,
})
export class Wallet {
  render() {
    return [
      <h1>
        {!!StellarSdk
          ? "The StellarSdk is ready to rock 🤘"
          : "Uh oh, the StellarSdk is missing 😱"}
      </h1>,
    ];
  }
}
```

You’ll notice we also include a few extra setup lines to get the StellarSdk loaded in and ready to use. Let’s ensure all those dependencies are loaded and ready to rock.

```bash
npm i -D stellar-sdk js-xdr
```

The last mod: point the `src/index.html` file to use this brand new component. Modify that file to match this.

```markup
<!DOCTYPE html>
<html dir="ltr" lang="en">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0"
    />
    <title>Bantu Wallet</title>
    <link rel="stylesheet" href="https://unpkg.com/reset-css/reset.css" />
    <script type="module" src="/build/bantu-wallet.esm.js"></script>
    <script nomodule src="/build/bantu-wallet.js"></script>
  </head>
  <body>
    <bantu-wallet />
  </body>
</html>
```

You should be all set up now. Restart the server and let’s get coding.

```bash
# Stop any current server and rerun
$ npm start
```


# Key Management Basics

Step one for any app is to sort out user onboarding. Since a Bantu wallet is an interface that gives a user access to an account stored on the ledger, and since that access is controlled by the account's secret key, the first thing you have to decide is how to handle a user's secret key, and how to append the Bantu account it unlocks to a user object.

The most important question: who will “own” the account? There are three possible answers:

1. You, the service provider, store the secret key and delegate usage rights to the user. This is a “custodial” service.
2. They, the user, will store their own account credentials and permission your app to send requests or delegate transaction signing. This is a “non-custodial” service.
3. A mixture of the two via multisig. This is especially useful for maintaining non-custodial status while still allowing for account recovery.

Be very careful taking a custodial approach or using any mixture where you are storing user secrets. It’s easy to get wrong, and the consequences can be devastating. For our purposes, we will assume choice #2. This tutorial will show you how to build a non-custodial service.

The non-custodial option poses some very real usability issues: your user has to securely store their own account credentials and safely navigate transaction signing on their end. There are ways to make that process more user-friendly, one of which is to create a keystore file guarded by a passphrase, and that's the approach we'll take in this tutorial.

Creating a keystore file and storing it locally in the browser allows a user to import or export it into other systems, and works pretty well as long as the user's passphrase is secure enough. We don't recommend this approach for high-security production applications, but it's the best way to get started, and for this tutorial, which lays out the foundation for a basic Bantu wallet, it’ll work just fine.

## Third-party Key Management Applications

There are also several apps and services that specialize in adding additional security layers to users' accounts. Check them out if you're interested:

* [BantuPay](https://bantupay.org/)


# Create a Basic Wallet

{% hint style="info" %}
In this tutorial, the goal is to get to a place where a user can create, store, and access their Bantu account using an intuitive pincode encryption method.
{% endhint %}

## User Flow

Because we've decided to build a non-custodial wallet, we don’t need to communicate with a server or database at all: everything can happen locally right on a user’s device. We’ll be doing all our work inside of `src/components/wallet/`, so head over there now. We’re going to use the `StellarSdk.Keypair.random()` from the StellarSdk to generate a new valid Bantu keypair. That's the easy part. The hard work will be storing that vital information in a secure yet accessible manner.

The user flow we're building will work like this: Click “Create Account” → UI modal popup asking for a pincode → Enter pincode, click “OK” → App encrypts a new Bantu keypair secret key with pincode → App saves encrypted secret to `localStorage`. On page reload, we’ll fetch the `publicKey` to “login” the user, but for any protected action such as “Copy Secret”, the modal will pop back up asking for the original pincode.

## Create a Popup Modal

To start, let's look at at the popup modal. We’ll be mimicking the browser’s `prompt` functionality with our own new, more powerful component. First things first we should generate a new component:

```bash
npm run generate
```

Call it `bantu-prompt`, and deselect both test files leaving only the styling. Once you have that, open `src/components/prompt/` and rename the `.css` file to `.scss`. Fill that style file with this:

```css
@import "../../global/style.scss";

:host {
  display: block;
  font-family: $font-family;
  font-size: 15px;

  .prompt-wrapper {
    position: absolute;
    top: 0;
    left: 0;
    bottom: 0;
    right: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    align-content: center;
    min-height: 100vh;
    min-width: 100vw;
    background-color: rgba(black, 0.2);
    z-index: 1;
  }
  .prompt {
    background-color: white;
    padding: 20px;
    max-width: 350px;
    width: 100%;
    position: relative;

    p {
      margin-bottom: 10px;
    }
    input {
      width: 100%;
      margin: 0;
      padding: 5px;
      outline: none;
      border: 1px solid black;
      text-transform: uppercase;

      &:focus {
        border-color: blue;
      }
    }
  }
  .select-wrapper {
    position: relative;
    display: inline-flex;

    select {
      border-color: blue;
      padding: 0 10px;
      min-width: 100px;
    }

    &:after,
    &:before {
      font-size: 12px;
      position: absolute;
      right: 10px;
      color: blue;
    }
    &:after {
      content: "◀";
      top: calc(50% - 5px);
      transform: translate(0, -50%) rotate(90deg);
    }
    &:before {
      content: "▶";
      top: calc(50% + 5px);
      transform: translate(0, -50%) rotate(90deg);
    }
  }
  .actions {
    display: flex;
    justify-content: flex-end;
    margin-top: 10px;

    button {
      margin: 0;
      min-width: 50px;
    }
    .cancel {
      background: none;
      border: 1px solid blue;
      color: blue;
    }
    .submit {
      margin-left: 10px;
    }
  }
}
```

Replace the `prompt.tsx` contents with this.

```typescript
import { Component, Prop, Element, Watch, h, State } from "@stencil/core";
import { defer as loDefer } from "lodash-es";

export interface Prompter {
  show: boolean;
  message?: string;
  placeholder?: string;
  options?: Array<any>;
  resolve?: Function;
  reject?: Function;
}

@Component({
  tag: "bantu-prompt",
  styleUrl: "prompt.scss",
  shadow: true,
})
export class Prompt {
  @Element() private element: HTMLElement;

  @Prop({ mutable: true }) prompter: Prompter;

  @State() private input: string;

  @Watch("prompter")
  watchHandler(newValue: Prompter, oldValue: Prompter) {
    if (newValue.show === oldValue.show) return;

    if (newValue.show) {
      this.input = null;

      if (newValue.options)
        this.input =
          this.input ||
          `${newValue.options[0].code}:${newValue.options[0].issuer}`;
      else
        loDefer(() => this.element.shadowRoot.querySelector("input").focus());
    } else {
      this.prompter.message = null;
      this.prompter.placeholder = null;
      this.prompter.options = null;
    }
  }

  componentDidLoad() {
    addEventListener("keyup", (e: KeyboardEvent) => {
      if (this.prompter.show)
        e.keyCode === 13
          ? this.submit(e)
          : e.keyCode === 27
          ? this.cancel(e)
          : null;
    });
  }

  cancel(e: Event) {
    e.preventDefault();

    this.prompter = {
      ...this.prompter,
      show: false,
    };
    this.prompter.reject(null);
  }

  submit(e: Event) {
    e.preventDefault();

    this.prompter = {
      ...this.prompter,
      show: false,
    };
    this.prompter.resolve(this.input);
  }

  update(e) {
    this.input = e.target.value.toUpperCase();
  }

  render() {
    return this.prompter.show ? (
      <div class="prompt-wrapper">
        <div class="prompt">
          {this.prompter.message ? <p>{this.prompter.message}</p> : null}

          {this.prompter.options ? (
            <div class="select-wrapper">
              <select onInput={(e) => this.update(e)}>
                {" "}
                {this.prompter.options.map((option) => (
                  <option
                    value={`${option.code}:${option.issuer}`}
                    selected={this.input === `${option.code}:${option.issuer}`}
                  >
                    {option.code}
                  </option>
                ))}
              </select>
            </div>
          ) : (
            <input
              type="text"
              placeholder={this.prompter.placeholder}
              value={this.input}
              onInput={(e) => this.update(e)}
            ></input>
          )}

          <div class="actions">
            <button
              class="cancel"
              type="button"
              onClick={(e) => this.cancel(e)}
            >
              Cancel
            </button>
            <button
              class="submit"
              type="button"
              onClick={(e) => this.submit(e)}
            >
              OK
            </button>
          </div>
        </div>
      </div>
    ) : null;
  }
}
```

One of the first things you’ll notice is the use of `lodash-es`. Let’s make sure we’ve got that imported before moving forward:

```bash
npm i -D lodash-es
```

There’s a lot going on in this file, but since this isn’t a Stencil tutorial we’ll skip the details. What this allows us to do it to use a `<bantu-prompt prompter={this.prompter} />` component elsewhere in our project. It's worth noting the variables available to us in the `prompter` property.

```typescript
export interface Prompter {
  show: boolean;
  message?: string;
  placeholder?: string;
  options?: Array<any>;
  resolve?: Function;
  reject?: Function;
}
```

The values we’ll be making most use of are those first three: `show`, `message` and `placeholder`. The last two—`resolve` and `reject`—are for promisifying the prompt so we can await a response before continuing with further logic. Don't worry: that statement will make more sense in a moment once we include this component in `src/components/wallet/`. Speaking of, let’s swing over to that component now.

We’ve got a lot of work to do in here so I’ll just paste the code in all its glory and we’ll walk through it block by block:

```typescript
import { Component, State } from "@stencil/core";

import componentWillLoad from "./events/componentWillLoad";
import render from "./events/render";

import createAccount from "./methods/createAccount";
import copyAddress from "./methods/copyAddress";
import copySecret from "./methods/copySecret";
import signOut from "./methods/signOut";
import setPrompt from "./methods/setPrompt";

import { Prompter } from "@prompt/prompt";

interface BantuAccount {
  publicKey: string;
  keystore: string;
}

@Component({
  tag: "bantu-wallet",
  styleUrl: "wallet.scss",
  shadow: true,
})
export class Wallet {
  @State() account: BantuAccount;
  @State() prompter: Prompter = { show: false };
  @State() error: any = null;

  // Component events
  componentWillLoad() {}
  render() {}

  // Stellar methods
  createAccount = createAccount;
  copyAddress = copyAddress;
  copySecret = copySecret;
  signOut = signOut;

  // Misc methods
  setPrompt = setPrompt;
}

Wallet.prototype.componentWillLoad = componentWillLoad;
Wallet.prototype.render = render;
```

They say the beginning is a good place to start. Let’s do that:

```javascript
import { Component, State } from "@stencil/core";

import componentWillLoad from "./events/componentWillLoad";
import render from "./events/render";

import createAccount from "./methods/createAccount";
import copyAddress from "./methods/copyAddress";
import copySecret from "./methods/copySecret";
import signOut from "./methods/signOut";
import setPrompt from "./methods/setPrompt";

import { Prompter } from "@prompt/prompt";
```

Just one import from a library we should already have installed.

The other relative path imports are all the *events* and *methods* we’ll create here in a moment. For now, just generate all those files in their appropriate directories. Ensure your console is at the root of the `bantu-wallet` project before running this string of commands:

```bash
mkdir -p src/components/wallet/{events,methods}
touch src/components/wallet/events/{componentWillLoad.ts,render.tsx}
touch src/components/wallet/methods/{createAccount,copyAddress,copySecret,signOut,setPrompt}.ts
```

Next we have this funky line which may seem like an npm team import, but is actually a fancy typescript module alias path.

```typescript
import { Prompter } from "@prompt/prompt";
```

This allows us to avoid long error prone `../../../` paths and just use `@{alias}/{path?}/{module}`. In order to get this past both the linter and compiler we’ll need to modify a couple files.

First, modify the `tsconfig.json` file to include these values in the `compilerOptions` object.

```javascript
{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@prompt/*": ["components/prompt/*"],
      "@services/*": ["services/*"]
    },
    // ...
  },
  // ...
}
```

Next,modify the `package.json` file to include a `_moduleAliases` key at the root of the object.

```javascript
{
  // ...
  "_moduleAliases": {
    "@prompt": "dist/collection/components/prompt",
    "@services": "dist/collection/services"
  }
}
```

Finally, install the `module-alias` package and add it to the top of the `src/index.ts` file.

```bash
npm i -D module-alias
```

```typescript
import "module-alias/register";
export * from "./components";
```

Cool! With any luck we should be able to use these slick alias imports for the prompt and services directories now.

## Create Bantu Account Class

```typescript
interface BantuAccount {
  publicKey: string;
  keystore: string;
}
```

`interface` is just the TypeScript way of setting up a tidy typed class. `BantuAccount` will be our account class. It includes the `publicKey` for easy reference later in Horizon or Astrograph calls and the Top Secret `keystore` key containing the encrypted account secret cipher.

```javascript
@Component({
  tag: 'bantu-wallet',
  styleUrl: 'wallet.scss',
  shadow: true
})
export class Wallet {
  @State() account: BantuAccount
  @State() prompter: Prompter = {show: false}
  @State() error: any = null

  ...
}
```

Pretty standard boring bits, setting up the `@Component` with its defining values and initializing with some `@State` and `@Prop` data. You can see we’re setting up an `account` state with our `BantuAccount` class as well as a `prompter` state with that `Prompter` class from the `bantu-prompt` we imported earlier. We’re initializing that `prompter` state with a `show` value of `false` so the prompt modal rendereth not initially.

Everything after this is the assignment of our imported events and methods from up above. Let’s begin with the `./events/componentWillLoad.ts`

```typescript
import { handleError } from "@services/error";
import { get } from "@services/storage";

export default async function componentWillLoad() {
  try {
    let keystore = await get("keyStore");

    this.error = null;

    if (keystore) {
      keystore = atob(keystore);

      const { publicKey } = JSON.parse(atob(JSON.parse(keystore).adata));

      this.account = {
        publicKey,
        keystore,
      };
    }
  } catch (err) {
    this.error = handleError(err);
  }
}
```

`componentWillLoad` is the Stencil way of pre-filling the state and prop values before actually rendering the component. In our case we’ll use this method to populate the `account` `@State` with the saved storage `keyStore` value if there is one. At first there won’t be, so we’ll come back to this once we’ve actually gone over how to create and save accounts. For now just know it’s here, and since you’re smart, I imagine you can already kind of see how it works.

“But wait!” you say, “What are the `@services/error` and `@services/storage` packages?” Fine, yes, we should go over those. Remember the module alias stuff from earlier? Well one was for `@prompt` and the other was for `@services`. Go ahead and create these two files and add them to the `src/services` directory.

```bash
mkdir -p src/services
touch src/services/{error,storage}.ts
```

`error.ts` will look like this.

```typescript
import { get as loGet } from "lodash-es";

export function handleError(err: any) {
  return loGet(err, "response.data", loGet(err, "message", err));
}
```

Nothing fancy, just a clean little error handler we’ll make use of later when processing API requests.

## Set Up Key Storage

Next is `storage.ts`.

```javascript
import { Plugins } from "@capacitor/core";

const { Storage } = Plugins;

export async function set(key: string, value: any): Promise<void> {
  await Storage.set({
    key,
    value,
  });
}

export async function get(key: string): Promise<any> {
  const item = await Storage.get({ key });
  return item.value;
}

export async function remove(key: string): Promise<void> {
  await Storage.remove({ key });
}
```

You’ll notice a new package `@capacitor/core`. Let’s install and set that up.

```bash
# Install dependencies
npm i -D @capacitor/core @capacitor/cli

# Initialize Capacitor
npx cap init
```

```bash
? App name Bantu Wallet
? App Package ID (in Java package format, no dashes) com.wallet.stellar
? Which npm client would you like to use? npm
✔ Initializing Capacitor project in /Users/Desktop/Web/Clients/bantu/bantu-demo-wallet in 1.91ms


🎉   Your Capacitor project is ready to go!  🎉

Add platforms using "npx cap add":

  npx cap add android
  npx cap add ios
  npx cap add electron

Follow the Developer Workflow guide to get building:
https://capacitor.ionicframework.com/docs/basics/workflow
```

We’re not really making full use of [ Capacitor ](https://capacitor.ionicframework.com/), but it is an amazing service so be sure and check it out! For now we just need it to make storing and retrieving our data a bit more stable.

This storage service is simply a key setter and getter helper for storing and retrieving data. We’ll use this for any persistent data we want to store. For now, that's our Bantu account data.

## Set Up Event Handling

That’s everything we need for the `componentWillLoad` event. On to the `./events/render.tsx` file.

```typescript
import { h } from "@stencil/core";

export default function render() {
  return [
    <bantu-prompt prompter={this.prompter} />,
    this.account ? (
      [
        <div class="account-key">
          <p>{this.account.publicKey}</p>
          <button
            class="small"
            type="button"
            onClick={(e) => this.copyAddress(e)}
          >
            Copy Address
          </button>
          <button
            class="small"
            type="button"
            onClick={(e) => this.copySecret(e)}
          >
            Copy Secret
          </button>
        </div>,
      ]
    ) : (
      <button type="button" onClick={(e) => this.createAccount(e)}>
        Create Account
      </button>
    ),

    this.error ? (
      <pre class="error">{JSON.stringify(this.error, null, 2)}</pre>
    ) : null,
    this.account ? (
      <button type="button" onClick={(e) => this.signOut(e)}>
        Sign Out
      </button>
    ) : null,
  ];
}
```

It looks messy, but it’s actually a pretty simple `.tsx` file rendering out our DOM based off a series of conditional values. You can see we’re including the `bantu-prompt` component, and setting the prompter prop to our `this.prompter` state. We then have a ternary operation toggling between a Create Account button and a basic account UI. If `this.account` has a truthy value, we’ll print out the account’s `publicKey` along with some interaction buttons. If `this.account` is falsey, we’ll print out a singular Create Account button connected to, you guessed it, the `createAccount` method. After that logic, we print out an error if there is one, and finally a Sign Out button if there’s an account to sign out of. Those are the two `Wallet` `@Component` events.

## Create Methods

Let’s look at the methods now beginning with the `./methods/createAccount.ts` file.

```typescript
import sjcl from "@tinyanvil/sjcl";
import { Keypair } from "stellar-sdk";

import { handleError } from "@services/error";
import { set } from "@services/storage";

export default async function createAccount(e: Event) {
  try {
    e.preventDefault();

    const pincode_1 = await this.setPrompt("Enter a keystore pincode");
    const pincode_2 = await this.setPrompt("Enter keystore pincode again");

    if (!pincode_1 || !pincode_2 || pincode_1 !== pincode_2)
      throw "Invalid pincode";

    this.error = null;

    const keypair = Keypair.random();

    this.account = {
      publicKey: keypair.publicKey(),
      keystore: sjcl.encrypt(pincode_1, keypair.secret(), {
        adata: JSON.stringify({
          publicKey: keypair.publicKey(),
        }),
      }),
    };

    await set("keyStore", btoa(this.account.keystore));
  } catch (err) {
    this.error = handleError(err);
  }
}
```

Aha! Finally something interesting. This method forms the meat of our component. Before we dive into it, though let’s install the missing `@tinyanvil/sjcl` package.

```bash
npm i -D @tinyanvil/sjcl
```

## Create an Account

Essentially all we’re doing is making a request to create an account, which triggers the Prompt modal to ask for a pincode. That pincode will be used in the `sjcl.encrypt` method to encrypt the secret key from the `Keypair.random()` method. We set the `this.account` with the `publicKey`, which encrypted the `keystore` cipher, and now we're storing that cipher in base64 format in `localStorage` via our `set('keyStore')` method for easy retrieval when the browser reloads. We could also encode that cipher into a QR code or a link to share with other devices. Since it requires the pincode that encrypted cipher, it's as secure as the pincode you encrypt it with.

## Copy Address

Now that we’ve created an account, there are three more actions we'll enable: `copyAddress`, `copySecret`, and `signOut`.

First `./methods/copyAddress.ts`

```typescript
import copy from "copy-to-clipboard";

export default async function copyAddress(e: Event) {
  e.preventDefault();
  copy(this.account.publicKey);
}
```

Well there you go, the easiest code you’ll see all day. Just `copy` the `publicKey` from the `this.account` object to the clipboard. Before we jump though don’t forget to install that `copy-to-clipboard` package.

```bash
npm i -D copy-to-clipboard
```

## Copy Secret

Next `./methods/copySecret.ts`

```typescript
import sjcl from "@tinyanvil/sjcl";
import copy from "copy-to-clipboard";

import { handleError } from "@services/error";

export default async function copySecret(e: Event) {
  try {
    e.preventDefault();

    const pincode = await this.setPrompt("Enter your keystore pincode");

    if (!pincode) return;

    this.error = null;

    const secret = sjcl.decrypt(pincode, this.account.keystore);
    copy(secret);
  } catch (err) {
    this.error = handleError(err);
  }
}
```

You may not actually include this in your production wallet, but for now it's a simple demonstration of how to programmatically gain access to the secret key at a later date for making payments, creating trustlines, etc. It’s essentially the `createAccount` in reverse: it asks for the pincode to decrypt the keystore which, once decrypted, we `copy` into the clipboard.

## Sign Out

Finally `./methods/signOut.ts`

```typescript
import { remove } from "@services/storage";
import { handleError } from "@services/error";

export default async function signOut(e: Event) {
  try {
    e.preventDefault();

    const confirmNuke = await this.setPrompt(
      "Are you sure? This will nuke your account",
      "Enter NUKE to confirm",
    );

    if (!confirm || !/nuke/gi.test(confirmNuke)) return;

    this.error = null;

    await remove("keyStore");
    location.reload();
  } catch (err) {
    this.error = handleError(err);
  }
}
```

It’s important to allow users to nuke their account, but we need to be careful to confirm that action with our faithful `setPrompt`. Once they opt to “NUKE” the account we can remove the `keyStore` and reload the app.

## Set Prompt

Speaking of `setPrompt` the last method in our `wallet.ts` file is `./methods/setPrompt.ts`.

```typescript
export default function setPrompt(
  message: string,
  placeholder?: string,
  options?: Array<any>,
): Promise<string> {
  this.prompter = {
    ...this.prompter,
    show: true,
    message,
    placeholder,
    options,
  };

  return new Promise((resolve, reject) => {
    this.prompter.resolve = resolve;
    this.prompter.reject = reject;
  });
}
```

In `setPrompt`, we see how the prompt state is set, and how the Promise is set up to allow us to wait on the prompt whenever we call this method. It’s actually pretty slick, and it might be worth looking back at the `src/components/prompt/prompt.tsx` to see how the `resolve` and `reject` functions get called. It’s not central to our wallet creation, but it’s a pretty handy little component that will serve us well in the future as we continue to request input from the user.

That’s it folks! Restart the server with `npm start` and you’ve got a perfectly legitimate, minimal Bantu wallet key creation and storage Web Component! It's a solid foundation for a non-custodial wallet that relies on a simple pincode.


# Make XBN Payments

In this tutorial we’re going to modify our base wallet app to include functionality to send XBN to other Bantu accounts.

In the [Build a Basic Wallet section](/building-apps/basic-wallet), we did the hard work of wiring up a secure client and rock-solid key creation and storage structure with a clear plan for key use and management. Now that we have a method for creating an account — and for storing that account's secrets so they're safe and easy to use — we're ready to start making payments. We'll start with XBN payments since they're a little simpler; in the [next section](/building-apps/custom-assets), we'll look at how to support payments for other assets.

There isn’t too much that's new or complicated here: we'll be building on what we already have. Again, most of the work will be in `src/components/wallet/`; however, before we dive into that file let’s clean up our project just a touch and add some helpful polish. Namely a loader component.

## Add Loader Component

Hopefully by now you’re familiar with how to generate new Stencil components.

```bash
npm run generate
```

We'll call the component `bantu-loader`, and deselect both test files leaving only the styling. Once you've done that, open `src/components/loader/` and rename the `.css` file to `.scss`. Then replace the `loader.tsx` contents with this:

```typescript
import Combinatorics from "js-combinatorics";
import { Component, h, State, Prop } from "@stencil/core";
import { isEqual as loIsEqual, sample as loSample } from "lodash-es";

@Component({
  tag: "bantu-loader",
  styleUrl: "loader.scss",
  shadow: true,
})
export class Loader {
  @State() chances: any = [];
  @State() chance: any = null;
  @Prop() interval: any;

  componentWillLoad() {
    return new Promise((resolve) => {
      if (!this.chances.length) this.generateChances(9);

      if (!this.interval)
        this.interval = setInterval(() => this.getChance(), 100);

      resolve();
    });
  }

  generateChances(int: number) {
    const baseN = Combinatorics.baseN([0, 1], int);

    this.chances = baseN.toArray();
    this.getChance();
  }
  getChance() {
    const chance = loSample(this.chances);

    if (loIsEqual(chance, this.chance)) this.getChance();
    else this.chance = chance;
  }

  render() {
    return (
      <div class="loader">
        {this.chance.map((int, i) => (
          <div class={int ? "on" : null} key={`${int}${i}`}></div>
        ))}
      </div>
    );
  }
}
```

Don’t forget to install new packages!

```bash
npm i -D js-combinatorics
```

It’s a fancy wackadoodle little component which at the end of the day just produces a little loader for use in our action buttons.

## Import Methods

We’re now ready to tackle the `src/components/wallet/wallet.ts` file.

```typescript
import { Component, State, Prop } from "@stencil/core";
import { Server, ServerApi } from "stellar-sdk";

import componentWillLoad from "./events/componentWillLoad";
import render from "./events/render";

import createAccount from "./methods/createAccount";
import updateAccount from "./methods/updateAccount";
import makePayment from "./methods/makePayment";
import copyAddress from "./methods/copyAddress";
import copySecret from "./methods/copySecret";
import signOut from "./methods/signOut";
import setPrompt from "./methods/setPrompt";

import { Prompter } from "@prompt/prompt";

interface StellarAccount {
  publicKey: string;
  keystore: string;
  state?: ServerApi.AccountRecord;
}

interface Loading {
  fund?: boolean;
  pay?: boolean;
  update?: boolean;
}

@Component({
  tag: "bantu-wallet",
  styleUrl: "wallet.scss",
  shadow: true,
})
export class Wallet {
  @State() account: StellarAccount;
  @State() prompter: Prompter = { show: false };
  @State() loading: Loading = {};
  @State() error: any = null;

  @Prop() server: Server;

  // Component events
  componentWillLoad() {}
  render() {}

  // Bantu methods
  createAccount = createAccount;
  updateAccount = updateAccount;
  makePayment = makePayment;
  copyAddress = copyAddress;
  copySecret = copySecret;
  signOut = signOut;

  // Misc methods
  setPrompt = setPrompt;
}

Wallet.prototype.componentWillLoad = componentWillLoad;
Wallet.prototype.render = render;
```

If you’ve followed other tutorials in this series much of this may be review, but we’ll just walk along this file and see exactly what’s going on.

```typescript
import { Component, State, Prop } from "@stencil/core";
import { Server, ServerApi } from "stellar-sdk";

import componentWillLoad from "./events/componentWillLoad"; // UPDATE
import render from "./events/render"; // UPDATE

import createAccount from "./methods/createAccount"; // UPDATE
import updateAccount from "./methods/updateAccount"; // NEW
import makePayment from "./methods/makePayment"; // NEW
import copyAddress from "./methods/copyAddress";
import copySecret from "./methods/copySecret";
import signOut from "./methods/signOut";
import setPrompt from "./methods/setPrompt";

import { Prompter } from "@prompt/prompt";
```

Imports galore! Nothing really noteworthy here other than you’ll notice we’re importing `./methods/updateAccount` and `./methods/makePayment` methods which we’ll be creating soon. There are a number of updates in the other methods from previous tutorials, and we’ll walk through all of those in a moment as well.

```typescript
interface StellarAccount {
  // UPDATE
  publicKey: string;
  keystore: string;
  state?: ServerApi.AccountRecord;
}

interface Loading {
  // NEW
  fund?: boolean;
  pay?: boolean;
  update?: boolean;
}
```

Here we’re setting up two TypeScript classes: one for our account and the other for our loading states.

```typescript
@Component({
  tag: 'bantu-wallet',
  styleUrl: 'wallet.scss',
  shadow: true
})
export class Wallet {
  @State() account: StellarAccount
  @State() prompter: Prompter = {show: false}
  @State() loading: Loading = {} // NEW
  @State() error: any = null

  @Prop() server: Server // NEW

  ...
}
```

Here we set up our `@State`’s, those dynamic properties with values that will change and alter the DOM of the component, and our `@Prop`, which in this case will hold a static reference to our Bantu server instance.

## Update Component Events

Next let’s update our two component events `./events/componentWillLoad.ts` and `./events/render.tsx`:

```typescript
import { Server } from "stellar-sdk";
import { handleError } from "@services/error";
import { get } from "@services/storage";

export default async function componentWillLoad() {
  try {
    let keystore = await get("keyStore");

    this.error = null;
    this.server = new Server("https://expansion-testnet.bantu.network/");

    if (keystore) {
      keystore = atob(keystore);

      const { publicKey } = JSON.parse(atob(JSON.parse(keystore).adata));

      this.account = {
        publicKey,
        keystore,
      };

      this.updateAccount();
    }
  } catch (err) {
    this.error = handleError(err);
  }
}
```

In Stencil’s `componentWillLoad` method, we set up default values for the States and Props we initialized earlier. Most notably, we’re setting our server and account. You’ll notice we’re using the public `Expansion-testnet` for now — we're just learning, and not ready to send live XBN — but in production you’d want to change this to the public `Expansion` endpoint or, if you're running your own Expansion, to one of your own Expansion API endpoints.

For the account we’re simply checking to see if a `keyStore` value has been stored, and if so we’re grabbing the public key and keystore from it and adding those to the account `@State`. The `state` value, which is optional, is not set here as we’ll need to run the method `updateAccount()` to find if the account exists, and if so what the state of that account looks like. We’ll get to that method shortly. For now let’s update our `render.tsx` method:

```typescript
import { h } from "@stencil/core";
import { has as loHas } from "lodash-es";

export default function render() {
  return [
    <bantu-prompt prompter={this.prompter} />,

    this.account ? (
      [
        <div class="account-key">
          <p>{this.account.publicKey}</p>
          <button
            class="small"
            type="button"
            onClick={(e) => this.copyAddress(e)}
          >
            Copy Address
          </button>
          <button
            class="small"
            type="button"
            onClick={(e) => this.copySecret(e)}
          >
            Copy Secret
          </button>
        </div>,

        <button
          class={this.loading.pay ? "loading" : null}
          type="button"
          onClick={(e) => this.makePayment(e)}
        >
          {this.loading.pay ? <bantu-loader /> : null} Make Payment
        </button>,
      ]
    ) : (
      <button
        class={this.loading.fund ? "loading" : null}
        type="button"
        onClick={(e) => this.createAccount(e)}
      >
        {this.loading.fund ? <bantu-loader /> : null} Create Account
      </button>
    ),

    this.error ? (
      <pre class="error">{JSON.stringify(this.error, null, 2)}</pre>
    ) : null,

    loHas(this.account, "state") ? (
      <pre class="account-state">
        {JSON.stringify(this.account.state, null, 2)}
      </pre>
    ) : null,

    this.account
      ? [
          <button
            class={this.loading.update ? "loading" : null}
            type="button"
            onClick={(e) => this.updateAccount(e)}
          >
            {this.loading.update ? <bantu-loader /> : null} Update Account
          </button>,
          <button type="button" onClick={(e) => this.signOut(e)}>
            Sign Out
          </button>,
        ]
      : null,
  ];
}
```

Yikers amirite!? Don’t worry though: it’s actually quite simple if you’ve spent any time with HTML in JS before. What we're looking at are a few ternary operators toggling the UI between different states based on the account status. Basically just a bunch of buttons wired up to their subsequent actions. Turns out creating those buttons is next on our to-do list!

## Create Buttons

`updateAccount` and `makePayment` are new; `createAccount` will just need a few tweaks. Let’s start with that one.

```typescript
import sjcl from "@tinyanvil/sjcl";
import axios from "axios";
import { Keypair } from "stellar-sdk";

import { handleError } from "@services/error";
import { set } from "@services/storage";

export default async function createAccount(e: Event) {
  try {
    e.preventDefault();

    const pincode_1 = await this.setPrompt("Enter a keystore pincode");
    const pincode_2 = await this.setPrompt("Enter keystore pincode again");

    if (!pincode_1 || !pincode_2 || pincode_1 !== pincode_2)
      throw "Invalid pincode";

    this.error = null;
    this.loading = { ...this.loading, fund: true };

    const keypair = Keypair.random();

    await axios(
      `https://friendbot.dev.bantu.network?addr=${keypair.publicKey()}`,
    ).finally(() => (this.loading = { ...this.loading, fund: false }));

    this.account = {
      publicKey: keypair.publicKey(),
      keystore: sjcl.encrypt(pincode_1, keypair.secret(), {
        adata: JSON.stringify({
          publicKey: keypair.publicKey(),
        }),
      }),
    };

    await set("keyStore", btoa(this.account.keystore));

    this.updateAccount();
  } catch (err) {
    this.error = handleError(err);
  }
}
```

## Fund Account Using Friendbot

The only new thing we’re adding here — other than a loading state and an initial `this.updateAccount()` call at the end — is the call to [Friendbot](https://friendbot.dev.bantu.network), which is a testnet tool that we can use to automatically fund our new testnet account with 10,000 XBN. Nice little shortcut to kickstart our development.

```typescript
await axios(
  `https://friendbot.dev.bantu.network?addr=${keypair.publicKey()}`,
).finally(() => (this.loading = { ...this.loading, fund: false }));
```

In production, you would have to find an actual source for funding the account. Some wallets fund users' accounts for them; some require the user to supply the funds.

So that’s the updated `./methods/createAccount.ts` file.

## Update Account Method

Now let’s create two new files for updating the account and making XBN payments.

```bash
touch src/components/wallet/methods/{updateAccount,makePayment}.ts
```

Let’s start with the simpler one, `./methods/updateAccount.ts`

```typescript
import { omit as loOmit, map as loMap } from "lodash-es";

import { handleError } from "@services/error";

export default async function updateAccount(e?: Event) {
  try {
    if (e) e.preventDefault();

    this.error = null;
    this.loading = { ...this.loading, update: true };

    await this.server
      .accounts()
      .accountId(this.account.publicKey)
      .call()
      .then((account) => {
        account.balances = loMap(account.balances, (balance) =>
          loOmit(balance, [
            "limit",
            "buying_liabilities",
            "selling_liabilities",
            "is_authorized",
            "last_modified_ledger",
            balance.asset_type !== "native" ? "asset_type" : null,
          ]),
        );

        this.account = {
          ...this.account,
          state: loOmit(account, [
            "id",
            "_links",
            "sequence",
            "subentry_count",
            "last_modified_ledger",
            "flags",
            "thresholds",
            "account_id",
            "signers",
            "paging_token",
            "data_attr",
          ]),
        };
      })
      .finally(() => (this.loading = { ...this.loading, update: false }));
  } catch (err) {
    this.error = handleError(err);
  }
}
```

All we’re doing here is looking up the state of the Bantu account on the ledger and saving it to the `this.account.state`. You’ll also notice we’re omitting several fields from the account and balances for easier readability. You may choose to save these and selectively display the values you care about, but in our example we’re just displaying the raw JSON, so cleaning things up a little is the right move.

`this.account = {...this.account, state: loOmit(account, ['id', ...])}` may feel odd, but it’s just the Stencil way of updating a state’s object key to trigger a re-render of the DOM. You’ll notice `this.loading` follows the same pattern. We’ll make use of this data further down in the `render` method, but for now just know this is how we would grab ahold of the account to get the latest state.

## Make Payment Method

Next let’s break down the main subject method for this tutorial, `./methods/makePayment.ts`:

```typescript
import sjcl from "@tinyanvil/sjcl";
import {
  Keypair,
  Account,
  TransactionBuilder,
  BASE_FEE,
  Networks,
  Operation,
  Asset,
} from "stellar-sdk";
import { has as loHas } from "lodash-es";

import { handleError } from "@services/error";

export default async function makePayment(e: Event) {
  try {
    e.preventDefault();

    let instructions = await this.setPrompt("{Amount} {Destination}");
    instructions = instructions.split(" ");

    const pincode = await this.setPrompt("Enter your keystore pincode");

    if (!instructions || !pincode) return;

    const keypair = Keypair.fromSecret(
      sjcl.decrypt(pincode, this.account.keystore),
    );

    this.error = null;
    this.loading = { ...this.loading, pay: true };

    await this.server
      .accounts()
      .accountId(keypair.publicKey())
      .call()
      .then(({ sequence }) => {
        const account = new Account(keypair.publicKey(), sequence);
        const transaction = new TransactionBuilder(account, {
          fee: BASE_FEE,
          networkPassphrase: Networks.TESTNET,
        })
          .addOperation(
            Operation.payment({
              destination: instructions[1],
              asset: Asset.native(),
              amount: instructions[0],
            }),
          )
          .setTimeout(0)
          .build();

        transaction.sign(keypair);
        return this.server.submitTransaction(transaction).catch((err) => {
          if (
            // Paying an account which doesn't exist, create it instead
            loHas(err, "response.data.extras.result_codes.operations") &&
            err.response.data.status === 400 &&
            err.response.data.extras.result_codes.operations.indexOf(
              "op_no_destination",
            ) !== -1
          ) {
            const transaction = new TransactionBuilder(account, {
              fee: BASE_FEE,
              networkPassphrase: Networks.TESTNET,
            })
              .addOperation(
                Operation.createAccount({
                  destination: instructions[1],
                  startingBalance: instructions[0],
                }),
              )
              .setTimeout(0)
              .build();

            transaction.sign(keypair);
            return this.server.submitTransaction(transaction);
          } else throw err;
        });
      })
      .then((res) => console.log(res))
      .finally(() => {
        this.loading = { ...this.loading, pay: false };
        this.updateAccount();
      });
  } catch (err) {
    this.error = handleError(err);
  }
}
```

This method is quite massive, so let’s break it down further so it's easier to understand exactly what is going on.

```typescript
export default async function makePayment(e: Event) {
  try {
    e.preventDefault()

    let instructions = await this.setPrompt('{Amount} {Destination}')
        instructions = instructions.split(' ')

    const pincode = await this.setPrompt('Enter your keystore pincode')

    if (
      !instructions
      || !pincode
    ) return
```

We’re going to need a couple pieces of info from the user: the amount of XBN to send, what address to send it to, and the pincode for unlocking the keystore file. We request those asynchronously and cancel out of the method if they aren’t provided.

```typescript
const keypair = Keypair.fromSecret(
  sjcl.decrypt(pincode, this.account.keystore),
);

this.error = null;
this.loading = { ...this.loading, pay: true };
```

Next we unpack the keystore with the pincode, reset any existing errors, and trigger the `pay` loading boolean:

```typescript
  await this.server
  .accounts()
  .accountId(keypair.publicKey())
  .call()
  .then(({sequence}) => {
    const account = new Account(keypair.publicKey(), sequence)
    const transaction = new TransactionBuilder(account, {
      fee: BASE_FEE,
      networkPassphrase: Networks.TESTNET
    })
    .addOperation(Operation.payment({
      destination: instructions[1],
      asset: Asset.native(),
      amount: instructions[0]
    }))
    .setTimeout(0)
    .build()

    transaction.sign(keypair)
    return this.server.submitTransaction(transaction)
```

From there we call the keypair account to retrieve its current sequence number so we can prepare a transaction with a payment operation. We set the destination and amount using the instructions from the prompt we collected and split earlier. Finally, we build, sign, and submit that transaction to the Bantu Expansion API server.

```typescript
  .catch((err) => {
    if ( // Paying an account which doesn't exist, create it instead
      loHas(err, 'response.data.extras.result_codes.operations')
      && err.response.data.status === 400
      && err.response.data.extras.result_codes.operations.indexOf('op_no_destination') !== -1
    ) {
      const transaction = new TransactionBuilder(account, {
        fee: BASE_FEE,
        networkPassphrase: Networks.TESTNET
      })
      .addOperation(Operation.createAccount({
        destination: instructions[1],
        startingBalance: instructions[0]
      }))
      .setTimeout(0)
      .build()

      transaction.sign(keypair)
      return this.server.submitTransaction(transaction)
    }

    else throw err
  })
```

If the account we want to send XBN is unfunded (and therefore doesn't yet exist on the ledger), we'll get back `op_no_destination`. This `catch` handles that issue by trying again with a `createAccount` operation. For any other issues we just pass the error on unmodified.

```typescript
    })
    .then((res) => console.log(res))
    .finally(() => {
      this.loading = {...this.loading, pay: false}
      this.updateAccount()
    })
  }

  catch (err) {
    this.error = handleError(err)
  }
}
```

Finally, we log any success transaction, kill the loader, and `updateAccount` to reflect the new balance in our account after successfully sending XBN. We also have our `catch` block that passes to the `handleError` service. We will render that in a nice error block in the UI.

There we go! That wasn’t so bad right? Pretty simple, and yet from this tutorial we have the power to hold and observe balances, and to make payments using the power of the Bantu ledger. Amazing!


# Handle Custom Assets

In this section of the tutorial, we'll add the ability to hold and transfer custom assets to the basic wallet we built in previous sections. It assumes that you've already completed Create a Basic Wallet and Make XBN Payments

## What's a Custom Asset?

Bantu allows anyone to easily issue an asset, and all assets can be held, transferred, and traded just like XBN, the network token. Every asset *other* than XBN exists on the network in the form of trustlines: an asset holder explicitly agrees to allow a balance of a specific token issued by a specific issuing account by creating a persistent ledger entry tied to the holding account. You can find out more in the guide to [creating custom assets](/issuing-assets/index).

Each trustline increases the user's [base reserve](/glossary/minimum-balance) by 0.5 XBN, and in this tutorial, we'll go over how to set up your wallet to create trustlines and manage the base reserve on behalf of a user.

## Add Trustlines Button

To enable custom asset handling, we need to modify three files and create one new one. Let’s start with our modifications. First up the `./events/render.tsx` file. We need to add a button for creating these new trustlines!

```typescript
import { h } from "@stencil/core";
import { has as loHas } from "lodash-es";

export default function render() {
  return [
    <stellar-prompt prompter={this.prompter} />,

    this.account ? (
      [
        <div class="account-key">
          <p>{this.account.publicKey}</p>
          <button
            class="small"
            type="button"
            onClick={(e) => this.copyAddress(e)}
          >
            Copy Address
          </button>
          <button
            class="small"
            type="button"
            onClick={(e) => this.copySecret(e)}
          >
            Copy Secret
          </button>
        </div>,

        <button
          class={this.loading.trust ? "loading" : null}
          type="button"
          onClick={(e) => this.trustAsset(e)}
        >
          {this.loading.trust ? <bantu-loader /> : null} Trust Asset
        </button>,
        <button
          class={this.loading.pay ? "loading" : null}
          type="button"
          onClick={(e) => this.makePayment(e)}
        >
          {this.loading.pay ? <bantu-loader /> : null} Make Payment
        </button>,
      ]
    ) : (
      <button
        class={this.loading.fund ? "loading" : null}
        type="button"
        onClick={(e) => this.createAccount(e)}
      >
        {this.loading.fund ? <bantu-loader /> : null} Create Account
      </button>
    ),

    this.error ? (
      <pre class="error">{JSON.stringify(this.error, null, 2)}</pre>
    ) : null,

    loHas(this.account, "state") ? (
      <pre class="account-state">
        {JSON.stringify(this.account.state, null, 2)}
      </pre>
    ) : null,

    this.account
      ? [
          <button
            class={this.loading.update ? "loading" : null}
            type="button"
            onClick={(e) => this.updateAccount(e)}
          >
            {this.loading.update ? <bantu-loader /> : null} Update Account
          </button>,
          <button type="button" onClick={(e) => this.signOut(e)}>
            Sign Out
          </button>,
        ]
      : null,
  ];
}
```

If you look closely you’ll spot the Trust Asset button right below our `account-key` div. Nothing funky here, just a button that triggers `this.trustAsset` method which we’ll add in a moment.

Next up, let’s update the `./methods/makePayment.ts` file.

```typescript
import sjcl from "@tinyanvil/sjcl";
import {
  Keypair,
  Account,
  TransactionBuilder,
  BASE_FEE,
  Networks,
  Operation,
  Asset,
} from "stellar-sdk";
import { has as loHas } from "lodash-es";

import { handleError } from "@services/error";

export default async function makePayment(e: Event) {
  try {
    e.preventDefault();

    let instructions = await this.setPrompt("{Amount} {Asset} {Destination}");
    instructions = instructions.split(" ");

    if (!/xlm/gi.test(instructions[1]))
      instructions[3] = await this.setPrompt(
        `Who issues the ${instructions[1]} asset?`,
        "Enter ME to refer to yourself",
      );

    const pincode = await this.setPrompt("Enter your keystore pincode");

    if (!instructions || !pincode) return;

    const keypair = Keypair.fromSecret(
      sjcl.decrypt(pincode, this.account.keystore),
    );

    if (/me/gi.test(instructions[3])) instructions[3] = keypair.publicKey();

    this.error = null;
    this.loading = { ...this.loading, pay: true };

    await this.server
      .accounts()
      .accountId(keypair.publicKey())
      .call()
      .then(({ sequence }) => {
        const account = new Account(keypair.publicKey(), sequence);
        const transaction = new TransactionBuilder(account, {
          fee: BASE_FEE,
          networkPassphrase: Networks.TESTNET,
        })
          .addOperation(
            Operation.payment({
              destination: instructions[2],
              asset: instructions[3]
                ? new Asset(instructions[1], instructions[3])
                : Asset.native(),
              amount: instructions[0],
            }),
          )
          .setTimeout(0)
          .build();

        transaction.sign(keypair);
        return this.server.submitTransaction(transaction).catch((err) => {
          if (
            // Paying an account which doesn't exist, create it instead
            loHas(err, "response.data.extras.result_codes.operations") &&
            err.response.data.status === 400 &&
            err.response.data.extras.result_codes.operations.indexOf(
              "op_no_destination",
            ) !== -1 &&
            !instructions[3]
          ) {
            const transaction = new TransactionBuilder(account, {
              fee: BASE_FEE,
              networkPassphrase: Networks.TESTNET,
            })
              .addOperation(
                Operation.createAccount({
                  destination: instructions[2],
                  startingBalance: instructions[0],
                }),
              )
              .setTimeout(0)
              .build();

            transaction.sign(keypair);
            return this.server.submitTransaction(transaction);
          } else throw err;
        });
      })
      .then((res) => console.log(res))
      .finally(() => {
        this.loading = { ...this.loading, pay: false };
        this.updateAccount();
      });
  } catch (err) {
    this.error = handleError(err);
  }
}
```

This is a big file that was covered in great detail in the [Make XBN Payments](/building-apps/xbn-payments) tutorial, so we’ll just focus on the changes we need to make to support custom asset payments.

```typescript
let instructions = await this.setPrompt("{Amount} {Asset} {Destination}");
instructions = instructions.split(" ");

if (!/xlm/gi.test(instructions[1]))
  instructions[3] = await this.setPrompt(
    `Who issues the ${instructions[1]} asset?`,
    "Enter ME to refer to yourself",
  );
```

This change allows us to indicate a specific asset code we’d like use to make a payment and triggers an additional prompt to set the issuer for that asset if it’s not the native XBN.

```typescript
if (/me/gi.test(instructions[3])) instructions[3] = keypair.publicKey();
```

This is just a nifty little helper shortcut to allow us to use the `ME` “issuer” to swap with our actual account publicKey. Niceties make the world go ‘round.

```typescript
asset: instructions[3] ? new Asset(instructions[1], instructions[3]) : Asset.native(),
```

The final noteworthy change is a ternary operation that switches our payment asset between the native XBN and a custom asset based off of responses to our prompt. Essentially, if `instructions[3]` exists — meaning there is an issuer — use that issuer and custom token as the asset for the payment. Otherwise, just use the native `Asset`.

The final changes are in the `wallet.ts` itself and tie together all the other updates as well as pull in the new `trustAsset` method.

```typescript
import { Component, State, Prop } from "@stencil/core";
import { Server, ServerApi } from "stellar-sdk";

import componentWillLoad from "./events/componentWillLoad";
import render from "./events/render";

import createAccount from "./methods/createAccount";
import updateAccount from "./methods/updateAccount";
import trustAsset from "./methods/trustAsset"; // NEW
import makePayment from "./methods/makePayment"; // UPDATE
import copyAddress from "./methods/copyAddress";
import copySecret from "./methods/copySecret";
import signOut from "./methods/signOut";
import setPrompt from "./methods/setPrompt";

import { Prompter } from "@prompt/prompt";

interface StellarAccount {
  publicKey: string;
  keystore: string;
  state?: ServerApi.AccountRecord;
}

interface Loading {
  // UPDATE
  fund?: boolean;
  pay?: boolean;
  trust?: boolean; // NEW
  update?: boolean;
}

@Component({
  tag: "stellar-wallet",
  styleUrl: "wallet.scss",
  shadow: true,
})
export class Wallet {
  @State() account: StellarAccount;
  @State() prompter: Prompter = { show: false };
  @State() loading: Loading = {};
  @State() error: any = null;

  @Prop() server: Server;

  // Component events
  componentWillLoad() {}
  render() {}

  // Batun methods
  createAccount = createAccount;
  updateAccount = updateAccount;
  trustAsset = trustAsset; // NEW
  makePayment = makePayment; // UPDATE
  copyAddress = copyAddress;
  copySecret = copySecret;
  signOut = signOut;

  // Misc methods
  setPrompt = setPrompt;
}

Wallet.prototype.componentWillLoad = componentWillLoad;
Wallet.prototype.render = render;
```

Only thing worth seeing here besides the inclusion of the new `trustAsset` method is the addition of the `trust?: boolean,` in the `Loading` class.

## Add Trustlines

Alright so, finally we get to the `./methods/trustAsset.ts` file!

```typescript
import sjcl from "@tinyanvil/sjcl";
import {
  Keypair,
  Account,
  TransactionBuilder,
  BASE_FEE,
  Networks,
  Operation,
  Asset,
} from "stellar-sdk";

import { handleError } from "@services/error";

export default async function trustAsset(
  e?: Event,
  asset?: string,
  issuer?: string,
  pincode?: string,
) {
  try {
    if (e) e.preventDefault();

    let instructions;

    if (asset && issuer) instructions = [asset, issuer];
    else {
      instructions = await this.setPrompt("{Asset} {Issuer}");
      instructions = instructions.split(" ");
    }

    pincode = pincode || (await this.setPrompt("Enter your keystore pincode"));

    if (!instructions || !pincode) return;

    const keypair = Keypair.fromSecret(
      sjcl.decrypt(pincode, this.account.keystore),
    );

    this.error = null;
    this.loading = { ...this.loading, trust: true };

    await this.server
      .accounts()
      .accountId(keypair.publicKey())
      .call()
      .then(({ sequence }) => {
        const account = new Account(keypair.publicKey(), sequence);
        const transaction = new TransactionBuilder(account, {
          fee: BASE_FEE,
          networkPassphrase: Networks.TESTNET,
        })
          .addOperation(
            Operation.changeTrust({
              asset: new Asset(instructions[0], instructions[1]),
            }),
          )
          .setTimeout(0)
          .build();

        transaction.sign(keypair);
        return this.server.submitTransaction(transaction);
      })
      .then((res) => console.log(res))
      .finally(() => {
        this.loading = { ...this.loading, trust: false };
        this.updateAccount();
      });
  } catch (err) {
    this.error = handleError(err);
  }
}
```

This is similar to the `makePayment` method but there are a couple tiny tweaks worth noting:

```typescript
export default async function trustAsset(
  e?: Event,
  asset?: string,
  issuer?: string,
  pincode?: string
) {
  try {
    if (e)
      e.preventDefault()

    let instructions

    if (
      asset
      && issuer
    ) instructions = [asset, issuer]

    else {
      instructions = await this.setPrompt('{Asset} {Issuer}')
      instructions = instructions.split(' ')
    }

    pincode = pincode || await this.setPrompt('Enter your keystore pincode')
```

We’re allowing the inclusion of several arguments in this function, namely `asset`, `issuer`, and `pincode`. We won’t be making use of them here, but transparently creating trustlines from within other functions will prove useful later.

If we have any of those variables set, we can “preload” our interface a bit, and even bypass user input altogether if a pincode is provided. Again, not something we’ll make use of quite yet, but once we look into depositing and withdrawing assets from an Anchor or accepting incoming payments for which we don’t yet have a trustline this functionality will prove useful.

So there we have it! The ability to accept and pay with custom assets on Bantu!


# Run a Core Node


# index

Bantu is a peer-to-peer network made up of nodes, which are computers that keep a common distributed [ledger](/glossary/ledger), and that communicate to validate and add [transactions](/glossary/transactions) to it. Nodes use a program called Bantu Core — an implementation of the [Bantu Consensus Protocol](/glossary/scp) — to stay in sync as they work to agree on the validity of transaction sets and to apply them to the ledger. Generally, nodes reach consensus, apply a transaction set, and update the ledger every 3-5 seconds.

You don’t need to run a node to build on Bantu: you can start developing with your [SDK of choice](/software-and-sdks/index), and use public instances of Horizon to query the ledger and submit transactions right away. In fact, the Bantu Development Foundation offers two public instances of Horizon — one for the public network and one for the testnet — which you can read more about in our [API reference docs](/api/introduction/index).

If you’re serious about building on Bantu, have a production-level product or service that requires high-availability access network, or want to help increase network health and decentralization, then you probably *do* want to run a node, or even a trio of nodes (more on that in the [Tier 1 section](/run-core-node/tier-1-orgs)).

If you’re going the DIY route, this section of the docs is for you. It explains the technical and operational aspects of installing, configuring, and maintaining a Bantu Core node, and should help you figure out the best way to set up your Bantu integration.

The basic flow, which you can navigate through using the menu on the left, goes like this:

* Choose which type of node you want to run
* Prepare Your Environment
* Install Bantu Core
* Configure Bantu Core
* Join the network
* Monitor and maintain your node
* Join the validators channels to stay on top of critical upgrades and network votes

## Types of nodes

All nodes perform the same basic functions: they run Bantu Core, connect to peers, submit transactions, store the state of the ledger in a SQL [database](/run-core-node/configuring#database), and keep a duplicate copy of the ledger in flat XDR files called [buckets](/run-core-node/configuring#buckets). All nodes also support [Horizon](/run-api-server/index), the Bantu API.

In addition to those basic functions, there are two key configuration options that determine how a node behaves. A node can:

* Participate in consensus to [validate transactions](/run-core-node/configuring#validating)
* Publish an [archive](/run-core-node/publishing-history-archives) that other nodes can consult to find the complete history of the network.

To make things easier, we’ll define four types of nodes based on permutations of those two options: **Watcher**, **Basic Validator**, **Full Validator**, and **Archiver**. You’ll notice that they *all* support Horizon and submit transactions to the network:

| Type of Node        | Supports Horizon | Submits Transactions | Validates Transactions | Publishes History |
| ------------------- | ---------------- | -------------------- | ---------------------- | ----------------- |
| **Watcher**         | ✅                | ✅                    |                        |                   |
| **Basic Validator** | ✅                | ✅                    | ✅                      |                   |
| **Full Validator**  | ✅                | ✅                    | ✅                      | ✅                 |
| **Archiver**        | ✅                | ✅                    |                        | ✅                 |

So why choose one type over another? Let’s break it down a bit and take a look at what each type is good for.

### Watcher

#### Non-validating, no public archive

A Watcher is the lightest node you can run. It keeps track of the ledger and submits transactions for possible inclusion, but it is *not* configured to participate in validation or to publish a history archive, which means it doesn’t do anything to support the network or increase decentralization.

Watchers pair well with Horizon, and if all you need is a Horizon instance to query the ledger or submit transactions, a Watcher is probably the right choice for you. While there are public instances of Horizon you can use — namely those maintained by [SDF](/api/introduction/index), [Lobstr](https://horizon.Bantu.lobstr.co), [Satoshipay](https://Bantu-horizon.satoshipay.io), and [Coinqvest](https://horizon.Bantu.coinqvest.com) — they’re all rate limited, so they won’t work if you need to scale your project or you want to offer your customers an SLA — 99% uptime, for instance.

**Use a Watcher to run Horizon, and to ensure reliable access to the network.**

### Basic Validator

#### Validating, no public archive

A Basic Validator is a lot like a Watcher, and has the same advantages and similar operational requirements. The difference between the two is that a Basic Validator requires a secret key, and is [configured to participate in consensus](/run-core-node/configuring#validating) by voting on — and signing off on — changes to the ledger.

The advantage: signatures can serve as official endorsements of specific ledgers in real time. That’s important if, for instance, you issue an asset on Bantu that represents a real-world asset: you can let your customers know that you will only honor transactions and redeem assets from ledgers signed by your validator, and in the unlikely scenario that something happens to the network, you can use your node as the final arbiter of truth. Setting up your node as a validator allows you to resolve any questions *up front and in writing* about how you plan to deal with disasters and disputes.

**Use a Basic Validator to run Horizon, ensure reliable access to the network, and sign off on transactions.**

### Full Validator

#### Validating, offers public archive

A Full Validator is the same as a Basic Validator except that it also publishes a [History Archive](/run-core-node/publishing-history-archives) containing snapshots of the ledger, including all transactions and their results. A Full Validator writes to an internet-facing blob store — such as AWS or Azure — so it's a bit more expensive and complex to run, but it also does the most to support the network’s resilience and decentralization.

When other nodes join the network — or experience difficulty and temporarily fall out of sync — they can consult archives offered by Full Validators to catch up on the history of the network. Redundant archives prevent a single point of failure, and allow network participants to verify the veracity of a given history.

Full Validators can support Horizon, but generally, organizations that run them don’t use them to query network data or submit transactions. In fact, they often run a Watcher to handle Horizon *in addition* to Full Validators. Most of those organizations are also part of — or on track to join — [Tier 1](/run-core-node/tier-1-orgs), which is a core group of network participants who run three Full Validators to contribute maximum redundancy.

**Use a Full Validator to sign off on transactions, and to contribute to the health and decentralization of the network.**

### Archiver

#### Non-validating, offers public archive

An Archiver is a rare bird: like a Full Validator, it publishes the activity of the network in long-term storage; unlike a Full Validator, it does not participate in consensus.

Archivers help with decentralization a bit by offering redundant accounts of the network’s history, but they don’t vote or sign ledgers, so their usefulness is fairly limited. If you run a Bantu-facing service, like a blockchain explorer, you may want to run one. Otherwise, you’re probably better off choosing one of the other three types.

**Use an archiver if you want to referee the network. Which is unlikely.**


# running-node

```javascript
import { CodeExample } from "components/CodeExample";
```

## Starting Bantu Core

Once you've [set up your environment](/run-core-node/prerequisites), [configured your node](/run-core-node/configuring), set up your [quorum set](/run-core-node/configuring#choosing-your-quorum-set), and selected archives to `get` [history from](/run-core-node/configuring#history), you're ready to start Bantu Core.

Use a command equivalent to:

`$ bantu-core run`

At this point, you're ready to observe your node's activity as it joins the network.

You may want to skip ahead and review the [logging](/run-core-node/running-node#logging) section to familiarize yourself with Bantu Core's output.

## Interacting With Your Instance

When your node is running, you can interact with Bantu Core via an administrative HTTP endpoint. Commands can be submitted using command-line HTTP tools such as `curl`, or by running a command such as

`$ bantu-core http-command <http-command>`

That HTTP endpoint is not intended to be exposed to the public internet. It's typically accessed by administrators, or by a mid-tier application to submit transactions to the Bantu network.

See [commands](/run-core-node/commands) for a description of the available commands.

## Joining the Network

Your node will go through the following phases as it joins the network:

### Establishing Connection to Other Peers.

You should see `authenticated_count` increase.

```javascript
"peers" : { "authenticated_count" : 3, "pending_count" : 4 }
```

### Observing Consensus

Until the node sees a quorum, it will say:

```javascript
"state" : "Joining SCP"
```

After observing consensus, a new field `quorum` will display information about network decisions. At this point the node will switch to "*Catching up*":

```javascript
"quorum" : { 
    "qset" : { 
        "ledger" : 22267866, 
        "agree" : 5, 
        "delayed" : 0, 
        "disagree" : 0, 
        "fail_at" : 3, 
        "hash" : "980a24", 
        "missing" : 0, 
        "phase" : "EXTERNALIZE" 
    }, 
    "transitive" : { 
        "intersection" : true, 
        "last_check_ledger" : 22267866, 
        "node_count" : 21 
    }
}, 
"state" : "Catching up"
```

### Catching up

This is a phase where the node downloads data from archives. The state will start with something like:

```javascript
"state" : "Catching up", 
"status" : [ "Catching up: Awaiting checkpoint (ETA: 35 seconds)" ] 
```

And then go through the various phases of downloading and applying state such as

```javascript
"state" : "Catching up", 
"status" : [ "Catching up: downloading ledger files 20094/119803 (16%)" ] 
```

You can specify how far back your node goes to catch up in your config file. If you set`CATCHUP_COMPLETE` to `true`, your node will replay the entire history of the network, which can take a long time. Weeks. SatoshiPay offers a [parallel catchup script](https://github.com/satoshipay/stellar-core-parallel-catchup) to speed up the process, but you only need to replay the complete network history if you're setting up a Full Validator. Otherwise, you can specify a starting point for catchup using `CATCHUP_RECENT`. See the [complete example configuration](https://github.com/stellar/stellar-core/blob/master/docs/stellar-core_example.cfg) for more details.

### Synced

When the node is done catching up, its state will change to:

```javascript
"state" : "Synced!"
```

## Logging

Bantu Core sends logs to standard output and `bantu-core.log` by default, configurable as `LOG_FILE_PATH`.

Log messages are classified by progressive *priority levels*:`TRACE`, `DEBUG`, `INFO`, `WARNING`, `ERROR` and `FATAL`. The logging system only emits those messages at or above its configured logging level.

The log level can be controlled by configuration, the `-ll` command-line flag, or adjusted dynamically by administrative (HTTP) commands. To do so, run:

`$ bantu-core http-command "ll?level=debug"`

while your system is running.

Log levels can also be adjusted on a partition-by-partition basis through the administrative interface. For example, the history system can be set to DEBUG-level logging by running:

`$ bantu-core http-command "ll?level=debug&partition=history"`

Against a running system.

The default log level is `INFO`, which is moderately verbose and should emit progress messages every few seconds under normal operation.

## Validator maintenance

Maintenance here refers to anything involving taking your validator temporarily out of the network (to apply security patches, system upgrade, etc).

As an administrator of a validator, you must ensure that the maintenance you are about to apply to the validator is safe for the overall network and for your validator.

Safe means that the other validators that depend on yours will not be affected too much when you turn off your validator for maintenance and that your validator will continue to operate as part of the network when it comes back up.

If you are changing some settings that may impact network-wide settings such as protocol version, review [the section on network configuration](/run-core-node/running-node#network-configuration).

If you're changing your quorum set configuration, also read the [section on what to do](/run-core-node/running-node#special-considerations-during-quorum-set-updates).

### Recommended steps to perform as part of a maintenance

We recommend performing the following steps in order (repeat sequentially as needed if you run multiple nodes).

1. Advertise your intention to others that may depend on you. Some coordination is required to avoid situations where too many nodes go down at the same time.
2. Dependencies should assess the health of their quorum, refer to the section "Understanding quorum and reliability".
3. If there is no objection, take your instance down
4. When done, start your instance that should rejoin the network
5. The instance will be completely caught up when it's both `Synced` and *there is no backlog in uploading history*.

#### Special considerations during quorum set updates

Sometimes an organization needs to make changes that impact other's quorum sets:

* taking a validator down for a long period of time
* adding new validators to their pool

In both cases, it's crucial to stage the changes to preserve quorum intersection and general good health of the network:

* removing too many nodes from your quorum set *before* the nodes are taken down: if different people remove different sets the remaining sets may not overlap between nodes and may cause network splits
* adding too many nodes in your quorum set at the same time: if not done carefully can cause those nodes to overpower your configuration

Recommended steps are for the entity that adds/removes nodes to do so first between their own nodes, and then have people reflect those changes gradually (over several rounds) in their quorum configuration.


# network-upgrades

```javascript
import { CodeExample } from "components/CodeExample";
```

The network itself has network-wide settings that can be updated.

This is performed by validators voting for and agreeing to new values the same way that consensus is reached for transaction sets, etc.

A node can be configured to vote for upgrades using the `upgrades` endpoint . See [Commands](/run-core-node/commands) for more information.

The network settings are:

* the version of the protocol used to process transactions
* the maximum number of transactions that can be included in a given ledger close
* the cost (fee) associated with processing operations
* the base reserve used to calculate the lumen balance needed to store things in the ledger

When the network time is later than the `upgradetime` specified in the upgrade settings, the validator will vote to update the network to the value specified in the upgrade setting. If the network time is passed the `upgradetime` by more than 12 hours, the upgrade will be ignored

When a validator is armed to change network values, the output of `info` will contain information about the vote.

For a new value to be adopted, the same level of consensus between nodes needs to be reached as for transaction sets.

## Important notes on network wide settings

Changes to network wide settings have to be orchestrated properly between validators as well as non validating nodes:

* a change is vetted between operators (changes can be bundled)
* an effective date in the future is picked for the change to take effect (controlled by `upgradetime`)
* if applicable, communication is sent out to all network users

An improper plan may cause issues such as:

* nodes missing consensus (aka "getting stuck"), and having to use history to rejoin
* network reconfiguration taking effect at a non deterministic time (causing fees to change ahead of schedule for example)

For more information look at [Network Upgrade](/run-core-node/network-upgrades).

## Example upgrade command

Example here is to upgrade the protocol version to version 9 on January-31-2018.

1. `$ bantu-core http-command 'upgrades?mode=set&upgradetime=2018-01-31T20:00:00Z&protocolversion=9'`
2. `$ bantu-core http-command info`

At this point `info` will tell you that the node is setup to vote for this upgrade:

```javascript
"status" : [ 
    "Armed with network upgrades: upgradetime=2018-01-31T20:00:00Z, protocolversion=9" 
    ]
```


# tier-1-orgs

To help with Bantu's decentralization, the most reliable and advanced Bantu teams join the ranks of “Tier 1 Organizations.” These organizations run three validators, coordinate any changes to their quorum sets, and hold themselves to a higher standard of uptime and responsiveness.

SDF works closely with Tier One Orgs to ensure the health of the network, maintain good quorum intersection, and build in redundancy to minimize network disruptions. This guide outlines what it takes to be a Tier 1 Org.

## Why Three Validators

The most important function of a Tier 1 Org is to set up and maintain three Full Validators. Why three?

On Bantu, validators choose to trust organizations when they build a quorum set. If you are a trustworthy organization, you want your presence on the network to persist even if a node fails or you take it down for maintenance. A trio of validating nodes allows that to happen: other participants can create a quorum slice for your organization that requires ⅔ of your validating nodes to agree. If 1 has issues, no big deal: the other two still vote on your organization’s behalf, so the show goes on. To ensure redundancy, it's also important that those three Full Validators are geographically dispersed: if they're in the same data center, they run the risk of going down at the same time.

Here’s what else Tier 1 Orgs expect of one another:

## Publish History Archives

In addition to participating in SCP, a full validator publishes an archive of network transactions. To do that, you need to configure Bantu Core to record history to a publicly accessible archive and add the location of that archive to your bantu.toml. To be a Tier 1 Org, you should set each of your nodes to record history to a separate archive.

Public archives make the network more resilient: when new nodes come online, or when existing nodes lose synch, they need to consult an archive to figure out what they missed. Sharing snapshots of the ledger, which detail transactions and their results, allows those nodes to catch up, and more archives mean more redundancy and greater decentralization. Plus, sharing history keeps everyone honest.

## Set Up a Safe Quorum Set

To maximize network resilience, we’re asking every Tier 1 node to use the same quorum set configuration, which is made up of sub-quorums of all validators from each Tier 1 Org.

That way, the validator community can experiment with a larger quorum and can analyze the results of those experiments without disrupting the network. Using existing Tier 1 Orgs as a safety net, we can work together to expand the quorum methodically and deliberately. To see what that quorum set currently looks like, check out the [example Full Validator config file](https://github.com/stellar/packages/blob/master/docs/examples/pubnet-validator-full/stellar-core.cfg).

## Declare Your Node

[SEP-20](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0020.md) is an open spec that explains how self-verification of validator nodes works. The steps it specifies are pretty simple: you set the homedomain of your validator’s Bantu account to your website, where you publish information about your node and your organization in a bantu.toml file.

It’s an easy way to propagate information, and it harnesses the network to allow other participants to discover your node and add it to their quorum sets without the need for a centralized database.

## Keep Your Nodes Up To Date

Running a validator requires vigilance. You need to keep an eye on your nodes, keep them up to date with the latest version of Bantu Core, and check in on public channels for information about what’s currently happening with other validators.

The best two ways to do that:

* Join the validators [email list](https://groups.google.com/forum/#!forum/stellar-validators)
* download Keybase and join the [#validators channel](https://keybase.io/team/stellar.public) on the bantu.public team

We always announce new Bantu Core releases in those channels. You can also find those releases on our github.

It’s also critical that you pay attention to information about what those updates mean: often, you’ll need to set your validators to vote on something timely, such as when to upgrade the network as a whole, or how high to set the operations-per-ledger limit.

## Coordinate With Other Validators

Whether you run a trio of validators or a single node, it’s important that you coordinate with other validators when you make a significant change or notice something wrong. You should let them know when you plan to:

* Take your node down for maintenance
* Make changes to your quorum set

Letting other validators know when you plan to take your node down for maintenance or to upgrade to the latest version of bantu-core prevents a critical mass of nodes from going offline at the same time.

Letting other validators know when you plan to change your quorum set allows them to respond, adjust, and think through the implications of expanding the quorum. For the quorum to expand safely, we all need to coordinate to ensure we maintain good quorum intersection.

## Monitor your quorum set

We recommend using Prometheus to to scrape and store your bantu-core metrics, and Grafana to render that data for human consumption. You can find step-by-step instructions for setting up monitoring and alerts in [Monitoring and Diagnostics](/run-core-node/monitoring), along with links to Grafana dashboards we’ve created to make things easier.

You can also use stellarbeat.io to view validators’ quorum configurations, and get information about their availability and uptime, and the quorum command to diagnose problems with the quorum set of the local node.

You should do regular check-ins on your quorum set. If nodes have bad uptime or prove otherwise unreliable, you may need to remove them from your quorum set so that you don’t get stuck and so that the network doesn’t halt. You may also want to add new organizations that come online and prove reliable. If you plan to do either of those things, remember to communicate and coordinate with other validators.

## Get in touch

If you think you can be a Tier 1 Org, let us know on the #validators channel on [Keybase](https://keybase.io/team/stellar.public). We can help you through the process, and once you’re up and running, we’ll work to fold you into the quorum so that you can take your rightful place as a pillar of the network. Once you’ve proven that you are responsive, reliable, and maintain good uptime, we will adjust the quorum set recipe above to include your validators.

As Bantu grows, and more and more businesses build on the network, Tier 1 Orgs will be crucial to the methodical expansion of the network.


# installation

There are three ways to install Bantu Core: you can use a Docker image, use pre-built packages, or build from source. Using a Docker image is the quickest and easiest method, so it's a good choice for a lot of developers, but if you're running Bantu Core in production you may need to use packages or, if you want to sacrficie convenience for maximum control, build from source. Whichever method you use, you should make sure to install the latest [release](https://github.com/stellar/stellar-core/releases) since releases are cumulative and backwards compatible.

## Docker-based installation

The SDF maintains a [quickstart image](https://github.com/stellar/docker-stellar-core-horizon) that bundles Bantu Core with Horizon and postgreSQL databases. It's a quick way to set up a default, non-validating, ephemeral configuration that should work for most developers.

The SDF also maintains a Bantu-Core-only [standalone image](https://github.com/stellar/docker-stellar-core) that starts a 3 node local bantu-core network, all on the same docker host

In addition to the SDF images, SatoshiPay maintains Docker separate images for [Bantu Core](https://github.com/satoshipay/docker-stellar-core) and [Horizon](https://github.com/satoshipay/docker-stellar-horizon). The SatoshiPay Bantu Core Docker image comes in a few flavors, including one with the AWS CLI installed and one with the Google Cloud SDK installed. The Horizon image supports all Horizon environment varilables.

## Package-based Installation

If you are using Ubuntu 16.04 LTS, we provide the latest stable releases of [bantu-core](https://github.com/stellar/stellar-core) and [bantu-horizon](https://github.com/stellar/go/tree/master/services/horizon) in Debian binary package format.

You may choose to install these packages individually, which offers the greatest flexibility but requires **manual** creation of the relevant configuration files and configuration of a **PostgreSQL** database.

Most people, however, choose to install the **bantu-quickstart** package which configures a **Testnet** `bantu-core` and `bantu-horizon` both backed by a local PostgreSQL database. Once installed you can easily modify either the Bantu Core configuration if you want to connect to the public network.

## Installing from source

See the [install from source](https://github.com/stellar/stellar-core/blob/master/INSTALL.md) for build instructions.

## Release version

In general you should install the latest [release](https://github.com/stellar/stellar-core/releases) bulds builds are backward compatible and are cumulative.

The version number scheme that we follow is `protocol_version.release_number.patch_number` where:

`protocol_version` is the maximum protocol version supported by that release (all versions are 100% backward compatible), `release_number` is bumped when a set of new features or bug fixes not impacting the protocol are included in the release, `patch_number` is used when a critical fix has to be deployed.


# prerequisites

You can install Bantu Core a [number of different ways](/run-core-node/installation), and once you do, you can [configure](/run-core-node/configuring) it to participate in the network on several [different levels](/run-core-node/index#types-of-nodes): it can be a Watcher, a Basic Validator, or a Full Validator. No matter how you install Bantu Core or what kind of node you run, however, you need to set up to connect to the peer-to-peer network, store the state of the ledger in a SQL [database](/run-core-node/configuring#database), and most likely connect to [expansion](/api/introduction/index), the Bantu API.

## Computer Requirements

Bantu Core with PostgreSQL running on the same machine worked well on a [m5.large](https://aws.amazon.com/ec2/instance-types/m5/) in AWS (dual-core 2.5 GHz Intel Xeon, 8 GB RAM). Storage-wise, 20 GB was enough for now, but the ledger will grow over time.

If you are running Bantu Core in conjunction with expansion, you will need to ensure that your setup is also equipped to handle expansion's [compute requirements](/run-api-server/prerequisites) as well.

Bantu Core is designed to run on relatively modest hardware so that a whole range of individuals and organizations can participate in the network, and basic nodes should be able to function pretty well without tremendous overhead. That said, the more you ask of your node, the greater the requirements.

## Network access

Bantu Core interacts with the peer-to-peer network to keep a distributed ledger in sync, which means that your node needs to make certain [TCP ports](https://en.wikipedia.org/wiki/Transmission_Control_Protocol#TCP_ports) available for inbound and outbound communication.

* **Inbound**: a Bantu Core node needs to allow all ips to connect to its `PEER_PORT` over TCP. You can specify a port when you [configure Bantu Core](/run-core-node/configuring), but most people use the default, which is **11625**.
* **Outbound**: a Bantu Core needs to connect to other nodes via thier `PEER_PORT`s TCP. But most use the default port, which is, again, **11625**.

## Internal System Access

Bantu Core also needs to connect to certain internal systems, though exactly how varies based on your setup.

* **Outbound**:
  * Bantu Core requires access to a postgreSQL database. If that databse resides on a different machine on your network, you'll need to allow that connection. You specify the databse when you configure Bantu Core.
  * You can block all other connections.
* **Inbound**: Bantu Core exposes an *unauthenticated* HTTP endpoint on its `HTTP_PORT`. You can specify a port when you [configure bantu Core](/run-core-node/configuring), but most people use the default, which is **11626**.
  * The `HTTP_PORT` is used by expansion to submit transactions, so may have to be exposed to the rest of your internal ips
  * It's also used to query bantu Core [info](/run-core-node/commands) and provide [metrics](/run-core-node/monitoring)
  * And to perform administrative commands such as [scheduling upgrades](/run-core-node/network-upgrades) and changing log levels
  * For more on that, see [commands](/run-core-node/commands)

Note: if you need to expose your HTTP endpoint to other hosts in your local network,we recommended using an intermediate reverse proxy server to implement authentication. Don't expose the HTTP endpoint to the raw and cruel open internet.


# configuring

Bantuimport { CodeExample } from "components/CodeExample";

After you've [installed](/run-core-node/installation) Bantu Core, your next step is to complete a configuration file that specifies crucial things about your node — like whether it connects to the testnet or the public network, what database it writes to, and which other nodes are in its [quorum set](/run-core-node/configuring#choosing-your-quorum-set). You do that using a [TOML](https://github.com/toml-lang/toml), and by default Bantu Core loads that file from `./bantu-core.cfg`. You can specify a different file to load using the command line:

`$ bantu-core --conf betterfile.cfg <COMMAND>`

This section of the docs will walk you through the key fields you'll need to include in your config file to get your node up and runninig.

## Example Configurations

This doc works best in conjunction with concrete config examples, so as you read through it, you may want to check out the following:

* The [complete example config](https://github.com/stellar/stellar-core/blob/master/docs/stellar-core_example.cfg) documents all possible configuration elements, as well as their default values. It's got every knob you can twiddle and every setting you can tweak along wiith detailed explanations of how to twiddle and tweak them. You don't need to put everything from the complete example config into your config file — fields you omit will assume the default setting, and the default setting will generally serve you well — but there are a few required fields, and this doc will explain what they are.
* If you want to connect to the tesnet, check out the [example test network config](https://github.com/stellar/docker-stellar-core-horizon/blob/master/testnet/core/etc/stellar-core.cfg). As you can see, most of the fields from the [complete example config](https://github.com/stellar/stellar-core/blob/master/docs/stellar-core_example.cfg) are omitted since the default settings work fine. You can easily tailor this config to meet your testnet needs.
* If you want to connect to the public network, check out this [public network config for a Full Validator](https://github.com/stellar/packages/blob/master/docs/examples/pubnet-validator-full/stellar-core.cfg). It includes a properly crafted quorum set with all the current [Tier 1 validators](/run-core-node/tier-1-orgs), which is a good place to start for most configurations. This node is set up to both [validate](/run-core-node/configuring#validating) and write history to a [public archive](/run-core-node/publishing-history-archives), but you can disable either feature to adjust this config so it's a little lighter.
* Here's a config for a [pubnet watcher](https://github.com/stellar/packages/blob/master/docs/stellar-core_pubnet_watcher.cfg). It's a little out of date — it still uses the old method for quorum set generation — but we'll fix it soon.

## Database

Bantu Core stores two copies of the ledger: one in a SQL database and one in XDR files on local disk called [buckets](/run-core-node/configuring#buckets). The database is consulted during consensus, and modified atomically when a transaction set is applied to the ledger. It's random access, fine-grained, and fast.

While a SQLite database works with Bantu Core, we generally recommend using a separate PostgreSQL server. Horizon, for instance, requires PostgreSQL. A Postgres database is the bread and butter of Bantu Core.

You specify your node's database in the aptly named `DATABASE` field of your config file, which you can can read more about in the [complete example config](https://github.com/stellar/stellar-core/blob/master/docs/stellar-core_example.cfg#L23). It defaults to an in-memory database, but you can specify a path as per the example config.

## Buckets

Bantu-core also stores a duplicate copy of the ledger in the form of flat XDR files called "buckets." These files are placed in a directory specified in the config file as `BUCKET_DIR_PATH`, which defaults to `buckets`. The bucket files are used for hashing and transmission of ledger differences to history archives.

Buckets should be stored on a fast local disk with sufficient space to store several times the size of the current ledger.

For the most part, the contents of both the database and buckets directories can be ignored as they are managed by Bantu Core. However, when running Bantu Core for the first time, you must initialize both with the following command:

`$ bantu-core new-db`

This command initializes the database and bucket directories, and then exits. You can also use this command if your DB gets corrupted and you want to restart it from scratch.

## Network Passphrase

Use the `NETWORK_PASSPHRASE` field to specify whether your node connects to the [testnet](/glossary/testnet) or the public network. Your choices:

* `NETWORK_PASSPHRASE="Test SDF Network ; September 2015"`
* `NETWORK_PASSPHRASE="Public Global` Bantu `Network ; September 2015"`

For more about the Network Passphrase and how it works, check out the [glossary entry](/glossary/network-passphrase).

## Validating

By default, Bantu Core isn't set up to validate. If you want your node to be a [Basic Validator](/run-core-node/index#basic-validator) or a [Full Validator](/run-core-node/index#full-validator), you need to configure it to do so, which means preparing it to take part in [SCP](/glossary/scp) and sign messages pledging that the network agrees to a particular transaction set.

Configuring a node to participate in SCP and sign messages is a three step process:

* Create a keypair `bantu-core gen-seed`
* Add `NODE_SEED="SD7DN..."` to your configuration file, where `SD7DN...` is the secret key from the keypair
* Add `NODE_IS_VALIDATOR=true` to your configuration file

If you want other validators to add your node to their quorum sets, you should also share your public key (GDMTUTQ... ) by publishing a bantu.toml file on your homedomain following specs laid out in [SEP-20](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0020.md).

It's essential to store and safeguard your node's secret key: if someone else has access to it, they can send messages to the network and they will appear to originate from your node. Each node you run should have its own secret key.

If you run more than one node, set the `HOME_DOMAIN` common to those nodes using the `NODE_HOME_DOMAIN` property. Doing so will allow your nodes to be grouped correctly during [quorum set generation](/run-core-node/configuring#home-domains-array).

## Choosing Your Quorum Set

No matter what kind of node you run — Watcher, Basic Validator, Full Validator, or Archiver — you need to select a quorum set, which consists of validators (grouped by organization) that your node checks with to determine whether to apply a transaction set to a ledger. If you want to know more about how qorum sets work, check this article about [how Bantu approaches quorums](https://www.stellar.org/developers-blog/why-quorums-matter-and-how-stellar-approaches-them). If you want to see what a quorum set consisting of all the Tier 1 validators looks like — at tried and true setup — check out the [public network config for a Full Validator](https://github.com/stellar/packages/blob/master/docs/examples/pubnet-validator-full/stellar-core.cfg)

A good quorum set:

* aligns with your organization’s priorities
* has enough redundancy to handle arbitrary node failures
* maintains good quorum intersection

Since crafting a good quorum set is a difficult thing to do, bantu core *automatically* generates a quorum set for you based on structured information you provide in your config file. You choose the validators you want to trust; bantu core configures them into an optimal quorum set.

To generate a quorum set, bantu core:

* Groups validators run by the same organization into a subquorum
* Sets the threshold for each of those subquorums
* Gives weights to those subquorums based on quality

While this does not absolve you of all responsibility — you still need to pick trustworthy validators and keep an eye on them to ensure that they’re consistent and reliable — it does make your life easier, and reduces the chances for human error.

### Validator discovery

When you add a validating node to your quorum set, it’s generally because you trust the *organization* running the node: you trust SDF, not some anonymous Bantu public key.

In order to create a self-verified link between a node and the organization that runs it, a validator declares a home domain on-chain using a `set_options` operation, and publishes organizational information in a bantu.toml file hosted on that domain. To find out how that works, take a look at [SEP-20](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0020.md).

As a result of that link, you can look up a node by its Bantu public key and check the bantu.toml to find out who runs it. It’s possible to do that manually, but you can also just consult the list of nodes on [Stellarbeat.io](https://stellarbeat.io/nodes). If you decide to trust an organization, you can use that list to collect the information necessary to add their nodes to your configuration.

When you look at that list, you will discover that the most reliable organizations actually run more than one validator, and adding all of an organization’s nodes to your quorum set creates the redundancy necessary to sustain arbitrary node failure. When an organization with a trio of nodes takes one down for maintenance, for instance, the remaining two vote on the organization’s behalf, and the organization’s network presence persists.

One important thing to note: you need to either depend on exactly one entity OR have **at least 4 entities** for automatic quorum set configuration to work properly. At least 4 is the better option.

### Home domains array

To create your quorum set, Bantu Core relies on two arrays of tables: `[[HOME_DOMAINS]]` and `[[VALIDATORS]]`. Check out the [example config](https://github.com/stellar/stellar-core/blob/master/docs/stellar-core_example.cfg#L372) to see those arrays in action.

`[[HOME_DOMAINS]]` defines a superset of validators: when you add nodes hosted by the same organization to your configuration, they share a home domain, and the information in the `[[HOME_DOMAINS]]` table, specifically the quality rating, will automatically apply to every one of those validators.

For each organization you want to add, create a separate `[[HOME_DOMAINS]]` table, and complete the following required fields:

| Field        | Requirements | Description                                                 |
| ------------ | ------------ | ----------------------------------------------------------- |
| HOME\_DOMAIN | string       | URL of home domain linked to a group of validators          |
| QUALITY      | string       | Rating for organization's nodes: `HIGH`, `MEDIUM`, or `LOW` |

Here’s an example:

```bash
[[HOME_DOMAINS]] 
    HOME_DOMAIN="testnet.bantu.org" 
    QUALITY="HIGH" 
[[HOME_DOMAINS]] 
    HOME_DOMAIN="some-other-domain" 
    QUALITY="LOW
```

### Validators array

For each node you would like to add to your quorum set, complete a `[[VALIDATORS]]` table with the following fields:

| Field        | Requirements | Description                                                                                    |
| ------------ | ------------ | ---------------------------------------------------------------------------------------------- |
| NAME         | string       | A unique alias for the node                                                                    |
| QUALITY      | string       | Rating for node (required unless specified in `[[HOME_DOMAINS]]`): `HIGH`, `MEDIUM`, or `LOW`. |
| HOME\_DOMAIN | string       | URL of home domain linked to validator                                                         |
| PUBLIC\_KEY  | string       | Bantu public key associated with validator                                                     |
| ADDRESS      | string       | Peer:port associated with validator (optional)                                                 |
| HISTORY      | string       | archive GET command associated with validator (optional)                                       |

If the node's `HOME_DOMAIN` aligns with an organization defined in the `[[HOME_DOMAINS]]` array, the quality rating specified there will apply to the node. If you’re adding an individual node that is *not* covered in that array, you’ll need to specify the `QUALITY` here.

Here’s an example:

```bash
[[VALIDATORS]]
    NAME="sdftest1" 
    HOME_DOMAIN="testnet.bantu.org" 
    PUBLIC_KEY="GDKXE2OZMJIPOSLNA6N6F2BVCI3O777I2OOC4BV7VOYUEHYX7RTRYA7Y" 
    ADDRESS="core-testnet1.bantublockchain.org" 
    HISTORY="curl -sf http://history.bantublockchain.org/prd/core-testnet/core_testnet_001/{0} -o {1}" 

[[VALIDATORS]] 
    NAME="sdftest2" 
    HOME_DOMAIN="testnet.bantublockchain.org" 
    PUBLIC_KEY="GCUCJTIYXSOXKBSNFGNFWW5MUQ54HKRPGJUTQFJ5RQXZXNOLNXYDHRAP" 
    ADDRESS="core-testnet2.bantublockchain.org" 
    HISTORY="curl -sf http://history.bantublockchain.org/prd/core-testnet/core_testnet_002/{0} -o {1}" 
    
[[VALIDATORS]] 
    NAME="rando-node"
    QUALITY="LOW" 
    HOME_DOMAIN="rando.com" 
    PUBLIC_KEY="GC2V2EFSXN6SQTWVYA5EPJPBWWIMSD2XQNKUOHGEKB535AQE2I6IXV2Z" 
    ADDRESS="core.rando.com" 
```

### Validator quality

`QUALITY` is a required field for each node you add to your quorum set. Whether you specify it for a suite of nodes in `[[HOME_DOMAINS]]` or for a single node in `[[VALIDATORS]]`, it means the same thing, and you have the same three rating options: HIGH, MEDIUM, or LOW.

**HIGH** quality validators are given the most weight in automatic quorum set configuration. Before assigning a high quality rating to a node, make sure it has low latency and good uptime, and that the organization running the node is reliable and trustworthy.

A high quality a validator:

* publishes an archive
* belongs to a suite of nodes that provide redundancy

Choosing redundant nodes is good practice. The archive requirement is programmatically enforced.

**MEDIUM** quality validators are nested below high quality validators, and their combined weight is equivalent to a *single high quality entity*. If a node doesn't publish an archive, but you deem it reliable, or have an organizational interest in including in your quorum set, give it a medium quality rating.

**LOW** quality validators are nested below medium quality validators, and their combined weight is equivalent to a *single medium quality entity*. Should they prove reliable over time, you can upgrade their rating to medium to give them a bigger role in your quorum set configuration.

### Automatic quorum set generation

Once you add validators to your configuration, bantu core automatically generates a quorum set using the following rules:

* Validators with the same home domain are automatically grouped together and given a threshold requiring a simple majority (2f+1)
* Heterogeneous groups of validators are given a threshold assuming byzantine failure (3f+1)
* Entities are grouped by QUALITY and nested from HIGH to LOW
* HIGH quality entities are at the top, and are given decision-making priority
* The combined weight of MEDIUM quality entities equals a single HIGH quality entity
* The combined weight of LOW quality entities equals a single MEDIUM quality entity

### Quorum and Overlay Network

It is generally a good idea to give information to your validator on other validators that you rely on. This is achieved by configuring `KNOWN_PEERS` and `PREFERRED_PEERS` with the addresses of your dependencies.

Additionally, configuring `PREFERRED_PEER_KEYS` with the keys from your quorum set might be a good idea to give priority to the nodes that allow you to reach consensus.

Without those settings, your validator depends on other nodes on the network to forward you the right messages, which is typically done as a best effort.

### Updating and Coordinating Your Quorum Set

When you join the ranks of node operators, it's also important to join the conversation. The best way to do that: get on the #validators channel on the [Bantu Keybase](https://keybase.io/team/stellar.public) and sign up for the [validators google group](https://groups.google.com/forum/#!forum/stellar-validators). That way, you can and coordinate changes with the rest of the network.

When you need to make changes to your validator or to your quorum set — say you take a validator down for maintenance or add new validators to your node's quorum set — it's crucial to stage the changes to preserve quorum intersection and general good health of the network:

* Don't remove too many nodes from your quorum set *before* the nodes are taken down. If different validators remove different sets, the remaining sets may not overlap, which could cause network splits
* Don't add too many nodes in your quorum set at the same time. If not done carefully, the new nodes could overpower your configuration

When you want to add or remove nodes, start by making changes to your own nodes' quorum sets, and then coordinate work with others to reflect those changes gradually.

## History

Bantu Core normally interacts with one or more history archive, which are configurable facilities where [Full Validators](/run-core-node/index#full-validator) and [Archivers](/run-core-node/index#archiver) store flat files containing history checkpoints: bucket files and history logs. History archives are usually off-site commodity storage services such as Amazon S3, Google Cloud Storage, Azure Blob Storage, or custom SCP/SFTP/HTTP servers. To find out how to *publish* a history archive, consult [Publishing History Archives](/run-core-node/publishing-history-archives).

No matter what kind of node you're running, you should configure it to `get` history from one or more public archives. You can configure any number of archives to download from: Bantu Core will automatically round-robin between them.

When you're [choosing your quorum set](/run-core-node/configuring#choosing-your-quorum-set), you should include high-quality nodes — which, by defintion, publish archives — and add the location for each node's archive in the `HISTORY` field in the [validators array](/run-core-node/configuring#validators-array).

You can also use command templates in the config file to specify additional archives you'd like to use and how to access them. The [example config](https://github.com/stellar/stellar-core/blob/master/docs/stellar-core_example.cfg) shows how to configure a history archive through command templates.

Note: if you notice a lot of errors related to downloading archives, you should check that all archives in your configuration are up to date.

## Automatic Maintenance

Some tables in Bantu Core's database act as a publishing queue for external systems such as Horizon and generate **meta data** for changes happening to the distributed ledger.

If not managed properly those tables will grow without bounds. To avoid this, a built-in scheduler will delete data from old ledgers that are not used anymore by other parts of the system (external systems included).

The settings that control the automatic maintenance behavior are: `AUTOMATIC_MAINTENANCE_PERIOD`, `AUTOMATIC_MAINTENANCE_COUNT` and `KNOWN_CURSORS`.

By default, Bantu Core will perform this automatic maintenance, so be sure to disable it until you have done the appropriate data ingestion in downstream systems (Horizon for example sometimes needs to reingest data).

If you need to regenerate the metadata, the simplest way is to replay ledgers for the range you're interested in after (optionally) clearing the database with `newdb`.

## Metadata Snapshots and Restoration

Some deployments of Bantu Core and Horizon will want to retain metadata for the *entire history* of the network. This metadata can be quite large and computationally expensive to regenerate anew by replaying ledgers in bantu-core from an empty initial database state, as described in the previous section.

This can be especially costly if run more than once. For instance, when bringing a new node online. Or even if running a single node with Horizon, having already ingested the meta data *once*: a subsequent version of Horizon may have a schema change that entails re-ingesting it *again*.

Some operators therefore prefer to shut down their bantu-core (and/or Horizon) processes and *take filesystem-level snapshots* or *database-level dumps* of the contents of Bantu Core's database and bucket directory, and/or Horizon's database, after metadata generation has occurred the first time. Such snapshots can then be restored, putting bantu-core and/or Horizon in a state containing metadata without performing full replay.

Any reasonably recent state will do — if such a snapshot is a little old, bantu-core will replay ledgers from whenever the snapshot was taken to the current network state anyways — but this procedure can greatly accelerate restoring validator nodes, or cloning them to create new ones.


# publishing history archives

If you want to run a [Full Validator](/run-core-node/index#full-validator) or an [Archiver](/run-core-node/index#archiver), you need to set up your node to publish a history archive. You can host an archive using a blob store such as Amazon's s3 or Digital Ocean's spaces, or you can simply serve a local archive directly via an HTTP server such as Nginx or Apache. If you're setting up a [Watcher](/run-core-node/index#watcher) or a [Basic Validator](/run-core-node/index#basic-validator), you can skip this section. No matter what kind of node you're planning to run, make sure to set it up to `get` history, which is covered in [Configuration](/run-core-node/configuring).

## Caching and History Archives

You can significantly reduce the data transfer costs associated with running a public History archive by using common caching techniques or a CDN.

Three simple rules apply to caching the History archives:

* Do not cache the archive state file `.well-known/history-bantu.json` (**"Cache-Control: no-cache"**)
* Do not cache HTTP 4xx responses (**"Cache-Control: no-cache"**)
* Cache everything else for as long as possible (**> 1 day**)

## Local History Archive Using nginx

To publish a local history archive using nginx:

* Add a history configuration stanza to your `/etc/bantu/bantu-core.cfg`:

```
[HISTORY.local]
get="cp /mnt/xvdf/bantu-core-archive/node_001/{0} {1}"
put="cp {0} /mnt/xvdf/bantu-core-archive/node_001/{1}"
mkdir="mkdir -p /mnt/xvdf/bantu-core-archive/node_001/{0}"
```

* Run new-hist to create the local archive

`# sudo -u bantu bantu-core --conf /etc/bantu/bantu-core.cfg new-hist local`

This command creates the history archive structure:

```
# tree -a /mnt/xvdf/bantu-core-archive/
/mnt/xvdf/bantu-core-archive
└── node_001
    ├── history
    │   └── 00
    │       └── 00
    │           └── 00
    │               └── history-00000000.json
    └── .well-known
        └── bantu-history.json

6 directories, 2 files
```

* Configure a virtual host to serve the local archive (Nginx)

```
server {
  listen 80;
  root /mnt/xvdf/bantu-core-archive/node_001/;

  server_name history.example.com;

  # default is to deny all
  location / { deny all; }

  # do not cache 404 errors
  error_page 404 /404.html;
  location = /404.html {
    add_header Cache-Control "no-cache" always;
  }

  # do not cache history state file
  location ~ ^/.well-known/bantu-history.json$ {
    add_header Cache-Control "no-cache" always;
    try_files $uri;
  }

  # cache entire history archive for 1 day
  location / {
    add_header Cache-Control "max-age=86400";
    try_files $uri;
  }
}
```

## Amazon S3 History Archive

To publish a history archive using Amazon S3:

* Add a history configuration stanza to your `/etc/bantu/bantu-core.cfg`:

```
[HISTORY.s3]
get='curl -sf http://history.example.com/{0} -o {1}' # Cached HTTP endpoint
put='aws s3 cp --region us-east-1 {0} s3://bucket.name/{1}' # Direct S3 access
```

* Run new-hist to create the s3 archive

`# sudo -u bantu bantu-core --conf /etc/bantu/bantu-core.cfg new-hist s3`

* Serve the archive using an Amazon S3 static site
* Optionally place a reverse proxy and CDN in front of the S3 static site

```
server {
  listen 80;
  root /srv/nginx/history.example.com;
  index index.html index.htm;

  server_name history.example.com;

  # use google nameservers for lookups
  resolver 8.8.8.8 8.8.4.4;

  # bucket.name s3 static site endpoint
  set $s3_bucket "bucket.name.s3-website-us-east-1.amazonaws.com";

  # default is to deny all
  location / { deny all; }

  # do not cache 404 errors
  error_page 404 /404.html;
  location = /404.html {
    add_header Cache-Control "no-cache" always;
  }

  # do not cache history state file
  location ~ ^/.well-known/bantu-history.json$ {
    add_header Cache-Control "no-cache" always;
    proxy_intercept_errors on;
    proxy_pass  http://$s3_bucket;
    proxy_read_timeout 120s;
    proxy_redirect off;
    proxy_buffering off;
    proxy_set_header        Host            $s3_bucket;
    proxy_set_header        X-Real-IP       $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto $scheme;
  }

  # cache history archive for 1 day
  location / {
    add_header Cache-Control "max-age=86400";
    proxy_intercept_errors on;
    proxy_pass  http://$s3_bucket;
    proxy_read_timeout 120s;
    proxy_redirect off;
    proxy_buffering off;
    proxy_set_header        Host            $s3_bucket;
    proxy_set_header        X-Real-IP       $remote_addr;
    proxy_set_header        X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header        X-Forwarded-Proto $scheme;
  }
}
```

## Backfilling a history archive

Given the choice, it's best to configure your history archive prior to your node's initial synch to the network. That way your validator's history publishes as you join/synch to the network.

However, if you have *not* published an archive during the node's initial synch, it's still possible to use the [bantu-archivist](https://github.com/bantu/go/tree/master/tools/bantu-archivist) command line tool to mirror, scan, and repair existing archives.

Using the [BBF package repositories](https://github.com/bantu/packages) you can install `bantu-archivist` by running `apt-get install bantu-archivist`

The steps required to create a History archive for an existing validator — in other words, to upgrade a Basic Validator to a Full Validator — are straightforward:

* Stop your bantu-core instance (`systemctl stop bantu-core`)
* Configure a history archive for the new node

```
[HISTORY.local]
get="cp /mnt/xvdf/bantu-core-archive/node_001/{0} {1}"
put="cp {0} /mnt/xvdf/bantu-core-archive/node_001/{1}"
mkdir="mkdir -p /mnt/xvdf/bantu-core-archive/node_001/{0}"
```

* Run new-hist to create the local archive

`# sudo -u bantu bantu-core --conf /etc/bantu/bantu-core.cfg new-hist local`

This command creates the History archive structure:

```
# tree -a /mnt/xvdf/bantu-core-archive/
/mnt/xvdf/bantu-core-archive
└── node_001
    ├── history
    │   └── 00
    │       └── 00
    │           └── 00
    │               └── history-00000000.json
    └── .well-known
        └── bantu-history.json

6 directories, 2 file
```

* Start your bantu Core instance (`systemctl start bantu-core`)
* Allow your node to join the network and watch it start publishing a few checkpoints to the newly created archive

```
2019-04-25T12:30:43.275 GDUQJ [History INFO] Publishing 1 queued checkpoints [16895-16895]: Awaiting 0/0 prerequisites of: publish-000041ff
```

At this stage your validator is successfully publishing its history, which enables other users to join the network using your archive (although it won't allow them to `CATCHUP_COMPLETE=true` as the archive only has partial network history).

## Complete History Archive

If you decide to publish a complete archive — which enables other users to join the network from the genesis ledger — it's also possible to use `bantu-archivist` to add all missing history data to your partial archive, and to verify the state and integrity of your archive. For example:

```
# bantu-archivist scan file:///mnt/xvdf/bantu-core-archive/node_001
2019/04/25 11:42:51 Scanning checkpoint files in range: [0x0000003f, 0x0000417f]
2019/04/25 11:42:51 Checkpoint files scanned with 324 errors
2019/04/25 11:42:51 Archive: 3 history, 2 ledger, 2 transactions, 2 results, 2 scp
2019/04/25 11:42:51 Scanning all buckets, and those referenced by range
2019/04/25 11:42:51 Archive: 30 buckets total, 30 referenced
2019/04/25 11:42:51 Examining checkpoint files for gaps
2019/04/25 11:42:51 Examining buckets referenced by checkpoints
2019/04/25 11:42:51 Missing history (260): [0x0000003f-0x000040ff]
2019/04/25 11:42:51 Missing ledger (260): [0x0000003f-0x000040ff]
2019/04/25 11:42:51 Missing transactions (260): [0x0000003f-0x000040ff]
2019/04/25 11:42:51 Missing results (260): [0x0000003f-0x000040ff]
2019/04/25 11:42:51 No missing buckets referenced in range [0x0000003f, 0x0000417f]
2019/04/25 11:42:51 324 errors scanning checkpoints
```

As you can tell from the output of the `scan` command, some history, ledger, transactions, and results are missing from the local history archive.

You can repair the missing data using bantu-archivist's `repair` command combined with a known full archive — such as the BBF public history archive:

`# bantu-archivist repair http://history.bantu.org/prd/core-testnet/core_testnet_001/ file:///mnt/xvdf/bantu-core-archive/node_001/`

```
2019/04/25 11:50:15 repairing http://history.bantu.org/prd/core-testnet/core_testnet_001/ -> file:///mnt/xvdf/bantu-core-archive/node_001/
2019/04/25 11:50:15 Starting scan for repair
2019/04/25 11:50:15 Scanning checkpoint files in range: [0x0000003f, 0x000041bf]
2019/04/25 11:50:15 Checkpoint files scanned with 244 errors
2019/04/25 11:50:15 Archive: 4 history, 3 ledger, 263 transactions, 61 results, 3 scp
2019/04/25 11:50:15 Error: 244 errors scanning checkpoints
2019/04/25 11:50:15 Examining checkpoint files for gaps
2019/04/25 11:50:15 Repairing history/00/00/00/history-0000003f.json
2019/04/25 11:50:15 Repairing history/00/00/00/history-0000007f.json
2019/04/25 11:50:15 Repairing history/00/00/00/history-000000bf.json
...
2019/04/25 11:50:22 Repairing ledger/00/00/00/ledger-0000003f.xdr.gz
2019/04/25 11:50:23 Repairing ledger/00/00/00/ledger-0000007f.xdr.gz
2019/04/25 11:50:23 Repairing ledger/00/00/00/ledger-000000bf.xdr.gz
...
2019/04/25 11:51:18 Repairing results/00/00/0e/results-00000ebf.xdr.gz
2019/04/25 11:51:18 Repairing results/00/00/0e/results-00000eff.xdr.gz
2019/04/25 11:51:19 Repairing results/00/00/0f/results-00000f3f.xdr.gz
...
2019/04/25 11:51:39 Repairing scp/00/00/00/scp-0000003f.xdr.gz
2019/04/25 11:51:39 Repairing scp/00/00/00/scp-0000007f.xdr.gz
2019/04/25 11:51:39 Repairing scp/00/00/00/scp-000000bf.xdr.gz
...
2019/04/25 11:51:50 Re-running checkpoing-file scan, for bucket repair
2019/04/25 11:51:50 Scanning checkpoint files in range: [0x0000003f, 0x000041bf]
2019/04/25 11:51:50 Checkpoint files scanned with 5 errors
2019/04/25 11:51:50 Archive: 264 history, 263 ledger, 263 transactions, 263 results, 241 scp
2019/04/25 11:51:50 Error: 5 errors scanning checkpoints
2019/04/25 11:51:50 Scanning all buckets, and those referenced by range
2019/04/25 11:51:50 Archive: 40 buckets total, 2478 referenced
2019/04/25 11:51:50 Examining buckets referenced by checkpoints
2019/04/25 11:51:50 Repairing bucket/57/18/d4/bucket-5718d412bdc19084dafeb7e1852cf06f454392df627e1ec056c8b756263a47f1.xdr.gz
2019/04/25 11:51:50 Repairing bucket/8a/a1/62/bucket-8aa1624cc44aa02609366fe6038ffc5309698d4ba8212ef9c0d89dc1f2c73033.xdr.gz
2019/04/25 11:51:50 Repairing bucket/30/82/6a/bucket-30826a8569cb6b178526ddba71b995c612128439f090f371b6bf70fe8cf7ec24.xdr.gz
...
```

A final scan of the local archive confirms that it has been successfully repaired

`# bantu-archivist scan file:///mnt/xvdf/bantu-core-archive/node_001`

```
2019/04/25 12:15:41 Scanning checkpoint files in range: [0x0000003f, 0x000041bf]
2019/04/25 12:15:41 Archive: 264 history, 263 ledger, 263 transactions, 263 results, 241 scp
2019/04/25 12:15:41 Scanning all buckets, and those referenced by range
2019/04/25 12:15:41 Archive: 2478 buckets total, 2478 referenced
2019/04/25 12:15:41 Examining checkpoint files for gaps
2019/04/25 12:15:41 Examining buckets referenced by checkpoints
2019/04/25 12:15:41 No checkpoint files missing in range [0x0000003f, 0x000041bf]
2019/04/25 12:15:41 No missing buckets referenced in range [0x0000003f, 0x000041bf]
```

Start your bantu-core instance (`systemctl start bantu-core`), and you should have a complete history archive being written to by your full validator.


# commands

Bantu Core can be controlled via the following commands.

## Common options

Common options can be placed at any place in the command line.

* **--conf `<FILE-NAME>`**: Specify a config file to use. You can use '-' and provide the config file via STDIN. *default 'Bantu-core.cfg'*
* **--ll `<LEVEL>`**: Set the log level. It is redundant with `http-command ll` but we need this form if you want to change the log level during test runs.
* **--metric `<METRIC-NAME>`**: Report metric METRIC on exit. Used for gathering a metric cumulatively during a test run.
* **--help**: Show help message for given command.

## Command line options

Command options can only by placed after command.

* **catchup `<DESTINATION-LEDGER/LEDGER-COUNT>`**: Perform catchup from history archives without connecting to network. For new instances (with empty history tables - only ledger 1 present in the database) it will respect LEDGER-COUNT configuration and it will perform bucket application on such a checkpoint that at least LEDGER-COUNT entries are present in history table afterwards. For instances that already have some history entries, all ledgers since last closed ledger will be replayed.
* **check-quorum**: Check quorum intersection from history to ensure there is closure over all the validators in the network.
* **convert-id `<ID>`**: Will output the passed ID in all known forms and then exit. Useful for determining the public key that corresponds to a given private key. For example:

`$ Bantu-core convert-id SDQVDISRYN2JXBS7ICL7QJAEKB3HWBJFP2QECXG7GZICAHBK4UNJCWK2`

* **dump-xdr `<FILE-NAME>`**: Dumps the given XDR file and then exits.
* **force-scp**: This command is used to start a network from scratch or when a network has lost quorum because of failed nodes or otherwise. It sets a flag in the database. The next time Bantu-core is run, Bantu-core will start emitting SCP messages based on its last known ledger. Without this flag Bantu-core waits to hear a ledger close from the network before starting SCP.  force-scp doesn't change the requirements for quorum so although this node will emit SCP messages SCP won't complete until there are also a quorum of other nodes also emitting SCP messages on this same ledger. Value of force-scp can be reset with --reset flag.
* **fuzz `<FILE-NAME>`**: Run a single fuzz input and exit.
* **gen-fuzz `<FILE-NAME>`**: Generate a random fuzzer input file.
* **gen-seed**: Generate and print a random public/private key and then exit.
* **help**: Print the available command line options and then exit..
* **http-command `<COMMAND>`** Send an [HTTP command](/run-core-node/commands#http-commands) to an already running local instance of Bantu-core and then exit. For example:

`$ Bantu-core http-command info`

* **infer-quorum**: Print a potential quorum set inferred from history.
* **load-xdr `<FILE-NAME>`**: Load an XDR bucket file, for testing.
* **new-db**: Clears the local database and resets it to the genesis ledger. If you connect to the network after that it will catch up from scratch.
* **new-hist `<HISTORY-LABEL>` ...**: Initialize the named history archives HISTORY-LABEL. HISTORY-LABEL should be one of the history archives you have specified in the Bantu-core.cfg. This will write a `.well-known/bantu-history.json` file in the archive root.
* **offline-info**: Returns an output similar to `--c info` for an offline instance
* **print-xdr `<FILE-NAME>`**: Pretty-print a binary file containing an XDR object. If FILE-NAME is "-", the XDR object is read from standard input.  Option --filetype \[auto|ledgerheader|meta|result|resultpair|tx|txfee]\*\* controls type used for printing (default: auto).  Option --base64 alters the behavior to work on base64-encoded XDR rather than raw XDR.
* **publish**: Execute publish of all items remaining in publish queue without connecting to network. May not publish last checkpoint if last closed ledger is on checkpoint boundary.
* **report-last-history-checkpoint**: Download and report last history checkpoint from a history archive.
* **run**: Runs Bantu-core service.
* **sec-to-pub**: Reads a secret key on standard input and outputs the corresponding public key. Both keys are in Bantu's standard base-32 ASCII format.
* **sign-transaction `<FILE-NAME>`**: Add a digital signature to a transaction envelope stored in binary format in `<FILE-NAME>`, and send the result to standard output (which should be redirected to a file or piped through a tool such as `base64`). The private signing key is read from standard input, unless `<FILE-NAME>` is "-" in which case the transaction envelope is read from standard input and the signing key is read from `/dev/tty`. In either event, if the signing key appears to be coming from a terminal, Bantu-core disables echo. Note that if you do not have a BANTU\_NETWORK\_ID environment variable, then before this argument you must specify the --netid option. For example, the production Bantu network is "`Public Bantu Network ; 10-10-2020`" while the test network is "`Bantu Testnet`".  Option --base64 alters the behavior to work on base64-encoded XDR rather than raw XDR.
* **test**: Run all the unit tests.
  * Suboptions specific to Bantu-core:
    * `--all-versions` : run with all possible protocol versions
    * `--version <N>` : run tests for protocol version N, can be specified multiple times (default latest)
    * `--base-instance <N>` : run tests with instance numbers offset by N, used to run tests in parallel
  * For [further info](https://github.com/philsquared/Catch/blob/master/docs/command-line.md) on possible options for test.
  * For example this will run just the tests tagged with `[tx]` using protocol versions 9 and 10 and stop after the first failure: `Bantu-core test -a --version 9 --version 10 "[tx]"`
* **upgrade-db**: Upgrades local database to current schema version. This is usually done automatically during Bantu-core run or other command.
* **version**: Print version info and then exit.
* **write-quorum**: Print a quorum set graph from history.

## HTTP Commands

By default Bantu-core listens for connections from localhost on port 11626. You can send commands to Bantu-core via a web browser, curl, or using the --c command line option (see above). Most commands return their results in JSON format.

* **bans** List current active bans
* **checkdb** Triggers the instance to perform a background check of the database's state.
* **checkpoint** Triggers the instance to write an immediate history checkpoint. And uploads it to the archive.
* **connect** `connect?peer=NAME&port=NNN`  Triggers the instance to connect to peer NAME at port NNN.
* **dropcursor** `dropcursor?id=ID`  Deletes the tracking cursor identified by `id`. See `setcursor` for more information.
* **droppeer** `droppeer?node=NODE_ID[&ban=D]`  Drops peer identified by NODE\_ID, when D is 1 the peer is also banned.
* **info** Returns information about the server in JSON format (sync state, connected peers, etc).
* **ll** `ll?level=L[&partition=P]`  Adjust the log level for partition P where P is one of Bucket, Database, Fs, Herder, History, Ledger, Overlay, Process, SCP, Tx (or all if no partition is specified). Level is one of FATAL, ERROR, WARNING, INFO, DEBUG, VERBOSE, TRACE.
* **logrotate** Rotate log files.
* **maintenance** `maintenance?[queue=true]`  Performs maintenance tasks on the instance.
  * `queue` performs deletion of queue data. See `setcursor` for more information.
* **metrics** Returns a snapshot of the metrics registry (for monitoring and debugging purpose).
* **clearmetrics** `clearmetrics?[domain=DOMAIN]`  Clear metrics for a specified domain. If no domain specified, clear all metrics (for testing purposes).
* **peers?\[\&fullkeys=true]** Returns the list of known peers in JSON format. If `fullkeys` is set, outputs unshortened public keys.
* **quorum** `quorum?[node=NODE_ID][&compact=true][&fullkeys=true][&transitive=true]`\
  Returns information about the quorum for `NODE_ID` (local node by default). If `transitive` is set, information is for the transitive quorum centered on `NODE_ID`, otherwise only for nodes in the quorum set of `NODE_ID`.

  `NODE_ID` is either a full key (`GABCD...`), an alias (`$name`) or an abbreviated ID (`@GABCD`).

  If `compact` is set, only returns a summary version.

  If `fullkeys` is set, outputs unshortened public keys.
* **setcursor** `setcursor?id=ID&cursor=N` Sets or creates a cursor identified by `ID` with value `N`. ID is an uppercase AlphaNum, N is an uint32 that represents the last ledger sequence number that the instance ID processed. Cursors are used by dependent services to tell Bantu-core which data can be safely deleted by the instance. The data is historical data stored in the SQL tables such as txhistory or ledgerheaders. When all consumers processed the data for ledger sequence N the data can be safely removed by the instance. The actual deletion is performed by invoking the `maintenance` endpoint or on startup. See also `dropcursor`.
* **getcursor** `getcursor?[id=ID]`  Gets the cursor identified by `ID`. If ID is not defined then all cursors will be returned.
* **scp** `scp?[limit=n][&fullkeys=true]`  Returns a JSON object with the internal state of the SCP engine for the last n (default 2) ledgers. Outputs unshortened public keys if fullkeys is set.
* **tx** `tx?blob=Base64`  Submit a transaction to the network. blob is a base64 encoded XDR serialized 'TransactionEnvelope', and it returns a JSON object with the following properties status:
  * "PENDING" - transaction is being considered by consensus
  * "DUPLICATE" - transaction is already PENDING
  * "ERROR" - transaction rejected by transaction engine error: set when status is "ERROR". Base64 encoded, XDR serialized 'TransactionResult'
* **upgrades**
  * `upgrades?mode=get`  Retrieves the currently configured upgrade settings.&#x20;
  * `upgrades?mode=clear`  Clears any upgrade settings.&#x20;
  * `upgrades?mode=set&upgradetime=DATETIME&[basefee=NUM]&[basereserve=NUM]&[maxtxsize=NUM]&[protocolversion=NUM]`&#x20;
    * upgradetime is a required date (UTC) in the form `1970-01-01T00:00:00Z`. It is the time the upgrade will be scheduled for. If it is in the past by less than 12 hours, the upgrade will occur immediately. If it's more than 12 hours, then the upgrade will be ignored&#x20;
    * fee (uint32) This is what you would prefer the base fee to be. It is in stroops&#x20;
    * basereserve (uint32) This is what you would prefer the base reserve to be. It is in stroops.&#x20;
    * maxtxsize (uint32) This defines the maximum number of transactions to include in a ledger. When too many transactions are pending, surge pricing is applied. The instance picks the top maxtxsize transactions locally to be considered in the next ledger. Where transactions are ordered by transaction fee(lower fee transactions are held for later). {" "}
    * protocolversion (uint32) defines the protocol version to upgrade to. When specified it must match one of the protocol versions supported by the node and should be greater than ledgerVersion from the current ledger&#x20;
* **surveytopology** `surveytopology?duration=DURATION&node=NODE_ID`  Starts a survey that will request peer connectivity information from nodes in the backlog. `DURATION` is the number of seconds this survey will run for, and `NODE_ID` is the public key you will add to the backlog to survey. Running this command while the survey is running will add the node to the backlog and reset the timer to run for `DURATION` seconds. By default, this node will respond to/relay a survey message if the message originated from a node in it's transitive quorum. This behaviour can be overridden by adding keys to `SURVEYOR_KEYS` in the config file, which will be the set of keys to check instead of the transitive quorum. If you would like to opt-out of this survey mechanism, just set `SURVEYOR_KEYS` to `$self` or a bogus key
* **stopsurvey** `stopsurvey`  Will stop the survey if one is running. Noop if no survey is running
* **getsurveyresult** `getsurveyresult`  Returns the current survey results. The results will be reset everytime a new survey is started

### The following HTTP commands are exposed on test instances

* **generateload** `generateload[?mode=(create|pay)&accounts=N&offset=K&txs=M&txrate=R&batchsize=L&spikesize=S&spikeinterval=I]`  Artificially generate load for testing; must be used with `ARTIFICIALLY_GENERATE_LOAD_FOR_TESTING` set to true. Depending on the mode, either creates new accounts or generates payments on accounts specified (where number of accounts can be offset). Additionally, allows batching up to 100 account creations per transaction via 'batchsize'. When a nonzero I is given, a spike will occur every I seconds injecting S transactions on top of `txrate`.
* **manualclose** If MANUAL\_CLOSE is set to true in the .cfg file. This will cause the current ledger to close.
* **testacc** `testacc?name=N`  Returns basic information about the account identified by name. Note that N is a string used as seed, but "root" can be used as well to specify the root account used for the test instance.
* **testtx** `testtx?from=F&to=T&amount=N&[create=true]`  Injects a payment transaction (or a create transaction if "create" is specified) from the account F to the account T, sending N XLM to the account. Note that F and T are seed strings but can also be specified as "root" as shorthand for the root account for the test instance.


# monitoring

Once your node is up and running, it's important to keep an eye on it to make sure it stays afloat and continues to contribute to the health of the overall network. To help with that, Bantu Core exposes vital information that you can use to monitor your node and diagnose potential problems.

You can access this information using commands and inspecting Bantu Core's output, which is what the first half of this doc covers. You can also connect [Prometheus](/run-core-node/monitoring#using-prometheus) to make monitoring easier, combine it with [Alertmanager](/run-core-node/monitoring#configure-notifications-using-alertmanager) to automate notification, and use pre-built [Grafana dashboards](/run-core-node/monitoring#visualize-metrics-using-grafana) to create visual representations of your node's well-being.

However you decide to monitor, the most important thing is that you have a system in place to ensure that your integration keeps ticking.

## General Node Information

If you run `$ bantu-core http-command 'info'`, the output will look something like this:

```javascript
{
      "build" : "v11.1.0",
      "history_failure_rate" : "0",
      "ledger" : {
         "age" : 3,
         "baseFee" : 100,
         "baseReserve" : 5000000,
         "closeTime" : 1560350852,
         "hash" : "40d884f6eb105da56bea518513ba9c5cda9a4e45ac824e5eac8f7262c713cc60",
         "maxTxSetSize" : 1000,
         "num" : 24311579,
         "version" : 11
      },
      "network" : "Public Bantu Network ; 10-10-2020",
      "peers" : {
         "authenticated_count" : 5,
         "pending_count" : 0
      },
      "protocol_version" : 10,
      "quorum" : {
         "qset" : {
            "agree" : 6,
            "delayed" : 0,
            "disagree" : 0,
            "fail_at" : 2,
            "hash" : "d5c247",
            "ledger" : 24311579,
            "missing" : 1,
            "phase" : "EXTERNALIZE"
         },
         "transitive" : {
            "critical" : null,
            "intersection" : true,
            "last_check_ledger" : 24311536,
            "node_count" : 21
         }
      },
      "startedOn" : "2019-06-10T17:40:29Z",
      "state" : "Catching up",
      "status" : [ "Catching up: downloading and verifying buckets: 30/30 (100%)" ]
   }
}
```

Some notable fields in `info` are:

* `build`: the build number for this Bantu Core instance
* `ledger`: the local state of your node, which may be different from the network state if your node was disconnected from the network. Some important sub-fields:
  * `age`: time elapsed since this ledger closed (during normal operation less than 10 seconds)
  * `num`: ledger number
  * `version`: protocol version supported by this ledger
* `network` is the [network passphrase](/glossary/network-passphrase) that this core instance is using to decide whether to connect to the testnet or the public network
* `peers`: information on the connectivity to the network
  * `authenticated_count`: the number of live connections
  * `pending_count`: the number of connections that are not fully established yet
* `protocol_version`: the maximum version of the protocol that this instance recognizes
* `state`: the node's synchronization status relative to the network
* `quorum`: summarizes the state of the SCP protocol participants, the same as the information returned by the `quorum` command ([see below](/run-core-node/monitoring#quorum-health)).

## Overlay information

The `peers` command returns information on the peers your node is connected to.

This list is the result of both inbound connections from other peers and outbound connections from this node to other peers.

`$ bantu-core http-command 'peers'`

```javascript
{
  "authenticated_peers": {
    "inbound": [
      {
        "address": "54.161.82.181:11625",
        "elapsed": 6,
        "id": "sdf1",
        "olver": 5,
        "ver": "v9.1.0"
      }
    ],
    "outbound": [
      {
        "address": "54.211.174.177:11625",
        "elapsed": 2303,
        "id": "sdf2",
        "olver": 5,
        "ver": "v9.1.0"
      },
      {
        "address": "54.160.175.7:11625",
        "elapsed": 14082,
        "id": "sdf3",
        "olver": 5,
        "ver": "v9.1.0"
      }
    ]
  },
  "pending_peers": {
    "inbound": ["211.249.63.74:11625", "45.77.5.118:11625"],
    "outbound": ["178.21.47.226:11625", "178.131.109.241:11625"]
  }
}
```

## Quorum Health

To help node operators monitor their quorum sets and maintain the health of the overall network, Bantu Core also provides metrics on other nodes in your quorum set. You should monitor them to make sure they're up and running, and that your quorum set is maintaining good overlap with the rest of the network.

### Quorum set diagnostics

The `quorum` command allows to diagnose problems with the quorum set of the local node.

If you run:

`$ bantu-core http-command 'quorum'`

The output will look something like:

```javascript
{
  "node": "GCTSFJ36M7ZMTSX7ZKG6VJKPIDBDA26IEWRGV65DVX7YVVLBPE5ZWMIO",
  "qset": {
    "agree": 6,
    "delayed": null,
    "disagree": null,
    "fail_at": 2,
    "fail_with": ["bbf_watcher1", "bbf_watcher2"],
    "hash": "d5c247",
    "ledger": 24311847,
    "missing": ["sampl1"],
    "phase": "EXTERNALIZE",
    "value": {
      "t": 3,
      "v": [
        "bff_watcher1",
        "bff_watcher2",
        "bff_watcher3",
        {
          "t": 3,
          "v": ["sampl1", "flor", "bantupay", "bral"]
        }
      ]
    }
  },
  "transitive": {
    "critical": [["GDM7M262ZJJPV4BZ5SLGYYUTJGIGM25ID2XGKI3M6IDN6QLSTWQKTXQM"]],
    "intersection": true,
    "last_check_ledger": 24311536,
    "node_count": 21
  }
}
```

This output has two main sections: `qset` and `transitive`. The former describes the node and its quorum set; the latter describes the transitive closure of the node's quorum set.

### Per-node Quorum-set Information

Entries to watch for in the `qset` section — which describe the node and its quorum set — are:

* `agree` : the number of nodes in the quorum set that agree with this instance.
* `delayed` : the nodes that are participating in consensus but seem to be behind.
* `disagree`: the nodes that are participating but disagreed with this instance.
* `fail_at` : the number of failed nodes that *would* cause this instance to halt.
* `fail_with`: an example of such potential failure.
* `missing` : the nodes that were missing during this consensus round.
* `value` : the quorum set used by this node (`t` is the threshold expressed as a number of nodes).

In the example above, 6 nodes are functioning properly, one is down (`sampl1`), and the instance will fail if any two nodes still working (or one node and one inner-quorum-set) fail as well.

If a node is stuck in state `Joining SCP`, this command allows to quickly find the reason:

* too many validators missing (down or without a good connectivity), solutions are:
  * [adjust your quorum set](/run-core-node/configuring#choosing-a-quorum-set) based on the nodes that are not missing
  * try to get a [better connectivity path](/run-core-node/configuring#quorum-and-overlay-network) to the missing validators
* network split would cause SCP to stick because of nodes that disagree. This would happen if either there is a bug in SCP, the network does not have quorum intersection, or the disagreeing nodes are misbehaving (compromised, etc).

Note that the node not being able to reach consensus does not mean that the network as a whole will not be able to reach consensus (and the opposite is true: the network may fail because of a different set of validators failing).

You can get a sense of the quorum set health of a different node using using: `$ bantu-core http-command 'quorum?node=$sdf1` or `$ bantu-core http-command 'quorum?node=@GABCDE`

Overall network health can be evaluated by walking through all nodes and looking at their health. Note that this is only an approximation, as remote nodes may not have received the same messages (in particular: `missing` for other nodes is not reliable).

### Transitive Closure Summary Information

When showing quorum-set information about the local node, a summary of the transitive closure of the quorum set is also provided in the `transitive` field. This has several important sub-fields:

* `last_check_ledger` : the last ledger in which the transitive closure was checked for quorum intersection. This will reset when the node boots and whenever a node in the transitive quorum changes its quorum set. It may lag behind the last-closed ledger by a few ledgers depending on the computational cost of checking quorum intersection.
* `node_count` : the number of nodes in the transitive closure, which are considered when calculating quorum intersection.
* `intersection` : whether or not the transitive closure enjoyed quorum intersection at the most recent check. This is of **utmost importance** in preventing network splits. It should always be true. If it is ever false, one or more nodes in the transitive closure of the quorum set is *currently* misconfigured, and the network is at risk of splitting. Corrective action should be taken immediately, for which two additional sub-fields will be present to help suggest remedies:
  * `last_good_ledger` : this will note the last ledger for which the `intersection` field was evaluated as true; if some node reconfigured at or around that ledger, reverting that configuration change is the easiest corrective action to take.
  * `potential_split` : this will contain a pair of lists of validator IDs, which is a potential pair of disjoint quorums allowed by the current configuration. In other words, a possible split in consensus allowed by the current configuration. This may help narrow down the cause of the misconfiguration: likely it involves too-low a consensus threshold in one of the two potential quorums, and/or the absence of a mandatory trust relationship that would bridge the two.
* `critical`: an "advance warning" field that lists nodes that *could cause* the network to fail to enjoy quorum intersection, if they were misconfigured sufficiently badly. In a healthy transitive network configuration, this field will be `null`. If it is non-`null` then the network is essentially "one misconfiguration" (of the quorum sets of the listed nodes) away from no longer enjoying quorum intersection, and again, corrective action should be taken: careful adjustment to the quorum sets of *nodes that depend on* the listed nodes, typically to strengthen quorums that depend on them.

### Detailed transitive quorum analysis

The quorum endpoint can also retrieve detailed information for the transitive quorum.

This is a format that's easier to process than what `scp` returns as it doesn't contain all SCP messages.

`$ bantu-core http-command 'quorum?transitive=true'`

The output looks something like:

```javascript
{
  "critical": null,
  "intersection": true,
  "last_check_ledger": 121235,
  "node_count": 4,
  "nodes": [
    {
      "distance": 0,
      "heard": 121235,
      "node": "GB7LI",
      "qset": {
        "t": 2,
        "v": ["bbf1", "bbf2", "bbf3"]
      },
      "status": "tracking",
      "value": "[ txH: d99591, ct: 1557426183, upgrades: [ ] ]",
      "value_id": 1
    },
    {
      "distance": 1,
      "heard": 121235,
      "node": "sdf2",
      "qset": {
        "t": 2,
        "v": ["bbf1", "bbf2", "bbf3"]
      },
      "status": "tracking",
      "value": "[ txH: d99591, ct: 1557426183, upgrades: [ ] ]",
      "value_id": 1
    },
    {
      "distance": 1,
      "heard": 121235,
      "node": "sdf3",
      "qset": {
        "t": 2,
        "v": ["bbf1", "bbf2", "bbf3"]
      },
      "status": "tracking",
      "value": "[ txH: d99591, ct: 1557426183, upgrades: [ ] ]",
      "value_id": 1
    },
    {
      "distance": 1,
      "heard": 121235,
      "node": "sdf1",
      "qset": {
        "t": 2,
        "v": ["bbf1", "bbf2", "bbf3"]
      },
      "status": "tracking",
      "value": "[ txH: d99591, ct: 1557426183, upgrades: [ ] ]",
      "value_id": 1
    }
  ]
}
```

The output begins with the same summary information as in the `transitive` block of the non-transitive query (if queried for the local node), but also includes a `nodes` array that represents a walk of the transitive quorum centered on the query node.

Fields are:

* `node` : the identity of the validator
* `distance` : how far that node is from the root node (ie. how many quorum set hops)
* `heard` : the latest ledger sequence number that this node voted on
* `qset` : the node's quorum set
* `status` : one of `behind|tracking|ahead` (compared to the root node) or `missing|unknown` (when there are no recent SCP messages for that node)
* `value_id` : a unique ID for what the node is voting for (allows to quickly tell if nodes are voting for the same thing)
* `value` : what the node is voting for

## Using Prometheus

Monitoring `bantu-core` using Prometheus is by far the simplest solution, especially if you already have a Prometheus server within your infrastructure. Prometheus is a free and open source time-series database with a simple yet incredibly powerful query language `PromQL`. Prometheus is also tightly integrated with Grafana, so you can render complex visualisations with ease.

In order for Prometheus to scrape `bantu-core` application metrics, you will need to install the bantu-core-prometheus-exporter (`apt-get install bantu-core-prometheus-exporter`) and configure your Prometheus server to scrape this exporter (default port: `9473`). On top of that grafana can be used to visualize metrics.

### Install a Prometheus server within your infrastructure

Installing and configuring a Prometheus server is out of scope of this document, however it is a fairly simple process: Prometheus is a single Go binary which you can download from <https://prometheus.io/docs/prometheus/latest/installation/>.

### Install the bantu-core-prometheus-exporter

The bantu-core-prometheus-exporter is an exporter that scrapes the `bantu-core` metrics endpoint (`http://localhost:11626/metrics`) and renders these metrics in the Prometheus text-based format available for Prometheus to scrape and store in its timeseries database.

The exporter needs to be installed on every Bantu Core node you wish to monitor.

* `apt-get install bantu-core-prometheus-exporter`

You will need to open up port `9473` between your Prometheus server and all your Bantu Core nodes for your Prometheus server to be able to scrape metrics.

### Point Prometheus to bantu-core-prometheus-exporter

Pointing your Prometheus instance to the exporter can be achieved by manually configuring a scrape job; however, depending on the number of hosts you need to monitor this can quickly become unwieldy. Luckily, the process can also be automated using Prometheus' various "service discovery" plugins. For example with AWS hosted instance you can use the `ec2_sd_config` plugin.

#### Manual

```yaml
- job_name: "bantu-core"
  scrape_interval: 10s
  scrape_timeout: 10s
  static_configs:
    - targets: [
          "core-node-001.example.com:9473",
          "core-node-002.example.com:9473",
        ] # bantu-core-prometheus-exporter default port is 9473
    - labels:
      application: "bantu-core"
```

#### Using Service Discovery (EC2)

```yaml
- job_name: bantu-core
  scrape_interval: 10s
  scrape_timeout: 10s
  ec2_sd_configs:
    - region: eu-west-1
      port: 9473
  relabel_configs:
    # ignore stopped instances
    - source_labels: [__meta_ec2_instance_state]
      regex: stopped
      action: drop
    # only keep with `core` in the Name tag
    - source_labels: [__meta_ec2_tag_Name]
      regex: "(.*core.*)"
      action: keep
    # use Name tag as instance label
    - source_labels: [__meta_ec2_tag_Name]
      regex: "(.*)"
      action: replace
      replacement: "${1}"
      target_label: instance
    # set application label to bantu-core
    - source_labels: [__meta_ec2_tag_Name]
      regex: "(.*core.*)"
      action: replace
      replacement: bantu-core
      target_label: application
```

### Create Alerting Rules

Once Prometheus scrapes metrics we can add alerting rules. Recommended rules are [**here**](https://github.com/bantu/packages/blob/master/docs/bantu-core-alerting.rules) (require Prometheus 2.0 or later). Copy rules to */etc/prometheus/bantu-core-alerting.rules* on the Prometheus server and add the following to the prometheus configuration file to include the file:

```yaml
rule_files:
  - "/etc/prometheus/bantu-core-alerting.rules"
```

Rules are documented in-line,and we strongly recommend that you review and verify all of them as every environment is different.

### Configure Notifications Using Alertmanager

Alertmanager is responsible for sending notifications. Installing and configuring an Alertmanager server is out of scope of this document, however it is a fairly simple process. Official documentation is [here](https://github.com/prometheus/alertmanager/).

All recommended alerting rules have "severity" label:

* **critical** normally require immediate attention. They indicate an ongoing or very likely outage. We recommend that critical alerts notify administrators 24x7
* **warning** normally can wait until working hours. Warnings indicate problems that likely do not have production impact but may lead to critical alerts or outages if left unhandled

The following example alertmanager configuration demonstrates how to send notifications using different methods based on severity label:

```yaml
global:
  smtp_smarthost: localhost:25
  smtp_from: alertmanager@example.com
route:
  receiver: default-receiver
  group_by: [alertname]
  group_wait: 30s
  group_interval: 5m
  repeat_interval: 1h
  routes:
    - receiver: critical-alerts
      match:
        severity: critical
    - receiver: warning-alerts
      match:
        severity: warning
receivers:
  - name: critical-alerts
    pagerduty_configs:
      - routing_key: <PD routing key>
  - name: warning-alerts
    slack_configs:
      - api_url: https://hooks.slack.com/services/slack/warning/channel/webhook
  - name: default-receiver
    email_configs:
      - to: alerts-fallback@example.com
```

In the above examples alerts with severity "critical" are sent to pagerduty and warnings are sent to slack.

### Useful Exporters

You may find the below exporters useful for monitoring your infrastructure as they provide incredible insight into your operating system and database metrics. Installing and configuring these exporters is out of the scope of this document but should be relatively straightforward.

* [node\_exporter](https://prometheus.io/docs/guides/node-exporter/) can be used to track all operating system metrics.
* [postgresql\_exporter](https://github.com/wrouesnel/postgres_exporter) can be used to monitor the local bantu-core database.

### Visualize metrics using Grafana

Once you've configured Prometheus to scrape and store your bantu-core metrics, you will want a nice way to render this data for human consumption. Grafana offers the simplest and most effective way to achieve this. Installing Grafana is out of scope of this document but is a very simple process, especially when using the [prebuilt apt packages](https://grafana.com/docs/installation/debian/#apt-repository)

We recommend that administrators import the following two dashboards into their grafana deployments:

* [**Bantu Core Monitoring**](https://grafana.com/grafana/dashboards/10603) - shows the most important metrics, node status and tries to surface common problems. It's a good troubleshooting starting point
* [**Bantu Core Full**](https://grafana.com/grafana/dashboards/10334) - shows a simple health summary as well as all metrics exposed by the `bantu-core-prometheus-exporter`. It's much more detailed than the *Bantu Core Monitoring* and might be useful during in-depth troubleshooting


# Run API Server


# prerequisites

Currently, Expansion is dependent on a Bantu Core server. While we are working to reduce that dependence, at the moment it still needs access to both the SQL database and the HTTP API published by Bantu Core. See [Run a Core Node](/run-core-node/index) to learn how to set up and administer a Bantu Core node.

Expansion is also dependent upon a postgres server, which it uses to store processed Core data for ease of use. Expansion requires postgres version >= 9.5.

In addition to the two prerequisites above, you may optionally install a redis server to be used for rate limiting requests.


# quickstart

This document describes how to quickly set up a **test** Bantu Core + Expansion node, that you can play around with to get a feel for how a bantu node operates. **This configuration is not secure!** It is **not** intended as a guide for production administration.

For detailed information about running Expansion and Bantu Core safely in production see the [Run and API Server](/run-api-server/index) and [Run a Core Node](/run-core-node/index).

If you're ready to roll up your sleeves and dig into the code, check out the [Developer Guide](https://github.com/bantu/go/blob/master/services/Expansion/internal/docs/developing.md).

## Install and run the Quickstart Docker Image

The fastest way to get up and running is using the [bantu Quickstart Docker Image](https://github.com/bantu/docker-bantu-core-Expansion). This is a Docker container that provides both `bantu-core` and `Expansion`, pre-configured for testing.

1. Install [Docker](https://www.docker.com/get-started).
2. Verify your Docker installation works: `docker run hello-world`
3. Create a local directory that the container can use to record state. This is helpful because it can take a few minutes to sync a new `bantu-core` with enough data for testing, and because it allows you to inspect and modify the configuration if needed. Here, we create a directory called `bantu` to use as the persistent volume: `cd $HOME; mkdir bantu`
4. Download and run the bantu Quickstart container, replacing `USER` with your username:

```bash
docker run --rm -it -p "8000:8000" -p "11626:11626" -p "11625:11625" -p"8002:5432" -v $HOME/bantu:/opt/bantu --name bantu bantu/quickstart --testnet
```

You can check out Bantu Core status by browsing to <http://localhost:11626>.

You can check out your Expansion instance by browsing to <http://localhost:8000>.

You can tail logs within the container to see what's going on behind the scenes:

```bash
docker exec -it bantu /bin/bash
supervisorctl tail -f bantu-core
supervisorctl tail -f expansion stderr
```

On a modern laptop this test setup takes about 15 minutes to synchronise with the last couple of days of testnet ledgers. At that point Expansion will be available for querying.

See the [Quickstart Docker Image](https://github.com/bantu/docker-bantu-core-Expansion) documentation for more details, and alternative ways to run the container.

You can test your Expansion instance with a query like: <http://localhost:8000/transactions?cursor=&limit=10&order=asc>. Use the [bantu Laboratory](https://laboratory.bantu.network/) to craft other queries to try out, and read about the available endpoints and see examples in the [Expansion API reference](/api/introduction/index).


# index

Expansion is responsible for providing an HTTP API to data in the bantu network. It ingests and re-serves the data produced by the bantu network in a form that is easier to consume than the performance-oriented data representations used by bantu-core.

This document describes how to administer a **production** Expansion instance. If you are just starting with Expansion and want to deploy it quickly to test it out, consider the [Quickstart Guide](/run-api-server/quickstart) instead. For information about developing on the Expansion codebase, check out the [Development Guide](https://github.com/bantu/go/blob/master/services/Expansion/internal/docs/developing.md).

## Why Run Expansion?

You don't need to run your own Expansion instance to build on bantu: the bantu Development Foundation runs two Expansion servers, one for the public network and one for the test network: <https://expansion.bantu.network/> and <https://expansion-testnet.bantu.network/>. These servers are free for anyone to use, and should be fine for development and small-scale projects. They are, however, rate limited, and we don't recommended using them for production services that need strong reliability.

Running Expansion within your own infrastructure provides a number of benefits. You can:

* Disable request rate limiting for guaranteed network access
* Have full operational control without dependency on the Bantu Blockchain Foundation
* Run multiple instances for redundancy and scalability


# installing

import { CodeExample } from "components/CodeExample";

To install Horizon, you have a choice: you can download a [prebuilt release for your target architecture](https://github.com/stellar/go/releases) and operation system or you can [build Horizon yourself](/run-api-server/installing#Building). When either approach is complete, you will find yourself with a directory containing a file named `horizon`. This file is a native binary.

After building or unpacking Horizon, you simply need to copy the native binary into a directory that is part of your PATH. Most unix-like systems have `/usr/local/bin` in PATH by default, so unless you have a preference or know better, we recommend you copy the binary there.

To test the installation, simply run `horizon --help` from a terminal. If the help for Horizon is displayed, your installation was successful. Note: some shells, such as zsh, cache PATH lookups. You may need to clear your cache (by using `rehash` in zsh, for example) before trying to run `horizon --help`.

## Building

Should you decide not to use one of our prebuilt releases, you may instead build Horizon from source. To do so, you need to install some developer tools:

* A unix-like operating system with the common core commands (cp, tar, mkdir, bash, etc.)
* A compatible distribution of Go (Go 1.13 or later)
* [git](https://git-scm.com/)
* [mercurial](https://www.mercurial-scm.org/)
* See the details in [README.md](https://github.com/stellar/go/blob/master/README.md#dependencies) for installing dependencies.
* Compile the Horizon binary: `go install github.com/stellar/go/services/horizon`. You should see the `horizon` binary in `$GOPATH/bin`.
* Add Go binaries to your PATH in your `bashrc` or equivalent, for easy access: `export PATH=${GOPATH//://bin:}/bin:$PATH`

Open a new terminal. Confirm everything worked by running `horizon --help` successfully.

Note: Building directly on windows is not supported.


# monitoring

import { CodeExample } from "components/CodeExample";

To ensure that your instance of Horizon is performing correctly, we encourage you to monitor it, and provide both logs and metrics to do so.

Horizon will output logs to standard out. Information about what requests are coming in will be reported, but more importantly, warnings or errors will also be emitted by default. A correctly running Horizon instance will not output any warning or error log entries.

Metrics are collected while a Horizon process is running and they are exposed at the `/metrics` path. You can see an example at <https://horizon-testnet.stellar.org/metrics>.

Below we present a few standard log entries with associated fields. You can use them to build metrics and alerts. Please note that these represent Horizon app metrics only. You should also monitor your hardware metrics like CPU or RAM Utilization.

### Starting HTTP request

| Key              | Value                                                                                          |
| ---------------- | ---------------------------------------------------------------------------------------------- |
| **`msg`**        | **`Starting request`**                                                                         |
| `client_name`    | Value of `X-Client-Name` HTTP header representing client name                                  |
| `client_version` | Value of `X-Client-Version` HTTP header representing client version                            |
| `app_name`       | Value of `X-App-Name` HTTP header representing app name                                        |
| `app_version`    | Value of `X-App-Version` HTTP header representing app version                                  |
| `forwarded_ip`   | First value of `X-Forwarded-For` header                                                        |
| `host`           | Value of `Host` header                                                                         |
| `ip`             | IP of a client sending HTTP request                                                            |
| `ip_port`        | IP and port of a client sending HTTP request                                                   |
| `method`         | HTTP method (`GET`, `POST`, ...)                                                               |
| `path`           | Full request path, including query string (ex. `/transactions?order=desc`)                     |
| `streaming`      | Boolean, `true` if request is a streaming request                                              |
| `referer`        | Value of `Referer` header                                                                      |
| `req`            | Random value that uniquely identifies a request, attached to all logs within this HTTP request |

### Finished HTTP request

| Key              | Value                                                                                          |
| ---------------- | ---------------------------------------------------------------------------------------------- |
| **`msg`**        | **`Finished request`**                                                                         |
| `bytes`          | Number of response bytes sent                                                                  |
| `client_name`    | Value of `X-Client-Name` HTTP header representing client name                                  |
| `client_version` | Value of `X-Client-Version` HTTP header representing client version                            |
| `app_name`       | Value of `X-App-Name` HTTP header representing app name                                        |
| `app_version`    | Value of `X-App-Version` HTTP header representing app version                                  |
| `duration`       | Duration of request in seconds                                                                 |
| `forwarded_ip`   | First value of `X-Forwarded-For` header                                                        |
| `host`           | Value of `Host` header                                                                         |
| `ip`             | IP of a client sending HTTP request                                                            |
| `ip_port`        | IP and port of a client sending HTTP request                                                   |
| `method`         | HTTP method (`GET`, `POST`, ...)                                                               |
| `path`           | Full request path, including query string (ex. `/transactions?order=desc`)                     |
| `route`          | Route pattern without query string (ex. `/accounts/{id}`)                                      |
| `status`         | HTTP status code (ex. `200`)                                                                   |
| `streaming`      | Boolean, `true` if request is a streaming request                                              |
| `referer`        | Value of `Referer` header                                                                      |
| `req`            | Random value that uniquely identifies a request, attached to all logs within this HTTP request |

### Metrics

Using the entries above you can build metrics that will help understand performance of a given Horizon node. For example:

* Number of requests per minute.
* Number of requests per route (the most popular routes).
* Average response time per route.
* Maximum response time for non-streaming requests.
* Number of streaming vs. non-streaming requests.
* Number of rate-limited requests.
* List of rate-limited IPs.
* Unique IPs.
* The most popular SDKs/apps sending requests to a given Horizon node.
* Average ingestion time of a ledger.
* Average ingestion time of a transaction.

### Alerts

Below are example alerts with potential causes and solutions. Feel free to add more alerts using your metrics:

| Alert                                            | Cause                                                    | Solution                               |
| ------------------------------------------------ | -------------------------------------------------------- | -------------------------------------- |
| Spike in number of requests                      | Potential DoS attack                                     | Lower rate-limiting threshold          |
| Large number of rate-limited requests            | Rate-limiting threshold too low                          | Increase rate-limiting threshold       |
| Ingestion is slow                                | Horizon server spec too low                              | Increase hardware spec                 |
| Spike in average response time of a single route | Possible bug in a code responsible for rendering a route | Report an issue in Horizon repository. |

## I'm Stuck! Help!

If any of the above steps don't work or you are otherwise prevented from correctly setting up Horizon, please join our community and let us know. Either [post a question at our Stack Exchange](https://stellar.stackexchange.com/) or [chat with us on Keybase in #dev\_discussion](https://keybase.io/team/stellar.public) to ask for help.


# Running

Once your Expansion database is configured, you're ready to run Expansion. To run Expansion you simply run `Expansion` or `Expansion serve`, both of which start the HTTP server and start logging to standard out. When run, you should see output similar to:

```
INFO[0000] Starting Expansion on :8000           pid=29013
```

The log line above announces that Expansion is ready to serve client requests. Note: the numbers shown above may be different for your installation. Next you can confirm that Expansion is responding correctly by loading the root resource. In the example above, that URL would be <http://127.0.0.1:8000/>, and simply running `curl http://127.0.0.1:8000/` shows you that the root resource can be loaded correctly.

If you didn't set up a Bantu Core node yet, you may see an error like this:

```
ERRO[2019-05-06T16:21:14.126+08:00] Error getting core latest ledger err="get failed: pq: relation \"ledgerheaders\" does not exist"
```

Expansion requires a functional Bantu Core node. Go back and set up Bantu Core as described in the [Run a Core Node guide](/run-core-node/index). In particular, you need to initialise the database as [described here](/run-core-node/configuring#buckets).

## Ingesting Live Bantu Core Data

Expansion provides most of its utility through ingested data. Your Expansion server can be configured to listen for and ingest transaction results from the connected Bantu Core instance.

To enable ingestion, you must either pass `--ingest=true` on the command line or set the `INGEST` environment variable to "true". As of Expansion 1.0.0, you can start multiple ingesting machines in your cluster.

### Ingesting Historical Data

To enable ingestion of historical data from Bantu Core, you need to run `Expansion db reingest range start end`. If you're running a [full validator](/run-core-node/index#full-validator) with published history archive, for example, you might want to ingest all of the network's history. You can run this process in the background while your Expansion server is up. This continuously decrements the `history.elder_ledger` in your /metrics endpoint until `NUM_LEDGERS` is reached and the backfill is complete.

### Ingesting Historical Data and Reingesting Ledgers

To reingest older ledgers — which you may need to do after a version upgrade — or to ingest ledgers closed by the network before you started Expansion use the `Expansion db reingest range [START_LEDGER] [END_LEDGER]` command:

```
expansion1> expansion db reingest range 1 10000
expansion2> expansion db reingest range 10001 20000
expansion3> expansion db reingest range 20001 30000
# ... etc.
```

This allows reingestion to be split up and done in parallel by multiple Expansion processes.

### Managing Storage for Historical Data

Over time, the recorded network history will grow unbounded, increasing storage used by the database. Expansion needs sufficient disk space to expand the data ingested from Bantu Core. Unless you need to maintain a [history archive](/run-core-node/publishing-history-archives), you may configure Expansion to only retain a certain number of ledgers in the database. This is done using the `--history-retention-count` flag or the `HISTORY_RETENTION_COUNT` environment variable. Set the value to the number of recent ledgers you wish to keep around, and every hour the Expansion subsystem will reap expired data. Alternatively, you may execute the command `Expansion db reap` to force a collection.

### Surviving Bantu Core Downtime

Expansion tries to maintain a gap-free window into the history of the Bantu network. This reduces the number of edge cases that Expansion-dependent software must deal with in an attempt to make the integration process simpler. To maintain a gap-free history, Expansion needs access to all of the metadata produced by Bantu Core in the process of closing a ledger, and there are instances when this metadata can be lost. Usually, this loss of metadata occurs because the Bantu Core node went offline and performed a catchup operation when restarted.

To ensure that the metadata required by Expansion is maintained, you have several options: You may either set the `CATCHUP_COMPLETE` Bantu Core configuration option to `true` or configure `CATCHUP_RECENT` to determine the amount of time your Bantu Core can be offline without having to rebuild your Expansion database.

Unless your node is a [Full Validator which publishes an archive](/run-core-node/index#full-validator) we *do not* recommend using the `CATCHUP_COMPLETE` method, as this will force Bantu Core to apply every transaction from the beginning of the ledger, which will take an ever increasing amount of time. Instead, we recommend you set the `CATCHUP_RECENT` config value. To do this, determine how long of a downtime you would like to survive (expressed in seconds) and divide by ten. This roughly equates to the number of ledgers that occur within your desired grace period since ledgers roughly close at a rate of one every ten seconds. With this value set, Bantu Core will replay transactions for ledgers that are recent enough, ensuring that the metadata needed by Expansion is present.

### Correcting Gaps in Historical Data

In the section above, we mentioned that Expansion *tries* to maintain a gap-free window. Unfortunately, it cannot directly control the state of Bantu-core and [so gaps may form](https://www.Bantu.org/developers/software/known-issues.html#gaps-detected) due to extended down time. When a gap is encountered, Expansion will stop ingesting historical data and complain loudly in the log with error messages (log lines will include "ledger gap detected"). To resolve this situation, you must re-establish the expected state of the Bantu Core database and purge historical data from Expansion's database. We leave the details of this process up to the reader as it is dependent upon your operating needs and configuration, but we offer one potential solution:

We recommend you configure the HISTORY\_RETENTION\_COUNT in Expansion to a value less than or equal to the configured value for CATCHUP\_RECENT in Bantu Core. Given this situation, any downtime that would cause a ledger gap will require a downtime greater than the amount of historical data retained by Expansion. To re-establish continuity:

1. Stop Expansion.
2. Run `Expansion db reap` to clear the historical database.
3. Clear the cursor for Expansion by running `Bantu-core -c "dropcursor?id=Expansion"` (ensure capitilization is maintained).
4. Clear ledger metadata from before the gap by running `Bantu-core -c "maintenance?queue=true"`.
5. Restart Expansion.

### Some endpoints are not available during state ingestion

Endpoints that display state information are not available during initial state ingestion and will return a `503 Service Unavailable`/`Still Ingesting` error. An example is the `/paths` endpoint (built using offers). Such endpoints will become available after state ingestion is done (usually within a couple of minutes).

### State ingestion is taking a lot of time

State ingestion shouldn't take more than a couple of minutes on an AWS `c5.xlarge` instance, or equivalent.

It's possible that the progress logs (see below) will not show anything new for a longer period of time or print a lot of progress entries every few seconds. This happens because of the way history archives are designed. The ingestion is still working but it's processing entries of type `DEADENTRY`'. If there is a lot of them in the bucket, there are no *active* entries to process. We plan to improve the progress logs to display actual percentage progress so it's easier to estimate ETA.

If you see that ingestion is not proceeding for a very long period of time:

1. Check the RAM usage on the machine. It's possible that system ran out of RAM and it using swap memory that is extremely slow.
2. If above is not the case, file a new issue in [the Expansion repository](https://github.com/Bantu/go/tree/master/services/Expansion).

### CPU usage goes high every few minutes

This is *by design*. Expansion runs a state verifier routine that compares state in local storage to history archives every 64 ledgers to ensure data changes are applied correctly. If data corruption is detected, Expansion will block access to endpoints serving invalid data.

We recommend keeping this security feature turned on; however, if it's causing problems (due to CPU usage) this can be disabled by `--ingest-disable-state-verification` CLI param or `INGEST-DISABLE-STATE-VERIFICATION` env variable.

### I see `Waiting for the next checkpoint...` messages

If you were running the new system in the past during experimental stage (`ENABLE_EXPERIMENTAL_INGESTION` flag) it's possible that the old and new systems are not in sync. In such case, the upgrade code will activate and will make sure the data is in sync. When this happens you may see `Waiting for the next checkpoint...` messages for up to 5 minutes.

## Reading the logs

In order to check the progress and the status of experimental ingestion you should check the logs. All logs connected to experimental ingestion are tagged with `service=expingest`.

It starts with informing you about state ingestion:

```
INFO[2019-08-29T13:04:13.473+02:00] Starting ingestion system from empty state...  pid=5965 service=expingest temp_set="*io.MemoryTempSet"
INFO[2019-08-29T13:04:15.263+02:00] Reading from History Archive Snapshot         ledger=25565887 pid=5965 service=expingest
```

During state ingestion, Expansion will log number of processed entries every 100,000 entries (there are currently around 7M entries in the public network):

```
INFO[2019-08-29T13:04:34.652+02:00] Processing entries from History Archive Snapshot  ledger=25565887 numEntries=100000 pid=5965 service=expingest
INFO[2019-08-29T13:04:38.487+02:00] Processing entries from History Archive Snapshot  ledger=25565887 numEntries=200000 pid=5965 service=expingest
INFO[2019-08-29T13:04:41.322+02:00] Processing entries from History Archive Snapshot  ledger=25565887 numEntries=300000 pid=5965 service=expingest
INFO[2019-08-29T13:04:48.429+02:00] Processing entries from History Archive Snapshot  ledger=25565887 numEntries=400000 pid=5965 service=expingest
INFO[2019-08-29T13:05:00.306+02:00] Processing entries from History Archive Snapshot  ledger=25565887 numEntries=500000 pid=5965 service=expingest
```

When state ingestion is finished, it will proceed to ledger ingestion starting from the next ledger after checkpoint ledger (25565887+1 in this example) to update the state using transaction meta:

```
INFO[2019-08-29T13:39:41.590+02:00] Processing entries from History Archive Snapshot  ledger=25565887 numEntries=5300000 pid=5965 service=expingest
INFO[2019-08-29T13:39:44.518+02:00] Processing entries from History Archive Snapshot  ledger=25565887 numEntries=5400000 pid=5965 service=expingest
INFO[2019-08-29T13:39:47.488+02:00] Processing entries from History Archive Snapshot  ledger=25565887 numEntries=5500000 pid=5965 service=expingest
INFO[2019-08-29T13:40:00.670+02:00] Processed ledger                              ledger=25565887 pid=5965 service=expingest type=state_pipeline
INFO[2019-08-29T13:40:00.670+02:00] Finished processing History Archive Snapshot  duration=2145.337575904 ledger=25565887 numEntries=5529931 pid=5965 service=expingest shutdown=false
INFO[2019-08-29T13:40:00.693+02:00] Reading new ledger                            ledger=25565888 pid=5965 service=expingest
INFO[2019-08-29T13:40:00.694+02:00] Processing ledger                             ledger=25565888 pid=5965 service=expingest type=ledger_pipeline updating_database=true
INFO[2019-08-29T13:40:00.779+02:00] Processed ledger                              ledger=25565888 pid=5965 service=expingest type=ledger_pipeline
INFO[2019-08-29T13:40:00.779+02:00] Finished processing ledger                    duration=0.086024492 ledger=25565888 pid=5965 service=expingest shutdown=false transactions=14
INFO[2019-08-29T13:40:00.815+02:00] Reading new ledger                            ledger=25565889 pid=5965 service=expingest
INFO[2019-08-29T13:40:00.816+02:00] Processing ledger                             ledger=25565889 pid=5965 service=expingest type=ledger_pipeline updating_database=true
INFO[2019-08-29T13:40:00.881+02:00] Processed ledger                              ledger=25565889 pid=5965 service=expingest type=ledger_pipeline
INFO[2019-08-29T13:40:00.881+02:00] Finished processing ledger                    duration=0.06619956 ledger=25565889 pid=5965 service=expingest shutdown=false transactions=29
INFO[2019-08-29T13:40:00.901+02:00] Reading new ledger                            ledger=25565890 pid=5965 service=expingest
INFO[2019-08-29T13:40:00.902+02:00] Processing ledger                             ledger=25565890 pid=5965 service=expingest type=ledger_pipeline updating_database=true
INFO[2019-08-29T13:40:00.972+02:00] Processed ledger                              ledger=25565890 pid=5965 service=expingest type=ledger_pipeline
INFO[2019-08-29T13:40:00.972+02:00] Finished processing ledger                    duration=0.071039012 ledger=25565890 pid=5965 service=expingest shutdown=false transactions=20
```

## Managing Stale Historical Data

Expansion ingests ledger data from a connected instance of Bantu Core. In the event that Bantu Core stops running (or if Expansion stops ingesting data for any other reason), the view provided by Expansion will start to lag behind reality. For simpler applications, this may be fine, but in many cases this lag is unacceptable and the application should not continue operating until the lag is resolved.

To help applications that cannot tolerate lag, Expansion provides a configurable "staleness" threshold. Given that enough lag has accumulated to surpass this threshold (expressed in number of ledgers), Expansion will only respond with an error: [`stale_history`](https://github.com/Bantu/go/blob/master/services/Expansion/internal/docs/reference/errors/stale-history.md). To configure this option, use either the `--history-stale-threshold` command line flag or the `HISTORY_STALE_THRESHOLD` environment variable. NOTE: non-historical requests (such as submitting transactions or finding payment paths) will not error out when the staleness threshold is surpassed.


# configuring

Expansion is configured using command line flags or environment variables. To see the list of command line flags that are available (and their default values) for your version of Horizon, run:

`expansion --help`

When you run the command above, you'll see that Horizon defines a large number of flags; however, only three are required:

| flag                  | envvar                    | example                                  |
| --------------------- | ------------------------- | ---------------------------------------- |
| `--db-url`            | `DATABASE_URL`            | postgres\://localhost/expansion\_testnet |
| `--bantu-core-db-url` | `BANTU_CORE_DATABASE_URL` | postgres\://localhost/core\_testnet      |
| `--bantu-core-url`    | `BANTU_CORE_URL`          | <http://localhost:11626>                 |

`--db-url` specifies the Horizon database, and its value should be a valid [PostgreSQL Connection URI](http://www.postgresql.org/docs/9.2/static/libpq-connect.html#AEN38419). `--bantu-core-db-url` specifies a Stellar Core database which will be used to load data from the Stellar ledger. Finally, `--bantu-core-url` specifies the HTTP control port for an instance of Stellar Core. This URL should be associated with the Stellar Core server that is writing to the database at `--bantu-core-db-url`.

Additionally, if you are running a single Expansion instance you should start ingestion using the following flags:

| flag                     | envvar                 | example                                                         |
| ------------------------ | ---------------------- | --------------------------------------------------------------- |
| `--history-archive-urls` | `HISTORY_ARCHIVE_URLS` | <http://history.stellar.org/prd/core-testnet/core_testnet_001/> |
| `--ingest`               | `INGEST`               | true                                                            |

Specifying command line flags every time you invoke Expansion can be cumbersome, and so we recommend using environment variables. There are many tools you can use to manage environment variables: we recommend either [direnv](http://direnv.net/) or [dotenv](https://github.com/bkeepers/dotenv).

## Preparing the Database

Before running the Expansion server, you must first prepare the Horizon database. This database will be used for all of the information produced by Horizon, notably historical information about successful transactions that have occurred on the stellar network.

To prepare a database for Horizon's use, you must first ensure the database is blank. It's easiest to simply create a new database on your postgres server specifically for Horizon's use. Next you must install the schema by running `horizon db init`. Remember to use the appropriate command line flags or environment variables to configure Horizon as explained in [Configuring ](/run-api-server/configuring). This command will log any errors that occur.

### Postgres Configuration

It is recommended to set `random_page_cost=1` in Postgres configuration if you are using SSD storage. With this setting Query Planner will make a better use of indexes, expecially for `JOIN` queries. We've noticed a huge speed improvement for some queries.


# Software and SDKs


# index

## Software

There are two key pieces of network software: Bantu Core, which tracks and adds transaction sets to the ledger, and Expansion, an API that allows programmatic access to submit transactions and query network data. To find out more about how they work together, visit the description of the [Bantu Stack](/start/bantu-stack).

You do not have to run a Bantu Core node or Expansion instance to build on Bantu: you can start developing in your language of choice by installing one of the [Bantu SDKs](/software-and-sdks/index#sdks) below, and interacting with a public Expansion instance. To find out more about how to interact with Expansion, check out the [API Reference](/api/introduction/index) section, which chronicles every Expansion endpoint, resource, aggregation, and error.

### Bantu Core

Bantu Core is the backbone of the Bantu network and does the hard work of validating and agreeing on the status of every transaction with other instances of Core through the Bantu Consensus Protocol. The processes for installing, configuring, and maintaining a Bantu Core node are covered in great detail in the [Run a Core Node](/run-core-node/index) section of the docs.

### Expansion

Expansion is the client-facing API server for the Bantu ecosystem. It acts as the interface between Bantu Core and applications that want to access the Bantu network. If you're running Bantu Core, you will probably also want to run Expansion. For more information on how to set up and operate a Expansion instance, see the [Run an API Server](/run-api-server/index) section of the docs.

## SDKs

There are a wide variety of Bantu SDKs, which means you can interact with the network in your language of choice. The Javascript, Java, and Go SDKs are maintained by the Bantu Development Foundation; the rest are maintained by dedicated community developers. They're all open source, so if you have a question, suggestion, or contribution to make, you can file a Github issue or pull request in the relevant SDK repository. You can also get in touch with SDK maintainers by joining the [Bantu Forum](https://bantutalk.org/).

Each SDK has its own source code and documentation, and we've linked to both in the list below. Often, the best place to find out how to use a given SDK is to check the documentation specific to it. Most offer practical examples that demonstrate how to construct and submit transactions and interact with Expansion endpoints.

### Javascript

* [Source](https://github.com/stellar/js-stellar-sdk)
* [Docs](https://stellar.github.io/js-stellar-sdk/)

### Java

* [Source](https://github.com/stellar/java-stellar-sdk)
* [Docs](https://stellar.github.io/java-stellar-sdk/)

### Go

The Go SDK is split up into a few separate packages, all of which you can find in [the Go monorepo README](https://github.com/stellar/go/blob/master/docs/reference/readme.md). The two key libraries for interacting with Expansion are `txnbuild`, which enables the construction, signing, and encoding of Bantu transactions, and `Expansionclient`, which provides a web client for interfacing with Expansion server REST endpoints to retrieve ledger information and submit transactions built with `txnbuild`.

* `txnbuild` [Source](https://github.com/stellar/go/tree/master/txnbuild)
* `txnbuild` [Docs](https://godoc.org/github.com/stellar/go/txnbuild)
* `Expansionclient` [Source](https://github.com/stellar/go/tree/master/clients/Expansionclient)
* `Expansionclient`[Docs](https://godoc.org/github.com/stellar/go/clients/Expansionclient)

### Python

* [Source](https://github.com/StellarCN/py-stellar-base)
* [Docs](https://stellar-sdk.readthedocs.io/en/latest/)
* [Examples](https://github.com/StellarCN/py-stellar-base/tree/master/examples)

### C# .NET

* [Source](https://github.com/elucidsoft/dotnet-stellar-sdk)
* [Docs](https://elucidsoft.github.io/dotnet-stellar-sdk/api/index.html)
* [Tutorials](https://elucidsoft.github.io/dotnet-stellar-sdk/tutorials/index.html)

### Ruby

* [Source](https://github.com/astroband/ruby-stellar-sdk)
* [Base Source](https://github.com/astroband/ruby-stellar-sdk/blob/master/base/README.md)
* [SDK Source](https://github.com/astroband/ruby-stellar-sdk/blob/master/sdk/README.md)
* [Docs](https://www.rubydoc.info/gems/stellar-sdk)
* [Base examples](https://github.com/astroband/ruby-stellar-sdk/tree/master/base/examples)
* [SDK examples](https://github.com/astroband/ruby-stellar-sdk/tree/master/sdk/examples)

### iOS

* [Source](https://github.com/Soneso/stellar-ios-mac-sdk)
* [Docs](https://github.com/Soneso/stellar-ios-mac-sdk/tree/master/docs)

### Scala

* [Source](https://github.com/Synesso/scala-stellar-sdk)
* [Docs](https://synesso.github.io/scala-stellar-sdk/)

### Qt/C++

* [Source](https://github.com/bnogalm/StellarQtSDK)
* [Docs](https://github.com/bnogalm/StellarQtSDK/wiki)

### Flutter

* [Source](https://github.com/Soneso/stellar_flutter_sdk)
* [Docs](https://github.com/Soneso/stellar_flutter_sdk/tree/master/documentation)
* [Examples](https://github.com/Soneso/stellar_flutter_sdk/tree/master/documentation/sdk_examples)

## Tools

The Bantu Development Foundation maintains a small suite of tools to make it easier for developers to interact with the network.

### [Laboratory](https://laboratory.bantu.network/)

The Bantu laboratory is a GUI that allows you to create accounts, construct and submit transactions, read XDRs, and query all of Expansion's endpoints. It exposes the relevant calls to Expansion, so it's a great way to experiment with and learn more about the Bantu API.

### [Dashboard](https://dashboard.bantu.network/)

The dashboard shows the current status of the public network and the test network.

## Reference Implementations

The Bantu Development Foundation maintains reference implementations of some [Bantu Ecosystem Proposals](https://github.com/stellar/stellar-protocol/tree/master/ecosystem) to jumpstart the process of building infrastructure on top of Bantu in a way that maximizes interoperability among ecosystem participants.

* [Polaris](https://github.com/stellar/django-polaris) is an extendable Django app that makes it easy for anchors to [facilitate cross-border payments and enable deposits and withdrawals](/software-and-sdks/index). Using Polaris, you can run a web server supporting any combination of SEP-1, 6, 10, 12, and 24.
* The [SEP-24 demo client](https://github.com/stellar/sep24-demo-client) makes it easy for anchors to test their deposit and withdrawal flows by implementing the client side of a Bantu SEP24 interactive flow.
* The [Federation Server](https://github.com/stellar/go/tree/master/services/federation) is a Go implementation of the federation protocol described in [SEP-2](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0002.md). It's designed to be dropped into your existing infrastructure.


# Glossary


# scp

The Bantu Consensus Protocol (SCP) provides a way to reach consensus without relying on a closed system to accurately record financial transactions. SCP has a set of provable safety properties that optimize for safety over liveness—in the event of partition or misbehaving nodes, it halts progress of the network until consensus can be reached. SCP simultaneously enjoys four key properties: decentralized control, low latency, flexible trust, and asymptotic security.

A few ways to explore SCP:

* Start out with the peer-reviewed [paper in SOSP 2019](https://www.stellar.org/papers/fast-and-secure-global-payments-with-stellar) on Bantu for a technical overview.
* To learn all about the Bantu Consensus Protocol, read the original [white paper](https://www.stellar.org/papers/stellar-consensus-protocol).
* A [Simplified SCP](http://www.scs.stanford.edu/~dm/blog/simplified-scp.html) description is shorter and easier to read than the full whitepaper.
* If you are implementing SCP, see the [internet draft](https://datatracker.ietf.org/doc/draft-mazieres-dinrg-scp/) and send questions and feedback to the [DIN mailing list](https://www.ietf.org/mailman/listinfo/Din).
* View or contribute to the code in [bantu-core](https://github.com/stellar/stellar-core).
* If white papers aren't your thing, here's a [technical summary](https://medium.com/a-stellar-journey/on-worldwide-consensus-359e9eb3e949).


# Claimable Balance

import { CodeExample } from "components/CodeExample";

Claimable Balances can be used to "split up" a payment into two parts, which allows the sending to only depend on the sending account, and the receipt to only depend on the receiving account. An account can initiate the "send" by creating a ClaimableBalanceEntry with [Create Claimable Balance](/start/list-of-operations#create-claimable-balance), and then that entry can be claimed by the claimants specified on the ClaimableBalanceEntry at a later time with [Claim Claimable Balance](/start/list-of-operations#claim-claimable-balance).

## Relevant operations

### Create Claimable Balance

#### Parameters

1. **Asset** - Asset that will be held in the ClaimableBalanceEntry in the form `asset_code:issuing_address` or native (for XLM).
2. **Amount** - Amount of **Asset** stored in the ClaimableBalanceEntry.
3. **List of Claimants** - A Claimant is an object that holds both the destination account that can claim the ClaimableBalanceEntry, and a ClaimPredicate that must evaluate to true for the claim to succeed. A ClaimPredicate is a recursive data structure that can be used to construct complex conditionals using different ClaimPredicateTypes. Here are some examples (The types have had the `CLAIM_PREDICATE_` prefix removed for readability) -
   * Can claim at anytime - `UNCONDITIONAL`
   * Can claim if the close time of the ledger including the claim is *before* X seconds + the ledger close time in which the ClaimableBalanceEntry was created - `BEFORE_RELATIVE_TIME(X)`
   * Can claim if the close time of the ledger including the claim is *before* X (Unix timestamp) - `BEFORE_ABSOLUTE_TIME(X)`
   * Can claim if the close time of the ledger including the claim is *at or after* X seconds + the ledger close time in which the ClaimableBalanceEntry was created - `NOT(BEFORE_RELATIVE_TIME(X))`
   * Can claim if the close time of the ledger including the claim is *at or after* X (Unix timestamp) - `NOT(BEFORE_ABSOLUTE_TIME(X))`
   * Can claim *between* X and Y Unix timestamps (given X < Y) - `AND(NOT(BEFORE_ABSOLUTE_TIME(X)), BEFORE_ABSOLUTE_TIME(Y))`
   * Can claim *outside* X and Y Unix timestamps (given X < Y) - `OR(BEFORE_ABSOLUTE_TIME(X), NOT(BEFORE_ABSOLUTE_TIME(Y))`

#### Operation Information

This operation will move Amount of Asset from the operation source account into a new ClaimableBalanceEntry.

Note that the baseReserve requirement for a ClaimableBalanceEntry is dependant on the number of Claimants. The [minimum balance](/glossary/minimum-balance) of the account will increase by `# of Claimants * baseReserve`.

#### BalanceID

A successful Create Claimable Balance operation will return a balanceID, which is the required parameter when actually claiming the newly-created entry via the Claim Claimable Balance operation, below. See [ClaimableBalanceID](/glossary/miscellaneous-core-objects#ClaimableBalanceID) for more information.

### Claim Claimable Balance

#### Parameters

1. BalanceID - The ID of the ClaimableBalanceEntry being claimed.

#### Operation Information

This operation will load the ClaimableBalanceEntry that corresponds to the BalanceID, and then search for the source account of this operation in the list of Claimants on the entry. If a match on the Claimant is found, and the ClaimPredicate evaluates to true, then the ClaimableBalanceEntry can be claimed. The balance on the entry will be moved to the source account if there are no limit or trustline issues (for non-native assets).

Once a ClaimableBalanceEntry has been claimed, it will be deleted.

## Example

The below code demonstrates via both the JavaScript and Go [SDKs](/software-and-sdks/index) how an account ("Account A") can create a `ClaimableBalanceEntry` with two Claimants: Account A (itself) and "Account B" (another recipient).

Each of these accounts can only claim the balance under certain, individual conditions. Namely, Account B has a full minute to claim the balance, after which Account A can "reclaim" the balance back for itself.

It's worth emphasizing that there is no "recovery" mechanism for a claimable balance in general: if none of the predicates can be fulfilled, the balance **cannot be recovered**. The "reclaim" paradigm below acts as a safety net for this situation.

{% tabs %}
{% tab title="JavaScript" %}

```go
const sdk = require("stellar-sdk");

async function main() {
  let server = new sdk.Server("https://horizon-testnet.stellar.org");

  let A = sdk.Keypair.fromSecret("SAQLZCQA6AYUXK6JSKVPJ2MZ5K5IIABJOEQIG4RVBHX4PG2KMRKWXCHJ");
  let B = sdk.Keypair.fromPublicKey("GAS4V4O2B7DW5T7IQRPEEVCRXMDZESKISR7DVIGKZQYYV3OSQ5SH5LVP");

  // NOTE: Proper error checks are omitted for brevity; always validate things!

  let aAccount = await server.loadAccount(A.publicKey()).catch(function (err) {
    console.error(`Failed to load ${A.publicKey()}: ${err}`)
  })
  if (!aAccount) { return }

  // Create a claimable balance with our two above-described conditions.
  let soon = Math.ceil((Date.now() / 1000) + 60); // .now() is in ms
  let bCanClaim = sdk.Claimant.predicateBeforeRelativeTime("60");
  let aCanReclaim = sdk.Claimant.predicateNot(
    sdk.Claimant.predicateBeforeAbsoluteTime(soon.toString())
  );

  // Create the operation and submit it in a transaction.
  let claimableBalanceEntry = sdk.Operation.createClaimableBalance({
    claimants: [
      new sdk.Claimant(B.publicKey(), bCanClaim),
      new sdk.Claimant(A.publicKey(), aCanReclaim)
    ],
    asset: sdk.Asset.native(),
    amount: "420",
  });

  let tx = new sdk.TransactionBuilder(aAccount, {fee: sdk.BASE_FEE})
    .addOperation(claimableBalanceEntry)
    .setNetworkPassphrase(sdk.Networks.TESTNET)
    .setTimeout(180)
    .build();

  tx.sign(A);
  let txResponse = await server.submitTransaction(tx).then(function() {
    console.log("Claimable balance created!");
  }).catch(function (err) {
    console.error(`Tx submission failed: ${err}`)
  });
}
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
    "fmt"
    "time"

    sdk "github.com/stellar/go/clients/horizonclient"
    "github.com/stellar/go/keypair"
    "github.com/stellar/go/network"
    "github.com/stellar/go/txnbuild"
    "github.com/stellar/go/xdr"
)

func main() {
    client := sdk.DefaultTestNetClient

    // Suppose that these accounts exist and are funded accordingly:
    A := "SCZANGBA5YHTNYVVV4C3U252E2B6P6F5T3U6MM63WBSBZATAQI3EBTQ4"
    B := "GA2C5RFPE6GCKMY3US5PAB6UZLKIGSPIUKSLRB6Q723BM2OARMDUYEJ5"

    // Load the corresponding account for A.
    aKeys := keypair.MustParseFull(A)
    aAccount, err := client.AccountDetail(sdk.AccountRequest{
        AccountID: aKeys.Address(),
    })

    // Create a claimable balance with our two above-described conditions.
    soon := time.Now().Add(time.Second * 60)
    bCanClaim := txnbuild.BeforeRelativeTimePredicate(60)
    aCanReclaim := txnbuild.NotPredicate(
        txnbuild.BeforeAbsoluteTimePredicate(soon.Unix()),
    )

    claimants := []txnbuild.Claimant{
        txnbuild.NewClaimant(B, &bCanClaim),
        txnbuild.NewClaimant(aKeys.Address(), &aCanReclaim),
    }

    // Create the operation and submit it in a transaction.
    claimableBalanceEntry := txnbuild.CreateClaimableBalance{
        Destinations: claimants,
        Asset:        txnbuild.NativeAsset{},
        Amount:       "420",
    }

    // Build, sign, and submit the transaction
    tx, err := txnbuild.NewTransaction(
        txnbuild.TransactionParams{
            SourceAccount:        &aAccount,
            IncrementSequenceNum: true,
            BaseFee:              txnbuild.MinBaseFee,
            // Use a real timeout in production!
            Timebounds: txnbuild.NewInfiniteTimeout(),
            Operations: []txnbuild.Operation{&claimableBalanceEntry},
        },
    )
    check(err)
    tx, err = tx.Sign(network.TestNetworkPassphrase, aKeys)
    check(err)
    txResp, err := client.SubmitTransaction(tx)
    check(err)

    fmt.Println("Claimable balance created!")
}
```

{% endtab %}
{% endtabs %}

At this point, the `ClaimableBalanceEntry` exists in the ledger, but we'll need its Balance ID to claim it. This can be acquired in a number of ways:

1. the submitter of the entry (Account A in this case) can retrieve the balance ID *prior* to submitting the transaction;&#x20;
2. the submitter parses the XDR of the transaction result's operations; **or**
3. someone queries the list of claimable balances (filtered accordingly, if necessary).

Either party could also check the `/effects` of the transaction, query `/claimable_balances` with different filters, etc. Note that while (1) may be unavailable in some SDKs as its just a helper, the other methods are universal.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
// Method 1: Not available in the JavaScript SDK yet.

// Method 2: Suppose `txResponse` comes from the transaction submission
// above.
let txResult = sdk.xdr.TransactionResult.fromXDR(
  txResponse.result_xdr, "base64");
let results = txResult.result().results();

// We look at the first result since our first (and only) operation
// in the transaction was the CreateClaimableBalanceOp.
let operationResult = results[0].value().createClaimableBalanceResult();
let balanceId = operationResult.balanceId().toXDR("hex");
console.log("Balance ID (2):", balanceId);

// Method 3: Account B could alternatively do something like:
let balances = await server
    .claimableBalances()
    .claimant(B.publicKey())
    .limit(1)       // there may be many in general
    .order("desc")  // so always get the latest one
    .call()
    .catch(function(err) {
      console.error(`Claimable balance retrieval failed: ${err}`)
    });
if (!balances) { return; }

balanceId = balances.records[0].id;
console.log("Balance ID (3):", balanceId);
```

{% endtab %}

{% tab title="Go" %}

```go
// Method 1: Suppose `tx` comes from the transaction built above.
//           Notice that this can be done *before* submission.
balanceId, err := tx.ClaimableBalanceID(0)
check(err)

// Method 2: Suppose `txResp` comes from the transaction submission above.
var txResult xdr.TransactionResult
err = xdr.SafeUnmarshalBase64(txResp.ResultXdr, &txResult)
check(err)

if results, ok := txResult.OperationResults(); ok {
    // We look at the first result since our first (and only) operation in the
    // transaction was the CreateClaimableBalanceOp.
    operationResult := results[0].MustTr().CreateClaimableBalanceResult
    balanceId, err := xdr.MarshalHex(operationResult.BalanceId)
    check(err)
    fmt.Println("Balance ID:", balanceId)
}

// Method 3: Account B could alternatively do something like:
balances, err := client.ClaimableBalances(sdk.ClaimableBalanceRequest{Claimant: B})
check(err)
balanceId := balances.Embedded.Records[0].BalanceID
```

{% endtab %}
{% endtabs %}

With the claimable balance ID acquired, either Account B or A can actually submit a claim, depending on which predicate is fulfilled. We'll assume here that a minute has passed, so Account A just reclaims the balance entry.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
let claimBalance = sdk.Operation.claimClaimableBalance({ balanceId: balanceId });
console.log(A.publicKey(), "claiming", balanceId);

let tx = new sdk.TransactionBuilder(aAccount, {fee: sdk.BASE_FEE})
  .addOperation(claimBalance)
  .setNetworkPassphrase(sdk.Networks.TESTNET)
  .setTimeout(180)
  .build();

tx.sign(A);
await server.submitTransaction(tx).catch(function (err) {
  console.error(`Tx submission failed: ${err}`)
});
```

{% endtab %}

{% tab title="Go" %}

```go
claimBalance := txnbuild.ClaimClaimableBalance{BalanceID: balanceId}
tx, err = txnbuild.NewTransaction(
    txnbuild.TransactionParams{
        SourceAccount:        &aAccount, // or Account B, depending on the condition!
        IncrementSequenceNum: true,
        BaseFee:              txnbuild.MinBaseFee,
        Timebounds:           txnbuild.NewInfiniteTimeout(),
        Operations:           []txnbuild.Operation{&claimBalance},
    },
)
check(err)
tx, err = tx.Sign(network.TestNetworkPassphrase, aKeys)
check(err)
txResp, err = client.SubmitTransaction(tx)
check(err)
```

{% endtab %}
{% endtabs %}

And that's it! At this point, since we opted for the "reclaim" path, Account A should have the same balance as what it started with (sans fees), and Account B should be unchanged.


# XDR

**XDR**, also known as *External Data Representation*, is used throughout the Bantu network and protocol. The ledger, transactions, results, history, and even the messages passed between computers running stellar-core are encoded using XDR.

XDR is specified in [RFC 4506](http://tools.ietf.org/html/rfc4506.html) and is similar to tools like Protocol Buffers or Thrift. XDR provides a few important features:

* It is very compact, so it can be transmitted quickly and stored with minimal disk space.
* Data encoded in XDR is reliably and predictably stored. Fields are always in the same order, which makes cryptographically signing and verifying XDR messages simple.
* XDR definitions include rich descriptions of data types and structures, which is not possible in simpler formats like JSON, TOML, or YAML.

## Parsing XDR

Since XDR is a binary format and not as widely known as simpler formats like JSON, the Stellar SDKs all include tools for parsing XDR and will do so automatically when retrieving data.

In addition, the Horizon API server generally exposes the most important parts of the XDR data in JSON, so they are easier to parse if you are not using an SDK. The XDR data is still included (encoded as a base64 string) inside the JSON in case you need direct access to it. .X files

Data structures in XDR are specified in an *interface definition file* (IDL). The IDL files used for the Bantu Network are available [on GitHub](https://github.com/stellar/stellar-core/tree/master/src/xdr).


# Assets

The Bantu distributed network can be used to track, hold, and transfer any type of **asset**: dollars, euros, bitcoin, stocks, gold, and other tokens of value. Any asset on the network can be traded and exchanged with any other using Bantu built-in [decentralized exchange](/glossary/decentralized-exchange).

For more information on assets, see the [Anatomy of an Asset](/issuing-assets/anatomy-of-an-asset) in the [Issue Assets](https://github.com/slideloft/new-docs/tree/046158a008b14dc6d54bdd6f4c48e078c303a05e/content/docs/issuing-assets/README.md) section of the docs.


# BUDS

The [BUDS](https://github.com/Bantu/Bantu-protocol/blob/master/ecosystem/sep-0002.md) maps Bantu addresses to more information about a given user. It's a way for Bantu client software to resolve email-like addresses such as `name*yourdomain.com` into account IDs like: `GCCVPYFOHY7ZB7557JKENAX62LUAPLMGIWNZJAFV2MITK6T32V37KEJU`. Buds addresses provide an easy way for users to share payment details by using a syntax that interoperates across different domains and providers.

The [Bantu federation protocol](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0002.md) maps Bantu addresses to more information about a given user. It's a way for Bantu client software to resolve email-like addresses such as `name*yourdomain.com` into account IDs like: `GCCVPYFOHY7ZB7557JKENAX62LUAPLMGIWNZJAFV2MITK6T32V37KEJU`. Federated addresses provide an easy way for users to share payment details by using a syntax that interoperates across different domains and providers.

## Buds addresses

Bantu federated addresses are divided into two parts separated by `*`: the username and the domain.

Bantu Buds addresses are divided into two parts separated by `*`: the username and the domain.

For example: `jed*bantu.org`:

For example: `flarcos*Bantu.network`:

* `flarcos` is the username,
* `Bantu.network` is the domain.

The domain can be any valid RFC 1035 domain name. The username is limited to printable UTF-8 with whitespace and the following characters excluded: <\*,>.

Note that the `@` symbol is allowed in the username. This means you can use email addresses in the username of a Buds address. For example: `maria@gmail.com*Bantu.network`.

## Supporting Buds

To support federation, first create a bantu.toml file, and publish it at `https://YOUR_DOMAIN/.well-known/bantu.toml`. Complete instructions for doing that can be found in the [bantu.toml specifciation](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0001.md) (aka SEP-1).

To support Buds, first create a Bantu.toml file, and publish it at `https://YOUR_DOMAIN/.well-known/Bantu.toml`. Complete instructions for doing that can be found in the [Bantu.toml specifciation](https://github.com/Bantu/Bantu-protocol/blob/master/ecosystem/sep-0001.md) (aka SEP-1).

In general, you will want to include any and all information about your Bantu integration in your bantu.toml. To support federation specifically, you need to add a `FEDERATION_SERVER` section to your bantu.toml file that tells other people the URL of your federation endpoint.

In general, you will want to include any and all information about your Bantu integration in your Bantu.toml. To support Buds specifically, you need to add a `Buds_SERVER` section to your Bantu.toml file that tells other people the URL of your Buds endpoint.

For example: `Buds_SERVER="https://api.yourdomain.com/buds"`

Please note that your Buds server **must** use `https` protocol.

Once you've published the location of your Buds server, implement Buds url HTTP endpoint that accepts an HTTP GET request and issues responses of the form detailed below:

&#x20;To make it easier to set up a federation server, the Bantu Development foundation server you can use the \[reference implementation]\(<https://github.com/stellar/go/tree/master/services/federation>) designed to be dropped into your existing infrastructure.

To make it easier to set up a Buds server, the Bantu Development foundation server you can use the \[reference implementation]\([https://github.com/Bantu/go/tree/master/services/Buds\\](https://github.com/Bantu/go/tree/master/services/Buds\)\\) designed to be dropped into your existing infrastructure..

## Buds Requests

You can use the federation endpoint to look up an account id if you have a Bantu address. You can also do reverse federation and look up a Bantu address from an account id or a transaction id. This is useful to see who has sent you a payment.

You can use the Buds endpoint to look up an account id if you have a Bantu address. You can also do reverse Buds and look up a Bantu address from an account id or a transaction id. This is useful to see who has sent you a payment.

Buds requests are http `GET` requests with the following form:

`?q=<string to look up>&type=<name,forward,id,txid>`

Supported types:

* **name**: Example: `https://YOUR_FEDERATION_SERVER/federation?q=jed*bantu.org&type=name`
* **forward**: Used for forwarding the payment on to a different network or different financial institution. The other parameters of the query will vary depending on what kind of institution is the ultimate destination of the payment and what you as the forwarding anchor supports. Your [bantu.toml](https://github.com/stellar/stellar-protocol/blob/master/ecosystem/sep-0001.md) file should specify what parameters you expect in a `forward` federation request. If you are unable to forward or the other parameters in the request are incorrect you should return an error to this effect. Example request: `https://YOUR_FEDERATION_SERVER/federation?type=forward&forward_type=bank_account&swift=BOPBPHMM&acct=2382376`
* **id**: *not supported by all federation servers* Reverse federation will return the federation record of the Bantu address associated with the given account ID. In some cases this is ambiguous. For instance if an anchor sends transactions on behalf of its users the account id will be of the anchor and the federation server won't be able to resolve the particular user that sent the transaction. In cases like that you may need to use **txid** instead. Example: `https://YOUR_FEDERATION_SERVER/federation?q=GD6WU64OEP5C4LRBH6NK3MHYIA2ADN6K6II6EXPNVUR3ERBXT4AN4ACD&type=id`
* **txid**: *not supported by all federation servers* Will return the federation record of the sender of the transaction if known by the server. Example: `https://YOUR_FEDERATION_SERVER/federation?q=c1b368c00e9852351361e07cc58c54277e7a6366580044ab152b8db9cd8ec52a&type=txid`
* **name**: Example: `https://YOUR_Buds_SERVER/Buds?q=flarcos*bantu.networ&type=name`
* **forward**: Used for forwarding the payment on to a different BUDS or different financial institution. The other parameters of the query will vary depending on what kind of institution is the ultimate destination of the payment and what you as the forwarding anchor supports. Your [Bantu.toml](https://github.com/Bantu/Bantu-protocol/blob/master/ecosystem/sep-0001.md) file should specify what parameters you expect in a `forward` Buds request. If you are unable to forward or the other parameters in the request are incorrect you should return an error to this effect. Example request: `https://YOUR_Buds_SERVER/Buds?type=forward&forward_type=bank_account&swift=BOPBPHMM&acct=2382376`
* **id**: *not supported by all Buds servers* Reverse Buds will return the Buds record of the Bantu address associated with the given account ID. In some cases this is ambiguous. For instance if an anchor sends transactions on behalf of its users the account id will be of the anchor and the Buds server won't be able to resolve the particular user that sent the transaction. In cases like that you may need to use **txid** instead. Example: `https://YOUR_Buds_SERVER/Buds?q=GD6WU64OEP5C4LRBH6NK3MHYIA2ADN6K6II6EXPNVUR3ERBXT4AN4ACD&type=id`
* **txid**: *not supported by all Buds servers* Will return the Buds record of the sender of the transaction if known by the server. Example: `https://YOUR_Buds_SERVER/Buds?q=c1b368c00e9852351361e07cc58c54277e7a6366580044ab152b8db9cd8ec52a&type=txid`

## Buds Response

The Buds server should respond with an appropriate HTTP status code, headers, and a JSON response.

You must enable CORS on the Buds server so clients can send requests from other sites. The following HTTP header must be set for all Buds server responses.

```
Access-Control-Allow-Origin: *
```

When a record has been found the response should return `200 OK` http status code and the following JSON body:

```
{
  "Bantu_address": <username*domain.tld>,
  "account_id": <account_id>,
  "memo_type": <"text", "id" , or "hash"> *optional*
  "memo": <memo to attach to any payment. if "hash" type then will be base64 encoded> *optional*
}
```

If a redirect is needed the Buds server should return `3xx` http status code and immediately redirect the user to the correct URL using the `Location` header.

When a record has not been found `404 Not Found` http status code should be returned.

Every other http status code will be considered an error. The body should contain error details:

```
{
   "detail": "extra details provided by the Buds server"
}
```

## Looking up Buds provider via a home domain entry

Accounts may optionally have a home domain specified. This allows an account to programmatically specify the main Buds provider for that account.

## Caching

You shouldn't cache responses from Buds servers. Some organizations may generate random IDs to protect their users' privacy. Those IDs may change over time.


# Inflation

Prior to Protocol 12, Bantu had a built-in inflation mechanism conceived to allow account holders to collectively direct inflation-generated spirits toward projects built on Bantu.

As the network evolved and grew, it became increasingly clear that inflation wasn't working as intended — account holders either didn't set their inflation destination or joined inflation pools to claim the inflation themselves, and the operational costs associated with inflation payments continued to rise — and so a [protocol change to disable inflation](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0026.md) was proposed, implemented, voted on by validators, and ultimately adopted as part of a network upgrade.

The inflation operation is now deprecated.

For more info, check [here](https://www.stellar.org/blog/our-proposal-to-disable-inflation)


# Miscellaneous Core Objects

## LedgerKey

LedgerKey holds information to identify a specific ledgerEntry. It is a union that can be any one of the LedgerEntryTypes (ACCOUNT, TRUSTLINE, OFFER, DATA, or CLAIMABLE\_BALANCE). Search for LedgerKey in [stellar-ledger-entries.x](https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-ledger-entries.x) for more information.

## OperationID

OperationID is a union with one possible type (ENVELOPE\_TYPE\_OP\_ID). It contains the transaction source account, sequence number, and the operation index of the CreateClaimableBalance operation in the transaction. Search for OperationID in [stellar-transactions.x](https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-transaction.x) for more information.

## ClaimableBalanceID

ClaimableBalanceID is a union with one possible type (CLAIMABLE\_BALANCE\_ID\_TYPE\_V0). It contains a SHA-256 hash of the OperationID for Claimable Balances.


# Testnet

The testnet is a small test Bantu network, run by the Bantu Blockchain Foundation (BBF). It's free to use, functions just like the main public network, and is the best place to start developing on Bantu since it doesn't connect to real money.

BBF runs 3 Bantu Core validators on the testnet.

You can connect a node to the testnet by configuring [Bantu Core](https://github.com/Bantu/Bantu-core) to use this [configuration](https://github.com/Bantu/Bantu-core/blob/master/docs/Bantu-core_testnet.cfg).

There is also an [Expansion instance](https://expansion.Bantu.network/) that can directly interact with the testnet.

## What is the Bantu testnet good for?

* [Creating test accounts](/tutorials/create-account) (with funding thanks to Friendbot).
* Developing applications and exploring tutorials on Bantu without the potential to lose any valuable [assets](/glossary/assets).
* Testing existing applications against new releases or release candidates of [Bantu Core](https://github.com/Bantu/Bantu-core/releases) and [Expansion](https://github.com/Bantu/go/releases).
* Performing data analysis on a smaller, non-trivial data set compared to the public network.

## What is the Bantu testnet not good for?

* Load and stress testing.
* High availability test infrastructure - BBF makes no guarantees about the availability of the testnet.
* Long term storage of data on the network - [the network is ephemeral, and resets periodically](/glossary/testnet#periodic-reset-of-testnet-data).
* A testing infrastructure that requires more control over the test environment, such as:
  * The ability to control the data reset frequency.
  * The need to secure private or sensitive data (before launching on the public network)

Keep in mind that you can always run your own test network for use cases that don't work well with BBF's testnet.

## Best Practices For Using Testnet

### Surge Pricing on the Testnet

The testnet has a capacity limit of **100 operations per ledger**. When more than 100 operations are submitted to a given ledger, the network enters [surge pricing mode](/glossary/fees#surge-pricing), which uses market dynamics to decide which submissions are included. It works exactly the same way as surge pricing on the public network.

If you are having trouble submitting transactions to the testnet, you may need to offer a higher fee. You can also take the opportunity to develop a fee strategy, which may prove useful when you move your project into production.

### Periodic Reset of Testnet Data

In order to preserve a good experience for developers, the BBF testnet is periodically reset to the genesis (initial) ledger. Resets declutter the network, remove spam, minimize the time required to catch up to the latest ledger, and help maintain the system over time.

A reset clears all ledger entries (such as accounts, trustlines, offers, etc), transactions, and historical data from both Bantu Core and Expansion, which is why developers should not rely on the persistence of any accounts or on the state of any balances when using testnet.

After a reset, you will need to take a few steps to re-join and re-synch to the testnet. Those steps are outlined [here](https://github.com/Bantu/packages#testnet-reset), along with line-by-line instructions for people using core + Expansion ubuntu packages. If you need help with other packages, check [Bantu's Stack Exchange](https://Bantu.stackexchange.com/) for guidance.

BBF will try to make testnet resets as painless as possible, and will announce the exact date at least two weeks in advance on the [Bantu Dashboard](https://dashboard.bantu.network/), and via several of Bantu’s online developer communities.

The testnet resets once per quarter (every three months). The 2021 dates:

* 3/17/21
* 6/16/21
* 9/15/21
* 12/15/21

The testnet will always restart on the announced reset date at 0900 UTC.

### Test Data Automation

Since most applications rely on data being present to do anything useful, it is highly recommended that you have testing infrastructure that can repopulate testnet with useful data after a reset. Not only will this make testing more reliable, but it will also help you scale out your testing infrastructure to a private test network if you choose to do so.

For example, you may want to:

* Generate issuers of assets for testing the development of a wallet.
* Generate orders on the order book (both current and historical) for testing the development of a trading client.

As a maintainer of an application, you will want to think about creating a data set that is representative enough to test your primary use cases, and allow for robust testing even when testnet is not available.

A script can automate this entire process by [creating an account with Friendbot](/tutorials/create-account), and submitting a set of transactions that are predefined as a part of your testing infrastructure.

For additional questions we recommend heading over to [Bantu's Stack Exchange](https://Bantu.stackexchange.com/).


# Accounts

Accounts are the central data structure in Bantu. They hold balances, sign transactions, and issue assets. All entries that persist on the ledger are owned by a particular account.

In addition to a valid keypair, an account needs a balance of XBN sufficient to meet the network reserve before it exists on the ledger.

## Keypair

Bantu relies on public key cryptography to ensure that transactions are secure: every account requires a valid keypair consisting of a *public key* a *private key*. The public key is, as the name suggests, public. It’s visible on the ledger, anyone can look it up, and it’s what others use to send payments to the account, identify the issuer an asset, and verify that a given transaction is authorized.

The private key, however, is something an account holder should guard closely. It’s kind of like the combination to a lock — anyone who knows it can access the account, sign transactions, send funds, whatever. Do not share your private key with anyone.

You can use any Bantu wallet or SDK to generate a valid keypair.

## Account Creation

A keypair alone doesn’t create an account: before an account exists on the ledger, it needs an XBN balance sufficient to meet the minimum network reserve. The minimum reserve, which is determined by validator vote, is intended to disincentivize the creation of tons of unused accounts in order to prevent ledger spam and maintain the efficiency and scalability of the network.

There is a specific operation, Create Account, which you use to make a payment to a valid public key that does not exist on the ledger, thereby creating the account.

## Account fields

Accounts have the following fields:

### Account ID

The public key that was used to create the account. Even if you replace the signer with a different key, the original account ID will always be used to identify the account.

### Sequence number

The current transaction sequence number of the account. This number starts equal to the ledger number at which the account was created, and increments upward as the account signs transactions.

### Number of subentries

Number of entries the account owns. This number is used to calculate the account's minimum balance: each subentry increases an account’s reserve by 0.5XBN. Subentries include:

*Trustlines* Offers *Signers* Data entries

### Thresholds

Operations have varying levels of access. This field specifies thresholds for low-, medium-, and high-access levels, as well as the weight of the master key. For more info, see [multi-sig](/glossary/multisig).

### Home domain

A [fully qualified domain name](https://en.wikipedia.org/wiki/Fully_qualified_domain_name) such as `example.com` linked to the account. A home domain is required of asset issuers, who use it to [publish meta-information](/issuing-assets/publishing-asset-info) for Bantu wallets and potential token holders, and for organizations running validators, who use it to [self-identify their nodes](https://github.com/Bantu/Bantu-protocol/blob/master/ecosystem/sep-0020.md).

To add a home domain to an account, use the [Set Options](/start/list-of-operations#set-options) operation.

### Flags

Asset issuers set flags at the account level if they want to control access to the assets they issue. There are three flags:

* **Authorization required (0x1)**: Requires the issuing account to grant an account permission to hold an asset.  With this flag set, an issuer can either grant full authorization to transact with its asset *or* it can grant limited authorization allowing the holder to maintain orders on the order books, but not to otherwise transact with the asset. &#x20;
* **Authorization revocable (0x2)**: Allows the issuing account to reduce the authorization level of an account from full to partial, from full to none, or from partial to none.
* **Authorization immutable (0x4)**: Prevents the issuer from setting either of the above flags or deleting the issuing account.

### Balances

Each account has a balance for each token the account holds, including XBN.

### Liabilities

Each account also tracks its liabilities. Buying liabilities equal the total amount of an asset offered to buy aggregated over all offers owned by this account, and selling liabilities equal the total amount of an asset offered to sell aggregated over all offers owned by this account.

An account must always have a balance sufficiently above the minimum reserve to satisfy its XBN selling liabilities, and a balance sufficiently below the maximum to accommodate its XBN buying liabilities.

### Signers

You can add signers to an account, and this field lists other public keys and their weights that can be used to authorize transactions. For more info, see [multisig](/glossary/multisig).


# Network Passphrase

Bantu has two public networks: the Public Network (pubnet), which is the main network used by applications in production, and the Test Network ([testnet](/glossary/testnet)), which is a network maintained by the Bantu Blockchain Foundation that developers can use to test their Applications.

Each Bantu network has its own unique passphrase, which is used when validating signatures on a given transaction. If you sign a transaction for one network but submit it to another, it won't be considered valid. By convention, the format of a passphrase is `'[Network Name] ; [Month of Creation] [Year of Creation]'`.

The current passphrases for the Bantu pubnet and testnet are:

* Pubnet: `'Public Bantu Network ; 10-10-2020'`
* Testnet: `'Bantu Testnet'`

The passphrase serves two main purposes:

* It is used as the seed for the root account (master network key) at genesis.
* It is used to build hashes of transactions, which are ultimately what is signed by each signer's secret key in a transaction envelope. Again, this allows you to verify that a transaction was intended for a specific network by its signers.

Most SDKs have the passphrases hardcoded for the Bantu pubnet and testnet, but if you're running a private network, you'll need to manually pass in a passphrase to be used whenever transaction hashes are generated. All of Bantu's official SDKs give you the ability to use a network with a custom passphrase.

## Moving To Production

When creating your application on top of the Bantu network, we recommend starting on the testnet, and migrate to pubnet after rigorous testing has proved it to be production ready (*we are talking about money here*).

For applications that don't rely on the state of a network (such as specific accounts needing to exist), moving to production is as simple as changing the network passphrase and ensuring your Expansion instance is connected to pubnet.

If you've been running a Bantu-core or Expansion instance against the test network, and want to switch to production, changing the passphrase will require both respective databases to be completely reinitialized.


# Ledger

A ledger represents the state of the Bantu universe at a given point in time. It’s shared across all the nodes that make up the network, and contains the list of all accounts and balances, all orders on the distributed exchange, and any other data that persists.

Every Bantu Consensus Protocol (SCP) round, the network reaches consensus on which transaction set to apply to the last closed ledger; when the new set is applied, a new "last closed ledger" is defined.

Each ledger is cryptographically linked to a unique previous ledger, creating a historical ledger chain that goes back to the genesis ledger, which is the first ledger in the history of the network.

The sequence number of a ledger is defined recursively:

* The genesis ledger has sequence number 1
* The ledger directly following a ledger with sequence number n has sequence number n+1

## Ledger header

Every ledger has a **ledger header**. This header has references to the actual data within the ledger as well as a reference to the previous ledger. References here are cryptographic hashes of the content being referenced — the hashes behave like pointers in typical data structures but with added security guarantees.

You can think of the historical ledger chain as a linked list of ledger headers:

\[Genesis] <---- \[LedgerHeader\_1] <----- ... <---- \[LedgerHeader\_n]

Every ledger header has the following fields:

* **Version**: Protocol version of this ledger.
* **Previous Ledger Hash**: Hash of the previous ledger. Forms a chain of ledgers stretching back to the genesis ledger.
* **SCP value**: During consensus all the validating nodes in the network run SCP and come to an agreement about a particular value, which is a transaction set they will apply to a ledger. This value is stored here and in the next three fields (transaction set hash, close time, and upgrades).
  * **Transaction set hash**: Hash of the transaction set that was applied to the previous ledger.
  * **Close time**: When the network closed this ledger. UNIX timestamp.
  * **Upgrades**: How the network adjusts overall values like the [base fee](/glossary/fees) and agrees to network-wide changes like switching to a new protocol version. This field is usually empty. When there *is* a network-wide upgrade in the works, the SDF will inform and help coordinate participants using the [#validators channel on Keybase](https://keybase.io/team/stellar.public). So if you run a validator, it’s important to join and monitor that channel. For more info, see [versioning](/glossary/versioning).
* **Transaction set result hash**: Hash of the results of applying the transaction set. This data is not, strictly speaking, necessary for validating the results of the transactions. However, it makes it easier for entities to validate the result of a given transaction without having to apply the transaction set to the previous ledger.
* **Bucket list hash**: Hash of all the objects in this ledger. The data structure that contains all the objects is called the [bucket list](https://github.com/stellar/stellar-core/tree/master/src/bucket).
* **Ledger sequence**: The sequence number of this ledger.
* **Total coins**: Total number of lumens in existence.
* **Fee pool**: Number of lumens that have been paid in fees. This number will be added to the inflation pool and reset to 0 the next time inflation runs. Note this is denominated in lumens, even though a transaction’s [`fee`](/glossary/transactions#fee) field is in stroops.
* **Inflation sequence**: Number of times inflation has been run.
* **ID pool**: The last used global ID. These IDs are used for generating objects.
* **Maximum Number of Transactions**: The maximum number of operations validators have agreed to process in a given ledger. If more transactions are submitted than this number, the network will enter into surge pricing mode, which uses a VCG auction to decide which to include in a ledger. For more info, see [fees and surge pricing](/glossary/fees)
* **Base fee**: The [fee](/glossary/fees) the network charges per [operation](/glossary/operations) in a [transaction](/glossary/transactions). This field is in stroops, which are 1/10,000,000th of a lumen.
* **Base reserve**: The [reserve](/glossary/minimum-balance) the network uses when calculating an account's minimum balance.
* **Skip list**: Hashes of ledgers in the past. Allows you to jump back in time in the ledger chain without walking back ledger by ledger. There are 4 ledger hashes stored in the skip list. Each slot contains the oldest ledger that is mod of either 50 5000 50000 or 500000 depending on index skipList\[0] mod(50), skipList\[1] mod(5000), etc.

## Ledger Entries

The ledger is a collection of **entries**. Currently there are 4 types of ledger entries. They're specified in [`src/xdr/Stellar-ledger-entries.x`](https://github.com/stellar/stellar-core/blob/master/src/xdr/Stellar-ledger-entries.x).

### Account entry

This entry represents an account. In Bantu, everything is built around accounts: transactions are performed by accounts, and accounts control the access rights to balances.

Other entries are add-ons, owned by a main account entry. With every new entry attached to the account, the minimum balance in XLM goes up for the account. For details, the docs on minimum balance.

### Trustline entry

Trustlines are lines of credit the account has given a particular issuer in a specific asset.

Trustline entries define the rules around the use of this currency. Rules can be defined by the user — who can set a maximum balance limit to limit risk — or by the issuer — who can set a flag to control access to the asset.

### Offer entry

Offers are entries that an account creates in the orderbook. They are a way to automate simple trading inside the Bantu network. For more on offers, refer to the distributed exchange documentation.

### Data entry

Data entries are key/value pairs attached to an account. They allow account controllers to attach arbitrary data to their account, and provide a flexible extension point to add application specific data to the ledger.


# Versioning

import { CodeExample } from "components/CodeExample";

This document describes the various mechanisms used to keep the overall system working as it evolves.

## Ledger versioning

### ledgerVersion

This uint32 stored in the ledger header describes the version number of the overall protocol. Protocol in this case is defined both as "wire format"--i.e., the serialized forms of all objects stored in the ledger — and its behavior.

This version number is incremented every time the protocol changes.

### Integration with consensus

Most of the time, consensus is simply reached on which transaction set needs to be applied to the previous ledger.

Consensus can also, however, be reached on upgrade steps.

One such upgrade step is "update ledgerVersion to value X after ledger N".

If nodes do not consider that the upgrade step is valid, they simply drop the upgrade step from their vote.

A node considers a step invalid either because they do not understand it or some condition is not met. In the previous example, it could be that X is not supported by the node or that the ledger number didn't reach N yet.

Upgrade steps are applied before applying the transaction set to ensure that the logic scheduling steps is the same that is processing it. Otherwise, the steps would have to be applied after the ledger is closed.

### Supported versions

Each node has its own way of tracking which version it supports--for example, a "min version", "max version"--but it can also include things like "black listed versions." Supported versions are not tracked from within the protocol.

Note that minProtocolVersion is distinct from the version an instance understands: typically an implementation understands versions n .. maxProtocolVersion, where n <= minProtocolVersion. The reason for this is that nodes must be able to replay transactions from history (down to version 'n'), yet there might be some issue/vulnerability that we don't want to be exploitable for new transactions.

### Ledger object versioning

Data structures that are likely to evolve over time contain the following extension point:

&#x20;\`\`\`cpp union switch(int v) { case 0: void; } ext; \`\`\`

In this case, the version 'v' refers to the version of the object and permits the addition of new arms.

This scheme offers several benefits:

* Implementations become wire compatible without code changes only by updating their protocol definition files.
* Even without updating the protocol definition files, older implementations continue to function as long as they don't encounter newer formats.
* It promotes code sharing between versions of the objects.

Note that while this scheme promotes code sharing for components consuming those objects, code sharing is not necessarily promoted for stellar-core itself because the behavior must be preserved for all versions: In order to reconstruct the ledger chain from arbitrary points in time, the behavior must be 100% compatible.

### Operations versioning

Operations are versioned as a whole: If a new parameter needs to be added or changed, versioning is achieved by adding a new operation. This causes some duplication of logic in clients but avoids introducing potential bugs. For example, code that would sign only certain types of transactions must be fully aware of what it's signing. Envelope versioning Pattern used to allow for extensibility of envelopes (signed content):

&#x20;\`\`\`cpp union TransactionEnvelope switch (int v) { case 0: struct { Transaction tx; DecoratedSignature signatures; } v0; }; \`\`\`

This pattern allows the capability to modify the envelope if needed and ensures that clients don't blindly consume content that they couldn't validate. Upgrading objects that don't have an extension point The object's schema must be cloned and its parent object must be updated to use the new object type. The assumption here is that there is no unversioned "root" object. Supported implementations lifetime considerations In order to keep the codebase in a maintainable state, implementations may not preserve the ability to play back from genesis. Instead they may opt to support a limited range--for example, only preserve the capability to replay the previous 3 months of transactions (assuming that the network's minProtocolVersion is more recent than that).

This does not change the ability of the node to (re)join or participate in the network; it only affects the ability for a node to do historical validation.

## Overlay versioning

Overlay follows a similar pattern for versioning: It has a min-maxOverlayVersion.

The versioning policy at the overlay layer is a lot more aggressive when it comes to the deprecation schedule; the set of nodes involved is limited to the ones that connect directly to the instance.

With this in mind, structures follow the "clone" model at this layer: if a message needs to be modified, a new message is defined by cloning the old message type using a new type identifier.

Knowing that the older implementation will be deleted anyway, the clone model makes it possible to refactor large parts of the code and avoids the headache of maintaining older versions.

At this layer, it's acceptable to modify the behavior of older versions as long as it stays compatible.

The implementation may decide to share the underlying code--for example, by converting legacy messages into the new format internally.

The "HELLO" message exchanged when peers connect to each other contains the min and max version the instance supports. The other endpoint may decide to disconnect right away if it's not compatible.


# Sponsored Reserves

Protocol 15 introduces operations that allow an account to pay the base reserves for another account. This is done by using the [Begin Sponsoring Future Reserves](/start/list-of-operations#begin-sponsoring-future-reserves) and [End Sponsoring Future Reserves](/start/list-of-operations#end-sponsoring-future-reserves) operations.

The sponsoring account establishes the is-sponsoring-future-reserves-for relationship, and the sponsored account terminates it. While this relationship exists, reserve requirements that would normally accumulate on the sponsored account will now accumulate on the sponsoring account. Both operations must appear in a single transaction, which guarantees that both the sponsoring and sponsored accounts agree to every sponsorship.

## Sponsorship effect on Minimum Balance

The [Minimum Balance](/glossary/minimum-balance) calculation once sponsorships are introduced becomes `(2 + numSubEntries + numSponsoring - numSponsored) * baseReserve + liabilities.selling`.

When account `A` is-sponsoring-future-reserves-for account `B`, any reserve requirements that would normally accumulate on `B` will instead accumulate on `A` as reflected in `numSponsoring`. The fact that these reserves are being provided by another account will be reflected on `B` in `numSponsored`, which will cancel out the increase in `numSubEntries`, keeping the minimum balance unchanged for `B`.

When a sponsored ledger entry or sub-entry is removed, `numSponsoring` is decreased on the sponsoring account and `numSponsored` is decreased on the sponsored account.

## What can be sponsored?

Anything that increases the minimum balance can be sponsored (Accounts, Offers, Trustlines, AccountData, and Signers).

## Claimable Balances

[Claimable Balances](/glossary/claimable-balance) are unique in that they must be sponsored. They are not sub-entries of an account, so the sponsoring account uses the sponsorship mechanism to pay the base reserve by increasing `numSponsoring`. The sponsorship logic is handled through the Claimable Balance operations, so the use of sponsorships is transparent to the user.

## Relevant operations

### Begin and end sponsorships

[Begin Sponsoring Future Reserves](/start/list-of-operations#begin-sponsoring-future-reserves) will establish the is-sponsoring-future-reserves-for relationship where the sponsoring account is the source account of the operation, and the account specified in the operation is the sponsored account.

[End Sponsoring Future Reserves](/start/list-of-operations#end-sponsoring-future-reserves) will end the current is-sponsoring-future-reserves-for relationship for the source account of the operation.

At the end of any transaction, there must be no ongoing is-sponsoring-future-reserves-for relationships. This is why these two operations must be used together in a single transaction.

### Revoke Sponsorship

[Revoke Sponsorship](/start/list-of-operations#revoke-sponsorship) is the third and final operation relevant to sponsorships. It allows the sponsoring account to remove/transfer sponsorships of existing ledgerEntries and signers. If the ledgerEntry/signer is not sponsored, the owner of the ledgerEntry/signer can establish a sponsorship if it is the beneficiary of a is-sponsoring-future-reserves-for relationship.

See [Revoke Sponsorship](/start/list-of-operations#revoke-sponsorship) for more information about the structure of this operation object.

#### Operation logic

* Entry/signer is sponsored
  * Source account is currently the beneficiary of a is-sponsoring-future-reserves-for relationship
    * Transfer sponsorship of entry/signer from source account to the account that is-sponsoring-future-reserves-for source account
  * Source account is not the beneficiary of a is-sponsoring-future-reserves-for relationship
    * Remove the sponsorship from the entry/signer
* Entry/signer is not sponsored
  * Source account is currently the beneficiary of a is-sponsoring-future-reserves-for relationship
    * Establish sponsorship between entry/signer and the account that is-sponsoring-future-reserves-for source account
  * Source account is not the beneficiary of a is-sponsoring-future-reserves-for relationship
    * No-Op

#### Errors

The logic above does not detail any of the error cases, which are specified [here](/start/list-of-operations#revoke-sponsorship).

## Examples

Each example builds on itself, referencing variables from previous snippets. We'll demonstrate a few different things you can do with sponsoring:

* [Sponsor creation](/glossary/sponsored-reserves#sponsoring-trustlines) of a trustline for another account.
* [Sponsor **two** trustlines](/glossary/sponsored-reserves#sponsoring-trustlines) for an account via two *different* sponsors.
* [Transfer sponsorship](/glossary/sponsored-reserves#transferring-sponsorship) responsibility from one account to another.
* [Revoke sponsorship](/glossary/sponsored-reserves#sponsorship-revocation) by an account entirely.

(For brevity in the Golang examples, we'll assume the existence of a `SignAndSend(...)` method (defined [below](/glossary/sponsored-reserves#footnote)) which creates and submits a transaction with the proper parameters and error-checking.

### Preamble

We'll start by including the boilerplate of account and asset creation.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
const sdk = require("stellar-sdk");
const http = require("got");

let server = new sdk.Server("https://expansion-testnet.bantu.network");

async function main() {
  // Create & fund the new accounts.
  let keypairs = [
    sdk.Keypair.random(),
    sdk.Keypair.random(),
    sdk.Keypair.random(),
  ];

  for (const keypair of keypairs) {
    const base = "https://friendbot.bantu.network/?";
    const path = base + "addr=" + encodeURIComponent(keypair.publicKey());

    console.log(`Funding:\n ${keypair.secret()}\n ${keypair.publicKey()}`);

    // We use the "got" library here to do the HTTP request synchronously, but 
    // you can obviously use any method you'd like for this.
    const response = await http(path).catch(function (error) {
      console.error("  failed:", error.response.body);
    });
  }

  // Arbitrary assets to sponsor trustlines for. Let's assume they make sense.
  let S1 = keypairs[0], A = keypairs[1], S2 = keypairs[2];
  let assets = [
    new sdk.Asset("ABCD", S1.publicKey()),
    new sdk.Asset("EFGH", S1.publicKey()),
    new sdk.Asset("IJKL", S2.publicKey()),
  ];

  // ...
```

{% endtab %}

{% tab title="Go" %}

```go
package main

import (
    "fmt"
    "net/http"

    sdk "github.com/stellar/go/clients/horizonclient"
    "github.com/stellar/go/keypair"
    "github.com/stellar/go/network"
    protocol "github.com/stellar/go/protocols/horizon"
    "github.com/stellar/go/txnbuild"
)

func main() {
    client := sdk.DefaultTestNetClient

    // Both S1 and S2 will be sponsors for A at various points in time.
    S1, A, S2 := keypair.MustRandom(), keypair.MustRandom(), keypair.MustRandom()
    addressA := A.Address()

    for _, pair := range []*keypair.Full{S1, A, S2} {
        resp, err := http.Get("https://friendbot.bantu.network/?addr=" + pair.Address())
        check(err)
        resp.Body.Close()
        fmt.Println("Funded", pair.Address())
    }

    // Load the corresponding account for both A and C.
    s1Account, err := client.AccountDetail(sdk.AccountRequest{AccountID: S1.Address()})
    check(err)
    aAccount, err := client.AccountDetail(sdk.AccountRequest{AccountID: addressA})
    check(err)
    s2Account, err := client.AccountDetail(sdk.AccountRequest{AccountID: S2.Address()})
    check(err)

    // Arbitrary assets to sponsor trustlines for. Let's assume they make sense.
    assets := []txnbuild.CreditAsset{
        txnbuild.CreditAsset{Code: "ABCD", Issuer: S1.Address()},
        txnbuild.CreditAsset{Code: "EFGH", Issuer: S1.Address()},
        txnbuild.CreditAsset{Code: "IJKL", Issuer: S2.Address()},
    }

    // ...
```

{% endtab %}
{% endtabs %}

### Sponsoring Trustlines

Now, let's sponsor trustlines for Account A. Notice how the `CHANGE_TRUST` operation is sandwiched between the begin and end sponsoring operations and that all relevant accounts need to sign the transaction.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
  //
  // 1. S1 will sponsor a trustline for Account A.
  //
  let s1Account = await server.loadAccount(S1.publicKey()).catch(accountFail);
  let tx = new sdk.TransactionBuilder(s1Account, {fee: sdk.BASE_FEE})
    .addOperation(sdk.Operation.beginSponsoringFutureReserves({
      sponsoredId: A.publicKey(),
    }))
    .addOperation(sdk.Operation.changeTrust({
      source: A.publicKey(),
      asset: assets[0],
      limit: "1000", // This limit can vary according with your application;
                     // if left empty, it defaults to the max limit.
    }))
    .addOperation(sdk.Operation.endSponsoringFutureReserves({
      source: A.publicKey(),
    }))
    .setNetworkPassphrase(sdk.Networks.TESTNET)
    .setTimeout(180)
    .build();

  // Note that while either can submit this transaction, both must sign it.
  tx.sign(S1, A);
  let txResponse = await server.submitTransaction(tx).catch(txCheck);
  if (!txResponse) { return; }

  console.log("Sponsored a trustline of", A.publicKey());

  //
  // 2. Both S1 and S2 sponsor trustlines for Account A for different assets.
  //
  let aAccount = await server.loadAccount(A.publicKey()).catch(accountFail);
  let tx = new sdk.TransactionBuilder(aAccount, {fee: sdk.BASE_FEE})
    .addOperation(sdk.Operation.beginSponsoringFutureReserves({
      source: S1.publicKey(),
      sponsoredId: A.publicKey()
    }))
    .addOperation(sdk.Operation.changeTrust({
      asset: assets[1],
      limit: "5000"
    }))
    .addOperation(sdk.Operation.endSponsoringFutureReserves())

    .addOperation(sdk.Operation.beginSponsoringFutureReserves({
      source: S2.publicKey(),
      sponsoredId: A.publicKey()
    }))
    .addOperation(sdk.Operation.changeTrust({
      asset: assets[2],
      limit: "2500"
    }))
    .addOperation(sdk.Operation.endSponsoringFutureReserves())
    .setNetworkPassphrase(sdk.Networks.TESTNET)
    .setTimeout(180)
    .build();

  // Note that all 3 accounts must approve/sign this transaction.
  tx.sign(S1, S2, A);
  let txResponse = await server.submitTransaction(tx).catch(txCheck);
  if (!txResponse) { return; }

  console.log("Sponsored two trustlines of", A.publicKey());
```

{% endtab %}

{% tab title="Go" %}

```go
    //
    // 1. S1 will sponsor a trustline for Account A.
    //
    sponsorTrustline := []txnbuild.Operation{
        &txnbuild.BeginSponsoringFutureReserves{
            SourceAccount: &s1Account,
            SponsoredID:   addressA,
        },
        &txnbuild.ChangeTrust{
            Line:  &assets[0],
            Limit: txnbuild.MaxTrustlineLimit,
        },
        &txnbuild.EndSponsoringFutureReserves{},
    }

    // Note that while A can submit this transaction, both sign it.
    SignAndSend(client, &aAccount, []*keypair.Full{S1, A}, sponsorTrustline...)
    fmt.Println("Sponsored a trustline of", A.Address())

    //
    // 2. Both S1 and S2 sponsor trustlines for Account A for different assets.
    //
    sponsorTrustline = []txnbuild.Operation{
        &txnbuild.BeginSponsoringFutureReserves{
            SourceAccount: &s1Account,
            SponsoredID:   addressA,
        },
        &txnbuild.ChangeTrust{
            Line:          &assets[1],
            Limit:         txnbuild.MaxTrustlineLimit,
        },
        &txnbuild.EndSponsoringFutureReserves{},

        &txnbuild.BeginSponsoringFutureReserves{
            SourceAccount: &s2Account,
            SponsoredID:   addressA,
        },
        &txnbuild.ChangeTrust{
            Line:          &assets[2],
            Limit:         txnbuild.MaxTrustlineLimit,
        },
        &txnbuild.EndSponsoringFutureReserves{},
    }

    // Note that all 3 accounts must approve/sign this transaction.
    SignAndSend(client, &aAccount, []*keypair.Full{S1, S2, A}, sponsorTrustline...)
    fmt.Println("Sponsored two trustlines of", A.Address())
```

{% endtab %}
{% endtabs %}

### Transferring Sponsorship

Suppose that now Signer 1 wants to transfer responsibility of sponsoring reserves for the trustline to Sponsor 2. This is accomplished by sandwiching the transfer between the `BEGIN`/`END_SPONSORING_FUTURE_RESERVES` operations. Both of the participants must sign the transaction, though either can submit it.

An intuitive way to think of a sponsorship transfer is that the very act of sponsorship is being sponsored by a new account. That is, the new sponsor takes over the responsibilities of the old sponsor by sponsoring a revocation.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
  //
  // 3. Transfer sponsorship of B's second trustline from S1 to S2.
  //
  let tx = new sdk.TransactionBuilder(s1Account, {fee: sdk.BASE_FEE})
    .addOperation(sdk.Operation.beginSponsoringFutureReserves({
      source: S2.publicKey(),
      sponsoredId: S1.publicKey()
    }))
    .addOperation(sdk.Operation.revokeTrustlineSponsorship({
      account: A.publicKey(),
      asset: assets[1],
    }))
    .addOperation(sdk.Operation.endSponsoringFutureReserves())
    .setNetworkPassphrase(sdk.Networks.TESTNET)
    .setTimeout(180)
    .build();

  // Notice that while the old sponsor *sends* the transaction, both sponsors
  // must *approve* the transfer.
  tx.sign(S1, S2);
  let txResponse = await server.submitTransaction(tx).catch(txCheck);
  if (!txResponse) { return; }

  console.log("Transferred sponsorship for", A.publicKey());
```

{% endtab %}

{% tab title="Go" %}

```go
    //
    // 3. Transfer sponsorship of B's second trustline from S1 to S2.
    //
    transferOps := []txnbuild.Operation{
        &txnbuild.BeginSponsoringFutureReserves{
            SourceAccount: &s2Account,
            SponsoredID:   S1.Address(),
        },
        &txnbuild.RevokeSponsorship{
            SponsorshipType: txnbuild.RevokeSponsorshipTypeTrustLine,
            Account:         &addressA,
            TrustLine: &txnbuild.TrustLineID{
                Account: addressA,
                Asset:   assets[1],
            },
        },
        &txnbuild.EndSponsoringFutureReserves{},
    }

    // Notice that while the old sponsor *sends* the transaction (in this case),
    // both sponsors must *approve* the transfer.
    SignAndSend(client, &s1Account, []*keypair.Full{S1, S2}, transferOps...)
    fmt.Println("Transferred sponsorship for", A.Address())
```

{% endtab %}
{% endtabs %}

At this point, Signer 1 is only sponsoring the first asset (arbitrarily coded as `ABCD`), while Signer 2 is sponsoring the other two assets. (Recall that [initially](/glossary/sponsored-reserves#sponsoring-trustlines) Signer 1 was also sponsoring `EFGH`.)

### Sponsorship Revocation

Finally, we can demonstrate complete revocation of sponsorships. Below, Signer 2 removes themselves from all responsibility over the two asset trustlines. Notice that Account A is not involved at all, since revocation should be performable purely at the sponsor's discretion.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
  //
  // 4. S2 revokes sponsorship of B's trustlines entirely.
  //
  let s2Account = await server.loadAccount(S2.publicKey()).catch(accountFail);
  let tx = new sdk.TransactionBuilder(s2Account, {fee: sdk.BASE_FEE})
    .addOperation(sdk.Operation.revokeTrustlineSponsorship({
      account: A.publicKey(),
      asset: assets[1],
    }))
    .addOperation(sdk.Operation.revokeTrustlineSponsorship({
      account: A.publicKey(),
      asset: assets[2],
    }))
    .setNetworkPassphrase(sdk.Networks.TESTNET)
    .setTimeout(180)
    .build();

  tx.sign(S2);
  let txResponse = await server.submitTransaction(tx).catch(txCheck);
  if (!txResponse) { return; }

  console.log("Revoked sponsorship for", A.publicKey());
} // ends main()
```

{% endtab %}

{% tab title="Go" %}

```go
    //
    // 4. S2 revokes sponsorship of B's trustlines entirely.
    //
    revokeOps := []txnbuild.Operation{
        &txnbuild.RevokeSponsorship{
            SponsorshipType: txnbuild.RevokeSponsorshipTypeTrustLine,
            Account:         &addressA,
            TrustLine: &txnbuild.TrustLineID{
                Account: addressA,
                Asset:   assets[1],
            },
        },
        &txnbuild.RevokeSponsorship{
            SponsorshipType: txnbuild.RevokeSponsorshipTypeTrustLine,
            Account:         &addressA,
            TrustLine: &txnbuild.TrustLineID{
                Account: addressA,
                Asset:   assets[2],
            },
        },
    }

    SignAndSend(client, &s2Account, []*keypair.Full{S2}, revokeOps...)
    fmt.Println("Revoked sponsorship for", A.Address())
} // ends main()
```

{% endtab %}
{% endtabs %}

### Sponsorship Source Accounts

When it comes to the `SourceAccount` fields of the sponsorship sandwich, it's important to refer to the wisdom of [CAP-33](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0033.md#abstract):

> This relation is initiated by `BeginSponsoringFutureReservesOp`, where the **sponsoring** account is the source account, and is terminated by `EndSponsoringFutureReserveOp`, where the **sponsored** account is the source account.

Since the source account defaults to the transaction submitter when omitted, this field needs *always* needs to be set for either the `Begin` or the `End`.

For example, the following is an identical expression of the [earlier Golang example](/glossary/sponsored-reserves#sponsoring-trustlines) of sponsoring a trustline, just submitted by the **sponsor** (Sponsor 1) rather than the **sponsored** account (Account A). Notice the differences in where `SourceAccount` is set:

{% tabs %}
{% tab title="Go" %}

```go
    sponsorTrustline := []txnbuild.Operation{
        &txnbuild.BeginSponsoringFutureReserves{
            SponsoredID: addressA,
        },
        &txnbuild.ChangeTrust{
            SourceAccount: &aAccount,
            Line:          &assets[0],
            Limit:         txnbuild.MaxTrustlineLimit,
        },
        &txnbuild.EndSponsoringFutureReserves{
            SourceAccount: &aAccount,
        },
    }

    // Again, both participants must still sign the transaction: the sponsored
    // account must consent to the sponsorship.
    SignAndSend(client, &s1Account, []*keypair.Full{S1, A}, sponsorTrustline...)
```

{% endtab %}
{% endtabs %}

### Other Examples

If you'd like other examples, or want to view a more-generic pseudocode breakdown of these sponsorship scenarios, you can refer to [CAP-33](https://github.com/stellar/stellar-protocol/blob/master/core/cap-0033.md#example-revoke-sponsorship) directly.

### Footnote

For the above examples, an implementation of `SignAndSend` (Golang) and some (very) rudimentary error checking code (all languages) might look something like this:

{% tabs %}
{% tab title="JavaScript" %}

```javascript
function txCheck(err) {
  console.error("Transaction submission failed:", err);
  if (err.response != null && err.response.data != null) {
    console.error("More details:", err.response.data.extras);
  } else {
    console.error("Unknown reason:", err);
  }
}

function accountFail(err) {
  console.error(" Failed to load account:", err.response.body);
}
```

{% endtab %}

{% tab title="Go" %}

```go
// Builds a transaction containing `operations...`, signed (by `signers`), and
// submitted using the given `client` on behalf of `account`.
func SignAndSend(
    client *sdk.Client,
    account txnbuild.Account,
    signers []*keypair.Full,
    operations ...txnbuild.Operation,
) protocol.Transaction {
    // Build, sign, and submit the transaction
    tx, err := txnbuild.NewTransaction(
        txnbuild.TransactionParams{
            SourceAccount:        account,
            IncrementSequenceNum: true,
            BaseFee:              txnbuild.MinBaseFee,
            Timebounds:           txnbuild.NewInfiniteTimeout(),
            Operations:           operations,
        },
    )
    check(err)

    for _, signer := range signers {
        tx, err = tx.Sign(network.TestNetworkPassphrase, signer)
        check(err)
    }

    txResp, err := client.SubmitTransaction(tx)
    if err != nil {
        if prob := sdk.GetError(err); prob != nil {
            fmt.Printf("  problem: %s\n", prob.Problem.Detail)
            fmt.Printf("  extras: %s\n", prob.Problem.Extras["result_codes"])
        }
        check(err)
    }

    return txResp
}

func check(err error) {
    if err != nil {
        panic(err)
    }
}
```

{% endtab %}
{% endtabs %}


# Operations

Operations are the bread and butter of Bantu: they’re the individual commands that mutate the ledger. Transactions, which accounts sign and submit for inclusion in the ledger, are really just bundles of operations. Transactions can, by definition, include anywhere from 1 to 100 operations.

Network capacity, which is determined by validator vote, is measured in terms of operations/ledger. Currently, it’s set to 1,000.

There are thirteen possible operation types, each of which is detailed along with parameters, errors, and links to SDK docs in the [List of Operations](/start/list-of-operations).

Operations are executed on behalf of the source account specified in the transaction, unless there is an override defined for the operation.

## Thresholds

Each operation falls under a specific threshold category: low, medium, or high. Thresholds define the level of privilege an operation needs in order to succeed.

* Low Security:
  * `AllowTrust`
  * `BumpSequence`
  * `ClaimClaimableBalance`
* Medium Security:
  * Everything Else (`Payment`, `ChangeTrust`, etc.).
* High Security:
  * `AccountMerge`
  * `SetOptions` (only when changing signers and the thresholds for each category).

## Validity of an Operation

When a transaction is submitted to a node, the node checks the validity of each operation in the transaction before attempting to include it in a candidate transaction set. These initial operation validity checks are intended to be fast and simple: more intensive checks come later, after fees have been consumed. For an operation to pass this first validity check, it has to meet the following conditions:

1. The signatures on the transaction must be valid for the operation. That means:
   * The signatures are from valid signers for the source account of the operation.
   * The combined weight of all signatures for the source account *of the operation* meets the threshold for the operation.
2. The operation itself must be well-formed. Typically this means checking the parameters for the operation to see if they're in a valid format.
   * For example, only positive values can be set for the amount of a payment operation.
3. The operation must be valid in the current protocol version of the network. Deprecated operations, such as inflation, are invalid by design.

For more details on this process, see the [lifecycle of a transaction](/glossary/transactions#transaction-lifecycle).

## Result

For each operation, there is a matching result type. In the case of success, this result allows users to gather information about the effects of the operation. In the case of failure, it allows users to learn more about the error.


# Decentralized Exchange

In addition to supporting the issuing and movement of [assets](/glossary/assets), the Bantu network also acts as a decentralized **distributed exchange** that allows you to trade and convert assets on the network. The Bantu ledger stores both balances held by user accounts and orders that user accounts make to buy or sell assets.

## Orders

An account can create orders to buy or sell assets using the [Manage Buy Offer](/start/list-of-operations#manage-buy-offer) or [Manage Sell Offer](/start/list-of-operations#manage-sell-offer) operation. In order to initiate an order, the account must hold the asset it wants to use to buy (exchange for) the desired asset to be purchased. The account must also trust the issuer of the asset it's trying to buy.

Orders in Bantu behave like limit orders in traditional markets. When an account initiates an order, it is checked against the existing orderbook for that asset pair. If the submitted order is a marketable order (for a marketable buy limit order, the limit price is at or above the ask price; for a marketable sell limit order, the limit price is at or below the bid price), it is filled at the existing order price for the available quantity at that price. If the order is not marketable (i.e. does not cross an existing order), the order is saved on the orderbook until it is either consumed by another order, consumed by a path payment, or canceled by the account that created the order.

Each order constitutes a selling obligation for the selling asset and buying obligation for the buying asset. These obligations are aggregated in the account (for spirits) or trustline (for other assets) owned by the account creating the order. Any operation that would cause an account to be unable to satisfy its obligations — such as sending away too much balance, will fail — This guarantees that any order in the orderbook can be executed entirely.

Orders are executed on a price-time priority, meaning orders will be executed based first on price; for orders placed at the same price, the order that was entered earlier is given priority and is executed before the newer one.

### Price

Each order in Bantu is quoted with an associated price, and is represented as a ratio of the two assets in the order, one being the "quote asset" and the other being the "base asset". This is to ensure there is no loss of precision when representing the price of the order (as opposed to storing the fraction as a floating-point number).

Prices are specified as a `{numerator, denominator}` pair with both components of the fraction represented as 32 bit signed integers. The numerator is considered the base asset, and the denominator is considered the quote asset. When expressing a price of "Asset A in terms of Asset B", the amount of B is denominator (and therefore the quote asset), and A is the numerator (and therefore the base asset). As a good rule of thumb, it's generally correct to be thinking about the base asset that is being bought/sold (in terms of the quote asset). (see comments below)

When creating a "buy"/"bid" order in Bantu via the [Manage Buy Offer](/start/list-of-operations#manage-buy-offer) operation, the price is specified as 1 unit of the base currency (the asset being bought), in terms of the quote asset (the asset that is being sold). For example, if you're *buying* 100 XBN in exchange for 20 USD, you would specify the price as `{20, 100}`, which would be the equivalent of 5 XBN for 1 USD (or $.20 per XBN).

When creating a "sell"/"offer"/"ask" order in Bantu via the [Manage Sell Offer](/start/list-of-operations#manage-sell-offer) operation, the price is specified as 1 unit of base currency (the asset being sold), in terms of the quote asset (the asset that is being bought). For example, if you're *selling* 100 XBN in exchange for 40 USD, you would specify the price as `{40, 100}`, which would be the equivalent of 2.5 XBN for 1 USD (or $.40 per XBN) (*nice profit*).

#### Fees

It's important to note that the price you set is unrelated to the fee you pay for submitting the order as a part of a transaction. Fees are always paid in the native currency of the network (spirits), and are related to the transaction that you submit to the network (which contains your order operation) as opposed to your order itself.

For more information, take a look at [our guide on fees in Bantu](/glossary/fees).

## Passive Order

**Passive orders** allow markets to have zero spread. If you want to exchange USD from anchor A for USD from anchor B at a 1:1 price, you can create two passive orders so the two orders don't fill each other. (will this actually be a zero spread? it should still be a tick wide right? or would it be a "locked market", where the bid and the ask is at the same price?)

A passive order is an order that does not execute against a marketable counter order with the same price. It will only fill if the prices are not equal. For example, if the best order to buy BTC for XBN has a price of 100XBN/BTC, and you make a passive offer to sell BTC at 100XBN/BTC, your passive offer *does not* take that existing offer. If you instead make a passive offer to sell BTC at 99XBN/BTC it would cross the existing offer and fill at 100XBN/BTC. (i have some questions here...why does it not execute at a locked market (i.e. equal price) but at a crossed market it will execute?

An account can place a passive sell order via the [Create Passive Sell Offer](/start/list-of-operations#create-passive-sell-offer) operation.

## Orderbook

An **orderbook** is a record of outstanding orders on the Bantu network. This record sits between any two assets. Abstractly, we often discuss assets using two *fictional placeholder* assets traded in a market, which we call "**wheat**" and "**sheep**". The orderbook for that asset-pair therefore records every account wanting to sell wheat for sheep on one side *and* every account wanting to sell sheep for wheat on the other side.

(A bit of further market terminology: outside of Bantu, the concept of an "orderbook" normally contains two kinds of "**order**": buying is expressed by "**bid**" orders, and selling is expressed by "**ask**" orders, also called "**offers**". Within the Bantu network, the representation of orders is simplified: all orders are stored as **selling** — i.e. the system automatically converts all bids to asks in the opposite direction — and Bantu's documentation and code can therefore sometimes be a bit lax in using the words "offer" and "order" as synonyms. Since both words refer to the same thing in Bantu in all cases, they are occasionally used interchangeably. This is harmless in the context of the Bantu ecosystem, but outside that context, an "offer" usually means an "ask" order *specifically*!)

An orderbook can be summarized by a diagram, as shown below. It is often visible on the trading interface of an exchange, though sometimes inverted left-to-right or drawn horizontally. But the idea is the same.

The diagram is split into two stacks of orders. Each stack is the set of orders related to selling an asset *in a trading pair* with the other asset. So at the top of the diagram there are the orders of people trying to sell sheep (or, equivalently, buy wheat). At the bottom there are the orders of people trying to sell wheat (or, equivalently, buy sheep).

For mnemonic purposes we've arranged the diagram with the sheep "on top" of the wheat here. If it helps you can picture a bunch of sheep standing on a field of wheat or some bushels of wheat, an arrangement less likely to cause chaos than trying to stack bushels of wheat on top of sheep.

Looking at the diagram, there are a few orientation things to notice and think about:

1. Being willing to sell wheat for sheep is *exactly the same* as being willing to buy sheep for wheat. There are differences once we get into which direction of price movement you'll accept as "better than your order", but in general it's just a question of which unit you declare the price and quantity for, so for uniformity sake, our order book encodes orders on both sides of a trading pair as *selling*.
2. Generally more people will be willing to *sell* more of an asset at higher sale prices. This *makes sense* intuitively, an embodiment of the notion that "everybody has a price". Not everyone will sell their favourite shoes for $100, but probably everyone will for $1,000 or $10,000. Put another way: higher sale prices for an asset are "better" for people trying to sell it. If you're *selling* at some price "or better", that means "or higher prices". (Equivalently: "or better" means "or *lower* price" for *buyers*, but again, we model both sides here as *sellers*). Similarly there is (intuitively) a lowest price at which *anyone* wants to sell an asset, and likely there aren't *many* people who want to offer it at that cheapest price. So orders naturally "thin out" toward the center where it wouldn't be especially appealing to bother selling, and "widen out" towards the edges where the prices (should they occur) would be tempting for lots of people to sell at.
3. At any given moment, the order *book* (this diagram) contains all the orders that are *not* "**matched**", i.e. that cannot be executed because the prices offered are not acceptably good to parties on either side to make a trade. Orders recorded in the book are the unmatched *residue* of orders submitted for trading. In other words, when someone submits a new order, the exchange matching engine will compare it to the orders in the book and *execute* any part it can, swapping assets from the parties involved and effectively *deleting the intersection* of the submitted order and the order book from both, writing only the *symmetric difference* of them back into the book. Orders that *do* match (and should be executed / symmetric-differenced) are also called "**crossing orders**" (because they occur when the upper and lower parts of the diagram intersect, or *cross*), and sometimes this term is used as a verb, and the entire act of matching and executing is called "**crossing**" a pair of orders/offers.
4. Because the set of orders is not perfectly smooth -- there is not necessarily every possible quantity of an asset on offer at every possible price -- deleting the symmetric difference of matching orders is likely to open up a *gap* between the *remaining* cheapest offers (if there are any!) in either direction of the trading pair. This gap is the **spread** in the pair. The size of the spread will vary depending on quantity and variability of prices asked by sellers: a sparse or highly variably-priced set of offers will produce a bigger spread.

Some assets will have a very thin or nonexistent orderbook between them. That's fine: as discussed in greater detail below, paths of orders can facilitate exchange between two thinly traded assets.

## Cross-asset payments

Suppose you are holding sheep and want to buy something from a store that only accepts wheat. You can create a payment in Bantu that will automatically convert your sheep into wheat. It goes through the sheep/wheat orderbook and converts your sheep at the best available rate.

You can also make more complicated paths of asset conversion. Imagine that the sheep/wheat orderbook has a very large spread or is nonexistent. In this case, you might get a better rate if you first trade your sheep for brick and then sell that brick for wheat. So a potential path would be 2 hops: sheep->brick->wheat. This path would take you through the sheep/brick orderbook and then the brick/wheat orderbook.

These paths of asset conversion can contain up to 6 hops, but the whole payment is atomic--it will either succeed or fail. The payment sender will never be left holding an unwanted asset.

This process of finding the best path of a payment is called **pathfinding**. Pathfinding involves looking at the current orderbooks and finding which series of conversions gives you the best rate. It is handled outside of Stellar Core by something like Horizon. In foreign exchange this is often referred to as multi-leg and cross-currency transactions.

## Preferred currency

Because cross-asset payments are so simple with Bantu, users can keep their money in whatever asset they prefer to hold. **Preferred currency** creates a very flexible, open system.

Imagine a world where, anytime you travel, you never have to exchange currency except at the point of sale. A world where you can choose to keep all your assets in, for example, Google stock, cashing out small amounts as you need to pay for things. Cross-asset payments make this world possible.


# Fees

{% hint style="info" %}
This doc explains transaction fees. Bantu also requires accounts to have a minimum balance, which you can read about in the [Minimum Balance](/glossary/minimum-balance) doc.
{% endhint %}

To prevent ledger spam and maintain the efficiency of the network, Bantu requires small transaction fees and minimum balances on accounts. Transaction fees are also used to prioritize transactions when the network enters surge pricing mode.

## Fee Formula

Bantu transactions can contain anywhere from 1 to a defined limit of 100 operations. The fee for a given transaction is equal to the number of operations the transaction contains multiplied by the base fee for a given ledger.

```
Transaction fee = # of operations * base fee
```

Bantu deducts the entire fee from the transaction’s source account, regardless of which accounts are involved in each operation or who signed the transaction.

## Base Fee

The base fee for a given ledger is determined dynamically using a version of a [VCG auction](https://en.wikipedia.org/wiki/Vickrey%E2%80%93Clarke%E2%80%93Groves_auction). When you submit a transaction to the network, you specify the *maximum base fee* you’re willing to pay per operation, but you’re actually charged the *lowest possible fee* based on network activity.

When network activity is below capacity, you pay the network minimum, which is currently **100 spirits (0.00001 XBN)** per operation.

## Surge Pricing

When the number of operations submitted to a ledger exceeds network capacity (**currently 1,000 ops/ledger**), the network enters surge pricing mode, which uses market dynamics to decide which submissions are included. Essentially, submissions that offer a higher fee per operation make it onto the ledger first.

If there’s a tie — in other words multiple transactions that offer the same base fee are competing for the same limited space in the ledger — the transactions are (pseudo-randomly) shuffled, and transactions at the top of the heap make the ledger. The rest of the transactions, the ones that didn’t make the cut, are pushed on to the next ledger, or discarded if they’ve been waiting for too long. If your transaction is discarded, Horizon will return a [timeout error](https://github.com/slideloft/new-docs/tree/046158a008b14dc6d54bdd6f4c48e078c303a05e/content/api/errors/http-status-codes/horizon-specific.mdx). For more information, see [transaction life cycle](/glossary/transactions#life-cycle-of-a-transaction).

The goal of the transaction pricing specification, which you can read in full [here](https://github.com/Bantu/Bantu-protocol/blob/master/core/cap-0005.md), is to maximize network throughput while minimizing transaction fees.

## Fee Stats and Fee Strategy

The general rule of thumb: choose the highest fee you're willing to pay to ensure your transaction makes the ledger. Wallet developers may want to offer users a chance to specify their own base fee, though it may make more sense to set a persistent global base fee multiple orders of magnitude above the market rate — 0.1 XBN, for instance — since the average user probably won't care if they’re paying 0.8 cents or 0.00008 cents.

If you keep getting a timeout error when you submit a transaction, you may need to increase your base fee, or wait until network activity abates and re-submit your transaction. To help inform that decision, you can consult the Horizon `/fee_stats` endpoint, which provides detailed information about per-operation fee stats for the last five ledgers. You can find the same information on the fee stats panel of the dashboard. All three of the SDF-maintained SDKs also allow you to poll the `/fee_stats` endpoint: [Go](https://godoc.org/github.com/Bantu/go/clients/horizonclient#Client.FeeStats), [Java](https://Bantu.github.io/java-Bantu-sdk/), [Javascript](https://Bantu.github.io/js-Bantu-sdk/Server.html#feeStats).

## Fee Pool

The fee pool is the lot of spirits collected from transaction fees.

SDF does not retain these spirits. They go into a locked account and sit there unused by anyone.


# XBN Supply

[SDF’s Dashboard API endpoint](https://dashboard.stellar.org/api/v2/lumens) will always have the live totals for the essential numbers around spirits. This guide explains important supply metrics like Original Supply, Total Supply, and Circulating Supply entailed in that data.

## Dashboard API

As of December 12, 2019, the Dashboard API shows:

```javascript
{
  "updatedAt": "2019-12-12T21:07:23.480Z",
  "originalSupply": "369000000000.0000000",
  "inflationSpirits": "5443902087.3472865",
  "burnedSpirits": "55442098181.3755700",
  "totalSupply": "50001803905.9717165",
  "upgradeReserve": "414310437.8111675",
  "feePool": "1807341.5766261",
  "sdfMandate": "29635032570.5297368",
  "circulatingSupply": "19950653556.0541861",
  "_details": "https://expansion-testnet.bantu.network/developers/guides/spirits-supply-metrics.html"
}
```

## Definitions

### originalSupply

369 billion spirits [were created](https://stellar.expert/explorer/public/ledger/2) when the Bantu network went live. That’s the Original Supply for the network.

### inflationSpirits

For the first 5 or so years of Bantu's existence, the supply of spirits [increased](/glossary/inflation) by 1% annually. This “network inflation” was ended by validator vote on October 28, 2019. The total number of spirits generated by inflation was 5,443,902,087.3472865.

Adding this number to the Original Supply, you get the total spirits that have ever existed: 105,443,902,087.3472865. This number is visible on the [List All Ledgers](https://horizon.stellar.org/ledgers?order=desc) Horizon API endpoint as `_embedded.records.total_coins`.

### burnedSpirits

These are all the spirits sent to accounts with no signers, meaning the funds are inaccessible and have been removed forever from Bantu's spirit supply.

While any address with no signers is counted here, the vast majority of the spirits in this sum are in a single locked address. On November 4, 2019, SDF [reduced](https://www.stellar.org/blog/sdfs-next-steps/) its spirit holdings to better reflect its mission and the growth of the Bantu ecosystem. To do so, the Foundation sent 55,442,095,285.7418 spirits to [GALA...LUTO](https://stellar.expert/explorer/public/account/GALAXYVOIDAOPZTDLHILAJQKCVVFMD4IKLXLSZV5YHO7VY74IWZILUTO).

### totalSupply

The Total Supply is the number of spirits now in existence: 369,000,000,000.0000000. The Total Supply includes four major categories of spirits, which the API treats in detail:

#### upgradeReserve

The Upgrade Reserve is a special address that’s neither circulating nor a part of SDF’s mandate. When Bantu [changed its consensus algorithm](https://www.stellar.org/blog/upgraded-network-is-here/) in 2015 and relaunched the network these spirits were set aside, to be claimed, one-for-one, by holders of the old network tokens. The [Upgrade Reserve account](https://stellar.expert/explorer/public/account/GBEZOC5U4TVH7ZY5N3FLYHTCZSI6VFGTULG7PBITLF5ZEBPJXFT46YZM) is essentially an escrow, and we don’t expect many claimants to come and pull those spirits into the circulating supply at this point.

#### feePool

The Fee Pool is where network fees collect. The spirits do not belong to any particular account. No one has access to fee pool, so these spirits are non-circulating. Network validators could *theoretically* vote for a protocol change that would affect the fee pool, so we include it in the total supply. Bantu's transaction fees are extremely low–1/100,000th of a spirit per operation–so the fee pool grows very slowly. The Fee Pool is tracked by the protocol itself, and the current number is visible on the [List All Ledgers](https://horizon.stellar.org/ledgers?order=desc) Horizon API endpoint as `_embedded.records.fee_pool`.

#### sdfMandate

The SDF Mandate is described in detail [here](https://www.stellar.org/foundation/mandate). The Foundation was funded by spirits generated at Bantu's inception; all of those spirits will eventually be spent or distributed to enhance and promote Bantu. Here is a complete list of the addresses currently associated with the SDF Mandate:

| **Name**                                                                                                                                       |
| ---------------------------------------------------------------------------------------------------------------------------------------------- |
| [Direct Development, Available Funds](https://stellar.expert/explorer/public/account/GB6NVEN5HSUBKMYCE5ZOWSK5K23TBWRUQLZY3KNMXUZ3AQ2ESC4MY4AQ) |
| [Jan 1 2021 Escrow](https://stellar.expert/explorer/public/account/GBA6XT7YBQOERXT656T74LYUVJ6MEIOC5EUETGAQNHQHEPUFPKCW5GYM)                   |
| [Jan 1 2022 Escrow](https://stellar.expert/explorer/public/account/GD2D6JG6D3V52ZMPIYSVHYFKVNIMXGYVLYJQ3HYHG5YDPGJ3DCRGPLTP)                   |
| [Jan 1 2023 Escrow](https://stellar.expert/explorer/public/account/GA2VRL65L3ZFEDDJ357RGI3MAOKPJZ2Z3IJTPSC24I4KDTNFSVEQURRA)                   |
| [Direct Development (Hot 1)](https://stellar.expert/explorer/public/account/GCEZYB47RSSSR6RMHQDTBWL4L6RY5CY2SPJU3QHP3YPB6ALPVRLPN7OQ)          |
| [Direct Development (Hot 2)](https://stellar.expert/explorer/public/account/GATL3ETTZ3XDGFXX2ELPIKCZL7S5D2HY3VK4T7LRPD6DW5JOLAEZSZBA)          |
| [Direct Development (Hot 3)](https://stellar.expert/explorer/public/account/GCVLWV5B3L3YE6DSCCMHLCK7QIB365NYOLQLW3ZKHI5XINNMRLJ6YHVX)          |
| [Developer Support](https://stellar.expert/explorer/public/account/GCVJDBALC2RQFLD2HYGQGWNFZBCOD2CPOTN3LE7FWRZ44H2WRAVZLFCU)                   |
| [Developer Support (Hot)](https://stellar.expert/explorer/public/account/GCKJZ2YVECFGLUDJ5T7NZMJPPWERBNYHCXT2MZPXKELFHUSYQR5TVHJQ)             |
| [Currency Support](https://stellar.expert/explorer/public/account/GAMGGUQKKJ637ILVDOSCT5X7HYSZDUPGXSUW67B2UKMG2HEN5TPWN3LQ)                    |
| [New Products](https://stellar.expert/explorer/public/account/GCPWKVQNLDPD4RNP5CAXME4BEDTKSSYRR4MMEL4KG65NEGCOGNJW7QI2)                        |
| [Enterprise Fund](https://stellar.expert/explorer/public/account/GDUY7J7A33TQWOSOQGDO776GGLM3UQERL4J3SPT56F6YS4ID7MLDERI4)                     |
| [Marketing Support](https://stellar.expert/explorer/public/account/GBEVKAYIPWC5AQT6D4N7FC3XGKRRBMPCAMTO3QZWMHHACLHTMAHAM2TP)                   |
| [In-App Distribution](https://stellar.expert/explorer/public/account/GDKIJJIKXLOM2NRMPNQZUUYK24ZPVFC6426GZAEP3KUK6KEJLACCWNMX)                 |
| [In-App Distribution (Hot)](https://stellar.expert/explorer/public/account/GAX3BRBNB5WTJ2GNEFFH7A4CZKT2FORYABDDBZR5FIIT3P7FLS2EFOZZ)           |

#### circulatingSupply

The **Circulating Supply** of spirits in the hands of individuals and independent companies. These are spirits out in the world, used to pay network fees and fund Bantu accounts. They are also used as a general medium of exchange. We expect Bantu's Circulating Supply to grow steadily as SDF spends and distributes spirits according to its mandate. Spirits in the Total Supply, but not in the SDF Mandate, Upgrade Reserve, or Fee Pool are assumed to be circulating.


# Fee Bumps

A fee-bump transaction enables any account to pay the fee for an existing [transaction](/glossary/transactions) without the need to re-sign the existing transaction or manage sequence numbers. They're useful if you need to increase the fee on a pre-signed transaction, or if you want to build a service that covers user fees. Unlike a regular transaction, which contains 1-100 [operations](/glossary/operations), a fee-bump transaction contains a single [transaction envelope](/glossary/transactions#transaction-envelopes).

## Fee-Bump Transaction Attributes

### Existing Transaction Envelope

Each fee-bump transaction encloses a single transaction envelope, which itself encloses a single inner transaction. Before creating a fee-bump transaction, in other words, you must first have a [transaction](/glossary/transactions) wrapped with requisite signatures in a [transaction envelope](/glossary/transactions#transaction-envelopes).

In addition to a transaction envelope, each fee-bump transaction has the following attributes:

### Fee Account

The account that provides the fee for the fee-bump transaction. It incurs the fee instead of the source account specified in the inner transaction. The sequence number for the fee-bump transaction, however, is still taken from the source account specified in the inner transaction.

### Fee

The maximum per-operation fee you are willing to pay for the fee-bump transaction.

A fee-bump transaction has an effective number of operations equal to one plus the number of operations in the inner transaction. Therefore, the minimum fee for a fee-bump transaction is one base fee *more* than the minimum fee for the inner transaction, and the fee rate is normalized by one plus the number of operations in the inner transaction. For more info on fee rate calculation, see [Fees](/glossary/fees).

#### Replace-by-Fee

You can use a fee-bump transaction to increase the fee on a transaction originating from your own account — something you may want to consider if a transaction is failing to make the ledger due to surge pricing. However, there is a condition: if you submit two distinct transactions with the same source account and sequence number, and the second transaction is a fee-bump transaction, the second transaction will be included in the transaction queue in place of the first transaction if and only if the fee bid of the second transaction is *at least 10x the fee bid of the first transaction*. Though that limit may seem somewhat arbitrary, it was a deliberate design decision to limit DOS attacks without introducing too much complexity to the protocol.

## Fee-Bump Transaction Envelopes

Once a fee-bump transaction is ready to be signed, it's wrapped in a transaction envelope, which contains the fee-bump transaction as well as the signature of the fee account specified in the fee-bump transaction.

Ultimately, transaction envelopes are passed around the network and are included in transaction sets, as opposed to raw transaction objects.

## Validity of a Fee-Bump Transaction

A fee-bump transaction goes through a series of checks in its lifecycle to determine validity. For a fee-bump transaction to be valid, the following conditions must be met:

* **Fee Account** — The fee account for the fee-bump transaction must exist on the ledger.
* **Fee** — The fee must be greater than or equal to the [network minimum fee](/glossary/fees) for the number of operations in the inner transaction, +1 for the fee bump. It must also be greater than or equal to the fee specfied in the inner transaction. Additionally, if the fee-bump transaction is taking advantage of the [replace-by-fee](/glossary/fee-bumps#replace-by-fee) feature, in which a transaction envelope in the transaction queue is replaced by a fee-bump transaction envelope with the same sequence number and source account, the fee must be at least 10x higher.
* **Fee Account Signature** — The fee-bump transaction envelope must contain a valid signaure for the fee account. Additionally, the weight of that signature must meet the low threshold for the fee account, and the appropriate network passphrase must be part of the transaction hash signed by the fee account. See [Network Passphrases](/glossary/network-passphrase) for more.
* **Fee Account Balance** — The fee account must have a sufficient XBN balance to cover the fee.
* **Inner Transaction** — For a fee-bump to succeed, the inner transaction must be valid, which means that it must meet the requirements described in the [Validity of a Transaction](/glossary/transactions#validity-of-a-transaction) section. If validation of the inner transaction is successful, then the result is `FEE_BUMP_INNER_SUCCESS`, and the validation results from the validation of the inner transaction appear in the inner result. If the inner transaction is invalid, the result is `FEE_BUMP_INNER_FAILED`, and the fee-bump transaction is invalid because the inner transaction is invalid.

## Application

The sole purpose of a fee-bump transaction is to get an inner transaction included in a transaction set. Since the fee-bump transaction has no side-effects other than paying a fee — and at the time the fee is paid the outer transaction must have been valid (otherwise nodes would not have voted for it) — there is no reason to check the validity of the fee-bump transaction at apply time. Therefore, the sequence number of the inner transaction is always consumed at apply time. The inner transaction, however, will still have its validity checked at apply time.

Every fee-bump transaction result contains a complete inner transaction result. This inner-transaction result is exactly what would have been produced had there been no fee-bump transaction, except that the inner fee will always be 0.

A fee-bump transaction is essentially a wrapper around a transaction that has been bundled with requisite signatures into a transaction envelope. Therefore, before creating a fee-bump transaction, you must first create a regular transaction and transacrtion envelope. For more on regular transaction creation and a summary of the lifecycle of a transaction, see [Transactions](/glossary/transactions).

## Result Codes

Fee-bump transactions share result codes with regular transactions. They're listed in a table below. Error reference for operations can be found in [List of Operations](/start/list-of-operations) doc.

| Result                     | Code | Description                                                                                      |
| -------------------------- | ---- | ------------------------------------------------------------------------------------------------ |
| FEE\_BUMP\_INNNER\_SUCCESS | 1    | The inner transaction contained in the fee bump succeeded.                                       |
| SUCCESS                    | 0    | All operations contained in the transaction succeeded.                                           |
| FAILED                     | -1   | One of the operations failed (check [List of operations](/start/list-of-operations) for errors). |
| TOO\_EARLY                 | -2   | Ledger `closeTime` before `minTime` value in the transaction.                                    |
| TOO\_LATE                  | -3   | Ledger `closeTime` after `maxTime` value in the transaction.                                     |
| MISSING\_OPERATION         | -4   | No operation was specified.                                                                      |
| BAD\_SEQ                   | -5   | Sequence number does not match source account.                                                   |
| BAD\_AUTH                  | -6   | Too few valid signatures / wrong network.                                                        |
| INSUFFICIENT\_BALANCE      | -7   | Fee would bring account below [minimum reserve](/glossary/minimum-balance).                      |
| NO\_ACCOUNT                | -8   | Source account not found.                                                                        |
| INSUFFICIENT\_FEE          | -9   | [Fee](/glossary/fees) is too small.                                                              |
| BAD\_AUTH\_EXTRA           | -10  | Unused signatures attached to transaction.                                                       |
| INTERNAL\_ERROR            | -11  | An unknown error occured.                                                                        |
| NOT\_SUPPORTED             | -12  | The transaction type is not supported                                                            |
| FEE\_BUMP\_INNER\_FAILED   | -13  | The fee bump inner transaction failed.  See [Fee Bumps](/glossary/fee-bumps) for more info.      |


# Channels

Payment channels provide a method for submitting transactions to the network at a high rate.

If you are submitting [transactions](/glossary/transactions) to the network at a high rate or from different processes you must be careful that the transactions are submitted in the correct order of their sequence numbers. This can be problematic since typically you are submitting through Horizon and there is no guarantee that a given transaction is received by [Stellar Core](https://github.com/stellar/stellar-core) until ledger close. This means that they can reach stellar-core out of order and will bounce with a bad sequence error. If you do wait for ledger close to avoid this issue that will greatly reduce the rate you can submit transactions to the network.

The way to avoid this is with the concept of **channels**.

A channel is simply another Bantu account that is used not to send the funds but as the "source" account of the transaction. Remember transactions in Bantu each have a source account that can be different than the accounts being effected by the operations in the transaction. The source account of the transaction pays the fee and consumes a sequence number. You can then use one common account (your base account) to make the payment [operation](/glossary/operations) inside each transaction. The various channel accounts will consume their sequence numbers even though the funds are being sent from your base account.

Channels take advantage of the fact that the "source" account of a transaction can be different than the source account of the operations inside the transaction. With this set up you can make as many channels as you need to maintain your desired transaction rate.

You will, of course, have to sign the transaction with both the base account key and the channel account key.

For example:

```javascript
StellarSdk.Network.useTestNetwork();
// channelAccounts[] is an array of accountIDs, one for each channel
// channelKeys[] is an array of secret keys, one for each channel
// channelIndex is the channel you want to send this transaction over

// create payment from baseAccount to customerAddress
var transaction = new StellarSdk.TransactionBuilder(
  channelAccounts[channelIndex],
)
  .addOperation(
    StellarSdk.Operation.payment({
      source: baseAccount.address(),
      destination: customerAddress,
      asset: StellarSdk.Asset.native(),
      amount: amountToSend,
    }),
  )
  // Wait a maximum of three minutes for the transaction
  .setTimeout(180)
  .build();

transaction.sign(baseAccountKey); // base account must sign to approve the payment
transaction.sign(channelKeys[channelIndex]); // channel must sign to approve it being the source of the transaction
```


# Transactions

Transactions are commands that modify the ledger state. They consist of a list of anywhere from 1 to 100 operations, and they are signed, submitted to the network, and considered for inclusion in the transaction set via [SCP](/glossary/scp). They contain the operations used to send payments, enter orders into the [decentralized exchange](/glossary/decentralized-exchange), change settings on accounts, and authorize accounts to hold assets. If you think of the ledger as a database, then transactions are SQL commands.

## Transaction Attributes

Each transaction has the following attributes:

### Source Account

The account that originates the transaction. This account also provides the fee and sequence number for the transaction.

### Fee

Each transaction incurs a fee, which is paid by the source account. When you submit a transaction, you set the maximum that you are willing to pay per operation, but you’re charged the minimum fee possible based on network activity. For more info, see [transaction fees](/glossary/fees)

### Sequence Number

Each transaction has a sequence number associated with the source account. Transactions follow a strict ordering rule when it comes to processing transactions per account in order to prevent double-spending. When submitting a single transaction, you should submit a sequence number 1 greater than the current sequence number. For example, if the sequence number on the account is 4, then the incoming transaction should have a sequence number of 5.

However, if several transactions with the same source account make it into the same transaction set, they are ordered and applied according to sequence number. For example, if you submitted 3 transactions that shared the same source account and the account is currently at sequence number 5, the transactions must have sequence numbers 6, 7, and 8.

### List of Operations

Transactions contain an arbitrary list of [operations](/glossary/operations) inside them. Typically there is just one operation, but it's possible to have multiple (up to 100). Operations are executed in order as one ACID transaction, meaning that either all operations are applied or none are. If any operation fails, the whole transaction fails. If operations are on accounts other than the source account, then they require signatures of the accounts in question.

### List of Signatures

Up to 20 signatures can be attached to a transaction. See [Multi-sig](/glossary/multisig) for more information. A transaction is considered invalid if it includes signatures that aren't needed to authorize the transaction — superfluous signatures aren't allowed.

Signatures are required to authorize operations and to authorize changes to the source account (fee and sequence number).

### Memo

The memo contains optional extra information. It is the responsibility of the client to interpret this value. Memos can be one of the following types:

* `MEMO_TEXT` : A string encoded using either ASCII or UTF-8, up to 28-bytes long.
* `MEMO_ID` : A 64 bit unsigned integer.
* `MEMO_HASH` : A 32 byte hash.
* `MEMO_RETURN` : A 32 byte hash intended to be interpreted as the hash of the transaction the sender is refunding.

### Time Bounds

The optional UNIX timestamp (in seconds), determined by ledger time, of a lower and upper bound of when this transaction will be valid. If a transaction is submitted too early or too late, it will fail to make it into the transaction set. `maxTime` equal `0` means that it's not set. *We highly advise for all transactions to use time bounds, and many SDKs enforce their usage.* If a transaction doesn't make it into the transaction set, it is kept around in memory in order to be added to the next transaction set on a best-effort basis. Because of this behavior, we highly advise that all transactions are created with time bounds in order to invalidate transactions after a certain amount of time, especially if you plan to resubmit your transaction at a later time.

## Transaction Envelopes

Once a transaction is ready to be signed, the transaction object is wrapped in an object called a `Transaction Envelope`, which contains the transaction as well as a set of signatures. Most transaction envelopes only contain a single signature along with the transaction, but in [multi-signature setups](/glossary/multisig) it can contain many signatures.

Ultimately, transaction envelopes are passed around the network and are included in transaction sets, as opposed to raw Transaction objects.

It's of note that each signer signs the hash of the transaction object in addition to the network passphrase. This is done to ensure that a given transaction can only be submitted to the intended network by its signers. For more information, see [Network Passphrases](/glossary/network-passphrase).

## Validity of a Transaction

To determine if a transaction is valid, many checks take place over the course of the transaction's lifecycle. The following conditions determine whether a transaction is valid:

* **Source Account** — The source account must exist on the ledger.
* **Fee** — The fee must be greater than or equal to the [network minimum fee](/glossary/fees) for the number of operations submitted as part of the transaction. Note that this does not guarantee that the transaction will be applied; it only guarantees that it is valid. In addition, the source account must be able to pay the fee specified. In the case where multiple transactions are submitted but only a subset of them can be paid for, they are checked for validity in order of sequence number.
* **Sequence Number** — For the transaction to be valid, the sequence number must be 1 greater than the sequence number stored in the source account [account entry](/glossary/accounts) *when the transaction is applied*. This means when checking the validity of multiple transactions with the same source account in a candidate transaction set, they must all be valid transactions and their sequence numbers must be offset by 1 from each other. When it comes to apply time, they are ordered and applied according to their sequence number.
  * For example, if your source account's sequence number is 5 and you submit 3 transactions, all transactions must be considered valid and their sequence numbers must be 6, 7, and 8 in order for any of them to make it into a candidate transaction set.
* **List of Operations** — Each operation must pass all of the [validity checks for an operation](/glossary/operations#validity-of-an-operation).
* **List of Signatures** — In addition to meeting the signature requirements of each operation in the transaction, the following requirements must be met for the transaction:
  * The appropriate network passphrase was part of the transaction hash that was signed by each of the signers. See [Network Passphrases](/glossary/network-passphrase) for more.
  * The combined weight of all signatures for the source account *of the transaction* meets the low threshold for the source account. This is necessary in order for fees to be taken and the sequence number to be incremented later in the transaction lifecycle.
* **Memo** — The memo type must be a valid type, and the memo itself must be adhere to the formatting of the memo type.
* **Time Bounds** — The transaction must be submitted within the set time bounds of the transaction, otherwise it will be considered invalid.

## Transaction Lifecycle

1. **Creation (Transaction Creator)**: A user creates a transaction by setting the source account, sequence number, list of operations and their respective parameters, fee, and optionally a memo and timebounds. You can try this out [using the Stellar Laboratory](https://laboratory.stellar.org/#txbuilder?network=test).
2. **Signing (Transaction Signers)**: Once the transaction is completely filled out, the transaction is formed into a transaction envelope, which contains the transaction itself and a list of signers. All the required signatures must be collected and added to the transaction envelope's list of signers. Commonly it's just the signature of the account doing the transaction, but more complicated setups can require collecting [signatures from multiple parties](/glossary/multisig).
3. **Submitting (Transaction Submitter)**: After signing, the transaction must be valid and can now be submitted to the Bantu network. If the transaction is invalid, it will be immediately rejected by stellar-core based on [the validity rules of a transaction](/glossary/transactions#validity-of-a-transaction), the account's sequence number will not be incremented, and no fee will be consumed from the source account. Multiple transactions for the same account can be submitted, provided each of their sequence numbers are off by one. If they are all valid, Stellar Core will craft a transaction set with each of those transactions applied in sequence number order. Transactions are typically submitted using [Horizon](/api/introduction/index), but you can also submit the transaction directly to an instance of [Stellar Core](/run-core-node/index).
4. **Propagating (Validator)**: Once Stellar Core has determined that a transaction is valid, it will then propagate the transaction to all of the other servers to which it's connected. In this way, a valid transaction is flooded to the entire Bantu network.
5. **Crafting a candidate transaction set (Validator)**: When it's time to close the ledger, each Stellar Core validator (a Stellar Core node participating in consensus) takes all valid transactions it is aware of since the last ledger close and collects them into a candidate transaction set. If it hears about any incoming transactions now, it puts them aside for the next ledger close. If the number of operations in the candidate transaction set is greater than the maximum number of operations per ledger, transactions will be prioritized by their fee for inclusion in the set. See the [Fees](/glossary/fees) doc for more info.
6. **Nominating a transaction set (Validator)**: Once each validator has crafted a candidate transaction set, the set is nominated to the network.
7. **Stellar Consensus Protocol (SCP) determines the final transaction set (Validator Network)**: [SCP](/glossary/scp) resolves any differences between candidate transaction sets, and ultimately determines a single transaction set to apply, the close time of the ledger, and any upgrades to the protocol that need to be applied network wide at apply time.
   * If a transaction doesn't make it into the transaction set, it is kept around in memory in

     order to be added to the next transaction set on a best effort basis.
   * If a transaction is kept in memory after a certain number of ledger closes, it will be banned

     for several additional ledgers. This means no attempt will be made to include it in a

     candidate transaction set additional ledgers during this time.
8. **Transaction apply order is determined (Validator Network)**: Once SCP agrees on a particular transaction set, the apply order is computed for the transaction set. This both shuffles the order of the set to create uncertainty for competing transactions and maintains the order of sequence numbers for multiple transactions per account.
9. **Fees are collected (Validator)**: [Fees](/glossary/fees) are collected for all transactions simultaneously.
10. **Application (Validator)**: Each transaction is applied in the order previously determined. For each transaction, the account's sequence number is consumed (increased by 1), the transaction's validity is checked again, and each operation is applied in the order they occur in the transaction. Operations may fail at this stage due to errors that can occur outside of the transaction and operation validity checks. For example, an insufficient balance for a payment is not checked at submission, and would fail at this time. If any operation fails, the entire transaction will fail, and all previous operations will be rolled back.
11. **Protocol Upgrades (Validator)**: Finally, upgrades are run if an upgrade took place. This can include arbitrary logic to upgrade the ledger state for protocol upgrades, along with ledger header modifications including the protocol version, base fee, maximum number of operations per ledger, etc. Once this has completed, the life cycle begins anew.

## Result Codes

Transactions return a result code listed in a table below. Error reference for operations can be found in [List of Operations](/start/list-of-operations) doc.

| Result                   | Code | Description                                                                                      |
| ------------------------ | ---- | ------------------------------------------------------------------------------------------------ |
| SUCCESS                  | 0    | All operations contained in the transaction succeeded.                                           |
| FAILED                   | -1   | One of the operations failed (check [List of operations](/start/list-of-operations) for errors). |
| TOO\_EARLY               | -2   | Ledger `closeTime` before `minTime` value in the transaction.                                    |
| TOO\_LATE                | -3   | Ledger `closeTime` after `maxTime` value in the transaction.                                     |
| MISSING\_OPERATION       | -4   | No operation was specified.                                                                      |
| BAD\_SEQ                 | -5   | Sequence number does not match source account.                                                   |
| BAD\_AUTH                | -6   | Too few valid signatures / wrong network.                                                        |
| INSUFFICIENT\_BALANCE    | -7   | Fee would bring account below [minimum reserve](/glossary/minimum-balance).                      |
| NO\_ACCOUNT              | -8   | Source account not found.                                                                        |
| INSUFFICIENT\_FEE        | -9   | [Fee](/glossary/fees) is too small.                                                              |
| BAD\_AUTH\_EXTRA         | -10  | Unused signatures attached to transaction.                                                       |
| INTERNAL\_ERROR          | -11  | An unknown error occured.                                                                        |
| NOT\_SUPPORTED           | -12  | The transaction type is not supported.                                                           |
| FEE\_BUMP\_INNER\_FAILED | -13  | The fee bump inner transaction failed.                                                           |
| BAD\_SPONSORSHIP         | -14  | The sponsorship is not confirmed.                                                                |


# Minimum Balance

&#x20;This doc explains minimum balance requirements. If you want to know about transaction fees, check out the [Fees](/glossary/fees) doc.

All Bantu accounts must maintain a minimum balance of spirits. The minimum balance is calculated using the **base reserve,** which is currently **0.5 XBN**:

```
Minimum Balance = (2 + # of entries + # of sponsoring entries - # of sponsored entries) * base reserve 
```

The absolute minimum balance for an account is 1 XBN, which is equal to `(2 + 0 entries) * 0.5 base reserve`. Each additional entry reserves an additional 0.5 XBN. Entries include:

* Trustlines
* Offers
* Signers
* Data entries

For example, an account with 1 trustline and 2 offers would have a minimum balance of `(2 + 3 entries) * 0.5 base reserve = 2.5 XBN`.

[Sponsored Reserves](/glossary/sponsored-reserves) will affect the # of sponsoring entries and # of sponsored entries.

[Claimable Balances](/glossary/claimable-balance) are reflected in the # of sponsoring entries. Each claimant in a claimable balance will require an additional 0.5 XBN. For example, an account that creates a claimable balance with 2 claimants would have a minimum balance of `(2 + 0 entries + 2 sponsoring entries) * 0.5 base reserve = 2 XBN`.

Any transaction that would reduce an account's balance to less than the minimum will be rejected with an `INSUFFICIENT_BALANCE` error. Likewise, spirit selling liabilities that would reduce an account's balance to less than the minimum plus spirit selling liabilities will be rejected with an `INSUFFICIENT_BALANCE` error.

The minimum balance is held in reserve, and closing an entry frees up the associated base reserve. For instance: if you zero-out a non-spirit balance and close the associated trustline, the 0.5 XBN base reserve that secured that trustline is added to your available balance.

## Changes to Transaction Fees and Minimum Balances

Validators can vote to change the base reserve — just as they can vote to change ledger limits and and the minimum base fee — but that's relatively uncommon. It should only happen every few years. For the most part, you can think of the base reserve as a fixed value. When it is changed, the change works by the same consensus process as any transaction. For details, see [versioning](/glossary/versioning).


# Multisig

## Transaction signatures

Bantu uses **signatures** as authorization. Transactions always need authorization from at least one public key in order to be considered valid. Generally, transactions only need authorization from the public key of the source account.

Transaction signatures are created by cryptographically signing the transaction object contents with a secret key. Bantu currently uses the ed25519 signature scheme, but there's also a mechanism for adding additional types of public/private key schemes. A transaction with an attached signature is considered to have authorization from that public key.

In two cases, a transaction may need more than one signature. If the transaction has operations that affect more than one account, it will need authorization from every account in question. A transaction will also need additional signatures if the account associated with the transaction has multiple public keys.

## Thresholds

[Operations](/glossary/operations) fall under a specific threshold category: low, medium, or high. The threshold for a given level can be set to any number from 0-255. This threshold is the amount of signature weight required to authorize an operation at that level.

Let's say Diyang sets the medium threshold on one of her accounts to 4. If that account submits a transaction that includes a payment operation (medium security), the transaction's threshold is 4, which means the signature weights on it need to be greater than or equal to 4 in order to run. If Diyang's master key — the key corresponding to the public key that identifies the account she owns — has a weight less than 4, she cannot authorize a transaction without other signers.

Once the signature threshold is met, if there are any leftover signatures then the transaction will fail. This will happen even if the signatures are valid. For example, if your transaction requires 3 signatures, providing more than 3 signatures will result in a failed transaction with an `TX_BAD_AUTH_EXTRA` error (even if they are all valid). The reason for this error is performance related, as unnecessary signature verification has a large effect on performance before accepting transactions in consensus.

Each account can set its own threshold values. By default all thresholds levels are set to 0, and the master key is set to weight 1. The [Set Options](/start/list-of-operations#set-options) operation allows you to change the weight of the master key and to add other signing keys with different weights.

Low Security:

* [Transaction processing](/glossary/transactions)
  * Charging a fee or updating the sequence number for the source account
* [Allow Trust](/start/list-of-operations#allow-trust) operation
  * Used to allow people to hold credit from this account without exposing the key that enables sending payments from this account.
* [Bump Sequence](/start/list-of-operations#bump-sequence)
  * Modify the account's sequence number directly

High Security:

* [Set Options](/start/list-of-operations#set-options) to change the signers or the thresholds
  * Allows you to create a set of signers that give or revoke access to the account.
* [Account Merge](/start/list-of-operations#account-merge) to merge accounts

Medium Security:

* All [other operations](/start/list-of-operations)

For most cases, it is recommended to set thresholds such that `low <= medium <= high`.

## Additional signing keys

Accounts are identified by a public key. The private key that corresponds to this public key is called the **master key**. Additional signing keys can be added to the account using the [Set Options](/start/list-of-operations#set-options) operation.

If the weight of the master key is updated to 0, it cannot be used to sign transactions (even for operations with a threshold value of 0), until restored by other signers meeting the high threshold. So be very, very careful if you're setting the master key weight to 0. If there are other signers listed on the account, they can still continue to sign transactions.

"Signers" refers to the master key or to any signing keys added later. A signer is defined as the pair: public key, weight.

Each additional signer beyond the master key increases the account's [minimum balance](/glossary/minimum-balance).

## Alternate Signature Types

To enable some advanced smart contract features there are a couple of additional signature types. These signature types also have weights and can be added and removed similarly to normal signature types. But rather than check a cryptographic signature for authorization they have a different method of proving validity to the network.

### Pre-authorized Transaction

It is possible for an account to pre-authorize a particular transaction by adding the hash of the future transaction as a "signer" on the account. To do that you need to prepare the transaction beforehand with proper sequence number. Then you can obtain the hash of this transaction and add it as signer to account.

Signers of this type are automatically removed from the account when a matching transaction is applied, regardless of whether the transaction succeeds or fails. In case a matching transaction is never submitted, the signer remains and must be manually removed using the [Set Options](/start/list-of-operations#set-options) operation.

This type of signer is especially useful in escrow accounts. You can pre-authorize two different transactions. Both could have the same sequence number but different destinations. This means that only one of them can be executed.

### Hash(x)

Adding a signature of type hash(x) allows anyone who knows `x` to sign the transaction. This type of signer is especially useful in [atomic cross-chain swaps](https://en.bitcoin.it/wiki/Atomic_cross-chain_trading) which are needed for inter-blockchain protocols like [lightning networks](https://lightning.network).

First, create a random 256 bit value, which we call `x`. The SHA256 hash of that value can be added as a signer of type hash(x). Then in order to authorize a transaction, `x` is added as one of the signatures of the transaction. Keep in mind that `x` will be known to the world as soon as a transaction is submitted to the network with `x` as a signature. This means anyone will be able to sign for that account with the hash(x) signer at that point. Often you want there to be additional signers so someone must have a particular secret key and know `x` in order to reach the weight threshold required to authorize transactions on the account.

## Envelopes

A transaction **envelope** wraps a transaction with a set of signatures. The transaction object is the thing that the signers are actually signing. Technically, a transaction envelope is the thing that is passed around the network and included in transaction sets.

## Authorization

To determine if a transaction has the necessary authorization to run, the weights of all the signatures in the transaction envelope are added up. If this sum is equal to or greater than the threshold (see below) set for that operation type, then the operation is authorized.

This scheme is very flexible. You can require many signers to authorize payments from a particular account. You can have an account that any number of people can authorize for. You can have a master key that grants access or revokes access from others. It supports any m of n setup.

## Operations

### Example 1: Anchors

> You run an anchor that would like to keep its issuing key offline. That way, it's less likely a bad actor can get ahold of the anchor's key and start issuing credit improperly. However, your anchor needs to authorize people holding credit by running the `Allow Trust` operation. Before you issue credit to an account, you need to verify that account is OK.

Multisig allows you to do all of this without exposing the master key of your anchor. You can add another signing key to your account with the operation `Set Options`. This additional key should have a weight below your anchor account's medium threshold. Since `Allow Trust` is a low-threshold operation, this extra key authorizes users to hold your anchor's credit. But, since `Payment` is a medium-threshold operation, this key does not allow anyone who compromises your anchor to issue credit.

Your account setup:

```
  Master Key Weight: 2
  Additional Signing Key Weight: 1
  Low Threshold: 0
  Medium Threshold: 2
  High Threshold: 2

```

### Example 2: Joint Accounts

> You want to set up a joint account with Bilal and Carina such that any of you can authorize a payment. You also want to set up the account so that, if you choose to change signers (e.g., remove or add someone), a high-threshold operation, all 3 of you must agree. You add Bilal and Carina as signers to the joint account. You also ensure that it takes all of your key weights to clear the high threshold but only one to clear the medium threshold.

Joint account setup:

```
  Master Key Weight: 1
  Low Threshold: 0
  Medium Threshold: 0
  High Threshold: 3
  Bilal's Signing Key Weight: 1
  Carina's Signing Key Weight: 1

```

### Example 3: Expense Accounts

> You fully control an expense account, but you want your two coworkers Diyuan and Emil to be able to authorize transactions from this account. You add Diyuan and Emil's signing keys to the expense account. If either Diyuan or Emil leave the company, you can remove their signing key, a high-threshold operation.

Expense account setup:

```
  Master Key Weight: 3
  Low Threshold: 0
  Medium Threshold: 0
  High Threshold: 3
  Diyuan's Key Weight: 1
  Emil's Key Weight: 1

```

{% hint style="info" %}
The next example involves setting the master key weight of an account to 0. Be very, very careful if you decide to do that: that key will no longer be able to sign any kind of transaction, so you are in danger of \_permanently\_ locking yourself out of your account. Make sure you've thought carefully about what you're doing, that you understand the implications, and that you change weights in the correct order.
{% endhint %}

### Example 4: Company Accounts

> Your company wants to set up an account that requires 3 of 6 employees to agree to *any* transaction from that account.

Company account setup:

```
  Master Key Weight: 0 (Turned off so this account can't do anything without an employee.)
  Low Threshold: 3
  Medium Threshold: 3
  High Threshold: 3
  Employee 1 Key Weight: 1
  Employee 2 Key Weight: 1
  Employee 3 Key Weight: 1
  Employee 4 Key Weight: 1
  Employee 5 Key Weight: 1
  Employee 6 Key Weight: 1

```


# Docs


# Index

In addition to the docs here, which are guides to building on Bantu, you'll also see a link at the bottom of the left nav to the API Reference. You should check those out, too: that's a resource most developers consult as they build on Bantu.

A rundown of what's here:

## Tutorials

If you’re new to Bantu and want to get an overview of the network, this is where to start. The early tutorials will show you how to do some basic things like create an account and make payments. The later tutorials cover more advanced topics like Bantu smart contracts. If you want to issue an asset or build an app, you're better served checking the sections dedicated to those paths.

## Issue Assets

Bantu is a multi-currency network by design. The ability to issue assets is fundamental to Bantu, and it's something you can do quickly, safely, and in a few lines of code. Once you've issued an asset, you can also publish canonical information about it for wallets and consumers, control access to it by setting simple flags, and make it available for trade on the Bantu decentralized exchange. This section will show you how.

## Anchor Assets

Organizations can connect assets issued on Bantu with external banking and payment systems, allowing users and businesses to transfer assets onto or through the Bantu network. Specifically, organizations can **anchor** assets issued on the Bantu network by facilitating 1-1 trades for the off-chain representation of the tokenized asset.

### Deposits & Withdrawals

For example, a USD anchor could accept $1000 USD from a customer's wire transfer and send 1000 USDX tokens to the customer's Bantu account. Conversely, another customer could send USDX tokens to the anchor on Bantu and expect an incoming $1000 USD wire transfer from the anchor. These kinds of deposit and withdrawal operations are facilitated by wallet applications and SEP-24 anchor servers.

### Cross-border Payments

Anchors can also facilitate payments made through Bantu instead of simply on Bantu.

For example, a customer could want to send $1000 USD worth of EUR to a friend's bank account in Germany. Anchor A could collect the sending and receiving customer's information, make a USD->EUR path payment on Bantu to Anchor B (in Germany), and Anchor B could deposit the funds into the recipent's bank account.

## Build Apps

Bantu is a self-serve distributed ledger that you can use as a backend to power all kinds of apps and services. Any app built on Bantu relies on the same basic functions: key storage, account creation, transaction signing, and queries to the Bantu database. This section of the docs will walk you through the process of building a basic wallet that does all those things, and will show you how to add features to it like the support for in-app deposits and withdrawals from anchors.

## Run a Core Node

This section explains the technical and operational aspects of installing, configuring, and maintaining a Bantu Core node, which is a server that connects to the Bantu peer-to-peer network to keep a common distributed ledger. You don’t have to run a node to get started on Bantu, but you will likely want to if you're in production, need high-availability access network, or want to help increase network health and decentralization.

## Run an API Server

Most developers access the network using Horizon, the Bantu API. It takes the performance-oriented data structures from Bantu Core and converts them into a friendlier format. If you're running your own Bantu Core node and using it to submit transactions or get network data, you will likely also want to run your own Horizon instance, and this section will show you how. If you're just looking to use Horizon (vs. setting up a Horizon server), consult the API Reference.

## Software and SDKs

This is where you'll find all the Bantu SDKs. There are a lot of them, and they're all pretty well maintained and documented, so you should be able to build on Bantu in your language of choice. This section is also home to some tools and reference implementations created and maintained by the Bantu Development Foundation to kickstart development.

## Glossary

This section defines all the terms and explains all the concepts germane to Bantu. Use it to look up a word, or to dig deeper into nitty-gritty details.


# API


# Introduction


# Index

Expansion is an API for interacting with the Bantu network.

This API serves as the bridge between apps and [Bantu Core](/run-core-node/index). Projects like wallets, decentralized exchanges, and asset issuers use Expansion to submit transactions, query an account balance, or stream events like transactions to an account.

Expansion is a [RESTful API](https://en.wikipedia.org/wiki/Representational_state_transfer) and can be accessed via cURL, a browser, or one of the [Bantu SDKs](/software-and-sdks/index). To reduce the complexity of your project, we recommend you use an SDK instead of making direct API calls.

The Bantu Blockchain Foundation (BBF) runs two instances of Expansion:

* [https://Expansion.Bantu.org/](https://Expansion.Bantu.network/) for interacting with the public network
* [https://Expansion-testnet.Bantu.org/](https://Expansion-testnet.Bantu.network/) for interacting with the [testnet](/glossary/testnet)

API Reference Sections

| [Introduction](/api/introduction) | How Expansion is structured.                   |
| --------------------------------- | ---------------------------------------------- |
| [Resources](/api/resources)       | Descriptions of resources and their endpoints. |
| [Aggregations](/api/aggregations) | Descriptions of specialized endpoints.         |
| [Errors](/api/errors)             | Potential errors and what they mean.           |


# Response Format

Horizon delivers responses as JSON objects formatted according to [HAL](https://en.wikipedia.org/wiki/Hypertext_Application_Language). The HAL format makes Horizon more explorable, paginates responses, and connects parent and child resources. Consuming this format is simple using one of the many [open source libraries available](https://github.com/mikekelly/hal_specification/wiki/Libraries) for most major programming languages.

HAL is just JSON with two reserved attribute names:

* `_links`
* `_embedded`

If a response is a single record, the `_links` section will provide links to any parent or child records, and there will be no `_embedded` property.

If a response is a collection, the `_links` section will provide [pagination](/api/introduction/pagination/index) links, and the response’s list of records will be nested underneath the `_embedded` property.

&#x20;\- ATTRIBUTES - DATA TYPE - DESCRIPTION - \\\_links - array - Provides links for navigating to other pages or to parents and children. - \\\_embedded - array - Present when querying an endpoint that responds with a collection of records.

```javascript
{
  "_links": {
    "self": {
      "href": "https://horizon.stellar.org/accounts/GCRGHKY6RBFVQLF2JCHB7TK7A5BIABITFKVIEOXK4BPEIDE446OEFYXZ/payments?cursor=&limit=5&order=asc"
    },
    "next": {
      "href": "https://horizon.stellar.org/accounts/GCRGHKY6RBFVQLF2JCHB7TK7A5BIABITFKVIEOXK4BPEIDE446OEFYXZ/payments?cursor=111764193027313665&limit=5&order=asc"
    },
    "prev": {
      "href": "https://horizon.stellar.org/accounts/GCRGHKY6RBFVQLF2JCHB7TK7A5BIABITFKVIEOXK4BPEIDE446OEFYXZ/payments?cursor=111720727958269953&limit=5&order=desc"
    }
  },
  "_embedded": {
    "records": [
      {
        "_links": {
          "self": {
            "href": "https://horizon.stellar.org/operations/111720727958269953"
          },
          "transaction": {
            "href": "https://horizon.stellar.org/transactions/4034838d5b47e4f8c23776faca4d9403637b1f037e436759b57fc892ae5cd96c"
          },
          "effects": {
            "href": "https://horizon.stellar.org/operations/111720727958269953/effects"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=111720727958269953"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=111720727958269953"
          }
        },
        "id": "111720727958269953",
        "paging_token": "111720727958269953",
        "transaction_successful": true,
        "source_account": "GAR4S3ASZ4HTJ6GQ2DEDLVL4YE6D64UPIOQI4I67L5VPYBGEZDGOI462",
        "type": "create_account",
        "type_i": 0,
        "created_at": "2019-09-26T12:34:24Z",
        "transaction_hash": "4034838d5b47e4f8c23776faca4d9403637b1f037e436759b57fc892ae5cd96c",
        "starting_balance": "20.0000000",
        "funder": "GAR4S3ASZ4HTJ6GQ2DEDLVL4YE6D64UPIOQI4I67L5VPYBGEZDGOI462",
        "account": "GCRGHKY6RBFVQLF2JCHB7TK7A5BIABITFKVIEOXK4BPEIDE446OEFYXZ"
      },
      {
        "_links": {
          "self": {
            "href": "https://horizon.stellar.org/operations/111721376498331649"
          },
          "transaction": {
            "href": "https://horizon.stellar.org/transactions/cbf92ce9e2b75b0182597acb1e7c0b58695ec6f69e84a8625c5ab1dda8df31bc"
          },
          "effects": {
            "href": "https://horizon.stellar.org/operations/111721376498331649/effects"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=111721376498331649"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=111721376498331649"
          }
        },
        "id": "111721376498331649",
        "paging_token": "111721376498331649",
        "transaction_successful": true,
        "source_account": "GAR4S3ASZ4HTJ6GQ2DEDLVL4YE6D64UPIOQI4I67L5VPYBGEZDGOI462",
        "type": "payment",
        "type_i": 1,
        "created_at": "2019-09-26T12:47:50Z",
        "transaction_hash": "cbf92ce9e2b75b0182597acb1e7c0b58695ec6f69e84a8625c5ab1dda8df31bc",
        "asset_type": "native",
        "from": "GAR4S3ASZ4HTJ6GQ2DEDLVL4YE6D64UPIOQI4I67L5VPYBGEZDGOI462",
        "to": "GCRGHKY6RBFVQLF2JCHB7TK7A5BIABITFKVIEOXK4BPEIDE446OEFYXZ",
        "amount": "5000.0000000"
      },
      {
        "_links": {
          "self": {
            "href": "https://horizon.stellar.org/operations/111722218311925761"
          },
          "transaction": {
            "href": "https://horizon.stellar.org/transactions/9fcf20d23862f40cd8a59f0ad0247a7d56a1b38cb79e4953b5aad8df5f2608a7"
          },
          "effects": {
            "href": "https://horizon.stellar.org/operations/111722218311925761/effects"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=111722218311925761"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=111722218311925761"
          }
        },
        "id": "111722218311925761",
        "paging_token": "111722218311925761",
        "transaction_successful": true,
        "source_account": "GBSIPZRLSM2KMLUZYEGKU2WMA6HPEE3NGB47YY4MLK43ISLLCJKFA2F2",
        "type": "payment",
        "type_i": 1,
        "created_at": "2019-09-26T13:05:12Z",
        "transaction_hash": "9fcf20d23862f40cd8a59f0ad0247a7d56a1b38cb79e4953b5aad8df5f2608a7",
        "asset_type": "native",
        "from": "GBSIPZRLSM2KMLUZYEGKU2WMA6HPEE3NGB47YY4MLK43ISLLCJKFA2F2",
        "to": "GCRGHKY6RBFVQLF2JCHB7TK7A5BIABITFKVIEOXK4BPEIDE446OEFYXZ",
        "amount": "10.0000000"
      },
      {
        "_links": {
          "self": {
            "href": "https://horizon.stellar.org/operations/111747107647434753"
          },
          "transaction": {
            "href": "https://horizon.stellar.org/transactions/44af49d32061cdb352d131559560559e7be815b16c45412b7682600c71224623"
          },
          "effects": {
            "href": "https://horizon.stellar.org/operations/111747107647434753/effects"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=111747107647434753"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=111747107647434753"
          }
        },
        "id": "111747107647434753",
        "paging_token": "111747107647434753",
        "transaction_successful": true,
        "source_account": "GCRGHKY6RBFVQLF2JCHB7TK7A5BIABITFKVIEOXK4BPEIDE446OEFYXZ",
        "type": "payment",
        "type_i": 1,
        "created_at": "2019-09-26T21:43:41Z",
        "transaction_hash": "44af49d32061cdb352d131559560559e7be815b16c45412b7682600c71224623",
        "asset_type": "credit_alphanum4",
        "asset_code": "BTC",
        "asset_issuer": "GATEMHCCKCY67ZUCKTROYN24ZYT5GK4EQZ65JJLDHKHRUZI3EUEKMTCH",
        "from": "GCRGHKY6RBFVQLF2JCHB7TK7A5BIABITFKVIEOXK4BPEIDE446OEFYXZ",
        "to": "GBSIPZRLSM2KMLUZYEGKU2WMA6HPEE3NGB47YY4MLK43ISLLCJKFA2F2",
        "amount": "0.0000709"
      },
      {
        "_links": {
          "self": {
            "href": "https://horizon.stellar.org/operations/111764193027313665"
          },
          "transaction": {
            "href": "https://horizon.stellar.org/transactions/7bd57f8cc75ce2d9740568eade9700cd7b19491a2c938232e5b3f0768f3e588a"
          },
          "effects": {
            "href": "https://horizon.stellar.org/operations/111764193027313665/effects"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=111764193027313665"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=111764193027313665"
          }
        },
        "id": "111764193027313665",
        "paging_token": "111764193027313665",
        "transaction_successful": true,
        "source_account": "GCRGHKY6RBFVQLF2JCHB7TK7A5BIABITFKVIEOXK4BPEIDE446OEFYXZ",
        "type": "payment",
        "type_i": 1,
        "created_at": "2019-09-27T03:37:57Z",
        "transaction_hash": "7bd57f8cc75ce2d9740568eade9700cd7b19491a2c938232e5b3f0768f3e588a",
        "asset_type": "credit_alphanum4",
        "asset_code": "BTC",
        "asset_issuer": "GATEMHCCKCY67ZUCKTROYN24ZYT5GK4EQZ65JJLDHKHRUZI3EUEKMTCH",
        "from": "GCRGHKY6RBFVQLF2JCHB7TK7A5BIABITFKVIEOXK4BPEIDE446OEFYXZ",
        "to": "GBSIPZRLSM2KMLUZYEGKU2WMA6HPEE3NGB47YY4MLK43ISLLCJKFA2F2",
        "amount": "0.0107156"
      }
    ]
  }
}
```

&#x20;


# Streaming

import { MethodTable } from "components/MethodTable";

Horizon provides a streaming mechanism for receiving events in near real time. Instead of repeatedly sending requests to Horizon for batch updates, a connection is established between a client and Horizon with updates to an endpoint response streaming as new ledgers close and updates occur.

This reduces requests that return no data and allows near instantaneous updates client-side.

All attributes for the endpoints that allow streaming are the same as regular responses. A caller can initiate streaming by setting ‘Accept: text/event-stream’ in the HTTP header when making the request. Study an example of using streaming in the [Follow Received Payments tutorial](/tutorials/follow-received-payments).

&#x20;

Endpoints with Streaming

| [Ledgers](https://developers.stellar.org/api/resources/ledgers/)                    |
| ----------------------------------------------------------------------------------- |
| [Transactions](https://developers.stellar.org/api/resources/transactions/)          |
| [Operations](https://developers.stellar.org/api/resources/operations/)              |
| [Payments](https://developers.stellar.org/api/resources/operations/object/payment/) |
| [Effects](https://developers.stellar.org/api/resources/effects/)                    |
| [Accounts](https://developers.stellar.org/api/resources/accounts/)                  |
| [Offers](https://developers.stellar.org/api/resources/offers/)                      |
| [Trades](https://developers.stellar.org/api/resources/trades/)                      |
| [Order Books](https://developers.stellar.org/api/aggregations/order-books/)         |


# Rate Limiting

Horizon rate limits on a per-IP-address basis. By default, a client is limited to 3600 requests per hour - one request per second on average.

While streaming, each update of the stream counts as a request and against a client’s allotted rate limit.


# XDR

In the Bantu network, transactions are encoded using a standardized protocol called [External Data Representation](https://en.wikipedia.org/wiki/External_Data_Representation) (XDR).

In Expansion, you will only encounter XDR when [posting](https://github.com/slideloft/new-docs/tree/046158a008b14dc6d54bdd6f4c48e078c303a05e/content/api/resources/transactions/post.mdx) and [getting](/api/resources/transactions/single) transactions and in the [ledger](/api/resources/ledgers/index) header.

When you post a transaction, a client will encode the transaction as XDR before submitting it to Expansion.

When you request a transaction, Expansion returns some data about the transaction in human-readable JSON. The full canonical data about the transaction is encoded in machine-readable XDR, available in XDR attributes at the end of the response.

You can decode this XDR in the Bantu Laboratory’s [XDR Viewer](https://laboratory.bantu.network/#xdr-viewer).

ATTRIBUTE TYPE DESCRIPTION

* envelope\_xdrstring

  The XDR encoded transaction as stellar-core sees it.
* result\_xdrstring

  The effects of a transaction encoded in XDR.
* result\_meta\_xdrstring

  The details about the effects of a transaction encoded in XDR.
* fee\_meta\_xdrstring

  The fees associated with the transaction encoded in XDR.

```javascript
{
  // Response truncated to highlight XDR-related attributes
  "envelope_xdr": "AAAAAPewD+/6X8o0bx3bp49Wf+mUhG3o+TUrcjcst717DWJVAAAAyAFvzscADTkNAAAAAAAAAAAAAAACAAAAAAAAAAYAAAACWE1BVEsAAAAAAAAAAAAAAAPvNOuztX4IjvV8pztsEc1/ZnTz0G3p5Cx4vcf04+xUAAONfqTGgAAAAAAAAAAABQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAAAAD2NyeXB0b21hcmluZS5ldQAAAAAAAAAAAAAAAAF7DWJVAAAAQK3vfUCZ8mbjW3ssMd0n1tJTF9Fv6EbuJ6cWKkYXBqG5itqanPbFzIQoZEHbPS8nr2vo4dROvKI0uQzNcfExKwM=",
  "result_xdr": "AAAAAAAAAMgAAAAAAAAAAgAAAAAAAAAGAAAAAAAAAAAAAAAFAAAAAAAAAAA=",
  "result_meta_xdr": "AAAAAQAAAAIAAAADAZU3mQAAAAAAAAAA97AP7/pfyjRvHdunj1Z/6ZSEbej5NStyNyy3vXsNYlUAAAAABlxskAFvzscADTkMAAAAAgAAAAAAAAAAAAAAD2NyeXB0b21hcmluZS5ldQABAAAAAAAAAAAAAAAAAAAAAAAAAQGVN5kAAAAAAAAAAPewD+/6X8o0bx3bp49Wf+mUhG3o+TUrcjcst717DWJVAAAAAAZcbJABb87HAA05DQAAAAIAAAAAAAAAAAAAAA9jcnlwdG9tYXJpbmUuZXUAAQAAAAAAAAAAAAAAAAAAAAAAAAIAAAACAAAAAwGVN5gAAAABAAAAAPewD+/6X8o0bx3bp49Wf+mUhG3o+TUrcjcst717DWJVAAAAAlhNQVRLAAAAAAAAAAAAAAAD7zTrs7V+CI71fKc7bBHNf2Z089Bt6eQseL3H9OPsVAAATfBgJfPoAAONfqTGgAAAAAABAAAAAAAAAAAAAAABAZU3mQAAAAEAAAAA97AP7/pfyjRvHdunj1Z/6ZSEbej5NStyNyy3vXsNYlUAAAACWE1BVEsAAAAAAAAAAAAAAAPvNOuztX4IjvV8pztsEc1/ZnTz0G3p5Cx4vcf04+xUAABN8GAl8+gAA41+pMaAAAAAAAEAAAAAAAAAAAAAAAIAAAADAZU3mQAAAAAAAAAA97AP7/pfyjRvHdunj1Z/6ZSEbej5NStyNyy3vXsNYlUAAAAABlxskAFvzscADTkNAAAAAgAAAAAAAAAAAAAAD2NyeXB0b21hcmluZS5ldQABAAAAAAAAAAAAAAAAAAAAAAAAAQGVN5kAAAAAAAAAAPewD+/6X8o0bx3bp49Wf+mUhG3o+TUrcjcst717DWJVAAAAAAZcbJABb87HAA05DQAAAAIAAAAAAAAAAAAAAA9jcnlwdG9tYXJpbmUuZXUAAQAAAAAAAAAAAAAAAAAAAA==",
  "fee_meta_xdr": "AAAAAgAAAAMBlTeXAAAAAAAAAAD3sA/v+l/KNG8d26ePVn/plIRt6Pk1K3I3LLe9ew1iVQAAAAAGXG1YAW/OxwANOQwAAAACAAAAAAAAAAAAAAAPY3J5cHRvbWFyaW5lLmV1AAEAAAAAAAAAAAAAAAAAAAAAAAABAZU3mQAAAAAAAAAA97AP7/pfyjRvHdunj1Z/6ZSEbej5NStyNyy3vXsNYlUAAAAABlxskAFvzscADTkMAAAAAgAAAAAAAAAAAAAAD2NyeXB0b21hcmluZS5ldQABAAAAAAAAAAAAAAAAAAAA"
  // Response truncated to highlight XDR-related attributes
}
```


# Pagination


# Index

To make it possible to explore the millions of records for resources like transactions and operations, Horizon paginates the data it returns for collection-based endpoints.

Each individual transaction, operation, ledger, etc. is returned as a record, and a group of records is called a collection. Records are returned as an array under the `_embedded` attribute.

To move between pages of a collection of records, use the links in the `next` and `prev` attributes nested under the top-level `_links` attribute.

&#x20;

* ATTRIBUTE DATA TYPE

  DESCRIPTION
* \_linksarray

  Provides links for navigating to other pages.Hide child attributes

  * \_links.selfarray

    An `href` key with a link to the response itself as the value.
  * \_links.nextarray

    An `href` key with a link to the next page for this endpoint as the value.
  * \_links.prevarray

    An `href` key with a link to the next page for this endpoint as the value.
* \_embeddedarray

  An `href` key with a link to the next page for this endpoint as the value.Hide child attributes

  * \_embedded.recordsarray

    Returns an array of records.

```javascript
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://horizon.stellar.org");

server
  .transactions()
  .call()
  .then(function (resp) {
    // page 1
    console.log(resp);
    return resp.next();
  })
  .then(function (resp) {
    // page 2
    console.log(resp);
  })
  .catch(function (err) {
    console.error(err);
  });
```

```javascript
{
  "_links": {
    "self": {
      "href": "https://horizon.stellar.org/transactions/2a09c3d79027721f2b8a78e2a936cbeda484bfe98a6e34856c1fee743b7e8906/operations?cursor=&limit=5&order=asc"
    },
    "next": {
      "href": "https://horizon.stellar.org/transactions/2a09c3d79027721f2b8a78e2a936cbeda484bfe98a6e34856c1fee743b7e8906/operations?cursor=113928152169844741&limit=5&order=asc"
    },
    "prev": {
      "href": "https://horizon.stellar.org/transactions/2a09c3d79027721f2b8a78e2a936cbeda484bfe98a6e34856c1fee743b7e8906/operations?cursor=113928152169844737&limit=5&order=desc"
    }
  },
  "_embedded": {
    "records": [
      {
        "_links": {
          "self": {
            "href": "https://horizon.stellar.org/operations/113928152169844737"
          },
          "transaction": {
            "href": "https://horizon.stellar.org/transactions/2a09c3d79027721f2b8a78e2a936cbeda484bfe98a6e34856c1fee743b7e8906"
          },
          "effects": {
            "href": "https://horizon.stellar.org/operations/113928152169844737/effects"
          }
        },
        "id": "113928152169844737",
        "paging_token": "113928152169844737",
        "transaction_successful": true,
        "source_account": "GDO2BIMNH7T6MOJVPKEJHWAGMYGOQU5QMK5BKT5XMVWPZKHAGA4JNAQZ",
        "type": "manage_buy_offer",
        "type_i": 12,
        "created_at": "2019-10-28T19:44:09Z",
        "transaction_hash": "2a09c3d79027721f2b8a78e2a936cbeda484bfe98a6e34856c1fee743b7e8906",
        "amount": "0.0000000",
        "price": "0.0001000",
        "price_r": {
          "n": 1,
          "d": 10000
        },
        "buying_asset_type": "native",
        "selling_asset_type": "credit_alphanum4",
        "selling_asset_code": "SLT",
        "selling_asset_issuer": "GCKA6K5PCQ6PNF5RQBF7PQDJWRHO6UOGFMRLK3DYHDOI244V47XKQ4GP",
        "offer_id": 126229445
      },
      {
        "_links": {
          "self": {
            "href": "https://horizon.stellar.org/operations/113928152169844738"
          },
          "transaction": {
            "href": "https://horizon.stellar.org/transactions/2a09c3d79027721f2b8a78e2a936cbeda484bfe98a6e34856c1fee743b7e8906"
          },
          "effects": {
            "href": "https://horizon.stellar.org/operations/113928152169844738/effects"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=113928152169844738"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=113928152169844738"
          }
        },
        "id": "113928152169844738",
        "paging_token": "113928152169844738",
        "transaction_successful": true,
        "source_account": "GDO2BIMNH7T6MOJVPKEJHWAGMYGOQU5QMK5BKT5XMVWPZKHAGA4JNAQZ",
        "type": "manage_buy_offer",
        "type_i": 12,
        "created_at": "2019-10-28T19:44:09Z",
        "transaction_hash": "2a09c3d79027721f2b8a78e2a936cbeda484bfe98a6e34856c1fee743b7e8906",
        "amount": "0.0000000",
        "price": "0.0001000",
        "price_r": {
          "n": 1,
          "d": 10000
        },
        "buying_asset_type": "native",
        "selling_asset_type": "credit_alphanum4",
        "selling_asset_code": "SLT",
        "selling_asset_issuer": "GCKA6K5PCQ6PNF5RQBF7PQDJWRHO6UOGFMRLK3DYHDOI244V47XKQ4GP",
        "offer_id": 126229446
      },
      {
        "_links": {
          "self": {
            "href": "https://horizon.stellar.org/operations/113928152169844739"
          },
          "transaction": {
            "href": "https://horizon.stellar.org/transactions/2a09c3d79027721f2b8a78e2a936cbeda484bfe98a6e34856c1fee743b7e8906"
          },
          "effects": {
            "href": "https://horizon.stellar.org/operations/113928152169844739/effects"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=113928152169844739"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=113928152169844739"
          }
        },
        "id": "113928152169844739",
        "paging_token": "113928152169844739",
        "transaction_successful": true,
        "source_account": "GDO2BIMNH7T6MOJVPKEJHWAGMYGOQU5QMK5BKT5XMVWPZKHAGA4JNAQZ",
        "type": "manage_buy_offer",
        "type_i": 12,
        "created_at": "2019-10-28T19:44:09Z",
        "transaction_hash": "2a09c3d79027721f2b8a78e2a936cbeda484bfe98a6e34856c1fee743b7e8906",
        "amount": "0.0000000",
        "price": "0.0001000",
        "price_r": {
          "n": 1,
          "d": 10000
        },
        "buying_asset_type": "native",
        "selling_asset_type": "credit_alphanum4",
        "selling_asset_code": "SLT",
        "selling_asset_issuer": "GCKA6K5PCQ6PNF5RQBF7PQDJWRHO6UOGFMRLK3DYHDOI244V47XKQ4GP",
        "offer_id": 126229447
      },
      {
        "_links": {
          "self": {
            "href": "https://horizon.stellar.org/operations/113928152169844740"
          },
          "transaction": {
            "href": "https://horizon.stellar.org/transactions/2a09c3d79027721f2b8a78e2a936cbeda484bfe98a6e34856c1fee743b7e8906"
          },
          "effects": {
            "href": "https://horizon.stellar.org/operations/113928152169844740/effects"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=113928152169844740"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=113928152169844740"
          }
        },
        "id": "113928152169844740",
        "paging_token": "113928152169844740",
        "transaction_successful": true,
        "source_account": "GDO2BIMNH7T6MOJVPKEJHWAGMYGOQU5QMK5BKT5XMVWPZKHAGA4JNAQZ",
        "type": "manage_buy_offer",
        "type_i": 12,
        "created_at": "2019-10-28T19:44:09Z",
        "transaction_hash": "2a09c3d79027721f2b8a78e2a936cbeda484bfe98a6e34856c1fee743b7e8906",
        "amount": "0.0000000",
        "price": "0.0001000",
        "price_r": {
          "n": 1,
          "d": 10000
        },
        "buying_asset_type": "native",
        "selling_asset_type": "credit_alphanum4",
        "selling_asset_code": "SLT",
        "selling_asset_issuer": "GCKA6K5PCQ6PNF5RQBF7PQDJWRHO6UOGFMRLK3DYHDOI244V47XKQ4GP",
        "offer_id": 126229448
      },
      {
        "_links": {
          "self": {
            "href": "https://horizon.stellar.org/operations/113928152169844741"
          },
          "transaction": {
            "href": "https://horizon.stellar.org/transactions/2a09c3d79027721f2b8a78e2a936cbeda484bfe98a6e34856c1fee743b7e8906"
          },
          "effects": {
            "href": "https://horizon.stellar.org/operations/113928152169844741/effects"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=113928152169844741"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=113928152169844741"
          }
        },
        "id": "113928152169844741",
        "paging_token": "113928152169844741",
        "transaction_successful": true,
        "source_account": "GDO2BIMNH7T6MOJVPKEJHWAGMYGOQU5QMK5BKT5XMVWPZKHAGA4JNAQZ",
        "type": "manage_buy_offer",
        "type_i": 12,
        "created_at": "2019-10-28T19:44:09Z",
        "transaction_hash": "2a09c3d79027721f2b8a78e2a936cbeda484bfe98a6e34856c1fee743b7e8906",
        "amount": "0.0000000",
        "price": "0.0001000",
        "price_r": {
          "n": 1,
          "d": 10000
        },
        "buying_asset_type": "native",
        "selling_asset_type": "credit_alphanum4",
        "selling_asset_code": "SLT",
        "selling_asset_issuer": "GCKA6K5PCQ6PNF5RQBF7PQDJWRHO6UOGFMRLK3DYHDOI244V47XKQ4GP",
        "offer_id": 126229449
      }
    ]
  }
}
```


# Page Arguments

```javascript
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://horizon.bantu.network");

server
  .operations()
  .forLedger("26478723")
  .cursor("113725249324879872")
  .limit(5)
  .order("asc")
  .call()
  .then(function (resp) {
    console.log(resp);
  })
  .catch(function (err) {
    console.error(err);
  });
```

<mark style="color:blue;">`GET`</mark> `/{endpoint}?cursor={paging_token}&order={asc,desc}&limit={1-200}`

#### Query Parameters

| Name   | Type   | Description                                                                                                                                                          |
| ------ | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| limit  | string | A number that points to a specific location in a collection of responses and is pulled from the `paging_token` value of a record.                                    |
| order  | string | A designation of the order in which records should appear. Options include `asc`(ascending) or `desc` (descending). If this argument isn’t set, it defaults to `asc` |
| cursor | string | A number that points to a specific location in a collection of responses and is pulled from the `paging_token` value of a record                                     |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "_links": {
    "self": {
      "href": "https://horizon.stellar.org/ledgers/26478723/operations?cursor=113725249324879872\u0026limit=5\u0026order=asc"
    },
    "next": {
      "href": "https://horizon.stellar.org/ledgers/26478723/operations?cursor=113725249324916737\u0026limit=5\u0026order=asc"
    },
    "prev": {
      "href": "https://horizon.stellar.org/ledgers/26478723/operations?cursor=113725249324879873\u0026limit=5\u0026order=desc"
    }
  },
  "_embedded": {
    "records": [
      {
        "_links": {
          "self": {
            "href": "https://horizon.stellar.org/operations/113725249324879873"
          },
          "transaction": {
            "href": "https://horizon.stellar.org/transactions/6adab48bbc0a38b9d40938b63a8ae0f5b334948c2d5acfb755dea616f98720d1"
          },
          "effects": {
            "href": "https://horizon.stellar.org/operations/113725249324879873/effects"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=113725249324879873"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=113725249324879873"
          }
        },
        "id": "113725249324879873",
        "paging_token": "113725249324879873",
        "transaction_successful": true,
        "source_account": "GCRGHKY6RBFVQLF2JCHB7TK7A5BIABITFKVIEOXK4BPEIDE446OEFYXZ",
        "type": "manage_offer",
        "type_i": 3,
        "created_at": "2019-10-25T20:45:51Z",
        "transaction_hash": "6adab48bbc0a38b9d40938b63a8ae0f5b334948c2d5acfb755dea616f98720d1",
        "amount": "5048.0792092",
        "price": "0.0000079",
        "price_r": {
          "n": 79,
          "d": 10000000
        },
        "buying_asset_type": "credit_alphanum4",
        "buying_asset_code": "BTC",
        "buying_asset_issuer": "GATEMHCCKCY67ZUCKTROYN24ZYT5GK4EQZ65JJLDHKHRUZI3EUEKMTCH",
        "selling_asset_type": "native",
        "offer_id": 125121197
      },
      {
        "_links": {
          "self": {
            "href": "https://horizon.stellar.org/operations/113725249324888065"
          },
          "transaction": {
            "href": "https://horizon.stellar.org/transactions/c4de60af4815d94b6f3aa9947403f96dd0e8c3ba7d84eccce9c2c798470381ed"
          },
          "effects": {
            "href": "https://horizon.stellar.org/operations/113725249324888065/effects"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=113725249324888065"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=113725249324888065"
          }
        },
        "id": "113725249324888065",
        "paging_token": "113725249324888065",
        "transaction_successful": true,
        "source_account": "GCFKS7EBBZQTBBM7HHOASGZBE3L2TADWG6OJBVJXYIS44LQFXFL766UB",
        "type": "manage_offer",
        "type_i": 3,
        "created_at": "2019-10-25T20:45:51Z",
        "transaction_hash": "c4de60af4815d94b6f3aa9947403f96dd0e8c3ba7d84eccce9c2c798470381ed",
        "amount": "103.0721239",
        "price": "0.0655000",
        "price_r": {
          "n": 131,
          "d": 2000
        },
        "buying_asset_type": "credit_alphanum4",
        "buying_asset_code": "USD",
        "buying_asset_issuer": "GDSRCV5VTM3U7Y3L6DFRP3PEGBNQMGOWSRTGSBWX6Z3H6C7JHRI4XFJP",
        "selling_asset_type": "native",
        "offer_id": 0
      },
      {
        "_links": {
          "self": {
            "href": "https://horizon.stellar.org/operations/113725249324892161"
          },
          "transaction": {
            "href": "https://horizon.stellar.org/transactions/6d0fe444dd346e05742776305f5e90dd102bd83dfa00a54cfd79bd94753beba0"
          },
          "effects": {
            "href": "https://horizon.stellar.org/operations/113725249324892161/effects"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=113725249324892161"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=113725249324892161"
          }
        },
        "id": "113725249324892161",
        "paging_token": "113725249324892161",
        "transaction_successful": true,
        "source_account": "GB44WLYD6HQXZHLRCPARIAMBASN35TDHXU5Q3I3DRHQMLLB3CBMIZLY5",
        "type": "manage_buy_offer",
        "type_i": 12,
        "created_at": "2019-10-25T20:45:51Z",
        "transaction_hash": "6d0fe444dd346e05742776305f5e90dd102bd83dfa00a54cfd79bd94753beba0",
        "amount": "2942.0268642",
        "price": "6.7000000",
        "price_r": {
          "n": 67,
          "d": 10
        },
        "buying_asset_type": "credit_alphanum4",
        "buying_asset_code": "SLT",
        "buying_asset_issuer": "GCKA6K5PCQ6PNF5RQBF7PQDJWRHO6UOGFMRLK3DYHDOI244V47XKQ4GP",
        "selling_asset_type": "native",
        "offer_id": 0
      },
      {
        "_links": {
          "self": {
            "href": "https://horizon.stellar.org/operations/113725249324908545"
          },
          "transaction": {
            "href": "https://horizon.stellar.org/transactions/36907ac7f802a079fa1d7e7fddeb31decbed1218b47924243daaa508af8b1bbe"
          },
          "effects": {
            "href": "https://horizon.stellar.org/operations/113725249324908545/effects"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=113725249324908545"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=113725249324908545"
          }
        },
        "id": "113725249324908545",
        "paging_token": "113725249324908545",
        "transaction_successful": true,
        "source_account": "GBVI7F7QBE3ZEHP7HUAXAEKENITQKCXR5CML5JMOCHJN6EOQXLTSJYJI",
        "type": "payment",
        "type_i": 1,
        "created_at": "2019-10-25T20:45:51Z",
        "transaction_hash": "36907ac7f802a079fa1d7e7fddeb31decbed1218b47924243daaa508af8b1bbe",
        "asset_type": "credit_alphanum4",
        "asset_code": "TFC",
        "asset_issuer": "GDS3XDJAA4VY6MJYASIGSIMPHZ7AQNZ54RKLWT7MWCOU5YKYEVCNLVS3",
        "from": "GBVI7F7QBE3ZEHP7HUAXAEKENITQKCXR5CML5JMOCHJN6EOQXLTSJYJI",
        "to": "GAMKXMT23OMMOFJMZIHT5T3C65JI5YGAOHJMDYBBS4JJBB3X2CVLT3GO",
        "amount": "0.0380000"
      },
      {
        "_links": {
          "self": {
            "href": "https://horizon.stellar.org/operations/113725249324916737"
          },
          "transaction": {
            "href": "https://horizon.stellar.org/transactions/785058d3363b448c95943a40230333b8d523607c9617089bb17c9202a2e7384a"
          },
          "effects": {
            "href": "https://horizon.stellar.org/operations/113725249324916737/effects"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=113725249324916737"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=113725249324916737"
          }
        },
        "id": "113725249324916737",
        "paging_token": "113725249324916737",
        "transaction_successful": true,
        "source_account": "GDD7ABRF7BCK76W33RXDQG5Q3WXVSQYVLGEMXSOWRGZ6Z3G3M2EM2TCP",
        "type": "manage_offer",
        "type_i": 3,
        "created_at": "2019-10-25T20:45:51Z",
        "transaction_hash": "785058d3363b448c95943a40230333b8d523607c9617089bb17c9202a2e7384a",
        "amount": "0.0000000",
        "price": "1.0000000",
        "price_r": {
          "n": 1,
          "d": 1
        },
        "buying_asset_type": "native",
        "selling_asset_type": "credit_alphanum4",
        "selling_asset_code": "BTC",
        "selling_asset_issuer": "GBVOL67TMUQBGL4TZYNMY3ZQ5WGQYFPFD5VJRWXR72VA33VFNL225PL5",
        "offer_id": 125126043
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}


# Resources


# Untitled


# Overview

Data on the Bantu ledger is organized according to resources. Each resource has several different endpoints.

Resource Types

| [Ledgers](/api/resources/ledgers)                                              |   |
| ------------------------------------------------------------------------------ | - |
| [Transactions](/api/resources/ledgers/transactions)                            |   |
| [Operations](/api/resources/transactions/operations)                           |   |
| [Effects](/api/resources/ledgers/effects)                                      |   |
| [Accounts](/glossary/accounts)                                                 |   |
| [Offers](/api/resources/accounts/offers)                                       |   |
| [Claimable Balances](/api/resources/operations/object/claim-claimable-balance) |   |
| [Trades](/api/resources/trades)                                                |   |
| [Assets](/api/resources/assets)                                                |   |


# Ledgers


# index

Each ledger stores the state of the network at a point in time and contains all the changes - transactions, operations, effects, etc. - to that state.

Learn more about [ledgers](/glossary/ledger).

Endpoints

| GET | [/ledgers/:ledger\_sequence](https://developers.stellar.org/api/resources/ledgers/single/)                    |
| --- | ------------------------------------------------------------------------------------------------------------- |
| GET | [/ledgers/:ledger\_sequence/transactions](https://developers.stellar.org/api/resources/ledgers/transactions/) |
| GET | [/ledgers/:ledger\_sequence/operations](https://developers.stellar.org/api/resources/ledgers/operations/)     |
| GET | [/ledgers/:ledger\_sequence/payments](https://developers.stellar.org/api/resources/ledgers/payments/)         |
| GET | [/ledgers/:ledger\_sequence/effects](https://developers.stellar.org/api/resources/ledgers/effects/)           |
| GET | [/ledgers](https://developers.stellar.org/api/resources/ledgers/list/)                                        |


# Object

When Horizon returns information about a ledger, it uses the following format:

* ATTRIBUTEDATA TYPE

  DESCRIPTION
* idstring

  A unique identifier for this ledger.
* paging\_tokennumber

  A cursor value for use in [pagination](https://developers.stellar.org/api/introduction/pagination/).
* hashstring

  A hex-encoded SHA-256 hash of this ledger’s [XDR](https://developers.stellar.org/docs/glossary/xdr/)-encoded form.
* prev\_hashstring

  The hash of the ledger immediately preceding this ledger.
* sequencenumber

  The sequence number of this ledger, and the parameter used in Horizon calls that require a ledger number.
* successful\_transaction\_countnumber

  The number of successful transactions in this ledger.
* failed\_transaction\_countnumber

  The number of failed transactions in this ledger.
* operation\_countnumber

  The number of operations applied in this ledger.
* closed\_atstring

  An [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) formatted string of when this ledger was closed.
* total\_coinsstring

  The total number of spirits in circulation.
* fee\_poolstring

  The sum of all transaction fees.
* base\_fee\_in\_stroopsnumber

  The fee the network charges per operation in a transaction.
* base\_reserve\_in\_stroopsnumber

  The reserve the network uses when calculating an account’s minimum balance.
* max\_tx\_set\_sizenumber

  The maximum number of transactions validators have agreed to process in a given ledger.
* protocol\_versionnumber

  The protocol version that the Stellar network was running when this ledger was committed.
* header\_xdrstring

  A base64 encoded string of the raw `LedgerHeader` xdr struct for this ledger.

```javascript
{
  "_links": {
    "self": {
      "href": "https://horizon.stellar.org/ledgers/26857634"
    },
    "transactions": {
      "href": "https://horizon.stellar.org/ledgers/26857634/transactions{?cursor,limit,order}",
      "templated": true
    },
    "operations": {
      "href": "https://horizon.stellar.org/ledgers/26857634/operations{?cursor,limit,order}",
      "templated": true
    },
    "payments": {
      "href": "https://horizon.stellar.org/ledgers/26857634/payments{?cursor,limit,order}",
      "templated": true
    },
    "effects": {
      "href": "https://horizon.stellar.org/ledgers/26857634/effects{?cursor,limit,order}",
      "templated": true
    }
  },
  "id": "548393ec23959e1959a62f003029ecf96be89e13df036073bf64918996ec4227",
  "paging_token": "115352659677937664",
  "hash": "548393ec23959e1959a62f003029ecf96be89e13df036073bf64918996ec4227",
  "prev_hash": "446d6eca81dd6db6daf50d93ca9d297bd60b1233b91de3765cccdf503cfffcb0",
  "sequence": 26857634,
  "successful_transaction_count": 27,
  "failed_transaction_count": 1,
  "operation_count": 133,
  "closed_at": "2019-11-18T19:27:21Z",
  "total_coins": "105443902087.3472865",
  "fee_pool": "1807038.9789761",
  "base_fee_in_stroops": 100,
  "base_reserve_in_stroops": 5000000,
  "max_tx_set_size": 1000,
  "protocol_version": 12,
  "header_xdr": "AAAADERtbsqB3W222vUNk8qdKXvWCxIzuR3jdlzM31A8//ywoQieYsSc05/BpgEqnLR7fKXz7t0K42V7NOjbGZA/wTEAAAAAXdLwmQAAAAAAAAAAplf68mTg/Z/DDyEZeLCoNbJnMZm4SYsYWjUjuDOSfPeRNFE4n9Hm19yKutjwVurFjk72JKVHI8J+ELwLZgWsywGZ0KIOoh6z7HlbYQAAEG9XKhRBAAABFgAAAAAH9M6YAAAAZABMS0AAAAPop9+CeMs1/7BHgFltiQPH+VT+ACYb5P0lSXh7RpBLtd34kEpeL8qKJxYz4ufmkQ2lEv/HMR/i3bi1Rt0PYj185/0kAZ3ZRbmm2mVRMzmaCOak1rn2vejHXDh+MGlr6D6vI2tc/M6VIumTKUa7SgumWDyW0r5FcJTbu/FXDQ/6C4YAAAAA"
}
```


# Single

The single ledger endpoint provides information on a specific ledger.

* sequencerequired

  The sequence number of a specific ledger.
* cursoroptional

  A number that points to a specific location in a collection of responses and is pulled from the `paging_token` value of a record.
* orderoptional

  A designation of the order in which records should appear. Options include `asc`(ascending) or `desc` (descending). If this argument isn’t set, it defaults to `asc`.
* limitoptional

  The total number of records returned. The limit can range from 1 to 200 - an upper limit that is hardcoded in Horizon for performance reasons. If this argument isn’t designated, it defaults to 10.
* include\_failedoptional

  Set to true to include failed transactions in results. Options include `true` and `false`.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://expansion-testnet.bantu.network");

server
  .ledgers()
  .ledger("69858")
  .call()
  .then(function (resp) {
    console.log(resp);
  })
  .catch(function (err) {
    console.error(err);
  });
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl "https://expansion-testnet.bantu.network/ledgers/69859"
```

{% endtab %}
{% endtabs %}

```bash
"_links": {
    "self": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27146933"
    },
    "transactions": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27146933/transactions{?cursor,limit,order}",
      "templated": true
    },
    "operations": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27146933/operations{?cursor,limit,order}",
      "templated": true
    },
    "payments": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27146933/payments{?cursor,limit,order}",
      "templated": true
    },
    "effects": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27146933/effects{?cursor,limit,order}",
      "templated": true
    }
  },
  "id": "e1218a337cecda05526fba902c61d3d7130baa58d8db41f544bf563f779b6329",
  "paging_token": "116595189421703168",
  "hash": "e1218a337cecda05526fba902c61d3d7130baa58d8db41f544bf563f779b6329",
  "prev_hash": "9eac16fecd885147067b58b7684f60d216f931b813f651265bbc97de4cea313d",
  "sequence": 27146933,
  "successful_transaction_count": 26,
  "failed_transaction_count": 9,
  "operation_count": 67,
  "closed_at": "2019-12-06T22:39:32Z",
  "total_coins": "105443902087.3472865",
  "fee_pool": "1807264.7509661",
  "base_fee_in_stroops": 100,
  "base_reserve_in_stroops": 5000000,
  "max_tx_set_size": 1000,
  "protocol_version": 12,
  "header_xdr": "AAAADJ6sFv7NiFFHBntYt2hPYNIW+TG4E/ZRJlu8l95M6jE9bsvzId+Gtul2mNMW4UZQ+KqSb/nbN8F1CTxAfQsyUy8AAAAAXerYpAAAAAAAAAAAXQNpS8daKGZUeY5quYUcIiJZBMB7LiLsZJsEx9qw79fx99Bu/lk+sIePNUNcuOC2euthzfhLuWJ1nZBuoQFDjgGeOrUOoh6z7HlbYQAAEG/dvCadAAABFgAAAAAIOwAqAAAAZABMS0AAAAPooSNtHXJNJKKWlBtgkAM1LBxzlzYjIlS0xwpjP+uCi76fQj59wgTy0+xtx7O1qTb+W6zcI2zWZnrUU/8v8RZHFBfoo20QYKh95+wWr348yZAexZpdrjhyCxbChxlVTZOX6nZfIgcYBMnZRkOTCLdPO76yeqpDhqu9KrPe3YPTO3wAAAAA"
}
```


# Transactions

This endpoint represents successful transactions in a given ledger.

## ​

<mark style="color:blue;">`GET`</mark> `/ledgers/:ledger_sequence/transactions?cursor={paging_token}&order={asc,desc}&limit={1-200}&include_failed{true,false}`

#### Query Parameters

| Name            | Type   | Description                                                                                                                                                                                          |
| --------------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| sequence        | string | The sequence number of a specific ledger.                                                                                                                                                            |
| cursor          | string | A number that points to a specific location in a collection of responses and is pulled from the `paging_token` value of a record.                                                                    |
| order           | string | A designation of the order in which records should appear. Options include `asc`(ascending) or `desc` (descending). If this argument isn’t set, it defaults to `asc`.                                |
| limit           | string | The total number of records returned. The limit can range from 1 to 200 - an upper limit that is hardcoded in Horizon for performance reasons. If this argument isn’t designated, it defaults to 10. |
| include\_failed | string | Set to true to include failed transactions in results. Options include `true` and `false`.                                                                                                           |

{% tabs %}
{% tab title="200 " %}

```javascript
{
  "_links": {
    "self": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27147222/transactions?cursor=\u0026limit=2\u0026order=asc"
    },
    "next": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27147222/transactions?cursor=116596430667259904\u0026limit=2\u0026order=asc"
    },
    "prev": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27147222/transactions?cursor=116596430667255808\u0026limit=2\u0026order=desc"
    }
  },
  "_embedded": {
    "records": [
      {
        "memo": "FadBcV0owHrsLQif/V2TxZKGA2YiX+MoifI7oB/UjJM=",
        "_links": {
          "self": {
            "href": "https://expansion-testnet.bantu.network/transactions/3a644389bbec63dd2b107a03c16711563fc549daa7b7f56f951a2e470f81f2e0"
          },
          "account": {
            "href": "https://expansion-testnet.bantu.network/accounts/GBKTEASNXABG2NC4TY6N3IA5UFC6YW53Y4KTIKY3CUEKQ2W5EIYRMYVB"
          },
          "ledger": {
            "href": "https://expansion-testnet.bantu.network/ledgers/27147222"
          },
          "operations": {
            "href": "https://expansion-testnet.bantu.network/transactions/3a644389bbec63dd2b107a03c16711563fc549daa7b7f56f951a2e470f81f2e0/operations{?cursor,limit,order}",
            "templated": true
          },
          "effects": {
            "href": "https://expansion-testnet.bantu.network/transactions/3a644389bbec63dd2b107a03c16711563fc549daa7b7f56f951a2e470f81f2e0/effects{?cursor,limit,order}",
            "templated": true
          },
          "precedes": {
            "href": "https://expansion-testnet.bantu.network/transactions?order=asc\u0026cursor=116596430667255808"
          },
          "succeeds": {
            "href": "https://expansion-testnet.bantu.network/transactions?order=desc\u0026cursor=116596430667255808"
          }
        },
        "id": "3a644389bbec63dd2b107a03c16711563fc549daa7b7f56f951a2e470f81f2e0",
        "paging_token": "116596430667255808",
        "successful": true,
        "hash": "3a644389bbec63dd2b107a03c16711563fc549daa7b7f56f951a2e470f81f2e0",
        "ledger": 27147222,
        "created_at": "2019-12-06T23:05:38Z",
        "source_account": "GBKTEASNXABG2NC4TY6N3IA5UFC6YW53Y4KTIKY3CUEKQ2W5EIYRMYVB",
        "source_account_sequence": "113943107245989112",
        "fee_charged": 100,
        "max_fee": 100,
        "operation_count": 1,
        "envelope_xdr": "AAAAAFUyAk24Am00XJ483aAdoUXsW7vHFTQrGxUIqGrdIjEWAAAAZAGUzqcAAFz4AAAAAQAAAAAAAAAAAAAAAAAAAAAAAAADFadBcV0owHrsLQif/V2TxZKGA2YiX+MoifI7oB/UjJMAAAABAAAAAQAAAADhZHiqD/Q3uSTgjYEWGVRfCCHYvFmeqJU12G9SkzJYEQAAAAEAAAAAB77j4CQxLlm5aPrq/PDwFhGNs2ldqbdCZwkSfzXhUhYAAAABTk9ETAAAAAB1jYLXrFzNBOWCoPnZSHI3PJAhHtc1TrCaiPuZwSf5pgAAAAAAAABNAAAAAAAAAALw9Tl2AAAAQLSLnL3XT3F6dq5m2WHZ7tH3HBrK/e+p2G7Hrl3WXQToUsgxKIRH6bhHT/mIWwnrfLZ9nlVjMCFoZPqVKrI2lgLdIjEWAAAAQCrZ9IbjDAB408VgQaqNpwX8hXr/OUsoEexkWGkN9PjcBG2nf4bKWdbjiaqHZ+LrvM+DdmhxtA3L9bzmCie8NgA=",
        "result_xdr": "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAA=",
        "result_meta_xdr": "AAAAAQAAAAIAAAADAZ471gAAAAAAAAAAVTICTbgCbTRcnjzdoB2hRexbu8cVNCsbFQioat0iMRYAAAAAB5tToAGUzqcAAFz3AAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAZ471gAAAAAAAAAAVTICTbgCbTRcnjzdoB2hRexbu8cVNCsbFQioat0iMRYAAAAAB5tToAGUzqcAAFz4AAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAABAAAAAMBnju+AAAAAQAAAAAHvuPgJDEuWblo+ur88PAWEY2zaV2pt0JnCRJ/NeFSFgAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAAAAIP38ASptjhEiAAAAAAAEAAAAAAAAAAAAAAAEBnjvWAAAAAQAAAAAHvuPgJDEuWblo+ur88PAWEY2zaV2pt0JnCRJ/NeFSFgAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAAAAAIP8wASptjhEiAAAAAAAEAAAAAAAAAAAAAAAMBnjvVAAAAAQAAAADhZHiqD/Q3uSTgjYEWGVRfCCHYvFmeqJU12G9SkzJYEQAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAALEhqMpZ//////////wAAAAEAAAAAAAAAAAAAAAEBnjvWAAAAAQAAAADhZHiqD/Q3uSTgjYEWGVRfCCHYvFmeqJU12G9SkzJYEQAAAAFOT0RMAAAAAHWNgtesXM0E5YKg+dlIcjc8kCEe1zVOsJqI+5nBJ/mmAAAALEhqMkl//////////wAAAAEAAAAAAAAAAA==",
        "fee_meta_xdr": "AAAAAgAAAAMBnjvKAAAAAAAAAABVMgJNuAJtNFyePN2gHaFF7Fu7xxU0KxsVCKhq3SIxFgAAAAAHm1QEAZTOpwAAXPcAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEBnjvWAAAAAAAAAABVMgJNuAJtNFyePN2gHaFF7Fu7xxU0KxsVCKhq3SIxFgAAAAAHm1OgAZTOpwAAXPcAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA==",
        "memo_type": "hash",
        "signatures": [
          "tIucvddPcXp2rmbZYdnu0fccGsr976nYbseuXdZdBOhSyDEohEfpuEdP+YhbCet8tn2eVWMwIWhk+pUqsjaWAg==",
          "Ktn0huMMAHjTxWBBqo2nBfyFev85SygR7GRYaQ30+NwEbad/hspZ1uOJqodn4uu8z4N2aHG0Dcv1vOYKJ7w2AA=="
        ],
        "valid_after": "1970-01-01T00:00:00Z"
      },
      {
        "_links": {
          "self": {
            "href": "https://expansion-testnet.bantu.network/transactions/83eabfa824b57436eda49bb9ac28675285f6d945325f69db41792078a83d3479"
          },
          "account": {
            "href": "https://expansion-testnet.bantu.network/accounts/GDUHYA43LEQWXLJ5GQGNDJHZY6TZN2OK3KA4YQXQ47BRMRDRIUUC4IU5"
          },
          "ledger": {
            "href": "https://expansion-testnet.bantu.network/ledgers/27147222"
          },
          "operations": {
            "href": "https://expansion-testnet.bantu.network/transactions/83eabfa824b57436eda49bb9ac28675285f6d945325f69db41792078a83d3479/operations{?cursor,limit,order}",
            "templated": true
          },
          "effects": {
            "href": "https://expansion-testnet.bantu.network/transactions/83eabfa824b57436eda49bb9ac28675285f6d945325f69db41792078a83d3479/effects{?cursor,limit,order}",
            "templated": true
          },
          "precedes": {
            "href": "https://expansion-testnet.bantu.network/transactions?order=asc\u0026cursor=116596430667259904"
          },
          "succeeds": {
            "href": "https://expansion-testnet.bantu.network/transactions?order=desc\u0026cursor=116596430667259904"
          }
        },
        "id": "83eabfa824b57436eda49bb9ac28675285f6d945325f69db41792078a83d3479",
        "paging_token": "116596430667259904",
        "successful": true,
        "hash": "83eabfa824b57436eda49bb9ac28675285f6d945325f69db41792078a83d3479",
        "ledger": 27147222,
        "created_at": "2019-12-06T23:05:38Z",
        "source_account": "GDUHYA43LEQWXLJ5GQGNDJHZY6TZN2OK3KA4YQXQ47BRMRDRIUUC4IU5",
        "source_account_sequence": "113383696345604336",
        "fee_charged": 100,
        "max_fee": 100,
        "operation_count": 1,
        "envelope_xdr": "AAAAAOh8A5tZIWutPTQM0aT5x6eW6cragcxC8OfDFkRxRSguAAAAZAGS0d8AACDwAAAAAQAAAAAAAAAAAAAAAF3q3zYAAAAAAAAAAQAAAAEAAAAASe2g7z3OqB0F+vr+sM6vGMHgnomyJOLJeGMMjT1jgPcAAAADAAAAAVJNVAAAAAAAyGdd4j3sXGNb4PWMyeROuBvzRJJbU+On/O1oSxFvcqwAAAAAAAAAAAAAABcAB2UNAJiWgAAAAAAAAAAAAAAAAAAAAAJxRSguAAAAQG/ksOk2naI/PrA/6qes3c0IhoOpnBghd038S62rayiophPSs3HyUbgbOEpYQCkJezrf1bP+s2fFXVCEXbgveQk9Y4D3AAAAQAINPdxGV1VIeQ1OUDDlqtrSy8FrWQ/+CtRxqVHcHg98SYh5co/gd83R4IGDxukCByTbYapkrUOD0XDJKC/hcg4=",
        "result_xdr": "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAADAAAAAAAAAAAAAAACAAAAAA==",
        "result_meta_xdr": "AAAAAQAAAAIAAAADAZ471gAAAAAAAAAA6HwDm1kha609NAzRpPnHp5bpytqBzELw58MWRHFFKC4AAAAAAKovMAGS0d8AACDvAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAZ471gAAAAAAAAAA6HwDm1kha609NAzRpPnHp5bpytqBzELw58MWRHFFKC4AAAAAAKovMAGS0d8AACDwAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAAAA==",
        "fee_meta_xdr": "AAAAAgAAAAMBnjukAAAAAAAAAADofAObWSFrrT00DNGk+cenlunK2oHMQvDnwxZEcUUoLgAAAAAAqi+UAZLR3wAAIO8AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEBnjvWAAAAAAAAAADofAObWSFrrT00DNGk+cenlunK2oHMQvDnwxZEcUUoLgAAAAAAqi8wAZLR3wAAIO8AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA==",
        "memo_type": "none",
        "signatures": [
          "b+Sw6Tadoj8+sD/qp6zdzQiGg6mcGCF3TfxLratrKKimE9KzcfJRuBs4SlhAKQl7Ot/Vs/6zZ8VdUIRduC95CQ==",
          "Ag093EZXVUh5DU5QMOWq2tLLwWtZD/4K1HGpUdweD3xJiHlyj+B3zdHggYPG6QIHJNthqmStQ4PRcMkoL+FyDg=="
        ],
        "valid_after": "1970-01-01T00:00:00Z",
        "valid_before": "2019-12-06T23:07:34Z"
      }
    ]
  }
}
```

{% endtab %}
{% endtabs %}


# Operations

This endpoint returns successful operations in a specific ledger.

&#x20;\| | | | --- | --- | | GET | /ledgers/:ledger\_sequence/operations?cursor={paging\_token}\&order={asc,desc}\&limit={1-200}\&include\_failed{true,false}\&join={transactions} |

**ARGUMENTS**

* ledger\_sequencerequired

  The sequence number of a specific ledger.
* cursoroptional

  A number that points to a specific location in a collection of responses and is pulled from the `paging_token` value of a record.
* orderoptional

  A designation of the order in which records should appear. Options include `asc`(ascending) or `desc` (descending). If this argument isn’t set, it defaults to `asc`.
* limitoptional

  The total number of records returned. The limit can range from 1 to 200 - an upper limit that is hardcoded in Horizon for performance reasons. If this argument isn’t designated, it defaults to 10.
* include\_failedoptional

  Set to true to include failed operations in results. Options include `true` and `false`.
* joinoptional

  Set to `transactions` to include the transactions which created each of the operations in the response.

Example RequestcURLJavaScript

{% tabs %}
{% tab title="JavaScript" %}

```javascript
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://expansion-testnet.bantu.network");

server
  .operations()
  .forLedger("27147222")
  .call()
  .then(function (resp) {
    console.log(resp);
  })
  .catch(function (err) {
    console.error(err);
  });
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl "https://expansion-testnet.bantu.network/ledgers/27147222/operations?limit=2"
```

{% endtab %}
{% endtabs %}

Example Response<br>

```bash
{
  "_links": {
    "self": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27147222/operations?cursor=\u0026limit=2\u0026order=asc"
    },
    "next": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27147222/operations?cursor=116596430667259905\u0026limit=2\u0026order=asc"
    },
    "prev": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27147222/operations?cursor=116596430667255809\u0026limit=2\u0026order=desc"
    }
  },
  "_embedded": {
    "records": [
      {
        "_links": {
          "self": {
            "href": "https://expansion-testnet.bantu.network/operations/116596430667255809"
          },
          "transaction": {
            "href": "https://expansion-testnet.bantu.network/transactions/3a644389bbec63dd2b107a03c16711563fc549daa7b7f56f951a2e470f81f2e0"
          },
          "effects": {
            "href": "https://expansion-testnet.bantu.network/operations/116596430667255809/effects"
          },
          "succeeds": {
            "href": "https://expansion-testnet.bantu.network/effects?order=desc\u0026cursor=116596430667255809"
          },
          "precedes": {
            "href": "https://expansion-testnet.bantu.network/effects?order=asc\u0026cursor=116596430667255809"
          }
        },
        "id": "116596430667255809",
        "paging_token": "116596430667255809",
        "transaction_successful": true,
        "source_account": "GDQWI6FKB72DPOJE4CGYCFQZKRPQQIOYXRMZ5KEVGXMG6UUTGJMBCASH",
        "type": "payment",
        "type_i": 1,
        "created_at": "2019-12-06T23:05:38Z",
        "transaction_hash": "3a644389bbec63dd2b107a03c16711563fc549daa7b7f56f951a2e470f81f2e0",
        "asset_type": "credit_alphanum4",
        "asset_code": "NODL",
        "asset_issuer": "GB2Y3AWXVROM2BHFQKQPTWKIOI3TZEBBD3LTKTVQTKEPXGOBE742NODL",
        "from": "GDQWI6FKB72DPOJE4CGYCFQZKRPQQIOYXRMZ5KEVGXMG6UUTGJMBCASH",
        "to": "GAD35Y7AEQYS4WNZND5OV7HQ6ALBDDNTNFO2TN2CM4ERE7ZV4FJBNXZ6",
        "amount": "0.0000077"
      },
      {
        "_links": {
          "self": {
            "href": "https://expansion-testnet.bantu.network/operations/116596430667259905"
          },
          "transaction": {
            "href": "https://expansion-testnet.bantu.network/transactions/83eabfa824b57436eda49bb9ac28675285f6d945325f69db41792078a83d3479"
          },
          "effects": {
            "href": "https://expansion-testnet.bantu.network/operations/116596430667259905/effects"
          },
          "succeeds": {
            "href": "https://expansion-testnet.bantu.network/effects?order=desc\u0026cursor=116596430667259905"
          },
          "precedes": {
            "href": "https://expansion-testnet.bantu.network/effects?order=asc\u0026cursor=116596430667259905"
          }
        },
        "id": "116596430667259905",
        "paging_token": "116596430667259905",
        "transaction_successful": true,
        "source_account": "GBE63IHPHXHKQHIF7L5P5MGOV4MMDYE6RGZCJYWJPBRQZDJ5MOAPOX7A",
        "type": "manage_offer",
        "type_i": 3,
        "created_at": "2019-12-06T23:05:38Z",
        "transaction_hash": "83eabfa824b57436eda49bb9ac28675285f6d945325f69db41792078a83d3479",
        "amount": "0.0000023",
        "price": "0.0484621",
        "price_r": {
          "n": 484621,
          "d": 10000000
        },
        "buying_asset_type": "native",
        "selling_asset_type": "credit_alphanum4",
        "selling_asset_code": "RMT",
        "selling_asset_issuer": "GDEGOXPCHXWFYY234D2YZSPEJ24BX42ESJNVHY5H7TWWQSYRN5ZKZE3N",
        "offer_id": 0
      }
    ]
  }
}
```


# Payments

This endpoint returns all payment-related operations in a specific ledger.

Operation types that can be returned by this endpoint include:

* `create_account`
* `payment`
* `path_payment`
* `account_merge`

{% tabs %}
{% tab title="JavaScript" %}

```javascript
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://expansion-testnet.bantu.network/");

server
  .payments()
  .forLedger("27521176")
  .call()
  .then(function (resp) {
    console.log(resp);
  })
  .catch(function (err) {
    console.error(err);
  });
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl "https://expansion-testnet.bantu.network/ledgers/27521176/payments?limit=1"
```

{% endtab %}
{% endtabs %}

```bash
{
  "_links": {
    "self": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27521176/payments?cursor=\u0026limit=1\u0026order=asc"
    },
    "next": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27521176/payments?cursor=118202550867476481\u0026limit=1\u0026order=asc"
    },
    "prev": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27521176/payments?cursor=118202550867476481\u0026limit=1\u0026order=desc"
    }
  },
  "_embedded": {
    "records": [
      {
        "_links": {
          "self": {
            "href": "https://expansion-testnet.bantu.network/operations/118202550867476481"
          },
          "transaction": {
            "href": "https://expansion-testnet.bantu.network/transactions/971454b84a82baa38afa975e9eb4ff2632821b5a3e7f7993a7e20bbd9d7633ea"
          },
          "effects": {
            "href": "https://expansion-testnet.bantu.network/operations/118202550867476481/effects"
          },
          "succeeds": {
            "href": "https://expansion-testnet.bantu.network/effects?order=desc\u0026cursor=118202550867476481"
          },
          "precedes": {
            "href": "https://expansion-testnet.bantu.network/effects?order=asc\u0026cursor=118202550867476481"
          }
        },
        "id": "118202550867476481",
        "paging_token": "118202550867476481",
        "transaction_successful": true,
        "source_account": "GDQWI6FKB72DPOJE4CGYCFQZKRPQQIOYXRMZ5KEVGXMG6UUTGJMBCASH",
        "type": "payment",
        "type_i": 1,
        "created_at": "2019-12-30T22:35:49Z",
        "transaction_hash": "971454b84a82baa38afa975e9eb4ff2632821b5a3e7f7993a7e20bbd9d7633ea",
        "asset_type": "credit_alphanum4",
        "asset_code": "NODL",
        "asset_issuer": "GB2Y3AWXVROM2BHFQKQPTWKIOI3TZEBBD3LTKTVQTKEPXGOBE742NODL",
        "from": "GDQWI6FKB72DPOJE4CGYCFQZKRPQQIOYXRMZ5KEVGXMG6UUTGJMBCASH",
        "to": "GDGJS7AXAUFDZARIRDVZ5V7CFW6XY47WSBE2OVLCCGCDWOE7INKYN3PS",
        "amount": "0.0000017"
      }
    ]
  }
}
```


# Effects

This endpoint returns the effects of a specific ledger.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://expansion-testnet.bantu.network");

server
  .effects()
  .forLedger(0)
  .call()
  .then(function (resp) {
    console.log(resp);
  })
  .catch(function (err) {
    console.error(err);
  });
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl "https://expansion-testnet.bantu.network/ledgers/0/effects"
```

{% endtab %}
{% endtabs %}

```bash
{
  "_links": {
    "self": {
      "href": "https://expansion-testnet.bantu.network/ledgers/0/effects?cursor=&limit=10&order=asc"
    },
    "next": {
      "href": "https://horizon.stellar.org/ledgers/0/effects?cursor=33676838572034-1&limit=10&order=asc"
    },
    "prev": {
      "href": "https://expansion-testnet.bantu.network/ledgers/0/effects?cursor=12884905985-1&limit=10&order=desc"
    }
  },
  "_embedded": {
    "records": [
      {
        "_links": {
          "operation": {
            "href": "https://expansion-testnet.bantu.network/operations/12884905985"
          },
          "succeeds": {
            "href": "https://expansion-testnet.bantu.network/effects?order=desc&cursor=12884905985-1"
          },
          "precedes": {
            "href": "https://expansion-testnet.bantu.network/effects?order=asc&cursor=12884905985-1"
          }
        },
        "id": "0000000012884905985-0000000001",
        "paging_token": "12884905985-1",
        "account": "GALPCCZN4YXA3YMJHKL6CVIECKPLJJCTVMSNYWBTKJW4K5HQLYLDMZTB",
        "type": "account_created",
        "type_i": 0,
        "created_at": "2015-09-30T17:15:54Z",
        "starting_balance": "20.0000000"
      },
      {
        "_links": {
          "operation": {
            "href": "https://expansion-testnet.bantu.network//operations/12884905985"
          },
          "succeeds": {
            "href": "https://expansion-testnet.bantu.network/effects?order=desc&cursor=12884905985-2"
          },
          "precedes": {
            "href": "https://expansion-testnet.bantu.network/effects?order=asc&cursor=12884905985-2"
          }
        },
        "id": "0000000012884905985-0000000002",
        "paging_token": "12884905985-2",
        "account": "GAAZI4TCR3TY5OJHCTJC2A4QSY6CJWJH5IAJTGKIN2ER7LBNVKOCCWN7",
        "type": "account_debited",
        "type_i": 3,
        "created_at": "2015-09-30T17:15:54Z",
        "asset_type": "native",
        "amount": "20.0000000"
      }
    ]
  }
}
```


# List

This endpoint lists all ledgers and can be used in [streaming](/api/introduction/streaming) mode.

Streaming mode allows you to listen for new ledgers as they close. If called in streaming mode, Horizon will start at the earliest known ledger unless a `cursor` is set, in which case it will start from that `cursor`. By setting the cursor value to `now`, you can stream ledgers since your request time.

* cursor (optional)

  A number that points to a specific location in a collection of responses and is pulled from the `paging_token` value of a record.
* order (optional)

  A designation of the order in which records should appear. Options include `asc`(ascending) or `desc` (descending). If this argument isn’t set, it defaults to `asc`.
* limit(optional)

  The total number of records returned. The limit can range from 1 to 200 - an upper limit that is hardcoded in Horizon for performance reasons. If this argument isn’t designated, it defaults to 10.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://expansion-testnet.bantu.network");

server
  .ledgers()
  .call()
  .then(function (ledgerResult) {
    // page 1
    console.log(ledgerResult.records)
    return ledgerResult.next()
  })
  .then(function (ledgerResult) {
    // page 2
    console.log(ledgerResult.records)
  })
  .catch(function(err) {
    console.log(err)
  });
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl "https://expansion-testnet.bantu.network/ledgers?limit=200&order=desc"
```

{% endtab %}
{% endtabs %}

```bash
{
  "_embedded": {
    "records": [
      {
        "_links": {
          "effects": {
            "href": "/ledgers/1/effects/{?cursor,limit,order}",
            "templated": true
          },
          "operations": {
            "href": "/ledgers/1/operations/{?cursor,limit,order}",
            "templated": true
          },
          "self": {
            "href": "/ledgers/1"
          },
          "transactions": {
            "href": "/ledgers/1/transactions/{?cursor,limit,order}",
            "templated": true
          }
        },
        "id": "e8e10918f9c000c73119abe54cf089f59f9015cc93c49ccf00b5e8b9afb6e6b1",
        "paging_token": "4294967296",
        "hash": "e8e10918f9c000c73119abe54cf089f59f9015cc93c49ccf00b5e8b9afb6e6b1",
        "sequence": 1,
        "transaction_count": 0,
        "successful_transaction_count": 0,
        "failed_transaction_count": 0,
        "operation_count": 0,
        "tx_set_operation_count": 0,
        "closed_at": "1970-01-01T00:00:00Z",
        "total_coins": "100000000000.0000000",
        "fee_pool": "0.0000000",
        "base_fee_in_stroops": 100,
        "base_reserve_in_stroops": 100000000,
        "max_tx_set_size": 50
      },
      {
        "_links": {
          "effects": {
            "href": "/ledgers/2/effects/{?cursor,limit,order}",
            "templated": true
          },
          "operations": {
            "href": "/ledgers/2/operations/{?cursor,limit,order}",
            "templated": true
          },
          "self": {
            "href": "/ledgers/2"
          },
          "transactions": {
            "href": "/ledgers/2/transactions/{?cursor,limit,order}",
            "templated": true
          }
        },
        "id": "e12e5809ab8c59d8256e691cb48a024dd43960bc15902d9661cd627962b2bc71",
        "paging_token": "8589934592",
        "hash": "e12e5809ab8c59d8256e691cb48a024dd43960bc15902d9661cd627962b2bc71",
        "prev_hash": "e8e10918f9c000c73119abe54cf089f59f9015cc93c49ccf00b5e8b9afb6e6b1",
        "sequence": 2,
        "transaction_count": 0,
        "successful_transaction_count": 0,
        "failed_transaction_count": 0,
        "operation_count": 0,
        "closed_at": "2015-07-16T23:49:00Z",
        "total_coins": "100000000000.0000000",
        "fee_pool": "0.0000000",
        "base_fee_in_stroops": 100,
        "base_reserve_in_stroops": 100000000,
        "max_tx_set_size": 100
      }
    ]
  },
  "_links": {
    "next": {
      "href": "/ledgers?order=asc&limit=2&cursor=8589934592"
    },
    "prev": {
      "href": "/ledgers?order=desc&limit=2&cursor=4294967296"
    },
    "self": {
      "href": "/ledgers?order=asc&limit=2&cursor="
    }
  }
}
```

```bash
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://expansion-testnet.bantu.network/");

var callback = function (resp) {
  console.log(resp);
};

var ledgerHandler = function (ledgerResponse) {
  console.log(ledgerResponse);
};

var es = server.ledgers()
  .cursor('now')
  .stream({
    onmessage: ledgerHandler
});
```


# Transactions


# Index

import { EndpointsTable } from "components/EndpointsTable";

Transactions are commands that modify the ledger state and consist of one or more operations.

Learn more about [transactions](/glossary/transactions).

Endpoints

| GET  | [/transactions/:transaction\_id](https://developers.stellar.org/api/resources/transactions/single/)                |
| ---- | ------------------------------------------------------------------------------------------------------------------ |
| GET  | [/transactions/:transaction\_id/operations](https://developers.stellar.org/api/resources/transactions/operations/) |
| GET  | [/transactions/:transaction\_id/effects](https://developers.stellar.org/api/resources/transactions/effects/)       |
| GET  | [/transactions](https://developers.stellar.org/api/resources/transactions/list/)                                   |
| POST | [/transactions](https://developers.stellar.org/api/resources/transactions/post/)                                   |


# Object

When Horizon returns information about a transaction, it uses the following format:

&#x20;

* ATTRIBUTEDATA TYPE

  DESCRIPTION
* idstring

  A unique identifier for this transaction.
* paging\_tokennumber

  A cursor value for use in [pagination](https://developers.stellar.org/api/introduction/pagination/).
* successfulboolean

  Indicates if this transaction was successful or not.
* hashstring

  A hex-encoded SHA-256 hash of this transaction’s [XDR](https://developers.stellar.org/docs/glossary/xdr/)-encoded form.
* ledgernumber

  The sequence number of the ledger that this transaction was included in.
* created\_atISO8601 string

  The date this transaction was created.
* source\_accountstring

  The account that originates the transaction.
* source\_account\_sequencestring

  The source account’s sequence number that this transaction consumed.
* fee\_chargednumber

  The fee (in [stroops](https://developers.stellar.org/docs/issuing-assets/anatomy-of-an-asset/#lumens-xlm)) paid by the source account to apply this transaction to the ledger.
* max\_feenumber

  The maximum fee (in [stroops](https://developers.stellar.org/docs/issuing-assets/anatomy-of-an-asset/#lumens-xlm)) that the source account was willing to pay.
* operation\_countnumber

  The number of operations contained within this transaction.
* envelope\_xdrstring

  A base64 encoded string of the raw `TransactionEnvelope` XDR struct for this transaction.
* result\_xdrstring

  A base64 encoded string of the raw `TransactionResult` XDR struct for this transaction.
* result\_meta\_xdrstring

  A base64 encoded string of the raw `TransactionMeta` XDR struct for this transaction
* fee\_meta\_xdrstring

  A base64 encoded string of the raw `LedgerEntryChanges` XDR struct produced by taking fees for this transaction.
* memostring

  The optional memo attached to a transaction.
* memo\_typestring

  The type of memo. Potential values include `MEMO_TEXT`, `MEMO_ID`, `MEMO_HASH`, `MEMO_RETURN`.
* signaturesstring

  An array of signatures used to sign this transaction.
* valid\_afterRFC3339 date-time string

  The date after which a transaction is valid.
* valid\_beforeRFC3339 date-time string

  The date before which a transaction is valid.

```bash
{
  "memo": "298424",
  "_links": {
    "self": {
      "href": "https://expansion-testnet.bantu.network/transactions/132c440e984ab97d895f3477015080aafd6c4375f6a70a87327f7f95e13c4e31"
    },
    "account": {
      "href": "https://expansion-testnet.bantu.network/accounts/GCO2IP3MJNUOKS4PUDI4C7LGGMQDJGXG3COYX3WSB4HHNAHKYV5YL3VC"
    },
    "ledger": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27956256"
    },
    "operations": {
      "href": "https://expansion-testnet.bantu.network/transactions/132c440e984ab97d895f3477015080aafd6c4375f6a70a87327f7f95e13c4e31/operations{?cursor,limit,order}",
      "templated": true
    },
    "effects": {
      "href": "https://expansion-testnet.bantu.network/transactions/132c440e984ab97d895f3477015080aafd6c4375f6a70a87327f7f95e13c4e31/effects{?cursor,limit,order}",
      "templated": true
    },
    "precedes": {
      "href": "https://expansion-testnet.bantu.network/transactions?order=asc\u0026cursor=120071205238677504"
    },
    "succeeds": {
      "href": "https://expansion-testnet.bantu.network/transactions?order=desc\u0026cursor=120071205238677504"
    }
  },
  "id": "132c440e984ab97d895f3477015080aafd6c4375f6a70a87327f7f95e13c4e31",
  "paging_token": "120071205238677504",
  "successful": true,
  "hash": "132c440e984ab97d895f3477015080aafd6c4375f6a70a87327f7f95e13c4e31",
  "ledger": 27956256,
  "created_at": "2020-01-27T22:13:17Z",
  "source_account": "GCO2IP3MJNUOKS4PUDI4C7LGGMQDJGXG3COYX3WSB4HHNAHKYV5YL3VC",
  "source_account_sequence": "64034663849209932",
  "fee_charged": 100,
  "max_fee": 100,
  "operation_count": 1,
  "envelope_xdr": "AAAAAJ2kP2xLaOVLj6DRwX1mMyA0mubYnYvu0g8OdoDqxXuFAAAAZADjfzAACzBMAAAAAQAAAAAAAAAAAAAAAF4vYIYAAAABAAAABjI5ODQyNAAAAAAAAQAAAAAAAAABAAAAAKdeYELovtcnTxqPEVsdbxHLMoMRalZsK7lo/+3ARzUZAAAAAAAAAADUFJPYAAAAAAAAAAHqxXuFAAAAQBpLpQyh+mwDd5nDSxTaAh5wopBBUaSD1eOK9MdiO+4kWKVTqSr/Ko3kYE/+J42Opsewf81TwINONPbY2CtPggE=",
  "result_xdr": "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAA=",
  "result_meta_xdr": "AAAAAQAAAAIAAAADAaqUIAAAAAAAAAAAnaQ/bEto5UuPoNHBfWYzIDSa5tidi+7SDw52gOrFe4UAAkRg8uGCXADjfzAACzBLAAAAAAAAAAEAAAAAnaQ/bEto5UuPoNHBfWYzIDSa5tidi+7SDw52gOrFe4UAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAaqUIAAAAAAAAAAAnaQ/bEto5UuPoNHBfWYzIDSa5tidi+7SDw52gOrFe4UAAkRg8uGCXADjfzAACzBMAAAAAAAAAAEAAAAAnaQ/bEto5UuPoNHBfWYzIDSa5tidi+7SDw52gOrFe4UAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAABAAAABAAAAAMBqoicAAAAAAAAAACnXmBC6L7XJ08ajxFbHW8RyzKDEWpWbCu5aP/twEc1GQAAAAAAmwWMAaVkEgAAAC0AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEBqpQgAAAAAAAAAACnXmBC6L7XJ08ajxFbHW8RyzKDEWpWbCu5aP/twEc1GQAAAADUr5lkAaVkEgAAAC0AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAMBqpQgAAAAAAAAAACdpD9sS2jlS4+g0cF9ZjMgNJrm2J2L7tIPDnaA6sV7hQACRGDy4YJcAON/MAALMEwAAAAAAAAAAQAAAACdpD9sS2jlS4+g0cF9ZjMgNJrm2J2L7tIPDnaA6sV7hQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEBqpQgAAAAAAAAAACdpD9sS2jlS4+g0cF9ZjMgNJrm2J2L7tIPDnaA6sV7hQACRGAezO6EAON/MAALMEwAAAAAAAAAAQAAAACdpD9sS2jlS4+g0cF9ZjMgNJrm2J2L7tIPDnaA6sV7hQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA==",
  "fee_meta_xdr": "AAAAAgAAAAMBqpQYAAAAAAAAAACdpD9sS2jlS4+g0cF9ZjMgNJrm2J2L7tIPDnaA6sV7hQACRGDy4YLAAON/MAALMEsAAAAAAAAAAQAAAACdpD9sS2jlS4+g0cF9ZjMgNJrm2J2L7tIPDnaA6sV7hQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAEBqpQgAAAAAAAAAACdpD9sS2jlS4+g0cF9ZjMgNJrm2J2L7tIPDnaA6sV7hQACRGDy4YJcAON/MAALMEsAAAAAAAAAAQAAAACdpD9sS2jlS4+g0cF9ZjMgNJrm2J2L7tIPDnaA6sV7hQAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA==",
  "memo_type": "text",
  "signatures": [
    "GkulDKH6bAN3mcNLFNoCHnCikEFRpIPV44r0x2I77iRYpVOpKv8qjeRgT/4njY6mx7B/zVPAg0409tjYK0+CAQ=="
  ],
  "valid_after": "1970-01-01T00:00:00Z",
  "valid_before": "2020-01-27T22:13:26Z"
}
```


# Single

The single transaction endpoint provides information on a specific transaction.

&#x20;**ARGUMENTS**

* hashrequired

  A hex-encoded SHA-256 hash of this transaction’s XDR-encoded form.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://expansion-testnet.bantu.network");

server
  .transactions()
  .transaction(
    "5ebd5c0af4385500b53dd63b0ef5f6e8feef1a7e1c86989be3cdcce825f3c0cc",
  )
  .call()
  .then(function (resp) {
    console.log(resp);
  })
  .catch(function (err) {
    console.error(err);
  });
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl "https://expansion-testnet.bantu.network/transactions/5ebd5c0af4385500b53dd63b0ef5f6e8feef1a7e1c86989be3cdcce825f3c0cc"
```

{% endtab %}
{% endtabs %}

```bash
{
  "_links": {
    "self": {
      "href": "https://expansion-testnet.bantu.network/transactions/5ebd5c0af4385500b53dd63b0ef5f6e8feef1a7e1c86989be3cdcce825f3c0cc"
    },
    "account": {
      "href": "https://expansion-testnet.bantu.network/accounts/GDI5EK4HNMBHJJQGP3GUXQJIIOHU2CJO3LABPWD6WYSPJZP5NP67TMNN"
    },
    "ledger": {
      "href": "https://expansion-testnet.bantu.network/ledgers/27963785"
    },
    "operations": {
      "href": "https://expansion-testnet.bantu.network/transactions/5ebd5c0af4385500b53dd63b0ef5f6e8feef1a7e1c86989be3cdcce825f3c0cc/operations{?cursor,limit,order}",
      "templated": true
    },
    "effects": {
      "href": "https://expansion-testnet.bantu.network/transactions/5ebd5c0af4385500b53dd63b0ef5f6e8feef1a7e1c86989be3cdcce825f3c0cc/effects{?cursor,limit,order}",
      "templated": true
    },
    "precedes": {
      "href": "https://expansion-testnet.bantu.network/transactions?order=asc\u0026cursor=120103542047408128"
    },
    "succeeds": {
      "href": "https://expansion-testnet.bantu.network/transactions?order=desc\u0026cursor=120103542047408128"
    }
  },
  "id": "5ebd5c0af4385500b53dd63b0ef5f6e8feef1a7e1c86989be3cdcce825f3c0cc",
  "paging_token": "120103542047408128",
  "successful": true,
  "hash": "5ebd5c0af4385500b53dd63b0ef5f6e8feef1a7e1c86989be3cdcce825f3c0cc",
  "ledger": 27963785,
  "created_at": "2020-01-28T10:03:33Z",
  "source_account": "GDI5EK4HNMBHJJQGP3GUXQJIIOHU2CJO3LABPWD6WYSPJZP5NP67TMNN",
  "source_account_sequence": "65046128646685383",
  "fee_charged": 100,
  "max_fee": 100,
  "operation_count": 1,
  "envelope_xdr": "AAAAANHSK4drAnSmBn7NS8EoQ49NCS7awBfYfrYk9OX9a/35AAAAZADnFxwAAALHAAAAAAAAAAAAAAABAAAAAQAAAADR0iuHawJ0pgZ+zUvBKEOPTQku2sAX2H62JPTl/Wv9+QAAAAEAAAAA+qpaPAsU/CGcSeS4KnvqE9y+Bcjhyr1l6jiwixRsvu8AAAABTkdOVAAAAAAs4YIuYne69wMuNtfiZ64gb7E3qnTR7A4yD4jZDSS8AQAAAdGpSiAAAAAAAAAAAAH9a/35AAAAQM5LSBiFRQKtFYRPpNabwVuvIhZSVRDaajf0KzYaqJmgaXAlFaTOYyYc11YuxUM1Fzl1VT4UEbI22BvO/8HzUgQ=",
  "result_xdr": "AAAAAAAAAGQAAAAAAAAAAQAAAAAAAAABAAAAAAAAAAA=",
  "result_meta_xdr": "AAAAAQAAAAIAAAADAaqxiQAAAAAAAAAA0dIrh2sCdKYGfs1LwShDj00JLtrAF9h+tiT05f1r/fkAAAAAAhWQlADnFxwAAALGAAAAAQAAAAEAAAAAhD8BLsZFQEF33rKS6YopQUT3b6iLBG4nspe68/DBNBYAAAAAAAAAAAEAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBqrGJAAAAAAAAAADR0iuHawJ0pgZ+zUvBKEOPTQku2sAX2H62JPTl/Wv9+QAAAAACFZCUAOcXHAAAAscAAAABAAAAAQAAAACEPwEuxkVAQXfespLpiilBRPdvqIsEbieyl7rz8ME0FgAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAAAQAAAADAan62gAAAAEAAAAA+qpaPAsU/CGcSeS4KnvqE9y+Bcjhyr1l6jiwixRsvu8AAAABTkdOVAAAAAAs4YIuYne69wMuNtfiZ64gb7E3qnTR7A4yD4jZDSS8AQAAAAAAAAALf/////////8AAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEBqrGJAAAAAQAAAAD6qlo8CxT8IZxJ5Lgqe+oT3L4FyOHKvWXqOLCLFGy+7wAAAAFOR05UAAAAACzhgi5id7r3Ay421+JnriBvsTeqdNHsDjIPiNkNJLwBAAAB0alKIAt//////////wAAAAEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwGp+6EAAAABAAAAANHSK4drAnSmBn7NS8EoQ49NCS7awBfYfrYk9OX9a/35AAAAAU5HTlQAAAAALOGCLmJ3uvcDLjbX4meuIG+xN6p00ewOMg+I2Q0kvAEAAAkQQwRXzX//////////AAAAAQAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABAaqxiQAAAAEAAAAA0dIrh2sCdKYGfs1LwShDj00JLtrAF9h+tiT05f1r/fkAAAABTkdOVAAAAAAs4YIuYne69wMuNtfiZ64gb7E3qnTR7A4yD4jZDSS8AQAABz6ZujfNf/////////8AAAABAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
  "fee_meta_xdr": "AAAAAgAAAAMBqfuhAAAAAAAAAADR0iuHawJ0pgZ+zUvBKEOPTQku2sAX2H62JPTl/Wv9+QAAAAACFZD4AOcXHAAAAsYAAAABAAAAAQAAAACEPwEuxkVAQXfespLpiilBRPdvqIsEbieyl7rz8ME0FgAAAAAAAAAAAQAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQGqsYkAAAAAAAAAANHSK4drAnSmBn7NS8EoQ49NCS7awBfYfrYk9OX9a/35AAAAAAIVkJQA5xccAAACxgAAAAEAAAABAAAAAIQ/AS7GRUBBd96ykumKKUFE92+oiwRuJ7KXuvPwwTQWAAAAAAAAAAABAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
  "memo_type": "none",
  "signatures": [
    "zktIGIVFAq0VhE+k1pvBW68iFlJVENpqN/QrNhqomaBpcCUVpM5jJhzXVi7FQzUXOXVVPhQRsjbYG87/wfNSBA=="
  ]
}
```


# Operations

This endpoint returns successful operations for a specific transaction.

&#x20;ARGUMENTS

* transaction\_hashrequired

  A hex-encoded SHA-256 hash of this transaction’s XDR-encoded form.
* cursoroptional

  A number that points to a specific location in a collection of responses and is pulled from the `paging_token` value of a record.
* orderoptional

  A designation of the order in which records should appear. Options include `asc`(ascending) or `desc` (descending). If this argument isn’t set, it defaults to `asc`.
* limitoptional

  The total number of records returned. The limit can range from 1 to 200 - an upper limit that is hardcoded in Horizon for performance reasons. If this argument isn’t designated, it defaults to 10.
* include\_failedoptional

  Set to true to include failed operations in results. Options include `true` and `false`.
* joinoptional

  Set to `transactions` to include the transactions which created each of the operations in the response

{% tabs %}
{% tab title="JavaScript" %}

```javascript
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://expansion-testnet.bantu.network");

server
  .operations()
  .forTransaction(
    "6b983a4e0dc3c04f4bd6b9037c55f70a09c434dfd01492be1077cf7ea68c2e4a",
  )
  .call()
  .then(function (resp) {
    console.log(resp);
  })
  .catch(function (err) {
    console.error(err);
  });
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl "https://expansion-testnet.bantu.network/transactions/6b983a4e0dc3c04f4bd6b9037c55f70a09c434dfd01492be1077cf7ea68c2e4a/operations"
```

{% endtab %}
{% endtabs %}

```bash
{
  "_links": {
    "self": {
      "href": "https://expansion-testnet.bantu.network/transactions/6b983a4e0dc3c04f4bd6b9037c55f70a09c434dfd01492be1077cf7ea68c2e4a/operations?cursor=\u0026limit=10\u0026order=asc"
    },
    "next": {
      "href": "https://expansion-testnet.bantu.network/transactions/6b983a4e0dc3c04f4bd6b9037c55f70a09c434dfd01492be1077cf7ea68c2e4a/operations?cursor=120133379185221636\u0026limit=10\u0026order=asc"
    },
    "prev": {
      "href": "https://expansion-testnet.bantu.network/transactions/6b983a4e0dc3c04f4bd6b9037c55f70a09c434dfd01492be1077cf7ea68c2e4a/operations?cursor=120133379185221633\u0026limit=10\u0026order=desc"
    }
  },
  "_embedded": {
    "records": [
      {
        "_links": {
          "self": {
            "href": "https://expansion-testnet.bantu.network/operations/120133379185221633"
          },
          "transaction": {
            "href": "https://expansion-testnet.bantu.network/transactions/6b983a4e0dc3c04f4bd6b9037c55f70a09c434dfd01492be1077cf7ea68c2e4a"
          },
          "effects": {
            "href": "https://expansion-testnet.bantu.network/operations/120133379185221633/effects"
          },
          "succeeds": {
            "href": "https://expansion-testnet.bantu.network/effects?order=desc\u0026cursor=120133379185221633"
          },
          "precedes": {
            "href": "https://expansion-testnet.bantu.network/effects?order=asc\u0026cursor=120133379185221633"
          }
        },
        "id": "120133379185221633",
        "paging_token": "120133379185221633",
        "transaction_successful": true,
        "source_account": "GDJX67SFY2N73H72TWMKKBQP5UPBNKBNUMNE2IGFKNES43S4327X6DHG",
        "type": "manage_buy_offer",
        "type_i": 12,
        "created_at": "2020-01-28T21:14:59Z",
        "transaction_hash": "6b983a4e0dc3c04f4bd6b9037c55f70a09c434dfd01492be1077cf7ea68c2e4a",
        "amount": "0.0000000",
        "price": "0.0001000",
        "price_r": {
          "n": 1,
          "d": 10000
        },
        "buying_asset_type": "native",
        "selling_asset_type": "credit_alphanum4",
        "selling_asset_code": "ETH",
        "selling_asset_issuer": "GBDEVU63Y6NTHJQQZIKVTC23NWLQVP3WJ2RI2OTSJTNYOIGICST6DUXR",
        "offer_id": 149983118
      },
      {
        "_links": {
          "self": {
            "href": "https://expansion-testnet.bantu.network/operations/120133379185221634"
          },
          "transaction": {
            "href": "https://expansion-testnet.bantu.network/transactions/6b983a4e0dc3c04f4bd6b9037c55f70a09c434dfd01492be1077cf7ea68c2e4a"
          },
          "effects": {
            "href": "https://expansion-testnet.bantu.network/operations/120133379185221634/effects"
          },
          "succeeds": {
            "href": "https://expansion-testnet.bantu.network/effects?order=desc\u0026cursor=120133379185221634"
          },
          "precedes": {
            "href": "https://expansion-testnet.bantu.network/effects?order=asc\u0026cursor=120133379185221634"
          }
        },
        "id": "120133379185221634",
        "paging_token": "120133379185221634",
        "transaction_successful": true,
        "source_account": "GDJX67SFY2N73H72TWMKKBQP5UPBNKBNUMNE2IGFKNES43S4327X6DHG",
        "type": "manage_buy_offer",
        "type_i": 12,
        "created_at": "2020-01-28T21:14:59Z",
        "transaction_hash": "6b983a4e0dc3c04f4bd6b9037c55f70a09c434dfd01492be1077cf7ea68c2e4a",
        "amount": "0.0000000",
        "price": "0.0001000",
        "price_r": {
          "n": 1,
          "d": 10000
        },
        "buying_asset_type": "native",
        "selling_asset_type": "credit_alphanum4",
        "selling_asset_code": "ETH",
        "selling_asset_issuer": "GBDEVU63Y6NTHJQQZIKVTC23NWLQVP3WJ2RI2OTSJTNYOIGICST6DUXR",
        "offer_id": 149983119
      },
      {
        "_links": {
          "self": {
            "href": "https://expansion-testnet.bantu.network/operations/120133379185221635"
          },
          "transaction": {
            "href": "https://expansion-testnet.bantu.network/transactions/6b983a4e0dc3c04f4bd6b9037c55f70a09c434dfd01492be1077cf7ea68c2e4a"
          },
          "effects": {
            "href": "https://expansion-testnet.bantu.network/operations/120133379185221635/effects"
          },
          "succeeds": {
            "href": "https://expansion-testnet.bantu.network/effects?order=desc\u0026cursor=120133379185221635"
          },
          "precedes": {
            "href": "https://expansion-testnet.bantu.network/effects?order=asc\u0026cursor=120133379185221635"
          }
        },
        "id": "120133379185221635",
        "paging_token": "120133379185221635",
        "transaction_successful": true,
        "source_account": "GDJX67SFY2N73H72TWMKKBQP5UPBNKBNUMNE2IGFKNES43S4327X6DHG",
        "type": "manage_offer",
        "type_i": 3,
        "created_at": "2020-01-28T21:14:59Z",
        "transaction_hash": "6b983a4e0dc3c04f4bd6b9037c55f70a09c434dfd01492be1077cf7ea68c2e4a",
        "amount": "98.5005752",
        "price": "0.0003494",
        "price_r": {
          "n": 17471,
          "d": 50000000
        },
        "buying_asset_type": "credit_alphanum4",
        "buying_asset_code": "ETH",
        "buying_asset_issuer": "GBDEVU63Y6NTHJQQZIKVTC23NWLQVP3WJ2RI2OTSJTNYOIGICST6DUXR",
        "selling_asset_type": "native",
        "offer_id": 0
      },
      {
        "_links": {
          "self": {
            "href": "https://expansion-testnet.bantu.network/operations/120133379185221636"
          },
          "transaction": {
            "href": "https://expansion-testnet.bantu.network/transactions/6b983a4e0dc3c04f4bd6b9037c55f70a09c434dfd01492be1077cf7ea68c2e4a"
          },
          "effects": {
            "href": "https://expansion-testnet.bantu.network/operations/120133379185221636/effects"
          },
          "succeeds": {
            "href": "https://expansion-testnet.bantu.network/effects?order=desc\u0026cursor=120133379185221636"
          },
          "precedes": {
            "href": "https://expansion-testnet.bantu.network/effects?order=asc\u0026cursor=120133379185221636"
          }
        },
        "id": "120133379185221636",
        "paging_token": "120133379185221636",
        "transaction_successful": true,
        "source_account": "GDJX67SFY2N73H72TWMKKBQP5UPBNKBNUMNE2IGFKNES43S4327X6DHG",
        "type": "manage_buy_offer",
        "type_i": 12,
        "created_at": "2020-01-28T21:14:59Z",
        "transaction_hash": "6b983a4e0dc3c04f4bd6b9037c55f70a09c434dfd01492be1077cf7ea68c2e4a",
        "amount": "291.8057980",
        "price": "0.0002565",
        "price_r": {
          "n": 250039,
          "d": 975000000
        },
        "buying_asset_type": "native",
        "selling_asset_type": "credit_alphanum4",
        "selling_asset_code": "ETH",
        "selling_asset_issuer": "GBDEVU63Y6NTHJQQZIKVTC23NWLQVP3WJ2RI2OTSJTNYOIGICST6DUXR",
        "offer_id": 0
      }
    ]
  }
}
```


# Effects

This endpoint returns the effects of a specific transaction.

&#x20;\- **ARGUMENT -**

* transaction\_hashrequired

  A hex-encoded SHA-256 hash of this transaction’s XDR-encoded form.
* cursoroptional

  A number that points to a specific location in a collection of responses and is pulled from the `paging_token` value of a record.
* orderoptional

  A designation of the order in which records should appear. Options include `asc`(ascending) or `desc` (descending). If this argument isn’t set, it defaults to `asc`.
* limitoptional

  The total number of records returned. The limit can range from 1 to 200 - an upper limit that is hardcoded in Horizon for performance reasons. If this argument isn’t designated, it defaults to 10.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://horizon.stellar.org");

server
  .effects()
  .forTransaction(
    "512a9946bc7ff4a363299f14f79e0beb9b9cdbd0103e3a69a44446a0aa6471a8",
  )
  .call()
  .then(function (resp) {
    console.log(resp);
  })
  .catch(function (err) {
    console.error(err);
  });
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl "https://horizon.stellar.org/transactions/512a9946bc7ff4a363299f14f79e0beb9b9cdbd0103e3a69a44446a0aa6471a8/effects"
```

{% endtab %}
{% endtabs %}

```bash
{
  "_links": {
    "self": {
      "href": "https://horizon.stellar.org/transactions/512a9946bc7ff4a363299f14f79e0beb9b9cdbd0103e3a69a44446a0aa6471a8/effects?cursor=\u0026limit=10\u0026order=asc"
    },
    "next": {
      "href": "https://horizon.stellar.org/transactions/512a9946bc7ff4a363299f14f79e0beb9b9cdbd0103e3a69a44446a0aa6471a8/effects?cursor=121628667754319873-2\u0026limit=10\u0026order=asc"
    },
    "prev": {
      "href": "https://horizon.stellar.org/transactions/512a9946bc7ff4a363299f14f79e0beb9b9cdbd0103e3a69a44446a0aa6471a8/effects?cursor=121628667754319873-1\u0026limit=10\u0026order=desc"
    }
  },
  "_embedded": {
    "records": [
      {
        "_links": {
          "operation": {
            "href": "https://horizon.stellar.org/operations/121628667754319873"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=121628667754319873-1"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=121628667754319873-1"
          }
        },
        "id": "0121628667754319873-0000000001",
        "paging_token": "121628667754319873-1",
        "account": "GAHK7EEG2WWHVKDNT4CEQFZGKF2LGDSW2IVM4S5DP42RBW3K6BTODB4A",
        "type": "account_credited",
        "type_i": 2,
        "created_at": "2020-02-20T21:18:33Z",
        "asset_type": "native",
        "amount": "1573.5112616"
      },
      {
        "_links": {
          "operation": {
            "href": "https://horizon.stellar.org/operations/121628667754319873"
          },
          "succeeds": {
            "href": "https://horizon.stellar.org/effects?order=desc\u0026cursor=121628667754319873-2"
          },
          "precedes": {
            "href": "https://horizon.stellar.org/effects?order=asc\u0026cursor=121628667754319873-2"
          }
        },
        "id": "0121628667754319873-0000000002",
        "paging_token": "121628667754319873-2",
        "account": "GA2XP4KMY4KWNPW4KUCUKYUF2J7Y6HO5HLPUEA3VPVSMYCM3TGNEZP5S",
        "type": "account_debited",
        "type_i": 3,
        "created_at": "2020-02-20T21:18:33Z",
        "asset_type": "native",
        "amount": "1573.5112616"
      }
    ]
  }
}
```


# List

This endpoint lists all successful transactions and can be used in [streaming](/api/introduction/streaming) mode. Streaming mode allows you to listen for new transactions as they are added to the Stellar ledger. If called in streaming mode, Horizon will start at the earliest known transaction unless a `cursor` is set, in which case it will start from that `cursor`. By setting the cursor value to `now`, you can stream transactions created since your request time.

&#x20;\- ARGUMENT -&#x20;

* transaction\_hash `required`

  A hex-encoded SHA-256 hash of this transaction’s XDR-encoded form.
* cursoroptional

  A number that points to a specific location in a collection of responses and is pulled from the `paging_token` value of a record.
* order `optional`

  A designation of the order in which records should appear. Options include `asc`(ascending) or `desc` (descending). If this argument isn’t set, it defaults to `asc`.
* limit `optional`

  The total number of records returned. The limit can range from 1 to 200 - an upper limit that is hardcoded in Horizon for performance reasons. If this argument isn’t designated, it defaults to 10.

{% tabs %}
{% tab title="JavaScript" %}

```javascript
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://horizon.stellar.org");

server
  .effects()
  .forTransaction(
    "512a9946bc7ff4a363299f14f79e0beb9b9cdbd0103e3a69a44446a0aa6471a8",
  )
  .call()
  .then(function (resp) {
    console.log(resp);
  })
  .catch(function (err) {
    console.error(err);
  });
```

{% endtab %}

{% tab title="cURL" %}

```bash
curl "https://expansion-testnet.bantu.network/transactions/512a9946bc7ff4a363299f14f79e0beb9b9cdbd0103e3a69a44446a0aa6471a8/effects"
```

{% endtab %}
{% endtabs %}

```bash
{
  "_links": {
    "self": {
      "href": "https://expansion-testnet.bantu.network/transactions/512a9946bc7ff4a363299f14f79e0beb9b9cdbd0103e3a69a44446a0aa6471a8/effects?cursor=\u0026limit=10\u0026order=asc"
    },
    "next": {
      "href": "https://expansion-testnet.bantu.network/transactions/512a9946bc7ff4a363299f14f79e0beb9b9cdbd0103e3a69a44446a0aa6471a8/effects?cursor=121628667754319873-2\u0026limit=10\u0026order=asc"
    },
    "prev": {
      "href": "https://expansion-testnet.bantu.network/transactions/512a9946bc7ff4a363299f14f79e0beb9b9cdbd0103e3a69a44446a0aa6471a8/effects?cursor=121628667754319873-1\u0026limit=10\u0026order=desc"
    }
  },
  "_embedded": {
    "records": [
      {
        "_links": {
          "operation": {
            "href": "https://expansion-testnet.bantu.network/operations/121628667754319873"
          },
          "succeeds": {
            "href": "https://expansion-testnet.bantu.network/effects?order=desc\u0026cursor=121628667754319873-1"
          },
          "precedes": {
            "href": "https://expansion-testnet.bantu.network/effects?order=asc\u0026cursor=121628667754319873-1"
          }
        },
        "id": "0121628667754319873-0000000001",
        "paging_token": "121628667754319873-1",
        "account": "GAHK7EEG2WWHVKDNT4CEQFZGKF2LGDSW2IVM4S5DP42RBW3K6BTODB4A",
        "type": "account_credited",
        "type_i": 2,
        "created_at": "2020-02-20T21:18:33Z",
        "asset_type": "native",
        "amount": "1573.5112616"
      },
      {
        "_links": {
          "operation": {
            "href": "https://expansion-testnet.bantu.network/operations/121628667754319873"
          },
          "succeeds": {
            "href": "https://expansion-testnet.bantu.network/effects?order=desc\u0026cursor=121628667754319873-2"
          },
          "precedes": {
            "href": "https://expansion-testnet.bantu.network/effects?order=asc\u0026cursor=121628667754319873-2"
          }
        },
        "id": "0121628667754319873-0000000002",
        "paging_token": "121628667754319873-2",
        "account": "GA2XP4KMY4KWNPW4KUCUKYUF2J7Y6HO5HLPUEA3VPVSMYCM3TGNEZP5S",
        "type": "account_debited",
        "type_i": 3,
        "created_at": "2020-02-20T21:18:33Z",
        "asset_type": "native",
        "amount": "1573.5112616"
      }
    ]
  }
}
```

```bash
var StellarSdk = require("stellar-sdk");
var server = new StellarSdk.Server("https://expansion-testnet.bantu.network");

var tradesHandler = function (resp) {
  console.log(resp);
};

var es = server
  .transactions()
  .cursor("now")
  .stream({ onmessage: tradesHandler });
```


# Operations


# Index

Operations are objects that represent a desired change to the ledger: payments, offers to exchange currency, changes made to account options, etc. Operations are submitted to the Stellar network grouped in a Transaction.

Each of Stellar’s operations have a unique operation object.

Endpoints

| GET | [/operations/:operation\_id](https://developers.stellar.org/api/resources/operations/single/)          |
| --- | ------------------------------------------------------------------------------------------------------ |
| GET | [/operations/:operation\_id/effects](https://developers.stellar.org/api/resources/operations/effects/) |
| GET | [/operations](https://developers.stellar.org/api/resources/operations/list/)                           |
| GET | [/payments](https://developers.stellar.org/api/resources/operations/list-payments/)                    |




---

[Next Page](/llms-full.txt/1)

