| title | Simple Account |
|---|---|
| sidebar_label | Simple Account |
| description | A minimal contract account secured by a single ed25519 public key. |
| sidebar_position | 15 |
This example implements the smallest possible contract account: each require_auth call delegates to one ed25519 public key. It shows how to store that key, run __check_auth, and surface authorization failures. Use this as the baseline before moving to the Complex Account example for multisig or policy enforcement.
:::danger
Implementing a contract account requires a very good understanding of authentication and authorization and requires rigorous testing and review. The example here is not a full-fledged account contract - use it as an API reference only.
:::
:::caution
While contract accounts are supported by the Stellar protocol and Soroban SDK, the full client support (such as transaction simulation) is still under development.
:::
- Finish the Setup checklist to install the Stellar CLI, Rust target, and required environment variables.
- Clone the
soroban-examplesrepository at thev23.0.0tag:
git clone -b v23.0.0 https://github.com/stellar/soroban-examples- If you prefer not to install anything locally, launch the repo in GitHub Codespaces or Codeanywhere.
Run the tests from the simple_account directory:
cd simple_account
cargo testExpected output:
running 1 test
test test::test_account ... ok
Open simple_account/src/lib.rs. The contract keeps one piece of state: the owner's ed25519 public key.
#[contracttype]
#[derive(Clone)]
pub enum DataKey {
Owner,
}
#[contractimpl]
impl SimpleAccount {
pub fn init(env: Env, public_key: BytesN<32>) {
if env.storage().instance().has(&DataKey::Owner) {
panic!("owner is already set");
}
env.storage().instance().set(&DataKey::Owner, &public_key);
}Call init once to persist the owner's public key. Subsequent calls panic to prevent replacement of the key.
#[allow(non_snake_case)]
pub fn __check_auth(
env: Env,
signature_payload: BytesN<32>,
signature: BytesN<64>,
_auth_context: Vec<Context>,
) {
let public_key: BytesN<32> = env
.storage()
.instance()
.get(&DataKey::Owner)
.unwrap();
env.crypto()
.ed25519_verify(&public_key, &signature_payload.into(), &signature);
}
}__check_auth runs whenever another contract invokes require_auth on this contract address. The implementation loads the stored key, verifies the signature, and panics on failure so the upstream require_auth call rejects. Once you need multiple keys or policy logic, follow the same pattern shown in Complex Account.
Open simple_account/src/test.rs. __check_auth is not exposed as a regular entry point, so tests call env.try_invoke_contract_check_auth to emulate the Soroban host and exercise the same path Soroban runs during require_auth.
#[test]
fn test_account() {
let env = Env::default();
let account_contract = SimpleAccountClient::new(&env, &env.register(SimpleAccount, ()));
let signer = Keypair::generate(&mut thread_rng());
account_contract.init(&signer.public.to_bytes().into_val(&env));
let payload = BytesN::random(&env);
env.try_invoke_contract_check_auth::<Error>(
&account_contract.address,
&payload,
sign(&env, &signer, &payload),
&vec![&env],
)
.unwrap();
assert!(env
.try_invoke_contract_check_auth::<Error>(
&account_contract.address,
&payload,
BytesN::<64>::random(&env).into(),
&vec![&env],
)
.is_err());
}try_invoke_contract_check_auth mimics the host path for require_auth, so the test proves both the success case and a failure case with random bytes.
Follow the same structure for any account:
- create a keypair and store the expected signer (for example, with
init) - call
try_invoke_contract_check_authwith a valid signature and assert it succeeds - call it again with an invalid signature or payload and assert it fails
To produce the Wasm executable, run:
stellar contract build
# add --package soroban-simple-account-contract when building inside the soroban-examples workspaceThe compiled file appears at target/wasm32v1-none/release/simple_account.wasm (the exact filename depends on your crate name).
- Complex account example – adds multisig support and spend limits.
- BLS signature contract – demonstrates custom signature schemes inside
__check_auth.