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
8 changes: 8 additions & 0 deletions .changeset/tidy-pandas-gather.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
"@domain/entity-aggregated-asset": minor
"@domain/entity-interest-rate": minor
"@domain/api-aggregated-assets": minor
"@features/platform-aggregated-assets": minor
---

Scaffold the DDD packages that will receive the dada-client code: the aggregated-asset and interest-rate entities, the aggregated-assets API client and its app-facing platform layer
67 changes: 67 additions & 0 deletions domain/api/aggregated-assets/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# @domain/api-aggregated-assets

> [!CAUTION]
> **Status: UNSTABLE** — Being migrated out of `live-common`; the API is still being assembled.

Domain API client for **DADA** — Dynamic Assets Data Aggregator — the service that returns
aggregated crypto asset metadata, market data and interest rates.

One DADA response carries six collections in a single payload:

| Collection | Owned by |
| --- | --- |
| `cryptoAssets` | `@domain/entity-aggregated-asset` |
| `interestRates` | `@domain/entity-interest-rate` |
| `cryptoOrTokenCurrencies` | `@domain/entity-currency` (via `@domain/api-currency-token`) |
| `networks` | **this package**, as a wire type — resolves to `@domain/entity-currency-crypto`, since a network *is* a chain |
| `currenciesOrder` | **this package**, as response metadata — a sort key and ordered ids, not a business object |
| `markets` | nobody yet — see below |

This package owns the cross-entity response contract, the requests, the transformations and the
RTK Query endpoints. It does not own UI or app composition.

## Why the wire types live here

`ApiAsset`, `ApiTokenCurrency`, `ApiCryptoCurrency` and `RawApiResponse` describe DADA's
**transport format**, so they belong to the API package. The entity packages own only canonical
shapes. `AssetsData` — the transformed aggregate — is this package's return contract, composed of
those entities.

This mirrors `domain/api/market-sentiment` + `domain/entity/market-sentiment`, where the entity
owns the canonical schema and the API owns the wire schema plus `transforms.ts`.

## `markets` is deliberately loosely typed

There is no `domain/entity/market`, and the `markets` collection keeps its existing type,
`PartialMarketItemResponse` = `Partial<MarketItemResponse>`.

Creating a real market entity would roughly double the migration and pull in a large surface owned
by another team. Because every field of a `Partial<>` is optional there is also nothing meaningful
to validate, so `markets` is excluded from the response-validation work.

### Accepted debt

`dadaIdToMarketId()` and the market item type are **copied** from
`libs/ledger-live-common/src/market`, not imported: a `domain/*` package must not import legacy
`libs/*`. Since every field is optional, future divergence from the original will **never** produce
a type error. Revisit when a real market entity exists.

## Invariants owned here

Both are load-bearing and neither can produce a type error, so do not "clean them up":

1. **`convertApiAssets` is lenient by design.** It silently drops tokens whose parent chain is
unknown, and *synthesises* a currency (placeholder `color`, empty `explorerViews`) for cryptos
missing from the local registry rather than dropping them. Assets DADA knows about but the local
CAL does not still render.
2. **`getChunkedAssetsData` succeeds if _any_ chunk resolves.** Requests are chunked by currency id;
partial results are returned rather than failing the whole query.

When response validation is added, it must use per-item `safeParse`-and-drop, never a top-level
`parse()`. DADA is a live aggregator whose shape evolves, and one unmodelled field failing the whole
response would blank out Market, Portfolio and the asset selector.

## Status

