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. Returns the assigned account number.
Fails if the UUID is already registered (duplicate). The relay handles re-registration by detecting existing accounts and recreating groups.
Authenticated by TEE signature over (uuid_bytes).
fn register(
env: soroban_sdk::Env,
signal_username: soroban_sdk::String,
uuid: soroban_sdk::String,
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 UUID (read-only).
fn get_account_no(env: soroban_sdk::Env, uuid: soroban_sdk::String) -> u64
Look up Stellar G-address by UUID (read-only).
fn get_stellar_address(
env: soroban_sdk::Env,
uuid: soroban_sdk::String,
) -> soroban_sdk::Address
Migrate a user's Stellar address. Requires BOTH the current address holder AND admin to authorize — prevents unilateral relay or user address changes.
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_uuid: soroban_sdk::String,
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_uuid: soroban_sdk::String,
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. This is the non-custodial exit: relay cannot prevent this. No hoster fee (no relay involved). No token whitelist check — users can always withdraw even if a token is de-listed (non-custodial guarantee).
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_uuid_bytes || signing_key).
fn register_sender_key(
env: soroban_sdk::Env,
signal_username: soroban_sdk::String,
sender_uuid: soroban_sdk::String,
signing_key: soroban_sdk::BytesN<32>,
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_uuid || sender_key_msg || seed || relay_data)
fn execute_signal_instruction(
env: soroban_sdk::Env,
signal_username: soroban_sdk::String,
sender_uuid: soroban_sdk::String,
sender_key_msg: soroban_sdk::Bytes,
seed: soroban_sdk::BytesN<32>,
relay_data: soroban_sdk::Bytes,
tee_signature: soroban_sdk::BytesN<64>,
) -> Result<(), ContractError>