Overview of Stellar Anchors and integration examples.
- 1. Overview
- 2. Deposits and Withdrawals
- 3. SEPs mentioned in this document
- 4. Integrating
- 5. Wallet CLI
The Stellar blockchain allows any wallet to hold different assets, as well as send/receive payments to/from other Stellar wallets in the blockchain.
Each asset is issued ("minted") by a Stellar account, called "issuer".
Any Stellar account can issue a token. Issuing a token is simply making a Stellar
payment of a new asset. For example:
- There's an arbitrary Stellar account with pubkey (
GAB...SAY). Let's call itACC1 - There's another arbitrary Stellar account with pubkey (
GDW...FAB). Let's call itACC2 ACC1wants to become an issuer of an asset namedASSET123ACC2creates a trustline toasset_code=ASSET123 asset_issuer=GAB...SAYACC1sends a payment ofasset_code=ASSET123 asset_issuer=GAB...SAYtoGDW...FABwithamount=100- Now, 100 units of
ASSET123were created and added toGDW...FAB - This now makes
ACC1the issuer ofASSET123, andACC2a holder ofASSET123 - Other Stellar accounts can also create the trustline and hold
ASSET123
In order to transfer off-chain funds in and out of the Stellar blockchain, we have deposits and withdrawals.
The entities (usually companies) that handle off-chain ← → on-chain transfers
are called Anchors.
Deposit is when a customer is adding off-chain funds (e.g. US dollars, Euros, etc) into a Stellar wallet. The basic flow for deposit is:
- Customer initiates the deposit from a Stellar Wallet (Lobstr, Stellarterm, Solar, Albedo, Freighter, etc)
- Stellar wallet sends a deposit request to an Anchor
- The Anchor collects the information it needs in order to process the deposit, this might include KYC/Bank information
- Anchor displays the deposit instructions to the user
- User makes the off-chain deposit
- Anchor detects the off-chain deposit
- Anchor sends the Stellar payment to the user, completing the deposit flow
Withdrawal is when a customer has funds in a Stellar wallet, and proceeds to move those funds to an off-chain destination (e.g. bank account, bitcoin address, cash pickup, etc). The basic flow for withdrawal is:
- Customer initiates the withdrawal from a Stellar Wallet (Lobstr, Stellarterm, Solar, Albedo, Freighter, etc)
- Stellar wallet sends a withdrawal request to an Anchor
- The Anchor collects the information it needs in order to process the withdrawal, this might include KYC/Bank information, plus the off-chain destination info (e.g. destination bank account, destination bitcoin address, etc)
- Wallet sends a Stellar payment to the Anchor
- Anchor detects the Stellar payment
- Anchor sends the off-chain funds to the off-chain destination
Stellar has protocols which the Wallet-Anchor interactions should occur for
deposits and withdrawals. Those protocols are called SEPs (Stellar Ecosystem Proposals).
Each SEP can be found in the Stellar git repository.
The main focus of this document is to guide Wallets on how to manage deposits
and withdrawals using a Stellar Anchor.
The relevant SEPs for this document are listed below.
Here are a few links to help you get started:
- Stellar Introduction
- Stellar Tools
- Stellar Awesome Links
- Javascript Tutorial
- SEP-24 Instructions for Wallets
- SEP-6 Instructions for Wallets
- Anchor Validator
Overview of the steps to do a deposit or withdrawal using an Anchor:
- Fetch stellar.toml
- The Wallet fetches the Anchor's
stellar.tomlfile, which contains information about currencies and URLs required by the SEPs
- The Wallet fetches the Anchor's
- Create trustlines to the Anchor
- In order to hold assets issued, the Wallet must create a trustline to the asset on each of its Stellar accounts. Otherwise the accounts won't be able to receive the deposits.
- Create a deposit or withdrawal transaction using SEP-6 or SEP-24
- The main difference between SEP-6 and SEP-24 is that SEP-6 requires the Wallet to collect all required fields from the user and submit them non-interactively to the Anchor. In SEP-24, the Anchor interactively collects all the information it needs from the user.
- It's up to the Wallet to decide to use SEP-6 or SEP-24 to manage transactions.
Stellar has two networks: TESTNET and PUBLIC.
Most Anchors only support deposits and withdrawals in the PUBLIC network, as
it's where you can find assets like USD, BTC, etc, which can be converted
to real-world USD and BTC.
On TESTNET there are a few Anchors, but they're mostly testing/development versions,
as TESTNET is Stellar's test network, where there's no real money involved and
you can get funded test accounts for free.
stellar.toml provides these variables, which are required for SEP-10, SEP-6, SEP-24 and SEP-31 respectively:
WEB_AUTH_ENDPOINTTRANSFER_SERVERTRANSFER_SERVER_SEP0024DIRECT_PAYMENT_SERVER
Python example on how to fetch the testnet stellar.toml:
import requests
import toml
stellar_toml = toml.loads(requests.get('https://<home_domain>/.well-known/stellar.toml').text)SEP-10 provides a mechanism to
prove ownership of a Stellar account and obtain a reusable JWT token which carries the ownership information.
The token is required in many transaction endpoints (SEP-24, SEP-6, etc)
and allow the Wallet to deposit and withdrawal funds on the account through a Stellar Anchor.
The token is usually valid for 1 day after it's generated, and can be used in all HTTP requests while it's valid.
Python example (based on Django Polaris code) on how to get a new JWT token:
from stellar_sdk.keypair import Keypair
from stellar_sdk.transaction_envelope import TransactionEnvelope
from stellar_sdk.network import Network
secret_key = 'SBYWIVPVH5PQPB...'
auth_url = stellar_toml['WEB_AUTH_ENDPOINT']
# get challenge transaction and sign it
client_signing_key = Keypair.from_secret(secret_key)
response = requests.get(f'{auth_url}?account={client_signing_key.public_key}')
content = json.loads(response.content)
envelope_xdr = content['transaction']
envelope_object = TransactionEnvelope.from_xdr(
envelope_xdr, network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE
)
envelope_object.sign(client_signing_key)
client_signed_envelope_xdr = envelope_object.to_xdr()
# submit the signed transaction to prove ownership of the account
response = requests.post(
auth_url,
json={"transaction": client_signed_envelope_xdr},
)
content = json.loads(response.content)
sep10_token = content['token']To deposit assets into a Stellar account, the account must first trust the asset.
A trustline operation is required only once and the trust will last forever on the account unless removed.
Python example on how to trust an Anchor asset:
from stellar_sdk.server import Server
from stellar_sdk.transaction_builder import TransactionBuilder
secret_key = 'SBYWIVPV...'
horizon_testnet = 'https://horizon-testnet.stellar.org/'
asset_info = stellar_toml['CURRENCIES'][0]
server = Server(horizon_url=horizon_testnet)
keypair = Keypair.from_secret(secret_key)
account = server.load_account(keypair.public_key)
builder = TransactionBuilder(source_account=account,
network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE)
builder.append_change_trust_op(asset_code=asset_info['code'],
asset_issuer=asset_info['issuer'])
envelope = builder.build()
envelope.sign(keypair)
response = server.submit_transaction(envelope)
assert response['successful']Deposits are a way for users to deposit real world currencies (ex: USD, EUR) into their Stellar account (usually managed by a Wallet).
For example, a user can have EUR on a bank account outside Stellar, and deposit that as CLPX into a Stellar account.
To deposit assets, the Wallet must create deposit transactions on our Anchor.
To create transactions, there are two options:
- SEP-6
- Non-interactive - Wallet must provide all required information through HTTP requests
- Does not require opening any external web page
- See example below
- SEP-24
- Interactive - all information needed from the user is collected by the Anchor using web pages
- Requires opening a popup window or iframe pointing to an URL provided by the Anchor
- See example below
def sep6_deposit():
data = {
'asset_code': 'PURPLE',
'account': 'GC75JLZ6...',
# these fields are specific for this Anchor,
# they're specified on the SEP-6 /info endpoint
'type': 'sepa', # SEPA transfer deposit
'first_name': 'John',
'last_name': 'Doe',
'email_address': 'johndoe@example.com',
'amount': '11.0',
}
headers = {
'Authorization': 'Bearer ' + sep10_token
}
url = stellar_toml['TRANSFER_SERVER'] + '/deposit'
response = requests.post(url, data=data, headers=headers).json()
return render_sep6_instructions(response) # display instructions to userdef sep24_deposit():
data = {
'asset_code': 'PURPLE',
'account': 'GC75JLZ6...',
}
headers = {
'Authorization': 'Bearer ' + sep10_token
}
url = stellar_toml['TRANSFER_SERVER_SEP0024'] + '/transactions/deposit/interactive'
response = requests.post(url, data=data, headers=headers).json()
return render_sep24_interactive(response['url']) # popup window on the clientWithdrawals are a way for users to obtain assets from their Stellar account as real world currencies (ex: USD, EUR).
For example, a user can have token balance in a Stellar account and withdraw that as fiat, receiving the fiat on a bank account outside Stellar.
To withdraw assets, the Wallet must create withdrawal transactions on the Anchor.
To create transactions, there are two options:
- SEP-6
- Non-interactive - Wallet must provide all required information through API requests
- Does not require opening any external web page
- See example below
- SEP-24
- Interactive - all information needed from the user is collected by the Anchor using web pages
- Requires opening a popup window or iframe pointing to an URL provided by the Anchor
- See example below
def sep6_withdrawal():
data = {
'asset_code': 'PURPLE',
# these fields,
# they're specified on the SEP-6 /info endpoint
'amount': '12.5',
'type': 'cash',
'dest_country': 'uk',
'benef_first_name': 'John',
'benef_last_name': 'Doe',
'benef_email': 'johndoe@example.com',
}
headers = {
'Authorization': 'Bearer ' + sep10_token
}
url = stellar_toml['TRANSFER_SERVER'] + '/withdraw'
response = requests.post(url, data=data, headers=headers).json()
return render_sep6_instructions(response) # display instructions to userdef sep24_withdrawal():
data = {
'asset_code': 'PURPLE',
}
headers = {
'Authorization': 'Bearer ' + sep10_token
}
url = stellar_toml['TRANSFER_SERVER_SEP0024'] + '/transactions/withdraw/interactive'
response = requests.post(url, data=data, headers=headers).json()
return render_sep24_interactive(response['url']) # popup window on the clientA direct transfer is a Stellar payment made between two Anchor accounts.
For example, let's say Anchor A is a bank and Anchor B is another bank.
A person who has a bank account in Anchor A wants to send a money transfer
to another person who has a bank account in Anchor B.
Normally, this can be accomplished via standard bank transfers like SEPA, Wire, etc.
With SEP-31,
Anchor A can send a Stellar payment to Anchor B instead of a bank transfer,
this allows the anchors to take advantage of the speed of Stellar payments and
avoid the burocracy of standard bank transfers (which can take days to take place).
- SEP-31
- Used for anchor-anchor direct transfers
- Non-interactive - Anchor must provide all required information through API requests
- Does not require opening any external web page
- See example below
def sep31_create_transaction():
payload = {
"amount": 15.0,
"asset_code": "PURPLE",
"fields": {
"transaction": {
"remitter_email": "test@test.test",
"remitter_first_name": "John",
"remitter_last_name": "Doe",
"remitter_phone_number": "+4906921999510",
"beneficiary_email": "test2@test.test",
"beneficiary_first_name": "Ana",
"beneficiary_last_name": "Doe",
"beneficiary_bank_iban": "GB02 REVO 0099 7040 2170 16",
"beneficiary_bank_bic": "CHASUS33",
"beneficiary_phone_number": "+4906921999510"
}
}
}
headers = {
'Authorization': 'Bearer ' + sep10_token
}
url = stellar_toml['TRANSFER_SERVER_SEP0031'] + '/transactions'
response = requests.post(url, json=payload, headers=headers).json()
return responseThis repository contains a CLI Stellar wallet implementation for demonstration purposes.
- Python3.6+
- pip3
cd wallet-cli
python3 -m pip install virtualenv
python3 -m virtualenv .venv
pip install -r requirements.txt
Activate virtualenv (required only once for a terminal session):
source .venv/bin/activate
Create the database:
python cli.py database create
Get started by looking at the options:
python cli.py --help
Examples:
# SEP-1
python cli.py sep1 fetch_stellar_toml
# Trust
python cli.py trust change_trust
# SEP-10
python cli.py sep10 auth
# See SEP-24 options
python cli.py sep24 --help