@morpho-dev/router 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -71
- package/dist/cli.js +3209 -1252
- package/dist/drizzle/migrations/0017_dusty_the_hunter.sql +1 -0
- package/dist/drizzle/migrations/0018_add_chain_collector_constraints.sql +3 -0
- package/dist/drizzle/migrations/0019_add-obligation-units-shares.sql +2 -0
- package/dist/drizzle/migrations/0020_add-session.sql +1 -0
- package/dist/drizzle/migrations/0021_drop_chain_collector_epoch_indexes.sql +2 -0
- package/dist/drizzle/migrations/0021_migrate-rate-to-price.sql +15 -0
- package/dist/drizzle/migrations/0022_consolidate-price.sql +15 -0
- package/dist/drizzle/migrations/meta/0017_snapshot.json +1525 -0
- package/dist/drizzle/migrations/meta/0018_snapshot.json +1572 -0
- package/dist/drizzle/migrations/meta/0019_snapshot.json +1586 -0
- package/dist/drizzle/migrations/meta/_journal.json +42 -0
- package/dist/evm/bytecode/erc20.txt +1 -0
- package/dist/evm/bytecode/factory.txt +1 -0
- package/dist/evm/bytecode/mempool.txt +1 -0
- package/dist/evm/bytecode/morpho.txt +1 -0
- package/dist/evm/bytecode/multicall3.txt +1 -0
- package/dist/evm/bytecode/oracle.txt +1 -0
- package/dist/evm/bytecode/terms.txt +1 -0
- package/dist/evm/bytecode/vault.txt +1 -0
- package/dist/evm/bytecode/vaultV1.txt +1 -0
- package/dist/index.browser.d.mts +1327 -816
- package/dist/index.browser.d.mts.map +1 -1
- package/dist/index.browser.d.ts +1358 -847
- package/dist/index.browser.d.ts.map +1 -1
- package/dist/index.browser.js +2209 -1668
- package/dist/index.browser.js.map +1 -1
- package/dist/index.browser.mjs +2173 -1632
- package/dist/index.browser.mjs.map +1 -1
- package/dist/index.node.d.mts +1885 -1222
- package/dist/index.node.d.mts.map +1 -1
- package/dist/index.node.d.ts +1885 -1222
- package/dist/index.node.d.ts.map +1 -1
- package/dist/index.node.js +1984 -1016
- package/dist/index.node.js.map +1 -1
- package/dist/index.node.mjs +1963 -1014
- package/dist/index.node.mjs.map +1 -1
- package/docs/integrator.md +78 -0
- package/package.json +11 -5
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Documentation
|
|
2
|
+
|
|
3
|
+
This document is intended for **technical integrators** and **product/design teams**. It explains how the router works end-to-end, how to integrate with it, and what the main user-facing implications are.
|
|
4
|
+
|
|
5
|
+
At a high level, the router indexes and exposes offers published to the on-chain mempool and computes the *takeable* amount for each offer. In practice, this means the router aims to surface offers with a takeable amount that can be executed without reverting at take time, minimizing execution failures for takers.
|
|
6
|
+
|
|
7
|
+
## Forge a supported offer
|
|
8
|
+
|
|
9
|
+
Not every offer published to the mempool will be indexed by the router. The router applies a **gatekeeper** that validates both **offer validity** (e.g., the offer is not expired) and the **supported format** (e.g., the callback type is supported by the router).
|
|
10
|
+
|
|
11
|
+
These checks serve the same goal: **minimize take-time failures**. Validity checks eliminate offers that are expected to revert on-chain. Supported-format checks further reduce failure risk by restricting the full set of protocol possibilities to a subset the router understands and can reason about, providing a more controlled integration surface and fewer unpredictable execution paths.
|
|
12
|
+
|
|
13
|
+
To run these checks and validate your offers, call **`POST /v1/validate`**. If validation fails, the response includes `data.issues` describing what needs to be fixed.
|
|
14
|
+
|
|
15
|
+
## Push offers to the mempool
|
|
16
|
+
|
|
17
|
+
Once your offer is valid, you must **publish it on-chain** to make it available to all mempool consumers. The router is one consumer of the mempool, but anyone can publish to it or listen to it.
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
POST /v1/validate → { payload, root } → sign(root) → publish(payload + sig)
|
|
21
|
+
{ offers: [...] }
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Offers are published as a *batch* using a Merkle tree so that a single signature can cover a whole group of offers. The Merkle **root** is a compact commitment to the full set: changing any offer changes the root, which makes the signature invalid. To keep calldata (and therefore gas costs) low, the offer set is embedded in the `payload` as a **gzipped** representation rather than being sent as plain JSON.
|
|
25
|
+
|
|
26
|
+
Use **`POST /v1/validate`** to obtain `payload` (the encoded representation of your offers, with the root already embedded) and `root` (the Merkle root to sign). To publish, append your signature to the payload: `data = payload + signature`.
|
|
27
|
+
|
|
28
|
+
## Router takeable computation
|
|
29
|
+
|
|
30
|
+
Publishing an offer to the mempool (even with a supported format) does **not** guarantee that it is currently takeable. A core responsibility of the router is to compute the **takeable** amount for each offer: the maximum amount that can be executed *without reverting*, given current state.
|
|
31
|
+
|
|
32
|
+
The router computes `takeable = min(availableLiquidity, offerRemaining)`.
|
|
33
|
+
|
|
34
|
+
`offerRemaining` is the remaining, unconsumed portion of the offer (i.e., the total offered amount minus what has already been taken by other takers).
|
|
35
|
+
|
|
36
|
+
`availableLiquidity` is the amount of liquidity the router can safely source for the offer at execution time. It is derived from the offer’s **callback**.
|
|
37
|
+
|
|
38
|
+
A **callback** is the on-chain "liquidity source" used during settlement: it tells the protocol *where* to pull assets from (e.g., the maker’s wallet, or a specific vault position) when the offer is filled. Callbacks are extremely flexible, but that flexibility also means the router cannot reliably compute takeability for arbitrary callback logic.
|
|
39
|
+
|
|
40
|
+
To keep takeability deterministic and indexable, the router only supports a **whitelist** of callback formats with well-defined semantics:
|
|
41
|
+
|
|
42
|
+
- **Buy with empty callback (`0x`)** (`BuyWithEmptyCallback`): source the buy-side liquidity from the maker’s ERC20 balance/allowance.
|
|
43
|
+
- **Buy with Vault V1 callback** (`BuyVaultV1Callback`): source the buy-side liquidity from specific Vault V1 positions listed in the callback data (and registered in the router’s allowed vault factories).
|
|
44
|
+
- **Sell with ERC20 callback** (`SellERC20Callback`): source the sell-side collateral from ERC20 collateral contracts listed in the callback data (must match the offer’s `collaterals`).
|
|
45
|
+
|
|
46
|
+
In practice, the router indexes the underlying source (wallet balances/allowances, Vault V1 positions, etc.) and caps the offer by what is actually available. If an offer uses a non-whitelisted callback address or an unsupported callback shape, it may still be valid on-chain, but it will be rejected by the router’s gatekeeper (see **`POST /v1/validate`**).
|
|
47
|
+
|
|
48
|
+
If `availableLiquidity = 0`, the offer is **not** treated as invalid. The offer remains indexed, but it is returned with `takeable = 0` (and therefore will not be executed by router consumers).
|
|
49
|
+
|
|
50
|
+
### Takeable by user intent
|
|
51
|
+
|
|
52
|
+
A "user intent" is the user-facing action an offer is meant to execute (e.g., enter supply, enter borrow, exit supply/withdraw, or exit borrow/repay). The router accounts for intent when computing takeable because each intent has different execution constraints and denominations (assets vs shares vs obligation units) and can rely on different sources of liquidity; as a result, the safe amount that can be taken without reverting must be capped using intent-specific rules.
|
|
53
|
+
|
|
54
|
+
#### Main: Enter supply
|
|
55
|
+
|
|
56
|
+
For *Enter supply*, the offer must be denominated in **assets**. The router decodes the callback to identify the liquidity source (the "position"), indexes the position balance, and caps the offer amount by the indexed balance.
|
|
57
|
+
|
|
58
|
+
#### Main: Enter borrow
|
|
59
|
+
|
|
60
|
+
For *Enter borrow*, the offer must be denominated in **assets**. The router evaluates takeable by inspecting the callback "position" (the same mechanism as **Enter supply**) and does **not** take Morpho V2 collateral or health into account when computing takeable; the callback is responsible for enforcing its own safety constraints. As a result, collateral already deposited on Morpho V2 does not increase borrow takeable. If a user wants to leverage more of their Morpho V2 collateral, they must withdraw that collateral and use it in a callback.
|
|
61
|
+
|
|
62
|
+
#### Secondary: exit supply (withdraw)
|
|
63
|
+
|
|
64
|
+
For *Exit supply (withdraw)*, the offer must be denominated in **shares**. The router checks the share balance on Morpho V2 and caps the offer amount by the share balance.
|
|
65
|
+
|
|
66
|
+
#### Secondary: exit borrow (repay)
|
|
67
|
+
|
|
68
|
+
For *Exit borrow (repay)*, the offer must be denominated in **obligation units**. The router checks the outstanding debt on Morpho V2.
|
|
69
|
+
|
|
70
|
+
### Overcommitting protection
|
|
71
|
+
|
|
72
|
+
When multiple offers rely on the same liquidity source, it is possible to **overcommit** liquidity (i.e., promise the same units of liquidity to multiple offers). Without protection, executing one offer can make another offer in the same batch invalid, causing **unpredictable batch failures**. The router’s goal is to make taker execution as reliable as possible, especially for **batched** execution (multicalls).
|
|
73
|
+
|
|
74
|
+
The router enforces a simple rule: **liquidity promised by a group of offers is reserved and cannot be promised twice**. For example, if a Vault V1 position has 80 units available and Offer A reserves 50 units, then a second offer attempting to reserve 50 units from the same position will only see 20 units as available and will be capped accordingly.
|
|
75
|
+
|
|
76
|
+
The same concept applies to exit intents (shares and debt): the router prevents overcommitting those balances as well.
|
|
77
|
+
|
|
78
|
+
To get the amount already reserved on a position you can query the user’s positions via `/v1/users/[:userAddress]/positions`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@morpho-dev/router",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "Router package for Morpho protocol",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"url": "git@github.com:morpho-org/router.git"
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
|
-
"dist/*"
|
|
16
|
+
"dist/*",
|
|
17
|
+
"docs/*"
|
|
17
18
|
],
|
|
18
19
|
"main": "./dist/index.node.js",
|
|
19
20
|
"module": "./dist/index.node.mjs",
|
|
@@ -66,11 +67,13 @@
|
|
|
66
67
|
"drizzle-orm": "^0.43.1",
|
|
67
68
|
"hono": "^4.9.7",
|
|
68
69
|
"js-base64": "^3.7.8",
|
|
70
|
+
"marked": "^17.0.1",
|
|
69
71
|
"openapi-fetch": "^0.15.0",
|
|
70
72
|
"openapi-metadata": "^0.2.2",
|
|
71
73
|
"pako": "^2.1.0",
|
|
72
74
|
"pg": "^8.16.0",
|
|
73
75
|
"reflect-metadata": "^0.2.2",
|
|
76
|
+
"smol-toml": "^1.6.0",
|
|
74
77
|
"viem": "^2.37.13",
|
|
75
78
|
"zod": "^4.1.12",
|
|
76
79
|
"zod-openapi": "^5.4.3"
|
|
@@ -88,11 +91,14 @@
|
|
|
88
91
|
"lint:ci": "biome ci src/",
|
|
89
92
|
"migrations:create": "drizzle-kit generate --config src/database/drizzle/drizzle.config.ts --name",
|
|
90
93
|
"migrations:run": "drizzle-kit migrate --config src/database/drizzle/drizzle.config.ts",
|
|
91
|
-
"openapi:generate": "tsx src/cli/dump-swagger.ts && pnpm openapi-typescript src/api/Schema/generated/swagger.json -o src/api/Schema/generated/swagger.d.ts && biome check --write src/api/Schema/generated",
|
|
92
|
-
"test": "vitest",
|
|
93
|
-
"test:
|
|
94
|
+
"openapi:generate": "node --import tsx src/cli/dump-swagger.ts && pnpm openapi-typescript src/api/Schema/generated/swagger.json -o src/api/Schema/generated/swagger.d.ts && biome check --write src/api/Schema/generated",
|
|
95
|
+
"test": "vitest run",
|
|
96
|
+
"test:e2e": "./scripts/e2e-run.sh",
|
|
97
|
+
"test:tenderly": "vitest --config vitest.tenderly.config.ts run",
|
|
94
98
|
"test:ui": "vitest --ui",
|
|
95
99
|
"test:watch": "vitest watch",
|
|
100
|
+
"e2e:setup": "./scripts/e2e-setup.sh",
|
|
101
|
+
"e2e:teardown": "./scripts/e2e-teardown.sh",
|
|
96
102
|
"typecheck": "pnpm openapi:generate && tsc --project tsconfig.json --noEmit --incremental"
|
|
97
103
|
}
|
|
98
104
|
}
|