OpenZeppelin Smart Accounts On Stellar

openzeppelin smart accounts

A smart account is a Stellar contract that implements the Openzeppelin library to create a fully programmable account that controls how authorization should be handled.

James On YouTube

Traditionally, an account uses a single ed25519 key, and everything flows from that. Smart accounts replace this with:

  • Context rules What a caller is allowed to do
  • Signers Who is allowed to act
  • Policies How permissions are enforced

Authorization becomes declarative, modular and auditable.

Instead of embedding signature checks into contracts, smart accounts let developers describe authorization policies and let the framework evaluate them.

How the Smart Account Library Works

The Openzeppelin accounts library provides a modular and flexible framework for building sophisticated, policy-controlled accounts on Soroban. At its core, the library offers three foundational sets of primitives that work together seamlessly.

Central to the system is the SmartAccount Trait, which extends Soroban’s built-in CustomAccountInterface. This trait supplies essential helper methods for creating context rules, managing signers, attaching policies, and evaluating transaction authorization. By implementing this trait, your contract defines the account’s behavior while the library handles the underlying complexity.

Authorization is governed by Context Rules, which define the precise circumstances under which an action is permitted. Each rule specifies a context type—such as calling a specific contract or creating a new one—alongside associated signers, policies, and an optional expiration. When a transaction is proposed, the account evaluates it using a matching algorithm: it gathers all non-expired rules, sorts them with the newest first, and evaluates each in sequence until a match is found. The matched rule’s policies are then enforced, approving the call. If no rule matches, the transaction is reverted.

The library supports two kinds of Signers for authentication. Delegated signers are standard Soroban addresses, while External signers are public keys linked to verifier contracts, enabling support for modern authentication schemes like passkeys, ed25519, secp256k1, BLS, or RSA. This design allows accounts to reuse trusted verifiers and adopt new authentication methods without requiring changes to the core account logic.

Finally, Policies serve as plug-in modules that enforce specific business rules and constraints. These can include threshold multisignature requirements, spending limits, session-based permissions, time-based restrictions, risk controls, and recovery or guardianship mechanisms. Each policy follows a clean lifecycle defined by four hooks: install for configuration, can_enforce for a read-only pre-check, enforce to commit state changes, and uninstall for cleanup. This separation ensures that authorization logic remains maintainable and extensible.

Together, these components create a powerful and structured system for managing smart account permissions and security.


Using Smart Accounts in Your Contract

To turn a contract into a smart account:

  1. Implement SmartAccount
  2. Implement CustomAccountInterface
  3. Delegate authentication to do_check_auth

Example:

#[contractimpl]
impl SmartAccount for MySmartAccount {
    fn add_context_rule(...) { ... }
    fn add_signer(...) { ... }
    fn add_policy(...) { ... }
}
#[contractimpl]
impl CustomAccountInterface for MySmartAccount {
    type Signature = Signatures;
    fn __check_auth(...) {
        do_check_auth(...)?; 
        Ok(())
    }
}

Once implemented, the account automatically becomes a programmable authorization engine.


A Multisig Treasury Built With Smart Accounts

The full code for this and the entire global payments demo is on Github at: https://github.com/jamesbachini/Stellar-Global-Payments/

To deploy on stellar testnet there is a helper script:

./deploy_testnet.sh

You can then run the backend and webserver with:

cd backend/
npm run install
npm run dev

Let’s take a look at the contract code inside contracts/multisig/src/lib.rs

This code uses the Openzeppelin smart account library to create a 3/4 multisig wallet.

Each request contains:

pub struct WithdrawalRequest {
    pub id: u32,
    pub to: Address,
    pub amount: i128,
    pub approvals: Vec<Address>,
    pub executed: bool,
    pub initiator: Address,
    pub created_at: u64,
    pub completed_at: u64,
}

A signer proposes a withdrawal:

pub fn propose_withdraw(env: Env, signer: Address, to: Address, amount: i128) -> u32 {
    signer.require_auth();              // signer must sign
    validate_signer(&env, &signer);     // signer must be in signer set
    ensure_destination_allowed(&env, &to);

Proposal creates a new request:

let approvals = Vec::from_array(&env, [signer.clone()]);
let mut request = WithdrawalRequest { ... };

It may auto-execute if threshold is 1:

if maybe_execute(&env, &mut request) { ... }

Finally stored:

write_request(&env, &request);

When a request is processed maybe_execute() gets called which checks if the threshold has been met and if so issues a SEP41 token transfer to send the USDC from the contract to the receiver.

fn maybe_execute(env: &Env, request: &mut WithdrawalRequest) -> bool {
    if request.executed {
        return true;
    }
    let threshold = read_threshold(env);
    if request.approvals.len() < threshold {
        return false;
    }
    ensure_destination_allowed(env, &request.to);
    let token = read_token(env);
    let client = token::Client::new(env, &token);
    let self_address = env.current_contract_address();
    client.transfer(&self_address, &request.to, &request.amount);
    request.executed = true;
    request.completed_at = env.ledger().timestamp();
    true
}

Smart accounts give developers the ability to build expressive, adaptable and modular authorization systems on Soroban. OpenZeppelin’s Smart Account library provides all the tools needed for production-grade composability:

  • Context rules determine the scope of permissions
  • Signers represent flexible authentication methods
  • Policies enforce business logic
  • Verifier contracts validate signatures or proofs
  • A matching algorithm coordinates it all

The multisig treasury example demonstrates how the framework keeps admin authorization cleanly separated from operational logic. Developers can confidently manage signers, thresholds and policies while relying on the Smart Account library to enforce security semantics.


Get The Blockchain Sector Newsletter, binge the YouTube channel and connect with me on Twitter

The Blockchain Sector newsletter goes out a few times a month when there is breaking news or interesting developments to discuss. All the content I produce is free, if you’d like to help please share this content on social media.

Thank you.

James Bachini

Disclaimer: Not a financial advisor, not financial advice. The content I create is to document my journey and for educational and entertainment purposes only. It is not under any circumstances investment advice. I am not an investment or trading professional and am learning myself while still making plenty of mistakes along the way. Any code published is experimental and not production ready to be used for financial transactions. Do your own research and do not play with funds you do not want to lose.