The wallet-backend serves as a backend service for Stellar wallet applications, providing transaction submission, account management, and payment tracking capabilities.
- Wallet-Backend
The wallet-backend service provides several key functionalities:
- Account Management: Registration, deregistration, and sponsored account creation
- Transaction Submission: Reliable transaction building, submission, and status tracking
- Payment History: Tracking and querying of payment records
- Channel Account Management: Creation and management of channel accounts for transaction signing
- Security: Request authentication and signature verification
The wallet-backend provides two core services that simplify Stellar transaction management for wallet applications:
-
Transaction Building (
POST /transactions/build
):- Uses pre-funded channel accounts to handle sequence numbers automatically
- Provides high throughput by eliminating client-side sequence number management
- Returns signed transaction XDRs ready for client signature
-
Fee Bump Transactions (
POST /tx/create-fee-bump
):- Automatically creates and signs fee bump transactions
- Uses a distribution account to cover transaction fees
- Returns a complete fee bump transaction ready for submission
The following diagram illustrates the complete transaction lifecycle:
sequenceDiagram
participant Client
participant WB as Wallet Backend Server
participant RPC as Stellar RPC
Note over Client,RPC: Complete Transaction Flow
%% Step 1: Build Transaction
Client->>WB: POST /transactions/build
Note right of Client: JWT authenticated request<br/>with operations array
WB->>RPC: GetAccountLedgerSequence(channelAccount)
RPC-->>WB: Current sequence number
WB->>WB: Get idle channel account<br/>Build transaction<br/>Sign with channel account
WB-->>Client: { transactionXdrs: ["..."] }
%% Step 2: Client Signs Transaction
Note over Client: Parse transaction XDR<br/>Sign with user keypairs<br/>Encode back to XDR
%% Step 3: Fee Bump Transaction
Client->>WB: POST /tx/create-fee-bump
Note right of Client: { transaction: "signedTxXDR" }
WB->>WB: Parse and validate transaction<br/>Create fee bump transaction<br/>Sign with distribution account
WB-->>Client: { transaction: "feeBumpTxXDR",<br/>networkPassphrase: "..." }
%% Step 4: Submit to RPC
Client->>RPC: sendTransaction(feeBumpTxXDR)
RPC-->>Client: { status: "PENDING", hash: "..." }
%% Step 5: Poll for Result
loop Poll for confirmation
Client->>RPC: getTransaction(hash)
RPC-->>Client: { status: "NOT_FOUND" }
Note over Client: Wait ~6 seconds for the transaction to be confirmed
end
Client->>RPC: getTransaction(hash)
RPC-->>Client: { status: "SUCCESS", ... }
π§ This is a work in progress.
Follow these steps to start the wallet-backend server for local development and contribution.
- Go 1.23.2 or later
- Docker and Docker Compose
-
Clone the repository:
git clone https://github.com/stellar/wallet-backend.git cd wallet-backend
-
Copy the example
.env.example
file and fill in the required environment variables:cp .env.example .env
-
Set environment variables (refer to
.env.example
for details):DATABASE_URL=postgres://postgres@localhost:5432/wallet-backend?sslmode=disable NETWORK=testnet STELLAR_ENVIRONMENT=development # The CHANNEL_ACCOUNT_ENCRYPTION_PASSPHRASE is used to encrypt/decrypt the channel accounts private keys. A strong passphrase is recommended. CHANNEL_ACCOUNT_ENCRYPTION_PASSPHRASE=<your_passphrase> # The DISTRIBUTION_ACCOUNT is used to sponsor fees and reserves for the client transactions. It must be an existing account with XLM balance. DISTRIBUTION_ACCOUNT_PRIVATE_KEY=<your_private_key> DISTRIBUTION_ACCOUNT_PUBLIC_KEY=<your_public_key> # CLIENT_AUTH_PUBLIC_KEYS is a comma-separated list of Stellar public keys whose private key(s) are authorized to sign the authentication header. They must be Stellar addresses. CLIENT_AUTH_PUBLIC_KEYS=<your_public_keys>
-
Start the server and its dependencies using one of the following methods:
This is the simplest way to start the wallet-backend server. All services will run in Docker containers as defined in the docker-compose.yaml
file.
-
Start the containers:
docker compose up
This will start the
api
,db
,ingest
, andstellar-rpc
services.
This setup is ideal for active development, allowing you to add debug points to the code.
-
Start the
db
andstellar-rpc
containers:docker compose up -d db stellar-rpc
-
Run
api
andingest
locally:-
API
-
Source the
.env
file:source .env
-
Run migrations:
go run main.go migrate up
-
Generate channel accounts:
go run main.go channel-account ensure 5
-
Start the API server:
go run main.go serve
-
-
Ingest
-
In a separate terminal, source the
.env
file and run the ingestion service:source .env go run main.go ingest
-
-
This allows us to establish a dev cycle where you can make changes to the code and restart the api
and ingest
services to test them. Based on the IDE you are using, you can add the build configurations for these services, along
with the environment variables to add breakpoints to your code.
The wallet-backend includes both unit and integration tests to ensure comprehensive coverage.
Unit tests can be run using the following command:
go test ./...
Dependencies:
- A
db
needs to be available and configured through theDATABASE_URL
environment variable.
Integration tests can be executed with the following command:
go run main.go integration-tests
Dependencies:
- Ensure your environment is configured as described in the setup section.
- Both
db
andstellar-rpc
services should be running, either in Docker containers or locally.
This setup allows you to verify both the isolated functionality of components (unit tests) and their interactions (integration tests) within the wallet-backend.
The wallet-backend uses JSON Web Tokens (JWT) with Ed25519 signatures for request authentication.
The JWT is signed using an Ed25519 private key derived from a Stellar secret seed. You can generate Stellar keypairs at the Stellar Laboratory.
The server can be configured to accept multiple (comma-separated) public keys through the CLIENT_AUTH_PUBLIC_KEYS
environment variable.
The JWT payload field should contain the following fields:
- (default)
exp
β The expiration time on and after which the JWT must not be accepted for processing, in seconds since Epoch. (Must be less thaniat
+15sec.) - (default)
iat
- The time at which the JWT was issued, in seconds since Epoch. - (default)
aud
β The audience for the JWT. This is the server's hostname. - (default)
sub
β The subject of the JWT, which is the public key of the Stellar account that is being authenticated. - (custom)
methodAndPath
β The HTTP method and path of the request (e.g.,GET /transactions/b9d0b2292c4e09e8eb22d036171491e87b8d2086bf8b265874c8d182cb9c9020
). - (custom)
bodyHash
, a hex-encoded SHA-256 hash of the raw HTTP request body, present even when the body is empty:func HashBody(body []byte) string { hashedBodyBytes := sha256.Sum256(body) return hex.EncodeToString(hashedBodyBytes[:]) }
For more details on the JWT implementation, please see jwt_manager.go
.
The CI/CD workflow, defined in publish_to_docker_hub.yaml
automates the process of building and publishing Docker images to Docker Hub. This workflow is triggered under two conditions:
Whenever updates are made to the develop
branch, the workflow creates and uploads a Docker image with two specific tags:
testing
testing-{DATE}-{SHA}
When a new release is published on GitHub, the workflow generates and uploads a Docker image with the following tags:
- The release tag, e.g.
x.y.z
latest
(applied only if the release is not marked as a pre-release)
When a pre-release is published on GitHub, the workflow generates and uploads a Docker image with the pre-release tag preceded by rc-
, e.g. rc-x.y.z