This document outlines the security considerations, threat model, authorization mechanisms, and best practices for the Bridgelet Core system.
The Bridgelet Core system is designed to operate in a trust-minimized environment. The following threat vectors have been considered and mitigated:
- Threat: An attacker attempts to sweep funds from an active Ephemeral Account to their own wallet.
- Mitigation:
- Authorization Signatures: Sweeping requires a valid Ed25519 signature from an authorized signer. The
SweepControllercontract verifies this signature before allowing a sweep. - Destination Locking: The
SweepControllercan be initialized with a lockedauthorized_destination. If set, funds can only be swept to that specific address, regardless of the signer. - Nonce Protection: Each sweep operation requires a unique nonce to prevent replay attacks.
- Authorization Signatures: Sweeping requires a valid Ed25519 signature from an authorized signer. The
- Threat: An attacker attempts to sweep the same account multiple times or replay a valid sweep signature.
- Mitigation:
- State Machine: The
EphemeralAccountcontract enforces a strict state machine (Active->PaymentReceived->Swept|Expired). Once the state transitions toSwept, subsequent calls fail withError::AlreadySwept. - Nonces: The
SweepControllermaintains a monotonically increasing nonce. Every signed sweep request must include the current nonce, and the nonce is incremented upon success.
- State Machine: The
- Threat: An attacker attempts to keep funds in an ephemeral account indefinitely or sweep them after they should have expired.
- Mitigation:
- Ledger-based Expiration: Expiration is tied to the Stellar ledger sequence number, providing an objective time source.
- Guard Clauses: The
sweepfunction explicitly checksis_expired()and fails if the account has passed its expiry ledger. - Recovery Mechanism: After expiration, the
expire()function allows funds to be recovered to a pre-definedrecovery_address, preventing funds from being permanently locked.
- Threat: An attacker initializes an account with a past expiry or invalid parameters.
- Mitigation:
- Initialization Checks: The
initializefunction validates thatexpiry_ledgeris in the future. - One-time Initialization: The
Initializedflag prevents re-initialization of an existing contract.
- Initialization Checks: The
Bridgelet Core uses a layered authorization model:
- Mechanism:
require_auth() - Scope: The creator address must authorize the initialization of an
EphemeralAccount.
The system supports two sweep paths, both routed through SweepController:
- Mechanism: Ed25519 Signatures + Soroban Auth
- Flow:
- Off-chain SDK generates a signature covering
hash(destination + nonce + contract_id). - Caller invokes
SweepController::execute_sweep. SweepControllerverifies the Ed25519 signature against the storedauthorized_signer.SweepControllerincrements the nonce to prevent replay.SweepControllerauthorizes itself as the invoker ofEphemeralAccount::sweep.EphemeralAccount::sweepvalidates its internal state, transitions toSwept, and reclaims the base reserve.
- Off-chain SDK generates a signature covering
- When to use: When the off-chain signer is available to produce a signature. Suitable for automated sweep pipelines.
- Mechanism: Soroban Authorization Entries
- Flow:
- The recipient signs a Soroban auth entry for
SweepController::claim. - Caller (or a relayer) invokes
SweepController::claim(recipient, ephemeral_account). SweepControllervalidates the destination matches the locked destination (if set).SweepControllerauthorizes itself as the invoker ofEphemeralAccount::sweep_claim.EphemeralAccount::sweep_claimvalidates state, transitions toSwept, and reclaims the base reserve.
- The recipient signs a Soroban auth entry for
- When to use: When the recipient is available to sign a Soroban auth entry directly. Suitable for SDK/integration-driven claims where no off-chain signer is needed.
- Mechanism: Soroban Auth (dual authorization)
- Flow:
- Caller invokes
SweepController.claimwithrecipientandephemeral_account. recipient.require_auth()ensures the recipient authorizes the claim.SweepControllerbuilds a Soroban auth entry authorizing itself as the invoker ofsweep_claimon the ephemeral account (authorize_as_current_contract).EphemeralAccount.sweep_claimis called, which verifies the Soroban auth entries and transitions the account toSwept.
- Caller invokes
- Note: This path does not use Ed25519 signatures. Instead, both the recipient and the controller contract must provide Soroban authorization, enabling a relayer/SDK to submit the transaction while the recipient only signs the authorization payload.
- Mechanism: Public (Permissionless)
- Scope: Once the expiry ledger is reached, anyone can call
expire()to return funds to the recovery address. This ensures funds are never stuck due to a missing signer.
The system employs the Checks-Effects-Interactions pattern and leverages Soroban's execution model:
- State Updates First: In
EphemeralAccount::sweep, the status is updated toSweptbefore any external calls or event emissions would typically occur (though currently, no external calls are made). - Atomic Execution: Soroban contract invocations are atomic. If any part of the sweep operation fails (e.g., signature verification), the entire transaction reverts, ensuring no partial state changes.
- Single-Threaded: Soroban executes transactions sequentially for a given contract instance, preventing race conditions.
- Single-Use: An account can effectively be swept only once.
- Time-Bounded: Funds are guaranteed to be either sweepable or recoverable after the expiry ledger.
- Auditability: All critical state transitions (
Created,PaymentReceived,Swept,Expired) emit on-chain events.
- EphemeralAccount Signature Verification: The
verify_sweep_authorizationfunction inEphemeralAccountis currently a placeholder ("TODO"). Do not rely onEphemeralAccount::sweepdirectly for security. Always route sweeps throughSweepController, which implements proper Ed25519 verification. - Token Transfers:
SweepControllerinvokestransfers::execute_transfersto move tokens from the ephemeral account to the destination after a successful sweep. The transfer logic is fully integrated and active. - EphemeralAccount Signature Verification: The
verify_sweep_authorizationfunction inEphemeralAccountis currently a placeholder. It only checks that the caller is the authorized controller — it does not verify the Ed25519 signature bytes. Do not rely onEphemeralAccount::sweepdirectly for security. Always route sweeps throughSweepController, which implements proper Ed25519 verification viaexecute_sweep, or through theclaimpath which uses Soroban auth instead of off-chain signatures.
- Asset Limit: The
EphemeralAccountsupports recording up to 10 distinct assets. - Gas Management: Users/Integrators are responsible for providing sufficient gas for sweep operations.
- Trust Assumption: The system assumes the
authorized_signerprivate key is kept secure off-chain.
- Use SweepController: Always use
SweepControllerto perform sweeps — either viaexecute_sweep(Ed25519 signature path) orclaim(Soroban auth path). Never callEphemeralAccount::sweeporEphemeralAccount::sweep_claimdirectly. - Choose the Right Path: Use
execute_sweepwhen you have an off-chain signer producing Ed25519 signatures. Useclaimwhen the recipient can sign a Soroban auth entry directly (e.g., via SDK or wallet integration). - Verify Expiry: When creating accounts, ensure
expiry_ledgerprovides enough buffer for network latency and confirmation times. - Monitor Events: Listen for
AccountCreated,PaymentReceived, andSweepCompletedevents to trigger off-chain workflows. - Key Management: Securely manage the Ed25519 private key used for generating sweep signatures. Use a hardware security module (HSM) or secure enclave if possible.
- Recovery: Monitor for expired accounts and trigger
expire()to reclaim funds to the recovery address.