Scaffolded and empty. Code arrives from `libs/ledger-live-common/src/dada-client` in `LIVE-35226`.
Tracking epic: `LIVE-35223`.
20 changes: 20 additions & 0 deletions domain/api/aggregated-assets/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module.exports = {
testEnvironment: "node",
roots: ["<rootDir>/src"],
testMatch: ["**/*.test.ts"],
transform: {
"^.+\\.(t|j)sx?$": [
"@swc/jest",
{
jsc: {
target: "esnext",
},
},
],
},
coverageReporters: ["json", ["lcov", { file: "lcov.info", projectRoot: "../../../" }], "text"],
reporters: [
"default",
["jest-sonar", { outputName: "sonar-executionTests-report.xml", reportedFilePath: "absolute" }],
],
};
29 changes: 29 additions & 0 deletions domain/api/aggregated-assets/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"name": "@domain/api-aggregated-assets",
"version": "0.1.0",
"private": true,
"description": "Domain API client for DADA (Dynamic Assets Data Aggregator): wire contract, RTK Query endpoints and response transforms",
"main": "src/index.ts",
"types": "src/index.ts",
"exports": {
".": "./src/index.ts",
"./package.json": "./package.json"
},
"sideEffects": false,
"scripts": {
"typecheck": "tsc --noEmit",
"test": "jest --passWithNoTests",
"test:watch": "jest --watch",
"coverage": "jest --coverage --passWithNoTests",
"unimported": "pnpm knip --directory ../../.. -W domain/api/aggregated-assets"
},
"devDependencies": {
"@jest/globals": "catalog:",
"@swc/core": "catalog:",
"@swc/jest": "catalog:",
"@types/jest": "catalog:",
"@types/node": "catalog:",
"jest": "catalog:",
"typescript": "catalog:"
}
}
8 changes: 8 additions & 0 deletions domain/api/aggregated-assets/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "@domain/api-aggregated-assets",
"targets": {
"build": {
"executor": "nx:noop"
}
}
}
1 change: 1 addition & 0 deletions domain/api/aggregated-assets/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
13 changes: 13 additions & 0 deletions domain/api/aggregated-assets/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"lib": ["ES2022"],
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"noEmit": true,
"types": ["jest"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "lib"]
}
31 changes: 31 additions & 0 deletions domain/entity/aggregated-asset/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# @domain/entity-aggregated-asset

> [!CAUTION]
> **Status: UNSTABLE** — Being migrated out of `live-common`; the API is still being assembled.

Domain entity for the **aggregated asset** — a meta-currency that groups several per-network
currencies under one logical asset.

| Type | Describes |
| --- | --- |
| `CryptoAssetMeta` | The aggregated asset: id, ticker, name, and `assetsIds` mapping each network to its currency id |

This resolves more than the lower-level currency / token / fiat types, which is what earns it its
own entity: it is grouped by network rather than being a single currency.

Deliberately **not** here:

| Type | Lives in | Why |
| --- | --- | --- |
| `NetworkInfo` | `@domain/api-aggregated-assets` as a wire type | `{ id, name }`; a network *is* a chain, already modelled by `@domain/entity-currency-crypto`. Resolve to that rather than duplicating the concept. |
| `CurrenciesOrder` | `@domain/api-aggregated-assets` response contract | `{ key, order, metaCurrencyIds }` is server sort metadata, not a business object. |

Pure entity package: runtime schemas, inferred types, defaults and mocks. **No network calls, no
feature state.** Requests, transformations and RTK Query endpoints belong to
`@domain/api-aggregated-assets`; the wire-format types belong there too, since they describe DADA's
transport shape rather than a canonical one.

## Status

Scaffolded and empty. Types arrive from `libs/ledger-live-common/src/dada-client/entities` in
`LIVE-35226`. Tracking epic: `LIVE-35223`.
20 changes: 20 additions & 0 deletions domain/entity/aggregated-asset/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module.exports = {
testEnvironment: "node",
roots: ["<rootDir>/src"],
testMatch: ["**/*.test.ts"],
transform: {
"^.+\\.(t|j)sx?$": [
"@swc/jest",
{
jsc: {
target: "esnext",
},
},
],
},
coverageReporters: ["json", ["lcov", { file: "lcov.info", projectRoot: "../../../" }], "text"],
reporters: [
"default",
["jest-sonar", { outputName: "sonar-executionTests-report.xml", reportedFilePath: "absolute" }],
],
};
28 changes: 28 additions & 0 deletions domain/entity/aggregated-asset/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "@domain/entity-aggregated-asset",
"version": "0.1.0",
"private": true,
"description": "Domain entity for aggregated assets: crypto-asset metadata, network info and currency ordering",
"main": "src/index.ts",
"types": "src/index.ts",
"exports": {
".": "./src/index.ts",
"./package.json": "./package.json"
},
"sideEffects": false,
"scripts": {
"typecheck": "tsc --noEmit",
"test": "jest --passWithNoTests",
"test:watch": "jest --watch",
"coverage": "jest --coverage --passWithNoTests",
"unimported": "pnpm knip --directory ../../.. -W domain/entity/aggregated-asset"
},
"devDependencies": {
"@jest/globals": "catalog:",
"@swc/core": "catalog:",
"@swc/jest": "catalog:",
"@types/jest": "catalog:",
"jest": "catalog:",
"typescript": "catalog:"
}
}
8 changes: 8 additions & 0 deletions domain/entity/aggregated-asset/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "@domain/entity-aggregated-asset",
"targets": {
"build": {
"executor": "nx:noop"
}
}
}
1 change: 1 addition & 0 deletions domain/entity/aggregated-asset/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
13 changes: 13 additions & 0 deletions domain/entity/aggregated-asset/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"lib": ["ES2022"],
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"noEmit": true,
"types": ["jest"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "lib"]
}
24 changes: 24 additions & 0 deletions domain/entity/interest-rate/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# @domain/entity-interest-rate

