Create an Account
Before we get started with working with Stellar in code, consider going through the following examples using the Stellar Lab. The lab allows you to create accounts, fund accounts on the Stellar test network, build transactions, run any operation, and inspect responses from Horizon via the Endpoint Explorer.
Accounts are a fundamental building block of Stellar: 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 Stellar is in some way tied to an account, the first thing you generally need to do when you start developing is create one. This beginner-level tutorial walks through the three building blocks you'll need: generating keys, funding an account, and fetching balances.
Create a Keypair
Stellar uses public key cryptography to secure every transaction: each Stellar account has a keypair 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; we'll learn how in the full example, below.
Create an Account
A valid keypair alone does not make an account. To prevent unused entries from bloating the ledger, Stellar requires every account to hold a minimum balance of 1 XLM before it actually exists.
On the test network you can ask Friendbot — a friendly funding service — to create and fund the account for you. In the SDK examples, below, you'll see that we "discover" the Friendbot endpoint via the RPC's getNetwork
call, then issue a funding request.
On the public network, things are a little different. You'd acquire lumens from an exchange or have someone create or sponsor an account on your behalf. This is the purpose of the CreateAccount
operation. We'll see in the examples, later, that given a funded account, you can then create a child account on the network with a starting balance.
Fetch Balances
Once your accounts exist you can query their state. On Stellar, assets can be held in a handful of different forms:
- your native balance is how much XLM you're holding, which is associated directly to your account
- you can also establish trustlines to various assets to hold your balance; these can be acquired both by simple payment operations and smart contract interactions
- finally, you can hold custom smart contract tokens which are non-standard assets
You'll learn about these in later tutorials.
Full Example
Let's combine these four concepts into a single cohesive example. Keep in mind that you will always see your USDC balance as zero, since we did not actually receive any of it, but it will be useful in the future when you hold more than just the native token.
For each of these, be sure to follow the installation and setup instructions corresponding to each SDK from their documentation.
- JavaScript
- Example
- Go
- Java
import {
Keypair,
BASE_FEE,
Networks,
Operation,
Asset,
humanizeEvents,
TransactionBuilder,
} from "@stellar/stellar-sdk";
import { Server } from "@stellar/stellar-sdk/rpc";
// See https://developers.stellar.org/docs/data/apis/api-providers
const server = new Server("https://soroban-testnet.stellar.org");
const testnetUsdc = new Asset(
"USDC",
"GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5",
);
async function main() {
//
// Generating keypairs
//
const parent = Keypair.random();
console.log("Secret:", parent.secret());
console.log("Public:", parent.publicKey());
//
// Account creation via friendbot (does not work on mainnet)
//
const friendbotResponse = await server.requestAirdrop(parent.publicKey());
console.log("SUCCESS! You have a new account:\n", friendbotResponse);
const parentAccount = await server.getAccount(parent.publicKey());
const childAccount = Keypair.random();
//
// Account creation via the CreateAccount operation
//
const createAccountTx = new TransactionBuilder(parentAccount, {
fee: BASE_FEE,
networkPassphrase: Networks.TESTNET,
})
.addOperation(
Operation.createAccount({
destination: childAccount.publicKey(),
startingBalance: "5",
}),
)
.addOperation(Operation.changeTrust({ asset: testnetUsdc }))
.setTimeout(180)
.build();
createAccountTx.sign(parent);
const sendTxResponse = await server.sendTransaction(createAccountTx);
if (sendTxResponse.status !== "PENDING") {
console.log(`There was an error: ${JSON.stringify(sendTxResponse)}`);
throw sendTxResponse;
}
const txResponse = await server.pollTransaction(sendTxResponse.hash);
if (txResponse.status !== "SUCCESS") {
console.log(
`Transaction status: ${txResponse.status}, events: ${humanizeEvents(
txResponse.diagnosticEvents,
)}`,
);
}
console.log("Created the new account", childAccount.publicKey());
//
// Fetching native and USDC balances
//
const accountEntry = await server.getAccountEntry(parent.publicKey());
console.log("Balance for account: " + parent.publicKey());
console.log("XLM:", accountEntry.balance().toString());
const trustlineEntry = await server.getTrustline(
parent.publicKey(),
testnetUsdc,
);
console.log("USDC:", trustlineEntry.balance().toString());
}
main().catch((err) => console.error(err));
```py
import requests
from stellar_sdk import *
from stellar_sdk.soroban_rpc import GetTransactionStatus, SendTransactionStatus
from stellar_sdk.xdr import *
RPC_URL = "https://soroban-testnet.stellar.org"
TESTNET_USDC_ISSUER = "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"
SERVER = SorobanServer(RPC_URL)
def main():
#
# Keypair generation
#
parent = Keypair.random()
print(f"Secret: {parent.secret}")
print(f"Public: {parent.public_key}")
#
# Account creation via friendbot
#
friendbot_url = get_friendbot_url()
print(f"Using {friendbot_url} for friendbot funding")
body = request_funds(friendbot_url, parent.public_key)
print(f"SUCCESS! You have a new account:\n{body}")
#
# Account creation via CreateAccount operation
#
child = Keypair.random()
parent_account = SERVER.load_account(parent.public_key)
usdc_asset = Asset("USDC", TESTNET_USDC_ISSUER)
tx = (
TransactionBuilder(
source_account=parent_account,
network_passphrase=Network.TESTNET_NETWORK_PASSPHRASE,
base_fee=TransactionBuilder.BASE_FEE,
)
.add_time_bounds(0, 0)
.append_operation(
CreateAccount(destination=child.public_key, starting_balance="5")
)
.build()
)
tx.sign(parent)
ledger_seq = submit_and_confirm(tx)
print(f"Transaction confirmed in ledger {ledger_seq}")
print(f"Created the new account {child.public_key}")
#
# Fetching native and USDC balances
#
native_balance = load_native_balance(parent.public_key)
print(f"Balance for account {parent.public_key}")
print(f"XLM: {native_balance}")
trustline_balance = get_trustline(parent.public_key, usdc_asset)
print(f"USDC trustline balance (raw): {trustline_balance}")
def request_funds(friendbot_url: str, account_id: str) -> str:
request_url = f"{friendbot_url}?addr={account_id}"
resp = requests.get(request_url, timeout=30)
check(resp.ok, f"friendbot returned {resp.status_code}: {resp.text}")
return resp.text
def get_friendbot_url() -> str:
response = SERVER.get_network()
if response.friendbot_url:
return response.friendbot_url.strip()
raise RuntimeError("Friendbot URL not provided by network")
def load_native_balance(account_id: str) -> int:
key = LedgerKey(
LedgerEntryType.ACCOUNT,
account=LedgerKeyAccount(
account_id=Keypair.from_public_key(account_id).xdr_account_id()
),
)
response = SERVER.get_ledger_entries([key])
check(response.entries, f"account {account_id} not found")
ledger_entry = LedgerEntryData.from_xdr(response.entries[0].xdr)
account = ledger_entry.account
check(account is not None, "ledger entry missing account data")
return account.balance.int64
def get_trustline(account_id: str, asset: Asset) -> int:
key = LedgerKey(
LedgerEntryType.TRUSTLINE,
trust_line=LedgerKeyTrustLine(
account_id=Keypair.from_public_key(account_id).xdr_account_id(),
asset=asset.to_trust_line_xdr_object(),
),
)
response = SERVER.get_ledger_entries([key])
if not response.entries:
return 0
ledger_entry = LedgerEntryData.from_xdr(response.entries[0].xdr)
trust_line = ledger_entry.trust_line
if trust_line is None:
return 0
return trust_line.balance.int64
def submit_and_confirm(transaction):
response = SERVER.send_transaction(transaction)
check(
response.status == SendTransactionStatus.PENDING,
f"Transaction submission failed: {response.status}",
)
tx_resp = SERVER.poll_transaction(response.hash)
if tx_resp.status == GetTransactionStatus.SUCCESS:
return tx_resp.ledger or tx_resp.latest_ledger
raise RuntimeError(f"Transaction failed: {tx_resp.diagnostic_events_xdr}")
def check(condition: bool, message: str) -> None:
if not condition:
raise RuntimeError(message)
if __name__ == "__main__":
main()
package main
import (
"context"
"fmt"
"io"
"log"
"net/http"
"net/url"
"strings"
"time"
"github.com/stellar/go/amount"
"github.com/stellar/go/keypair"
"github.com/stellar/go/network"
"github.com/stellar/go/txnbuild"
"github.com/stellar/go/xdr"
"github.com/stellar/stellar-rpc/client"
"github.com/stellar/stellar-rpc/protocol"
)
const (
rpcURL = "https://soroban-testnet.stellar.org"
testnetUSDCIssuer = "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5"
pollAttempts = 10
pollInitialDelay = time.Second
)
func main() {
//
// Generating keypairs
//
parent := keypair.MustRandom()
log.Printf("Secret: %s", parent.Seed())
log.Printf("Public: %s", parent.Address())
//
// Funding via friendbot
//
ctx := context.Background()
cli := client.NewClient(rpcURL, nil)
defer cli.Close()
networkInfo, err := cli.GetNetwork(ctx)
check(err)
friendbotURL := strings.TrimSpace(networkInfo.FriendbotURL)
if friendbotURL == "" {
log.Fatal("friendbot URL not provided by network")
}
log.Printf("Using %s for friendbot funding", friendbotURL)
body := requestAirdrop(friendbotURL, parent.Address())
log.Printf("SUCCESS! You have a new account:\n%s", body)
//
// Funding a new account via CreateAccount operation
//
child := keypair.MustRandom()
parentAccount, err := cli.LoadAccount(ctx, parent.Address())
check(err)
tx, err := txnbuild.NewTransaction(txnbuild.TransactionParams{
SourceAccount: parentAccount,
IncrementSequenceNum: true,
BaseFee: txnbuild.MinBaseFee,
Preconditions: txnbuild.Preconditions{
TimeBounds: txnbuild.NewInfiniteTimeout(),
},
Operations: []txnbuild.Operation{
&txnbuild.CreateAccount{
Destination: child.Address(),
Amount: "5",
},
},
})
check(err)
tx, err = tx.Sign(network.TestNetworkPassphrase, parent)
check(err)
resp := submitAndAwait(ctx, cli, tx)
log.Printf("Transaction confirmed in ledger %d", resp.Ledger)
log.Printf("Created the new account %s", child.Address())
//
// Fetch native and USDC balances for account
//
accountEntry := getAccountEntry(ctx, cli, parent.Address())
log.Printf("Balance for account %s", parent.Address())
log.Printf("XLM: %s", amount.String(accountEntry.Balance))
usdcAsset := txnbuild.CreditAsset{Code: "USDC", Issuer: testnetUSDCIssuer}
trustlineEntry := getTrustlineEntry(ctx, cli, parent.Address(), usdcAsset)
log.Printf("USDC trustline balance (raw): %d", trustlineEntry.Balance)
}
func requestAirdrop(friendbotURL, address string) string {
requestURL := fmt.Sprintf("%s?addr=%s", friendbotURL, url.QueryEscape(address))
resp, err := http.Get(requestURL)
check(err)
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
check(err)
if resp.StatusCode >= 400 {
log.Fatalf("friendbot returned %s: %s", resp.Status, string(body))
}
return string(body)
}
func submitAndAwait(ctx context.Context, cli *client.Client, tx *txnbuild.Transaction) protocol.GetTransactionResponse {
txnB64, err := tx.Base64()
check(err)
sendResp, err := cli.SendTransaction(ctx, protocol.SendTransactionRequest{Transaction: txnB64})
check(err)
if sendResp.Status != "PENDING" {
log.Fatalf("transaction submission failed with status %s", sendResp.Status)
}
return pollTransaction(ctx, cli, sendResp.Hash)
}
func pollTransaction(ctx context.Context, cli *client.Client, hash string) protocol.GetTransactionResponse {
delay := pollInitialDelay
for range pollAttempts {
resp, err := cli.GetTransaction(ctx, protocol.GetTransactionRequest{Hash: hash})
check(err)
switch resp.Status {
case protocol.TransactionStatusSuccess:
return resp
case protocol.TransactionStatusFailed:
log.Fatalf("transaction failed: %s", strings.Join(resp.DiagnosticEventsXDR, "\n"))
case protocol.TransactionStatusNotFound:
// keep polling
default:
// unexpected status, continue polling in case it's transient
}
select {
case <-ctx.Done():
log.Fatalf("context cancelled while polling: %v", ctx.Err())
case <-time.After(delay):
}
delay += pollInitialDelay
}
log.Fatalf("transaction %s not found after polling", hash)
return protocol.GetTransactionResponse{}
}
func getAccountEntry(ctx context.Context, cli *client.Client, address string) *xdr.AccountEntry {
ledgerKey := xdr.LedgerKey{
Type: xdr.LedgerEntryTypeAccount,
Account: &xdr.LedgerKeyAccount{
AccountId: xdr.MustAddress(address),
},
}
keyB64, err := xdr.MarshalBase64(ledgerKey)
check(err)
resp, err := cli.GetLedgerEntries(ctx, protocol.GetLedgerEntriesRequest{Keys: []string{keyB64}})
check(err)
if len(resp.Entries) == 0 {
log.Fatalf("account %s not found", address)
}
var ledgerData xdr.LedgerEntryData
check(xdr.SafeUnmarshalBase64(resp.Entries[0].DataXDR, &ledgerData))
if ledgerData.Account == nil {
log.Fatalf("ledger entry for %s missing account data", address)
}
return ledgerData.Account
}
func getTrustlineEntry(ctx context.Context, cli *client.Client, account string, asset txnbuild.CreditAsset) xdr.TrustLineEntry {
trustlineAsset, err := asset.MustToTrustLineAsset().ToXDR()
check(err)
ledgerKey := xdr.LedgerKey{
Type: xdr.LedgerEntryTypeTrustline,
TrustLine: &xdr.LedgerKeyTrustLine{
AccountId: xdr.MustAddress(account),
Asset: trustlineAsset,
},
}
keyB64, err := xdr.MarshalBase64(ledgerKey)
check(err)
resp, err := cli.GetLedgerEntries(ctx, protocol.GetLedgerEntriesRequest{Keys: []string{keyB64}})
check(err)
if len(resp.Entries) == 0 {
check(fmt.Errorf("trustline for %s:%s not found", asset.Code, asset.Issuer))
}
var ledgerData xdr.LedgerEntryData
check(xdr.SafeUnmarshalBase64(resp.Entries[0].DataXDR, &ledgerData))
return ledgerData.MustTrustLine()
}
func check(err error) {
if err != nil {
log.Fatal(err)
}
}
// File: main.java
// Dependencies (Maven coordinates):
// org.stellar:java-stellar-sdk:0.43.0-SNAPSHOT
// com.fasterxml.jackson.core:jackson-databind:2.17.2
import java.io.IOException;
import java.net.URI;
import java.net.http.*;
import java.time.Duration;
import java.util.Collections;
import org.stellar.sdk.*;
import org.stellar.sdk.requests.sorobanrpc.*;
import org.stellar.sdk.xdr.*;
public final class main {
private static final String RPC_URL = "https://soroban-testnet.stellar.org";
private static final String TESTNET_USDC_ISSUER = "GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5";
private static final HttpClient HTTP = HttpClient.newBuilder()
.connectTimeout(Duration.ofSeconds(20))
.build();
private static final SorobanServer SERVER = new SorobanServer(RPC_URL);
public static void main(String[] args) throws Exception {
//
// Generate keypairs
//
KeyPair parent = KeyPair.random();
log("Secret: %s", new String(parent.getSecretSeed()));
log("Public: %s", parent.getAccountId());
//
// Fund a new account via friendbot
//
GetNetworkResponse networkInfo = SERVER.getNetwork();
String friendbotUrl = networkInfo != null ? networkInfo.getFriendbotUrl() : null;
check(friendbotUrl != null && !friendbotUrl.isBlank(), "Friendbot URL not provided by network");
log("Using %s for friendbot funding", friendbotUrl);
String friendbotResponse = requestFunds(friendbotUrl, parent.getAccountId());
log("SUCCESS! You have a new account:\n%s", friendbotResponse);
//
// Fund a new account via CreateAccount operation
//
KeyPair child = KeyPair.random();
Account parentAccount = SERVER.loadAccount(parent.getAccountId());
Transaction transaction = new TransactionBuilder(parentAccount, Network.TESTNET)
.setBaseFee(Transaction.MIN_BASE_FEE)
.setTimeout(TransactionBuilder.TIMEOUT_INFINITE)
.addOperation(new CreateAccountOperation.Builder(child.getAccountId(), "5").build())
.build();
transaction.sign(parent);
long ledgerSeq = submitAndConfirm(transaction);
log("Transaction confirmed in ledger %d", ledgerSeq);
log("Created the new account %s", child.getAccountId());
//
// Fetch native and USDC balances for account
//
long nativeBalance = loadNativeBalance(parent.getAccountId());
log("Balance for account %s", parent.getAccountId());
log("XLM: %d", nativeBalance);
AssetTypeCreditAlphaNum12 usdcAsset = Asset.createNonNative("USDC", TESTNET_USDC_ISSUER);
long trustlineBalance = getTrustlineBalance(parent.getAccountId(), usdcAsset);
log("USDC trustline balance (raw): %d", trustlineBalance);
}
private static String requestFunds(String friendbotUrl, String accountId)
throws IOException, InterruptedException {
String requestUrl = friendbotUrl + "?addr=" + accountId;
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create(requestUrl))
.timeout(Duration.ofSeconds(30))
.GET()
.build();
HttpResponse<String> response = HTTP.send(request, HttpResponse.BodyHandlers.ofString());
check(response.statusCode() / 100 == 2, "Friendbot returned " + response.statusCode() + ": " + response.body());
return response.body();
}
private static long loadNativeBalance(String accountId) throws IOException {
LedgerKey key = new LedgerKey();
key.setDiscriminant(LedgerEntryType.ACCOUNT);
LedgerKeyAccount accountKey = new LedgerKeyAccount();
accountKey.setAccountID(KeyPair.fromAccountId(accountId).getXdrAccountId());
key.setAccount(accountKey);
GetLedgerEntriesResponse response = SERVER.getLedgerEntries(Collections.singletonList(key));
check(!response.getEntries().isEmpty(), "Account " + accountId + " not found");
LedgerEntryData data = LedgerEntryData.fromXdr(response.getEntries().get(0).getXdr());
check(data.getAccount() != null, "Ledger entry missing account data");
return data.getAccount().getBalance().getInt64();
}
private static long getTrustlineBalance(String accountId, org.stellar.sdk.Asset asset) throws IOException {
LedgerKey key = new LedgerKey();
key.setDiscriminant(LedgerEntryType.TRUSTLINE);
LedgerKeyTrustLine trustLineKey = new LedgerKeyTrustLine();
trustLineKey.setAccountID(KeyPair.fromAccountId(accountId).getXdrAccountId());
trustLineKey.setAsset(asset.toXdr());
key.setTrustLine(trustLineKey);
GetLedgerEntriesResponse response = SERVER.getLedgerEntries(Collections.singletonList(key));
if (response.getEntries().isEmpty()) {
return 0L;
}
LedgerEntryData data = LedgerEntryData.fromXdr(response.getEntries().get(0).getXdr());
TrustLineEntry trustLine = data.getTrustLine();
if (trustLine == null) {
return 0L;
}
return trustLine.getBalance().getInt64();
}
private static long submitAndConfirm(Transaction transaction) throws IOException, InterruptedException {
SendTransactionResponse sendResp = SERVER.sendTransaction(transaction);
check(sendResp.getStatus() == SendTransactionStatus.PENDING,
"Transaction submission failed: " + sendResp.getStatus());
GetTransactionResponse txResp = SERVER.pollTransaction(sendResp.getHash());
check(txResp.getStatus() == GetTransactionResponse.Status.SUCCESS,
"Transaction failed: " + txResp.getStatus());
Long ledger = txResp.getLedger() != null ? txResp.getLedger() : txResp.getLatestLedger();
check(ledger != null, "Transaction completed but ledger sequence unavailable");
return ledger;
}
private static void check(boolean condition, String message) {
if (!condition) {
throw new IllegalStateException(message);
}
}
private static void log(String format, Object... args) {
System.out.println(String.format(format, args));
}
}
Now that you’ve got an account and check its asset balances, you can start sending and receiving payments, or, if you're ready to hunker down, you can skip ahead and build a wallet or issue a Stellar asset.
Guides in this category:
📄️ Create an account
Learn about creating Stellar accounts, keypairs, funding, and account basics.
📄️ Send and receive payments
Learn to send payments and watch for received payments on the Stellar network.
📄️ Channel accounts
Create channel accounts to submit transactions to the network at a high rate.
📄️ Claimable balances
Split a payment into two parts by creating a claimable balance.
📄️ Clawbacks
Use clawbacks to burn a specific amount of a clawback-enabled asset from a trustline or claimable balance.
📄️ Fee-bump transactions
Use fee-bump transactions to pay for transaction fees on behalf of another account without re-signing the transaction.
📄️ Sponsored reserves
Use sponsored reserves to pay for base reserves on behalf of another account.
📄️ Path payments
Send a payment where the asset received differs from the asset sent.
📄️ Pooled accounts: muxed accounts and memos
Use muxed accounts to differentiate between individual accounts in a pooled account.
📄️ Install and deploy a smart contract with code
Install and deploy a smart contract with code.
📄️ Install WebAssembly (Wasm) bytecode using code
Install the Wasm of the contract using js-stellar-sdk.
📄️ Invoke a contract function in a transaction using SDKs
Use the Stellar SDK to create, simulate, and assemble a transaction.
📄️ simulateTransaction RPC method guide
simulateTransaction examples and tutorials guide.
📄️ Submit a transaction to Stellar RPC using the JavaScript SDK
Use a looping mechanism to submit a transaction to the RPC.