OISY Wallet

repository·main·Indexed 41 days ago

https://github.com/dfinity/oisy-wallet

A browser-based, network-custodial, multi-chain wallet powered by Internet Computer's chain fusion technology. It provides a user experience similar to custodial wallets with trust assumptions comparable to self-custody through decentralized network custody. The project features a Rust backend using tECDSA for threshold signatures and a Svelte frontend.

Tokens
140.1K
Snippets
219
Records
596
Agent score
49%

What's inside OISY

  1. Overview of OISY Third-Party Integrations

    main

    OISY depends on several external APIs and services to provide blockchain functionality across EVM, Solana, and XRP Ledger. These integrations handle tasks such as NFT indexing, real-time transaction subscriptions, balance checks, and transaction broadcasting.

    Supported Providers and Scopes

    ProviderTransportScopePrimary Use Case
    Alchemyviem PublicClient + NFT API v3 + WSEVM + SolanaNFT indexing, real-time tx subscriptions, Solana HTTP RPC
    Infuraethers.js InfuraProvider + Gas RESTEVMPrimary EVM JSON-RPC (balances, fees/gas, contract reads, tx broadcast, ckETH logs)
    Etherscanethers.js EtherscanProvider (REST)EVMEVM transaction history (native, internal, ERC-20/721/1155 transfers)
    QuickNode@solana/kit WSS + HTTP JSON-RPCSolanaSolana WS tx confirmation (mainnet) + SPL token metadata (getAsset)
    XRP Ledgerfetch HTTP JSON-RPCXRP LedgerNative XRP balance (account_info) and signed-transaction submission (submit)
    OnRamperBackend HMAC signing—Buy-widget URL signing

    Note: XRP Ledger integration is currently disabled in user-facing builds.

  2. Overview of XRP Ledger (XRPL) integration

    main

    The XRP Ledger (XRPL) integration adds support for native XRP assets within the OISY wallet. This integration follows the pattern established by the Solana integration, providing users with the ability to view native XRP balances, send and receive XRP, view transaction history, and access an explorer link.

    Key technical details for this integration include:

    • Signing Mechanism: Uses threshold cryptography on the chain-fusion signer canister (no new IC primitives required).
    • Cryptography: Utilizes the Ed25519 curve.
    • Architecture: Implemented within a dedicated $xrp top-level folder rather than a generic chain adapter to manage the specific requirements of the XRPL.
    • Status: The feature is currently in implementation and is shipped disabled until a specific enable PR is merged.
  3. Backend technology stack overview

    main

    The OISY wallet backend is built using the following stack:

    • Language: Rust 2021 edition.
    • Architecture: A Cargo workspace where src/backend, src/cycles_ledger/, and src/shared are members. Dependencies are centralized in the root Cargo.toml and referenced via pkg = { workspace = true }.
    • Linting: Strict workspace lints using pedantic and deny attributes.
    • Target: wasm32-unknown-unknown for canister builds.
    • Stable State: Managed via ic-stable-structures. All persisted state is contained within a single thread_local! STATE cell located in src/backend/src/state/mod.rs.
    • Cryptography: Threshold signatures must be routed through the chain-fusion signer canister. Direct calls to sign_with_ecdsa or sign_with_schnorr are disallowed by Clippy.
    • Interface: Defined in src/backend/backend.did and regenerated using npm run generate.
    • Testing: Unit tests are inline (#[cfg(test)]). Integration tests are located in src/backend/tests/it/ and use pocket-ic. The test driver is scripts/test.backend.sh.
  4. Understand the Liquidium integration scope and features

    main

    The Liquidium integration allows users to supply assets to earn yield (which also acts as collateral) and borrow assets against that collateral.

    Supported Actions (v1)

    • Supply: Deposit supported assets into a Liquidium pool.
    • Borrow: Take out a supported asset against supplied collateral (subject to max-LTV).
    • Repay: Full or partial repayment of an open borrow position.
    • Withdraw: Remove supplied assets (provided the position remains healthy).

    Supported Assets (v1)

    • BTC (ckBTC)
    • ETH (ckETH)
    • USDC
    • USDT

    Note: Pools not yet live on Liquidium mainnet are displayed as "Coming soon" and are non-selectable, driven by client.market availability.

  5. Understand the Backend Crate Structure

    main

    The backend is organized into a Cargo workspace with several specialized crates. The primary canister logic resides in the backend crate, while shared types and ledger helpers are separated into other crates.

    Workspace Crates

    CratePathPurpose
    backendsrc/backend/The IC canister itself (crate-type = ["cdylib"], target wasm32).
    sharedsrc/shared/Public types reused across crates and exposed in the Candid interface.
    ic-cycles-ledger-clientsrc/cycles_ledger/client/Generated client for the cycles-ledger canister.
    ic-cycles-ledger-picsrc/cycles_ledger/pic/Pocket-IC helpers for cycles-ledger.
    ic-cycles-ledger-typessrc/cycles_ledger/types/Types for the cycles-ledger.

    Note: Do not add a new crate without explicit user approval.

    The backend crate's public API surface is defined by the Candid file src/backend/backend.did and the generated bindings under src/declarations/backend/.

  6. Understand BTC swap transaction lifecycle and statuses

    main

    When performing a BTC swap via the NEAR Intents provider, the transaction lifecycle follows the 1Click status model. During the period between a BTC deposit being broadcast and its confirmation on the Bitcoin network, the transaction is considered non-terminal.

    Status Mapping:

    • While awaiting Bitcoin confirmation, the transaction row in the UI remains in a pending state. This state persists through modal closures and page refreshes.
    • The underlying 1Click statuses are PENDING_DEPOSIT or KNOWN_DEPOSIT_TX (once the broadcast txid is submitted via /deposit/submit).
    • OISY maps these as non-terminal states in near-intents-active-tx.utils.ts.
    • Once the deposit is confirmed and 1Click settles the swap, the global poller moves the transaction row to Succeeded.

    Important Constraints:

    • Minimum Amounts: The 1Click bridge has a minimum requirement for BTC sources (e.g., 8300 sats for BTC to ETH). Quotes below this minimum will fail with an HTTP 400 error. Currently, this may manifest in the UI as a generic "swap not offered" message.
  7. Frontend technology stack and patterns

    main

    The frontend is in a mid-migration state between Svelte versions and uses specific patterns for state and localization:

    • Svelte: The project is migrating to Svelte 5. New code must use runes. Existing Svelte stores should remain in place for cross-route state; do not migrate them in unrelated PRs.
    • i18n (Internationalization):
      • Keys are generated from src/frontend/src/lib/i18n/en.json using the command npm run i18n.
      • The auto-update-i18n workflow structurally syncs non-en locales (adding missing keys as empty strings and dropping extra keys) but does not translate content. Do not author non-en translations unless explicitly requested.
    • Testing: Uses Vitest 4, @testing-library/svelte, and jsdom.
      • Important: Test specs must live in src/frontend/src/tests/ and must mirror the source structure. Do not co-locate tests with source files.
    • Analytics: All product events must use the single trackEvent entry point. Refer to analytics.md and the PLAUSIBLE_* enums for the canonical event and property schema.
    • Path Aliases: Relative imports under src/frontend/src/ are prohibited by ESLint. Use the following aliases:
      • $lib, $routes, $btc, $eth, $evm, $icp, $sol, $xrp, $icp-eth, $env, $declarations, $tests.
    npm run i18n
  8. Report unexpected errors using `PLAUSIBLE_EVENTS.ERROR`

    main

    The PLAUSIBLE_EVENTS.ERROR event is a specialized, generic event used exclusively for reporting broken invariants (code paths that should be unreachable/dead code). It is not for legitimate business logic failures (like a user being rate-limited).

    Requirements for ERROR events:

    • event_context / event_subcontext: Must specify which invariant broke.
    • result_error_severity: This field is required for the error event so that the impact can be assessed.
    • Avoid noise: Do not call this from reactive blocks (like Svelte derived values) that might fire repeatedly, as this turns the error dashboard into noise.
  9. Handle gas overrides in WalletConnect EVM requests

    main

    When processing a WalletConnect request that includes its own gas limit, the wallet must ensure that fee priority options and the displayed fee row are priced against the same value to avoid discrepancies.

    In EthWalletConnectSendReview.svelte, the gas used for pricing should be derived as signedGas = requestedGas ?? $feeStore?.gas.

    To ensure consistency, EthFeePriority must be provided with the same gas override used by EthFeeDisplay. This ensures that both the priority tier options and the resulting fee row price the transaction based on the actual signed gas limit rather than just the OISY estimate.

    // Logic for deriving the gas to use for pricing in EthWalletConnectSendReview.svelte
    const signedGas = requestedGas ?? $feeStore?.gas;
  10. Understand OISY Trade price warnings and limit orders

    main

    OISY Trade uses two independent references to price limit orders: the current value (derived from the wallet's internal USD price feed) and the venue's order book (best bid/ask).

    Price Warning Logic

    • Crossing the book: A sell order at or below the best bid, or a buy order at or above the best ask, will fill almost immediately. The UI explicitly warns that the order will fill immediately.
    • Resting orders (Sub-1% gap): If a resting order is within 1% of the current value (on the 'wrong' side), no warning is shown and the value-difference figure remains neutral.
    • Resting orders (1% to 5% gap): If a resting order sits more than 1% on the wrong side of current value (a sell below it, a buy above it), a warning is displayed. The warning color is amber if the gap is under 5% and red if it is 5% or greater.
    • Resting orders (>5% gap): At or beyond a 5% give-up, the "Place order" button is disabled. The user must tick a confirmation checkbox to proceed. The checkbox acknowledgement varies based on whether the order is crossing or resting.

    Fill-or-Kill (FOK) Orders

    FOK orders are an exception to the standard warning logic. Because a FOK order can only execute by crossing the book, if a provided price cannot cross, it results in a blocking error stating "it would be canceled". This error disables the Review step and takes precedence over other warnings.

  11. Perform Candid interface compatibility checks

    main

    The backend includes a test check_candid_interface_compatibility in lib.rs that is ignored by default.

    To prevent accidental breaking changes, the CI runs this test with the --ignored candid flag when src/backend/backend.did is modified in a PR that is not explicitly marked as a breaking change (using !: in the title and BREAKING CHANGE: in the body). If you are intentionally changing the interface, ensure you follow the breaking change documentation.

  12. Security model for shared personal notes

    main

    The OISY wallet implements a multi-layered security model for sharing personal notes via links. The primary goal is to allow unauthenticated recipients to read a note while ensuring the canister (backend) never sees the plaintext content.

    Core Security Layers

    • Client-Side Encryption: Notes are encrypted client-side using a fresh per-share AES-GCM-256 key. This key is stored exclusively in the URL fragment (#k=...), which is never sent to the server via HTTP. The canister only stores the ciphertext (ct_content), the token, expiry, single_use flag, and creator.
    • Cryptographic Binding: The ciphertext is bound to its token using AES-GCM additional authenticated data (<token>:note) to prevent replay attacks under different tokens.
    • Capability-Based Access: Access is granted via possession of a 128-bit cryptographically random token. There is no identity check for recipients, allowing logged-out users to view shared notes.
    • Endpoint Authentication Rules:
      • create_personal_note_share: Authenticated (caller_is_registered_user).
      • get_personal_note_share (Query): Anonymous.
      • consume_personal_note_share (Update): Anonymous.

    Constraints and Protections

    • Rate Limiting:
      • create: Per-caller limit of 20/min.
      • consume (Update): Global limit of 600 calls/min shared across all anonymous callers.
      • get (Query): No stateful limiter (bounded by token search space and O(log n) lookup).
    • Time-Bounding: Expiry is mandatory (maximum 30 days) and enforced server-side. Expired entries return NotFound and are pruned via lazy access or hourly sweeps.
    • Single-Use Semantics: For single-use shares, the content is returned exactly once by consume, which atomically deletes the entry. The get method never returns content for single-use shares.
    • Privacy & Anti-Enumeration:
      • The creator field is used for rate-limiting/caps but is never returned in reads to prevent identifying the creator.
      • All error states (expired, consumed, unknown, or type mismatch) return the same NotFound error to prevent enumeration attacks.
    • Usage Caps: Users are limited to 100 active shares (returns TooManyShares).