> [!CAUTION]
> **Status: UNSTABLE** — Being migrated out of `live-common`; the API is still being assembled.

Domain entity for asset interest rates. Owns the canonical rate shape and the set of rate types the
apps understand:

| Type | Describes |
| --- | --- |
| `InterestRate` | A rate for one currency: `currencyId`, `rate`, `type` and `fetchAt` |
| `ApyType` | The recognised rate kinds — `NRR`, `APY`, `APR` |

`ApyType` is an allowlist, and it is enforced at the consumer boundary: a rate arriving with any
other `type` is dropped rather than displayed. That filtering behaviour is characterized in the
consuming hook's tests.

Pure entity package: runtime schemas, inferred types, defaults and mocks. **No network calls, no
feature state.**

## Status

Scaffolded and empty. Types arrive from `libs/ledger-live-common/src/dada-client` in `LIVE-35226`.
Tracking epic: `LIVE-35223`.
20 changes: 20 additions & 0 deletions domain/entity/interest-rate/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module.exports = {
testEnvironment: "node",
roots: ["<rootDir>/src"],
testMatch: ["**/*.test.ts"],
transform: {
"^.+\\.(t|j)sx?$": [
"@swc/jest",
{
jsc: {
target: "esnext",
},
},
],
},
coverageReporters: ["json", ["lcov", { file: "lcov.info", projectRoot: "../../../" }], "text"],
reporters: [
"default",
["jest-sonar", { outputName: "sonar-executionTests-report.xml", reportedFilePath: "absolute" }],
],
};
28 changes: 28 additions & 0 deletions domain/entity/interest-rate/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"name": "@domain/entity-interest-rate",
"version": "0.1.0",
"private": true,
"description": "Domain entity for asset interest rates: the canonical rate shape and its APY type",
"main": "src/index.ts",
"types": "src/index.ts",
"exports": {
".": "./src/index.ts",
"./package.json": "./package.json"
},
"sideEffects": false,
"scripts": {
"typecheck": "tsc --noEmit",
"test": "jest --passWithNoTests",
"test:watch": "jest --watch",
"coverage": "jest --coverage --passWithNoTests",
"unimported": "pnpm knip --directory ../../.. -W domain/entity/interest-rate"
},
"devDependencies": {
"@jest/globals": "catalog:",
"@swc/core": "catalog:",
"@swc/jest": "catalog:",
"@types/jest": "catalog:",
"jest": "catalog:",
"typescript": "catalog:"
}
}
8 changes: 8 additions & 0 deletions domain/entity/interest-rate/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "@domain/entity-interest-rate",
"targets": {
"build": {
"executor": "nx:noop"
}
}
}
1 change: 1 addition & 0 deletions domain/entity/interest-rate/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export {};
13 changes: 13 additions & 0 deletions domain/entity/interest-rate/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"extends": "../../../tsconfig.base.json",
"compilerOptions": {
"lib": ["ES2022"],
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"noEmit": true,
"types": ["jest"]
},
"include": ["src/**/*"],
"exclude": ["node_modules", "lib"]
}
Loading
Loading