Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ examples/*/dist
examples/*/docs
packages/*/coverage
packages/*/dist
packages/*/docs
scripts/coverage

# yarn v3 (w/o zero-install)
Expand Down
5 changes: 2 additions & 3 deletions packages/stellar-wallet-snap/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,8 @@ High-level layout of `packages/snap` (nested implementation folders like `servic

## Use cases

End-to-end flows (handler → services → UI) live under [`docs/use-cases/`](./docs/use-cases/).
Background synchronization overview: [`docs/misc/synchronization/overview.md`](./docs/misc/synchronization/overview.md).
Shared transaction build / validate / send: [`docs/misc/transaction/`](./docs/misc/transaction/README.md).
End-to-end flows (handler → services → UI) live under [`docs/use-cases/`](./docs/use-cases/README.md).
Background synchronization overview: [`docs/misc/synchronization/synchronization.md`](./docs/misc/synchronization/synchronization.md).

## API examples

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
# Synchronization: accounts

On-chain account snapshots (balances, trustlines, SEP-41 tokens) for **activated** keyring accounts.

| | |
| ---------------- | ---------------------------------------------------------------------------------------------------------------- |
| **Service** | `[OnChainAccountSynchronizeService](../../../src/services/on-chain-account/OnChainAccountSynchronizeService.ts)` |
| **Orchestrator** | `[SynchronizeService](../../../src/services/sync/SynchronizeService.ts)` |
| **Snap state** | `[OnChainAccountRepository](../../../src/services/on-chain-account/OnChainAccountRepository.ts)` |
| **Overview** | [synchronization.md](./synchronization.md) |

## Participants

| Component | Path | Role |
| ---------------------------------- | --------------------------- | --------------------------------------------------------------------------------------------------------------------------------- |
| `SynchronizeService` | `services/sync` | Load activated pairs + SEP-41 catalog; run account + tx sync |
| `OnChainAccountSynchronizeService` | `services/on-chain-account` | Merge snapshots, persist, emit |
| `OnChainAccountService` | `services/on-chain-account` | Resolve live on-chain account |
| `NetworkService` | `services/network` | [Horizon](https://developers.stellar.org/docs/data/apis/horizon/api-reference/retrieve-an-account) account + SEP-41 balance reads |
| `SyncAccountsHandler` | `handlers/cronjob` | Cron / scheduled entry |

## Request / response

Triggered via `SynchronizeService.synchronize` (see [syncAccounts.md](../../use-cases/cron-job/syncAccounts.md)). Emits keyring events after snap state is persisted:

- `AccountBalancesUpdated`
- `AccountAssetListUpdated`

SEP-41 token balances are **not** from Horizon — they use Soroban RPC `balance(Address)` simulation.

## Step-by-step

1. `SynchronizeService` loads **activated** account pairs from [Horizon](https://developers.stellar.org/docs/data/apis/horizon/api-reference/retrieve-an-account); unfunded / not-yet-activated accounts are skipped.
2. Load SEP-41 asset metadata for the scope (shared with transaction sync on the same run).
3. `OnChainAccountSynchronizeService.synchronize` — batch-fetch SEP-41 token balances (best effort).
4. Load previous snap-state snapshots as merge baseline.
5. Per activated account — apply SEP-41 balances, then **merge** persisted gaps (classic tombstones + SEP-41 backfill).
6. **Compute deltas** — `#computeKeyringSyncDeltas` compares pre-sync snapshot vs merged on-chain view (visibility transitions → balance / asset-list payloads).
7. Persist snapshots atomically, then emit `AccountBalancesUpdated` and `AccountAssetListUpdated`.

Account sync and transaction sync run **in parallel** when both are enabled on the same `synchronize` call.

## Tombstones, merge, and deltas

Merge and deltas are two linked steps:

1. `#mergePersistedEntriesIntoOnChainAccount` — fill gaps so the in-memory on-chain view is complete before diffing.
2. `#computeKeyringSyncDeltas` — compare **persisted snapshot** vs **merged on-chain view** for visibility transitions:

- newly visible → `added` (+ balance)
- no longer visible → `removed` (+ balance `0`)
- already not visible → omit (avoid flooding zeros)

## Sequence

```mermaid
sequenceDiagram
participant Cron as SyncAccountsHandler
participant Sync as SynchronizeService
participant OnChain as OnChainAccountSynchronizeService
participant Network as NetworkService
participant State as OnChainAccountRepository
participant MM as MetaMask controller

Cron->>Sync: synchronize(accounts, scope)
Sync->>Network: resolve activated pairs (GET /accounts/:id)
Sync->>OnChain: synchronize(pairs, scope, sep41Assets)
OnChain->>Network: SEP-41 balance simulation
OnChain->>State: load last snapshots
loop per activated account
OnChain->>OnChain: apply SEP-41 balances
OnChain->>OnChain: merge (classic tombstones + SEP-41 backfill)
OnChain->>OnChain: computeKeyringSyncDeltas
end
OnChain->>State: saveMany
OnChain->>MM: AccountBalancesUpdated (delta balances)
OnChain->>MM: AccountAssetListUpdated (delta added/removed)
```

## Data source

| Data | Source |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| Native + classic trustline balances | **Live on-chain** via [Horizon](https://developers.stellar.org/docs/data/apis/horizon/api-reference/retrieve-an-account) `GET /accounts/:account_id` |
| SEP-41 token balances | **Live on-chain** (Soroban simulation) |
| Persisted snapshot | **Snap state** (`OnChainAccountRepository`) |
| Keyring-facing balances / asset list | Emitted from latest snapshot (can be slightly stale until next sync) |

## Related

- [Horizon — Accounts](https://developers.stellar.org/docs/data/apis/horizon/api-reference/resources/accounts)
- [Horizon — Retrieve an Account](https://developers.stellar.org/docs/data/apis/horizon/api-reference/retrieve-an-account)
- [syncAccounts.md](../../use-cases/cron-job/syncAccounts.md) — cron entry and params
- [keyring.md](../../use-cases/keyring/keyring.md) — `listAccountAssets` / `getAccountBalances` read snap snapshots
- [transaction.md](./transaction.md) — transaction sync on the same run
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Synchronization: assets

Asset metadata catalog refresh from the token API.

| | |
| ---------------- | -------------------------------------------------------------------------------------------------- |
| **Service** | [`AssetMetadataService.synchronize`](../../../src/services/asset-metadata/AssetMetadataService.ts) |
| **Orchestrator** | [`SynchronizeService.synchronizeAssets`](../../../src/services/sync/SynchronizeService.ts) |
| **Snap state** | [`AssetMetadataRepository`](../../../src/services/asset-metadata/AssetMetadataRepository.ts) |
| **Overview** | [synchronization.md](./synchronization.md) |

## Participants

| Component | Path | Role |
| ---------------------- | ----------------------------------- | ----------------------------------- |
| `SyncAssetsHandler` | `handlers/cronjob` | Cron entry |
| `SynchronizeService` | `services/sync` | `synchronizeAssets(scope)` delegate |
| `AssetMetadataService` | `services/asset-metadata` | Fetch + persist catalog |
| `TokenApiClient` | `services/asset-metadata/token-api` | Token API |

## Request / response

Triggered by the `synchronizeAssets` cron (see [syncAssets.md](../../use-cases/cron-job/syncAssets.md)). Always uses **mainnet** scope — asset metadata is only available there, regardless of the user's selected network.

Wire format: [SIP-29 Snap Assets API](https://metamask.github.io/SIPs/SIPS/sip-29) (lookup handlers read from the persisted catalog).

## Step-by-step

1. `SyncAssetsHandler` cron fires (always **mainnet** scope).
2. `SynchronizeService.synchronizeAssets(scope)` delegates to `AssetMetadataService.synchronize`.
3. Fetch full token list from the token API.
4. Persist catalog via `AssetMetadataRepository.saveMany`.
5. Failures are logged / tracked; the cron does not fail the whole Snap lifecycle.

During account / transaction sync, `SynchronizeService` also preloads SEP-41 metadata via `fetchSep41AssetsOrSyncOnce` so transaction mapping and balance reads have catalog data without waiting for the assets cron.

## Sequence

```mermaid
sequenceDiagram
participant Cron as SyncAssetsHandler
participant Sync as SynchronizeService
participant Meta as AssetMetadataService
participant API as TokenApiClient
participant State as AssetMetadataRepository

Cron->>Sync: synchronizeAssets(mainnet)
Sync->>Meta: synchronize(mainnet)
Meta->>API: fetch token list
API-->>Meta: tokens metadata
Meta->>State: saveMany
```

## Data source

| Data | Source |
| ----------------------------------------- | ------------------------------------------------ |
| Asset catalog (symbol, decimals, icon, …) | **Token API** → persisted in **snap state** |
| On-demand lookup (`onAssetsLookup`) | Snap state catalog (fetch + persist missing ids) |

## Related

- [syncAssets.md](../../use-cases/cron-job/syncAssets.md) — cron entry
- [assets.md](../../use-cases/assets/assets.md) — `onAssets*` handlers
- [transaction.md](./transaction.md) — SEP-41 metadata used during tx mapping
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Synchronization

How background sync wires accounts, transactions, and asset catalog together.

| | |
| ---------------- | -------------------------------------------------------------------------------- |
| **Orchestrator** | [`SynchronizeService`](../../../src/services/sync/SynchronizeService.ts) |
| **Accounts** | [accounts.md](./accounts.md) — on-chain snapshots (balances, trustlines, SEP-41) |
| **Transactions** | [transaction.md](./transaction.md) — Horizon history, mapping, pending reconcile |
| **Assets** | [assets.md](./assets.md) — token metadata catalog |

## Participants

| Component | Path | Role |
| ------------------------- | ------------------ | ------------------------------------------------------ |
| `CronjobHandler` | `handlers/cronjob` | Gate — skip if inactive / locked |
| `SyncAccountsHandler` | `handlers/cronjob` | Cron / scheduled entry for accounts + txs |
| `SyncAssetsHandler` | `handlers/cronjob` | Cron entry for asset catalog |
| `TrackTransactionHandler` | `handlers/cronjob` | Poll until terminal → then synchronize |
| `SynchronizeService` | `services/sync` | Hub — mutex, parallel account + tx sync, asset catalog |

## Step-by-step

### `synchronizeAccounts` run

1. Load **activated** account pairs (skip unfunded).
2. Preload SEP-41 metadata for the scope.
3. Run **[accounts](./accounts.md)** and **[transactions](./transaction.md)** sync **in parallel**.
4. Per-task failures are logged; they do not fail the whole run.

## Sequence

```mermaid
sequenceDiagram
participant Sync as SynchronizeService
participant Assets as assets sync
participant Acc as accounts sync
participant Tx as transaction sync

Sync->>Assets: preload SEP-41 metadata
Assets-->>Sync: sep41Assets
par
Sync->>Acc: synchronize
and
Sync->>Tx: synchronize
end
```

Component detail: [accounts](./accounts.md) · [transactions](./transaction.md) · [assets](./assets.md)

## Skip synchronization / delay synchronization

`SynchronizeService` uses a mutex so only **one exclusive sync** runs at a time:

| Overlapping request | Behavior |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------- |
| Same accounts already in the current run | **Skip** (common during onboarding) |
| Other accounts (e.g. after account switch) | **Delay** — schedule `synchronizeAccounts` background event (~2s) instead of waiting on the mutex |

This avoids Snap request timeouts when syncs overlap.

```mermaid
sequenceDiagram
participant A as Sync request A
participant B as Sync request B
participant Sync as SynchronizeService
participant Cron as delayed synchronizeAccounts

A->>Sync: synchronize(accounts A)
Note over Sync: mutex held
B->>Sync: synchronize(accounts B)
alt B accounts already in A's run
Sync-->>B: skip
else B has other accounts
Sync->>Cron: schedule (~2s)
Note over Sync: mutex released later
Cron->>Sync: synchronize(accounts B)
end
```

## Related

| Use case | Doc |
| ------------------- | ------------------------------------------------------------------- |
| Cron gate | [cronjob.md](../../use-cases/cron-job/cronjob.md) |
| Sync accounts entry | [syncAccounts.md](../../use-cases/cron-job/syncAccounts.md) |
| Sync assets entry | [syncAssets.md](../../use-cases/cron-job/syncAssets.md) |
| Track submitted tx | [trackTransaction.md](../../use-cases/cron-job/trackTransaction.md) |
Loading
Loading