@logswap/sdk 0.5.55 → 0.5.57
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 +2 -75
- package/dist/actions.d.ts +1 -1
- package/dist/actions.js +1 -1
- package/dist/admin.d.ts +7 -7
- package/dist/admin.js +7 -7
- package/dist/claims.js +15 -34
- package/dist/client.d.ts +13 -6
- package/dist/client.js +74 -17
- package/dist/fpool.d.ts +53 -53
- package/dist/fpool.js +51 -51
- package/dist/keys.d.ts +1 -1
- package/dist/liquidity.d.ts +7 -7
- package/dist/liquidity.js +6 -6
- package/dist/onboard.d.ts +11 -0
- package/dist/onboard.js +23 -9
- package/dist/pools.d.ts +2 -2
- package/dist/pools.js +1 -1
- package/dist/write.d.ts +13 -1
- package/dist/write.js +26 -1
- package/package.json +3 -2
- package/src/actions.ts +1 -1
- package/src/admin.ts +8 -8
- package/src/claims.ts +14 -36
- package/src/client.ts +78 -19
- package/src/fpool.ts +68 -68
- package/src/keys.ts +1 -1
- package/src/liquidity.ts +8 -8
- package/src/onboard.ts +25 -8
- package/src/pools.ts +2 -2
- package/src/write.ts +27 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# logswap-sdk
|
|
2
2
|
|
|
3
|
-
TypeScript SDK for [Logswap](https://
|
|
3
|
+
TypeScript SDK for [Logswap](https://logswap.org), the liquidity
|
|
4
4
|
primitive introducing the **logarithmic invariant** — typed wrappers over the `CPoolManager` /
|
|
5
5
|
`FPoolManager` / `LogswapRouter` / `LogswapLens` surface.
|
|
6
6
|
|
|
@@ -38,48 +38,6 @@ Both are computed **offline** — no RPC, no deployment needed to address a mark
|
|
|
38
38
|
`fPoolShareBalance`, `fPoolShareValue`; `previewZapIn` reads the **lens** (the router's F flows
|
|
39
39
|
live in a facet, its previews moved to the lens).
|
|
40
40
|
|
|
41
|
-
## The derivation gate
|
|
42
|
-
|
|
43
|
-
Both derivations are **tier-1 frozen** (`docs/app.md`): `poolId` is the primary key of the entire
|
|
44
|
-
read model, and the position id is `(1 << 255) | (keccak256(poolId ‖ packed) >> 1)`, baked into every
|
|
45
|
-
client. If the TypeScript and the Solidity disagree, a client addresses the wrong pool or the wrong
|
|
46
|
-
position — silently, with funds behind it.
|
|
47
|
-
|
|
48
|
-
So `test/derivation.test.ts` checks against **vectors generated by the contract**, not by hand:
|
|
49
|
-
|
|
50
|
-
```bash
|
|
51
|
-
npm run vectors # regenerate from the pinned contracts
|
|
52
|
-
npm test # 45 vectors × poolId, positionId, pack/unpack round-trip
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
That makes it a genuine cross-implementation check. It is also verified non-vacuous: changing the
|
|
56
|
-
id derivation's shift by one bit fails all 45 position-id vectors.
|
|
57
|
-
|
|
58
|
-
A diff in `vectors/derivation-vectors.json` during an ordinary change means something is wrong — by
|
|
59
|
-
definition, a change there is a tier-1 event.
|
|
60
|
-
|
|
61
|
-
## The pipeline
|
|
62
|
-
|
|
63
|
-
```
|
|
64
|
-
logswap (forge) ──► contracts/abi/*.json ──► wagmi codegen ──► src/generated.ts
|
|
65
|
-
▲ │
|
|
66
|
-
contracts.ref (pinned SHA) the SDK wrappers
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
Nothing is ever hand-copied. `contracts.ref` pins the SHA; `npm run codegen` clones it, builds, and
|
|
70
|
-
regenerates; `tsc` is the drift gate, so a contract change that breaks the SDK is a red build rather
|
|
71
|
-
than a runtime surprise.
|
|
72
|
-
|
|
73
|
-
```bash
|
|
74
|
-
npm run codegen # sync the pinned contracts, forge build, wagmi generate -> src/generated.ts
|
|
75
|
-
npm run typecheck # consumes generated.ts; run codegen first on a fresh checkout
|
|
76
|
-
npm run build
|
|
77
|
-
npm test
|
|
78
|
-
```
|
|
79
|
-
|
|
80
|
-
`typecheck` and `build` both *consume* `src/generated.ts` and neither produces it, so codegen
|
|
81
|
-
runs once and explicitly rather than being chained into both.
|
|
82
|
-
|
|
83
41
|
## Conventions
|
|
84
42
|
|
|
85
43
|
- **The SDK hides the manager/router/lens split.** Which contract hosts which action is currently
|
|
@@ -97,38 +55,7 @@ runs once and explicitly rather than being chained into both.
|
|
|
97
55
|
npm i @logswap/sdk viem
|
|
98
56
|
```
|
|
99
57
|
|
|
100
|
-
Published as **`@logswap/sdk`** from v0.5.35
|
|
101
|
-
`@unimodular/logswap-sdk`, which is deprecated and points here — the code is identical, only the
|
|
102
|
-
name moved, along with the repos to the `logswap` org.
|
|
58
|
+
Published as **`@logswap/sdk`** from v0.5.35; earlier versions used another name.
|
|
103
59
|
|
|
104
60
|
`viem` is a peer dependency. The package ships `dist/` plus `src/`, including the generated ABIs, so
|
|
105
61
|
the contract surface it was built against travels with it.
|
|
106
|
-
|
|
107
|
-
## Release
|
|
108
|
-
|
|
109
|
-
Push-driven, and there is **no npm token anywhere**: publishing uses OIDC trusted publishing, so npm
|
|
110
|
-
verifies a GitHub-issued token per run and there is no long-lived credential to rotate or leak.
|
|
111
|
-
|
|
112
|
-
```
|
|
113
|
-
logswap-contract push
|
|
114
|
-
→ Update SDK contracts pin (GitHub App token, cross-repo)
|
|
115
|
-
→ commit contracts.ref here
|
|
116
|
-
→ Release on contracts pin change
|
|
117
|
-
→ codegen at the pinned SHA → build → test → version bump → npm publish
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
`workflow_dispatch` covers SDK-only fixes with no contracts change: same pipeline, manual trigger.
|
|
121
|
-
The version bump touches `package.json` only, not `contracts.ref`, so it cannot re-trigger itself.
|
|
122
|
-
|
|
123
|
-
Two things that are easy to get wrong:
|
|
124
|
-
|
|
125
|
-
- **`codegen` must run before `typecheck`.** `src/generated.ts` is gitignored, so it exists on a
|
|
126
|
-
developer machine and never in CI — a `typecheck`-first pipeline fails only in CI, which is where
|
|
127
|
-
it is least convenient to debug.
|
|
128
|
-
- **No provenance since 0.5.55.** The repository went private (2026-09-24), and npm generates no
|
|
129
|
-
provenance from a private source repo; every release after 0.1.0 up to 0.5.54 carries it.
|
|
130
|
-
Trusted publishing is unaffected.
|
|
131
|
-
Put `--provenance` back in `release.yml` if the repo is made public again.
|
|
132
|
-
- **`0.1.0` has no provenance attestation either.** Trusted publishing cannot be
|
|
133
|
-
configured for a package that does not exist yet, so the first publish is necessarily manual.
|
|
134
|
-
`prepublishOnly` runs the build, so a hand-run publish cannot ship a stale `dist/`.
|
package/dist/actions.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* actions.ts — the router's transaction: a BATCH
|
|
2
|
+
* actions.ts — the router's transaction: a BATCH.
|
|
3
3
|
*
|
|
4
4
|
* `execute(actions, settle)` runs a list of actions — mint, burn, update, claim, swap, path, in any
|
|
5
5
|
* order, across any C markets — under ONE manager lock. Each action accrues deltas on the router
|
package/dist/actions.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* actions.ts — the router's transaction: a BATCH
|
|
2
|
+
* actions.ts — the router's transaction: a BATCH.
|
|
3
3
|
*
|
|
4
4
|
* `execute(actions, settle)` runs a list of actions — mint, burn, update, claim, swap, path, in any
|
|
5
5
|
* order, across any C markets — under ONE manager lock. Each action accrues deltas on the router
|
package/dist/admin.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* admin — the protocol's writes: the C manager's cut of the mint fee (the F manager's is a
|
|
3
|
-
* constant
|
|
3
|
+
* constant), the policies a pool may be born with (the registry's), the
|
|
4
4
|
* policies' own numbers, and the two-step collector handover. Plain writes to the managers,
|
|
5
5
|
* the registry and the policies; no router.
|
|
6
6
|
*
|
|
7
|
-
* One vocabulary
|
|
7
|
+
* One vocabulary: both managers call the protocol's cut of the mint fee
|
|
8
8
|
* `MINT_FEE_PROTOCOL_CUT`; C sets it through `setFeeParams(uint256)`, F holds it as a constant
|
|
9
|
-
* (049). The harvest's cut is the policy's answer at every harvest
|
|
9
|
+
* (049). The harvest's cut is the policy's answer at every harvest, set on the
|
|
10
10
|
* policy (`setHarvestCut`) and capped by the manager (`HARVEST_CUT_MAX`); the release's
|
|
11
11
|
* protocol share likewise (`POT_CUT_MAX`).
|
|
12
12
|
*/
|
|
@@ -15,7 +15,7 @@ import type { LogswapClient } from "./client.js";
|
|
|
15
15
|
import { type FPoolKind } from "./fpool.js";
|
|
16
16
|
/** The C manager's `setFeeParams(mintFeeProtocolCut)` — one cut. */
|
|
17
17
|
export declare function setFeeParamsC(c: LogswapClient, mintFeeProtocolCutWad: bigint): Promise<Hash>;
|
|
18
|
-
/** The F manager's `MINT_FEE_PROTOCOL_CUT` — a constant since F version 18
|
|
18
|
+
/** The F manager's `MINT_FEE_PROTOCOL_CUT` — a constant since F version 18; there is no setter. */
|
|
19
19
|
export declare function fMintFeeProtocolCut(c: LogswapClient): Promise<bigint>;
|
|
20
20
|
/**
|
|
21
21
|
* The policy registry the F manager asks at every birth — `addresses.policyRegistry` when the
|
|
@@ -28,7 +28,7 @@ export declare function policyApproved(c: LogswapClient, policy: Address): Promi
|
|
|
28
28
|
/** One policy on the registry's list, with what it says it is. */
|
|
29
29
|
export interface RegistryEntry {
|
|
30
30
|
policy: Address;
|
|
31
|
-
/** The policy's own `kind()` tag
|
|
31
|
+
/** The policy's own `kind()` tag — what a pool born under it is. */
|
|
32
32
|
kind: FPoolKind;
|
|
33
33
|
name: string;
|
|
34
34
|
/** The block it closes to new pools from; 0 = open. */
|
|
@@ -37,7 +37,7 @@ export interface RegistryEntry {
|
|
|
37
37
|
approved: boolean;
|
|
38
38
|
}
|
|
39
39
|
/**
|
|
40
|
-
* Every policy listed, sunset ones included — one flat list
|
|
40
|
+
* Every policy listed, sunset ones included — one flat list, each with the kind
|
|
41
41
|
* it says it is; `kind` narrows it to one, which is what a create form's dropdown reads. Empty
|
|
42
42
|
* with no registry.
|
|
43
43
|
*/
|
|
@@ -62,7 +62,7 @@ export declare function registryAcceptOwner(c: LogswapClient): Promise<Hash>;
|
|
|
62
62
|
*/
|
|
63
63
|
export declare function setPolicyApproved(c: LogswapClient, policy: Address, approved: boolean): Promise<Hash>;
|
|
64
64
|
/**
|
|
65
|
-
* Move a pool to another approved policy of the same kind
|
|
65
|
+
* Move a pool to another approved policy of the same kind: the registry
|
|
66
66
|
* owner's alone, orchestrated on the registry — `migrate(manager, poolId, to)`. The new policy
|
|
67
67
|
* takes the authority and the name from the old; the operator and the lists void; no swap lands
|
|
68
68
|
* in the block.
|
package/dist/admin.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* admin — the protocol's writes: the C manager's cut of the mint fee (the F manager's is a
|
|
3
|
-
* constant
|
|
3
|
+
* constant), the policies a pool may be born with (the registry's), the
|
|
4
4
|
* policies' own numbers, and the two-step collector handover. Plain writes to the managers,
|
|
5
5
|
* the registry and the policies; no router.
|
|
6
6
|
*
|
|
7
|
-
* One vocabulary
|
|
7
|
+
* One vocabulary: both managers call the protocol's cut of the mint fee
|
|
8
8
|
* `MINT_FEE_PROTOCOL_CUT`; C sets it through `setFeeParams(uint256)`, F holds it as a constant
|
|
9
|
-
* (049). The harvest's cut is the policy's answer at every harvest
|
|
9
|
+
* (049). The harvest's cut is the policy's answer at every harvest, set on the
|
|
10
10
|
* policy (`setHarvestCut`) and capped by the manager (`HARVEST_CUT_MAX`); the release's
|
|
11
11
|
* protocol share likewise (`POT_CUT_MAX`).
|
|
12
12
|
*/
|
|
@@ -24,11 +24,11 @@ function write(c, address, abi, functionName, args) {
|
|
|
24
24
|
export async function setFeeParamsC(c, mintFeeProtocolCutWad) {
|
|
25
25
|
return write(c, c.addresses.cPoolManager, cPoolManagerAbi, "setFeeParams", [mintFeeProtocolCutWad]);
|
|
26
26
|
}
|
|
27
|
-
/** The F manager's `MINT_FEE_PROTOCOL_CUT` — a constant since F version 18
|
|
27
|
+
/** The F manager's `MINT_FEE_PROTOCOL_CUT` — a constant since F version 18; there is no setter. */
|
|
28
28
|
export async function fMintFeeProtocolCut(c) {
|
|
29
29
|
return c.public.readContract({ address: c.addresses.fPoolManager, abi: fPoolManagerAbi, functionName: "MINT_FEE_PROTOCOL_CUT" });
|
|
30
30
|
}
|
|
31
|
-
// ─── the registry
|
|
31
|
+
// ─── the registry: the LOG authority's, not the collector's ─────────────────
|
|
32
32
|
const ZERO = "0x0000000000000000000000000000000000000000";
|
|
33
33
|
/**
|
|
34
34
|
* The policy registry the F manager asks at every birth — `addresses.policyRegistry` when the
|
|
@@ -48,7 +48,7 @@ export async function policyApproved(c, policy) {
|
|
|
48
48
|
return c.public.readContract({ address: registry, abi: policyRegistryAbi, functionName: "isApproved", args: [policy] });
|
|
49
49
|
}
|
|
50
50
|
/**
|
|
51
|
-
* Every policy listed, sunset ones included — one flat list
|
|
51
|
+
* Every policy listed, sunset ones included — one flat list, each with the kind
|
|
52
52
|
* it says it is; `kind` narrows it to one, which is what a create form's dropdown reads. Empty
|
|
53
53
|
* with no registry.
|
|
54
54
|
*/
|
|
@@ -111,7 +111,7 @@ export async function setPolicyApproved(c, policy, approved) {
|
|
|
111
111
|
return approved ? registryApprove(c, policy, await fPoolKindOfPolicy(c, policy)) : registryRevoke(c, policy);
|
|
112
112
|
}
|
|
113
113
|
/**
|
|
114
|
-
* Move a pool to another approved policy of the same kind
|
|
114
|
+
* Move a pool to another approved policy of the same kind: the registry
|
|
115
115
|
* owner's alone, orchestrated on the registry — `migrate(manager, poolId, to)`. The new policy
|
|
116
116
|
* takes the authority and the name from the old; the operator and the lists void; no swap lands
|
|
117
117
|
* in the block.
|
package/dist/claims.js
CHANGED
|
@@ -59,40 +59,21 @@ export async function isRouterApproved(c, owner) {
|
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
61
|
export async function checkReadiness(c, key, owner, need = {}) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
]
|
|
74
|
-
const [operator, baseAllow, quoteAllow] = await Promise.all([
|
|
62
|
+
// A token is ready when BOTH legs of its pull path are: the ERC-20 allowance to whoever pulls
|
|
63
|
+
// (Permit2 when wired) and, with Permit2, its unexpired allowance to the router. This used to
|
|
64
|
+
// read the ERC-20 allowance to the ROUTER, which on a Permit2 deployment is never granted — so
|
|
65
|
+
// it said "approve" forever, and approving the router changed nothing.
|
|
66
|
+
const { checkTokenReadiness } = await import("./onboard.js");
|
|
67
|
+
const ready = async (token, amount) => {
|
|
68
|
+
if (amount === undefined)
|
|
69
|
+
return true;
|
|
70
|
+
const r = await checkTokenReadiness(c, token, owner, amount);
|
|
71
|
+
return !r.needsTokenApproval && !r.needsPermit2Approval;
|
|
72
|
+
};
|
|
73
|
+
const [operator, baseOk, quoteOk] = await Promise.all([
|
|
75
74
|
need.positions ? isRouterApproved(c, owner) : Promise.resolve(true),
|
|
76
|
-
need.base
|
|
77
|
-
|
|
78
|
-
address: key.base,
|
|
79
|
-
abi: erc20,
|
|
80
|
-
functionName: "allowance",
|
|
81
|
-
args: [owner, c.addresses.router],
|
|
82
|
-
})
|
|
83
|
-
: Promise.resolve(0n),
|
|
84
|
-
need.quote
|
|
85
|
-
? c.public.readContract({
|
|
86
|
-
address: key.quote,
|
|
87
|
-
abi: erc20,
|
|
88
|
-
functionName: "allowance",
|
|
89
|
-
args: [owner, c.addresses.router],
|
|
90
|
-
})
|
|
91
|
-
: Promise.resolve(0n),
|
|
75
|
+
ready(key.base, need.base),
|
|
76
|
+
ready(key.quote, need.quote),
|
|
92
77
|
]);
|
|
93
|
-
return {
|
|
94
|
-
needsOperator: !operator,
|
|
95
|
-
needsBaseAllowance: need.base !== undefined && baseAllow < need.base,
|
|
96
|
-
needsQuoteAllowance: need.quote !== undefined && quoteAllow < need.quote,
|
|
97
|
-
};
|
|
78
|
+
return { needsOperator: !operator, needsBaseAllowance: !baseOk, needsQuoteAllowance: !quoteOk };
|
|
98
79
|
}
|
package/dist/client.d.ts
CHANGED
|
@@ -22,7 +22,7 @@ export interface LogswapAddresses {
|
|
|
22
22
|
*/
|
|
23
23
|
fPoolManager?: Address;
|
|
24
24
|
/**
|
|
25
|
-
* The protocol's four F policies, one per kind
|
|
25
|
+
* The protocol's four F policies, one per kind: every F pool names one in its key,
|
|
26
26
|
* and the pool's roles, gates and terms live on it. A deployment predating them carries none.
|
|
27
27
|
*/
|
|
28
28
|
publicPolicy?: Address;
|
|
@@ -30,7 +30,7 @@ export interface LogswapAddresses {
|
|
|
30
30
|
polPolicy?: Address;
|
|
31
31
|
padPolicy?: Address;
|
|
32
32
|
/**
|
|
33
|
-
* The policy registry
|
|
33
|
+
* The policy registry: what a pool of each kind may be born under, and the
|
|
34
34
|
* owner that may move a pool between policies. Read from the F manager (`registry()`) when
|
|
35
35
|
* absent; a deployment with none asks nobody.
|
|
36
36
|
*/
|
|
@@ -62,18 +62,18 @@ export declare function createLogswapClient(args: {
|
|
|
62
62
|
* manager sailed through `assertVersion` while `FEE_MAX()` still answered on it. Move this in the
|
|
63
63
|
* same change as `CParams.VERSION`, never after it.
|
|
64
64
|
*
|
|
65
|
-
* **3** since 2026-09-03
|
|
65
|
+
* **3** since 2026-09-03: `burnById` and `update` take an `owner`, `update`
|
|
66
66
|
* mints to `to`, `positions()` returns `aps` as uint128 — and a returned quote flow now carries the
|
|
67
|
-
* swept fees
|
|
67
|
+
* swept fees, which is why `exit`'s settle-to-one-asset math changed with it.
|
|
68
68
|
*
|
|
69
|
-
* **4** since 2026-09-18
|
|
69
|
+
* **4** since 2026-09-18: `PoolKey` is `(base, quote, tickSpacing, phi)` —
|
|
70
70
|
* the variance kernel and its two key fields are gone, so every pool id changed; `phiEff` is
|
|
71
71
|
* `phiOf`; `getPool` lost `sigma2Ema`; `Initialize` lost two fields; the cut is
|
|
72
72
|
* `MINT_FEE_PROTOCOL_CUT` / `setFeeParams`.
|
|
73
73
|
*/
|
|
74
74
|
export declare const EXPECTED_VERSION = 4n;
|
|
75
75
|
/**
|
|
76
|
-
* The F manager's own tag (
|
|
76
|
+
* The F manager's own tag (5 — the key's salt, pool names, the sponsor registry;
|
|
77
77
|
* 4 was the income-only harvest cut).
|
|
78
78
|
*
|
|
79
79
|
* A FLOOR, not an equality — unlike `EXPECTED_VERSION` above. The F primitive is iterating fast and
|
|
@@ -85,3 +85,10 @@ export declare const EXPECTED_VERSION = 4n;
|
|
|
85
85
|
*/
|
|
86
86
|
export declare const EXPECTED_F_VERSION = 18n;
|
|
87
87
|
export declare function assertVersion(c: LogswapClient, expected?: bigint): Promise<void>;
|
|
88
|
+
/**
|
|
89
|
+
* The versions say the managers are the right code; this says the rest of the addresses belong
|
|
90
|
+
* to THEM. A stale router or lens from a previous deployment passes every version check and then
|
|
91
|
+
* fails in the worst way: approvals go to a router that pulls for another manager, or every pull
|
|
92
|
+
* reverts because the router's Permit2 is not the one the user approved.
|
|
93
|
+
*/
|
|
94
|
+
export declare function assertWiring(c: LogswapClient): Promise<void>;
|
package/dist/client.js
CHANGED
|
@@ -8,7 +8,39 @@
|
|
|
8
8
|
* the protocol, not configuration that grows.
|
|
9
9
|
*/
|
|
10
10
|
export function createLogswapClient(args) {
|
|
11
|
-
return { public: args.public, wallet: args.wallet, addresses: args.addresses };
|
|
11
|
+
return { public: args.public, wallet: args.wallet && guardChain(args.wallet, args.public), addresses: args.addresses };
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The wallet, with every signing call first checking that it is on the chain the reads come from.
|
|
15
|
+
*
|
|
16
|
+
* An injected wallet switched to another network would otherwise estimate and simulate against
|
|
17
|
+
* the public client's chain and then sign and send on its own — with deterministic deploy
|
|
18
|
+
* addresses, possibly into a different contract. Checked at each call, not once: a user can
|
|
19
|
+
* switch networks mid-session.
|
|
20
|
+
*/
|
|
21
|
+
function guardChain(wallet, pub) {
|
|
22
|
+
if (typeof wallet.getChainId !== "function")
|
|
23
|
+
return wallet; // a test double
|
|
24
|
+
let readChain = pub.chain?.id;
|
|
25
|
+
const check = async () => {
|
|
26
|
+
readChain ??= await pub.getChainId();
|
|
27
|
+
const walletChain = await wallet.getChainId();
|
|
28
|
+
if (walletChain !== readChain) {
|
|
29
|
+
throw new Error(`logswap: the wallet is on chain ${walletChain} but this app reads chain ${readChain} — switch the wallet's network`);
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
const guarded = new Set(["writeContract", "sendTransaction", "signTypedData"]);
|
|
33
|
+
return new Proxy(wallet, {
|
|
34
|
+
get(target, prop, receiver) {
|
|
35
|
+
const v = Reflect.get(target, prop, receiver);
|
|
36
|
+
if (!guarded.has(prop) || typeof v !== "function")
|
|
37
|
+
return v;
|
|
38
|
+
return async (...a) => {
|
|
39
|
+
await check();
|
|
40
|
+
return v.apply(target, a);
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
});
|
|
12
44
|
}
|
|
13
45
|
/**
|
|
14
46
|
* Assert the deployment matches the ABI this build was generated against.
|
|
@@ -24,18 +56,18 @@ export function createLogswapClient(args) {
|
|
|
24
56
|
* manager sailed through `assertVersion` while `FEE_MAX()` still answered on it. Move this in the
|
|
25
57
|
* same change as `CParams.VERSION`, never after it.
|
|
26
58
|
*
|
|
27
|
-
* **3** since 2026-09-03
|
|
59
|
+
* **3** since 2026-09-03: `burnById` and `update` take an `owner`, `update`
|
|
28
60
|
* mints to `to`, `positions()` returns `aps` as uint128 — and a returned quote flow now carries the
|
|
29
|
-
* swept fees
|
|
61
|
+
* swept fees, which is why `exit`'s settle-to-one-asset math changed with it.
|
|
30
62
|
*
|
|
31
|
-
* **4** since 2026-09-18
|
|
63
|
+
* **4** since 2026-09-18: `PoolKey` is `(base, quote, tickSpacing, phi)` —
|
|
32
64
|
* the variance kernel and its two key fields are gone, so every pool id changed; `phiEff` is
|
|
33
65
|
* `phiOf`; `getPool` lost `sigma2Ema`; `Initialize` lost two fields; the cut is
|
|
34
66
|
* `MINT_FEE_PROTOCOL_CUT` / `setFeeParams`.
|
|
35
67
|
*/
|
|
36
68
|
export const EXPECTED_VERSION = 4n;
|
|
37
69
|
/**
|
|
38
|
-
* The F manager's own tag (
|
|
70
|
+
* The F manager's own tag (5 — the key's salt, pool names, the sponsor registry;
|
|
39
71
|
* 4 was the income-only harvest cut).
|
|
40
72
|
*
|
|
41
73
|
* A FLOOR, not an equality — unlike `EXPECTED_VERSION` above. The F primitive is iterating fast and
|
|
@@ -47,43 +79,43 @@ export const EXPECTED_VERSION = 4n;
|
|
|
47
79
|
*/
|
|
48
80
|
// 7 (2026-09-15): the reserves are the state and the marks derived, `lockStrike`, the dividend
|
|
49
81
|
// harvest, the relaunch (`dissolve(poolId, successor)`, `rollIn`), the reserve-named seed, the
|
|
50
|
-
// sponsor facet
|
|
51
|
-
// 8 (2026-09-15): the relaunch is in place
|
|
82
|
+
// sponsor facet.
|
|
83
|
+
// 8 (2026-09-15): the relaunch is in place — every share id carries the
|
|
52
84
|
// generation (`fPoolShareId(poolId, gen)`), `dissolve(poolId)`, `redeem`, `claim` / `dividendOf`
|
|
53
85
|
// / `rollIn` / `previewRollIn` take the generation, `Pool.gen` replaces `dissolved` and
|
|
54
86
|
// `successor`. A 7 deployment's share ids do not even resolve with this SDK.
|
|
55
|
-
// 9 (2026-09-16): `deepen` is gone, `Pool.leverTheta` is `harvestedTheta
|
|
87
|
+
// 9 (2026-09-16): `deepen` is gone, `Pool.leverTheta` is `harvestedTheta`.
|
|
56
88
|
// 10 (2026-09-17): no floor guard — `Pool.minBuffer`, `raiseMinBuffer`, `LEVER_FLOOR` gone;
|
|
57
|
-
// `dissolve` needs an empty bid on every pool
|
|
89
|
+
// `dissolve` needs an empty bid on every pool; the key bit is the CLASS, `pad`
|
|
58
90
|
// (036): `seed` mints a pad's shares to 0xdead, and the operator, the gates and the list are
|
|
59
91
|
// refused on it — `fPoolLaunch` no longer transfers the seed itself.
|
|
60
|
-
// 11 (2026-09-17): roles and entries
|
|
92
|
+
// 11 (2026-09-17): roles and entries — the operator runs the pool (`harvest`,
|
|
61
93
|
// `dissolve`, `setLegs`) and is the authority until appointed, two-step (`acceptOperator`,
|
|
62
94
|
// `Pool.pendingOperator`); `setLegL`/`admitLeg` are gone, `setLegs` edits the next generation's
|
|
63
95
|
// table while unseeded; `fPoolRelaunch` takes `legs`.
|
|
64
96
|
// `getPool` decodes one field fewer, so a 9 deployment's struct no longer matches this SDK.
|
|
65
|
-
// 12 (2026-09-17): one contract
|
|
97
|
+
// 12 (2026-09-17): one contract — the sponsor facet is folded back into the
|
|
66
98
|
// manager; `sponsor()` and the fallback are gone, every entry, error and event unchanged, so
|
|
67
99
|
// `fPoolManagerAbi` is the manager's own ABI and no longer a union. Layout unchanged.
|
|
68
|
-
// 13 (2026-09-18): one vocabulary
|
|
100
|
+
// 13 (2026-09-18): one vocabulary — `HARVEST_FEE` is `HARVEST_PROTOCOL_CUT` (a
|
|
69
101
|
// cut of harvested income, not a fee on the harvest) and the exemption is `harvestCutExempt` /
|
|
70
102
|
// `setHarvestCutExempt`; `acceptAuthority` clears the operator, pending and accepted (a mandate
|
|
71
103
|
// is voided by the handover of the role that granted it); `setLegs` needs a generation to have
|
|
72
104
|
// lived (`gen > 0`); `initialize` refuses a codeless base and `mint` refuses `to = address(0)`
|
|
73
105
|
// on both managers. Layout unchanged.
|
|
74
|
-
// 14 (2026-09-20): the policy
|
|
106
|
+
// 14 (2026-09-20): the policy — `FPoolKey.policy`, every pool id moves; `dissolve` is
|
|
75
107
|
// `retire`, `regenerate` releases the dead shares' pot, `harvestable`/`unharvested` are the
|
|
76
108
|
// readouts; `feePerL`, `edgePerL`, `HARVEST_PROTOCOL_CUT`, `harvestCutExempt` are gone.
|
|
77
|
-
// 15 (2026-09-20): the policy answers every question
|
|
109
|
+
// 15 (2026-09-20): the policy answers every question — `FPoolKey.authority` is
|
|
78
110
|
// `creator` (every pool id moves again); the roles, gates and list are the pool's POLICY's
|
|
79
111
|
// (`FPolicyBase` / `PrivatePolicy`), not the manager's: `getPool` has `policy` and `asks` and no
|
|
80
112
|
// role; `proposeAuthority`, `appointOperator`, `setGates`, `setAllowed` (`setMinters` / `setTakers` since 17) are policy writes;
|
|
81
113
|
// `policyApproved(kind, policy)` / `setPolicyApproved(kind, policy, ok)`; `Regenerated` names the payee;
|
|
82
114
|
// `HARVEST_CUT_MAX` / `POT_CUT_MAX` cap what a policy may route to the protocol.
|
|
83
|
-
// 16 (2026-09-21): the word is POLICY
|
|
115
|
+
// 16 (2026-09-21): the word is POLICY — a rename, no pool id moves: `FPoolKey.policy`,
|
|
84
116
|
// `IFPolicy`, `FPolicyBase`, `PublicPolicy` … `PadPolicy`, `policyApproved` / `setPolicyApproved`,
|
|
85
117
|
// `PolicyApproved`; `PolPolicy.setPolicy` / `PolicySet` are `setRules` / `RulesSet`.
|
|
86
|
-
// 17 (2026-09-21): the registry, the move, the stamp, two lists, one base
|
|
118
|
+
// 17 (2026-09-21): the registry, the move, the stamp, two lists, one base — no
|
|
87
119
|
// pool id moves: the F manager takes a `PolicyRegistry` at construction and asks it at a birth
|
|
88
120
|
// (`policyApproved` / `setPolicyApproved` left the manager: `registryApprove` …); the key's policy
|
|
89
121
|
// is the policy at birth, `migrate` moves a pool for the registry's owner; `stamp` / `setAsks` are
|
|
@@ -101,7 +133,7 @@ export async function assertVersion(c, expected = EXPECTED_VERSION) {
|
|
|
101
133
|
throw new Error(`logswap: manager at ${c.addresses.cPoolManager} reports version ${got}, but this SDK was built ` +
|
|
102
134
|
`against version ${expected}. Upgrade the SDK or point at the matching deployment.`);
|
|
103
135
|
}
|
|
104
|
-
// the F manager carries its own tag
|
|
136
|
+
// the F manager carries its own tag; assert it the same way when deployed
|
|
105
137
|
if (c.addresses.fPoolManager) {
|
|
106
138
|
const { fPoolManagerAbi } = await import("./generated.js");
|
|
107
139
|
const gotF = await c.public.readContract({
|
|
@@ -114,4 +146,29 @@ export async function assertVersion(c, expected = EXPECTED_VERSION) {
|
|
|
114
146
|
`at least F version ${EXPECTED_F_VERSION}. Upgrade the deployment or point at a newer one.`);
|
|
115
147
|
}
|
|
116
148
|
}
|
|
149
|
+
await assertWiring(c);
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The versions say the managers are the right code; this says the rest of the addresses belong
|
|
153
|
+
* to THEM. A stale router or lens from a previous deployment passes every version check and then
|
|
154
|
+
* fails in the worst way: approvals go to a router that pulls for another manager, or every pull
|
|
155
|
+
* reverts because the router's Permit2 is not the one the user approved.
|
|
156
|
+
*/
|
|
157
|
+
export async function assertWiring(c) {
|
|
158
|
+
const { logswapRouterAbi, logswapLensAbi } = await import("./generated.js");
|
|
159
|
+
const rd = (address, abi, functionName) => c.public.readContract({ address, abi, functionName });
|
|
160
|
+
const ZERO = "0x0000000000000000000000000000000000000000";
|
|
161
|
+
const want = [
|
|
162
|
+
["router.mgr()", rd(c.addresses.router, logswapRouterAbi, "mgr"), c.addresses.cPoolManager],
|
|
163
|
+
["router.permit2()", rd(c.addresses.router, logswapRouterAbi, "permit2"), c.addresses.permit2 ?? ZERO],
|
|
164
|
+
["lens.mgr()", rd(c.addresses.lens, logswapLensAbi, "mgr"), c.addresses.cPoolManager],
|
|
165
|
+
];
|
|
166
|
+
if (c.addresses.fPoolManager) {
|
|
167
|
+
want.push(["router.fmgr()", rd(c.addresses.router, logswapRouterAbi, "fmgr"), c.addresses.fPoolManager]);
|
|
168
|
+
want.push(["lens.fmgr()", rd(c.addresses.lens, logswapLensAbi, "fmgr"), c.addresses.fPoolManager]);
|
|
169
|
+
}
|
|
170
|
+
const got = await Promise.all(want.map((w) => w[1]));
|
|
171
|
+
const bad = want.flatMap(([what, , expected], i) => got[i].toLowerCase() === expected.toLowerCase() ? [] : [`${what} is ${got[i]}, expected ${expected}`]);
|
|
172
|
+
if (bad.length)
|
|
173
|
+
throw new Error(`logswap: the deployment's addresses do not belong together — ${bad.join("; ")}`);
|
|
117
174
|
}
|