Initialize the contract with Signal's trust root, relay admin, verifier, and treasury. trust_root_key: 32-byte Montgomery u-coordinate (from Signal's config). verifier_contract: Nethermind Groth16 verifier (stored once, used by register_tee). protocol_treasury: Stellar address for protocol fee collection.
fn initialize(
env: soroban_sdk::Env,
admin: soroban_sdk::Address,
trust_root_key: soroban_sdk::BytesN<32>,
verifier_contract: soroban_sdk::Address,
protocol_treasury: soroban_sdk::Address,
)
Update the relay admin address (admin-only).
fn update_admin(env: soroban_sdk::Env, new_admin: soroban_sdk::Address)
Set the trusted Docker image hash (admin-only). Only TEE keys generated by containers matching this image will be accepted.
fn set_trusted_image(env: soroban_sdk::Env, image_digest: soroban_sdk::BytesN<32>)
Upgrade the contract WASM (admin-only).
fn upgrade(env: soroban_sdk::Env, new_wasm_hash: soroban_sdk::BytesN<32>)
Register a new user account via a specific relay. Auto-assigns an account number. sender_identity: SHA256(GroupIdentifier || UUID) — opaque composite identity. Authenticated by TEE signature over (sender_identity).
fn register(
env: soroban_sdk::Env,
signal_username: soroban_sdk::String,
sender_identity: soroban_sdk::BytesN<32>,
stellar_address: soroban_sdk::Address,
tee_signature: soroban_sdk::BytesN<64>,
) -> u64
Look up account number by Stellar G-address.
fn account_no(env: soroban_sdk::Env, stellar_address: soroban_sdk::Address) -> u64
Look up account number by identity (read-only).
fn get_account_no(env: soroban_sdk::Env, identity: soroban_sdk::BytesN<32>) -> u64
Look up Stellar G-address by identity (read-only).
fn get_stellar_address(
env: soroban_sdk::Env,
identity: soroban_sdk::BytesN<32>,
) -> soroban_sdk::Address
Migrate a user's Stellar address. Requires BOTH the current address holder AND admin to authorize.
fn migrate_stellar_address(
env: soroban_sdk::Env,
account_no: u64,
new_address: soroban_sdk::Address,
)
Add a whitelisted token by symbol (e.g. "USDC") and contract address.
fn add_token(
env: soroban_sdk::Env,
symbol: soroban_sdk::String,
token_address: soroban_sdk::Address,
)
fn remove_token(
env: soroban_sdk::Env,
symbol: soroban_sdk::String,
token_address: soroban_sdk::Address,
)
Set the protocol base fee (admin-only). Applied to all transactions regardless of relay.
fn set_protocol_fee_bps(env: soroban_sdk::Env, fee_bps: u32)
Update the protocol treasury address (admin-only).
fn set_protocol_treasury(env: soroban_sdk::Env, treasury: soroban_sdk::Address)
Authorize a Signal username as a relay identity (admin-only). The TEE operating under this username can self-register via register_tee.
fn authorize_relay(env: soroban_sdk::Env, signal_username: soroban_sdk::String)
Deauthorize a relay (admin-only). Removes authorization and relay info.
fn deauthorize_relay(env: soroban_sdk::Env, signal_username: soroban_sdk::String)
Post a broadcast message (admin-only). All relays poll this and distribute to their users. Used for migration notices, upgrades, etc.
fn set_broadcast(env: soroban_sdk::Env, message: soroban_sdk::String)
Get the latest broadcast message. Returns None if no broadcast has been set.
fn get_broadcast(env: soroban_sdk::Env) -> Option
Deposit any whitelisted token into a user's balance. Anyone can deposit to any account number.
fn deposit(
env: soroban_sdk::Env,
token: soroban_sdk::Address,
funding_address: soroban_sdk::Address,
account_no: u64,
amount: i128,
)
Sweep a user's G-address balance for a specific token into their contract balance. Uses the SAC transfer_from with a pre-existing approval set during registration. Only moves funds IN — never out. Callable only by the account holder's S-key.
fn sweep(env: soroban_sdk::Env, account_no: u64, token: soroban_sdk::Address) -> i128
Query a user's balance for a given token.
fn balance(env: soroban_sdk::Env, account_no: u64, token: soroban_sdk::Address) -> i128
Query the protocol base fee in basis points.
fn protocol_fee_bps(env: soroban_sdk::Env) -> u32
Query a relay's hoster fee in basis points.
fn relay_fee_bps(env: soroban_sdk::Env, signal_username: soroban_sdk::String) -> u32
Query relay info (for discovery catalogue).
fn relay_info(env: soroban_sdk::Env, signal_username: soroban_sdk::String) -> RelayInfo
Query invite info (read-only, used by relay for collision checking).
fn get_invite(env: soroban_sdk::Env, invite_code: u32) -> Invite
Claim a sub-account code during the join registration flow. Called BEFORE the child can send instructions (they just registered). TEE signature authenticates the relay (same pattern as register).
fn claim_sub_account(
env: soroban_sdk::Env,
signal_username: soroban_sdk::String,
claim_code: u32,
child_identity: soroban_sdk::BytesN<32>,
tee_signature: soroban_sdk::BytesN<64>,
)
Claim an invite during the "register <code>" DM flow. Called immediately after register() for users who aren't in the group yet (so they don't have a SenderKey). TEE signature is the only auth.
For users already in the group, "claim <code>" goes through execute_signal_instruction with full SenderKey verification instead.
fn claim_invite(
env: soroban_sdk::Env,
signal_username: soroban_sdk::String,
claimer_identity: soroban_sdk::BytesN<32>,
invite_code: u32,
tee_signature: soroban_sdk::BytesN<64>,
) -> Result<(), ContractError>
Freeze account — only callable by the user's own Stellar key
fn freeze(env: soroban_sdk::Env, account_no: u64)
Unfreeze account — only callable by the user's own Stellar key
fn unfreeze(env: soroban_sdk::Env, account_no: u64)
Withdraw funds — only callable by the user's own Stellar key. Non-custodial exit: relay cannot prevent this.
fn withdraw(
env: soroban_sdk::Env,
account_no: u64,
token: soroban_sdk::Address,
amount: i128,
destination: soroban_sdk::Address,
)
Register/re-register a TEE relay with Groth16 attestation proof.
PERMISSIONLESS — no admin.require_auth(). The Groth16 proof serves as authentication: only a genuine TEE running the trusted image can produce a valid proof. Admin controls access by pre-authorizing Signal usernames.
Journal layout (104 bytes): pubkey(32) || image_digest(32) || expiry(8 BE) || signal_id_hash(32)
On restart, the TEE generates a fresh ephemeral key and re-registers. The signal_username is stable across restarts (identity on persistent disk).
fn register_tee(
env: soroban_sdk::Env,
signal_username: soroban_sdk::String,
seal: soroban_sdk::Bytes,
image_id: soroban_sdk::BytesN<32>,
journal: soroban_sdk::Bytes,
hoster_address: soroban_sdk::Address,
hoster_fee_bps: u32,
)
Register a SenderKey signing key for a user on a specific relay.
The TEE receives a SenderKey Distribution Message (SKDM) over the Signal group, decrypts it inside the enclave, and signs (sender_uuid || signing_key) with its attested Ed25519 key. The contract verifies this TEE signature and stores the binding per (relay, user).
signing_key: Curve25519 public key from the SenderKey (32 bytes, no prefix). tee_signature: Ed25519 signature over (sender_identity || signing_key).
fn register_sender_key(
env: soroban_sdk::Env,
signal_username: soroban_sdk::String,
sender_identity: soroban_sdk::BytesN<32>,
signing_key: soroban_sdk::BytesN<32>,
tee_signature: soroban_sdk::BytesN<64>,
)
Migrate an account from the old UUID-keyed schema to the new composite identity schema. Called by the relay at startup after WASM upgrade. Old entries are orphaned (TTL expiry). The relay queries old data via simulation BEFORE the upgrade, then calls this after.
TEE signature over (new_identity || account_no as u64 BE bytes) authenticates the relay.
fn migrate_account(
env: soroban_sdk::Env,
signal_username: soroban_sdk::String,
new_identity: soroban_sdk::BytesN<32>,
account_no: u64,
stellar_address: soroban_sdk::Address,
frozen: bool,
balances: soroban_sdk::Vec<(soroban_sdk::Address, i128)>,
sender_key: Option>,
msg_counter: Option,
tee_signature: soroban_sdk::BytesN<64>,
)
Execute an instruction verified by on-chain SenderKey decryption + TEE attestation.
The contract independently verifies the Signal SenderKey message:
Parameters: sender_key_msg: Raw SenderKeyMessage bytes (version + protobuf + 64-byte signature) seed: SenderMessageKey seed (32 bytes, from the SenderKey chain ratchet) relay_data: Relay-appended data (e.g., invite code). Appended to decrypted instruction with space separator. Empty if no relay data. tee_signature: TEE signs (sender_identity || sender_key_msg || seed || relay_data)
fn execute_signal_instruction(
env: soroban_sdk::Env,
signal_username: soroban_sdk::String,
sender_identity: soroban_sdk::BytesN<32>,
sender_key_msg: soroban_sdk::Bytes,
seed: soroban_sdk::BytesN<32>,
relay_data: soroban_sdk::Bytes,
tee_signature: soroban_sdk::BytesN<64>,
) -> Result<(), ContractError>