@logswap/sdk 0.5.47 → 0.5.48
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/dist/admin.d.ts +19 -19
- package/dist/admin.js +29 -29
- package/dist/client.d.ts +6 -6
- package/dist/client.js +11 -8
- package/dist/fpool.d.ts +82 -82
- package/dist/fpool.js +99 -99
- package/dist/generated.d.ts +103 -103
- package/dist/generated.js +132 -79
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/admin.ts +29 -29
- package/src/client.ts +16 -13
- package/src/fpool.ts +133 -133
- package/src/generated.ts +132 -79
- package/src/index.ts +1 -1
package/src/fpool.ts
CHANGED
|
@@ -14,15 +14,15 @@
|
|
|
14
14
|
*
|
|
15
15
|
* **Two products, one contract.** The basket (n > 1) and the launch (n = 1, the creator alone on
|
|
16
16
|
* the lever) are the same bytecode under two key fields — the KIND (what the manager promises
|
|
17
|
-
* structurally) and the
|
|
17
|
+
* structurally) and the POLICY (the pool's policy: its roles, its gates, the terms of every harvest
|
|
18
18
|
* and release; decisions 041, 044) — and nothing in the maths. `describeFPool` below reports
|
|
19
19
|
* which shape a given pool is, because a UI has to decide what to render.
|
|
20
20
|
*
|
|
21
|
-
* **The roles are the
|
|
22
|
-
* authority, operator, gate or list: it asks the pool's
|
|
23
|
-
* answer. So `getFPool` carries the
|
|
24
|
-
* roles FROM the
|
|
25
|
-
* is a write to the
|
|
21
|
+
* **The roles are the policy's, not the manager's** (F version 15). The manager holds no
|
|
22
|
+
* authority, operator, gate or list: it asks the pool's policy `may(who, action)` and acts on the
|
|
23
|
+
* answer. So `getFPool` carries the policy and what it declared, {@link getFPoolRoles} reads the
|
|
24
|
+
* roles FROM the policy, and every role write — the handover, the operator, the gates, the list —
|
|
25
|
+
* is a write to the policy ({@link fPoolProposeAuthority}, {@link fPoolSetGates}, …).
|
|
26
26
|
*
|
|
27
27
|
* **Zaps live on the ROUTER, not here.** Permit2 is AllowanceTransfer mode, whose standing
|
|
28
28
|
* allowance is keyed by spender, so a second router would cost the user an allowance per token
|
|
@@ -31,28 +31,28 @@
|
|
|
31
31
|
*/
|
|
32
32
|
|
|
33
33
|
import { encodeFunctionData, toFunctionSelector, type Address, type Hash, type Hex } from "viem";
|
|
34
|
-
import { fPoolManagerAbi, logswapRouterAbi,
|
|
34
|
+
import { fPoolManagerAbi, logswapRouterAbi, padPolicyAbi, polPolicyAbi, privatePolicyAbi, publicPolicyAbi } from "./generated.js";
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* One contract, one ABI (decisions 039, F version 12): `harvest`, `claim`, `retire`, `regenerate`,
|
|
38
38
|
* `setLegs`, the names, the fee parameters are `FPoolManager`'s own code. The roles left it for
|
|
39
|
-
* the
|
|
40
|
-
* {@link
|
|
39
|
+
* the policies in F version 15 (decisions 044): {@link fPolicyBaseAbi} is what every policy shares,
|
|
40
|
+
* {@link privatePolicyAbi} the desk's, {@link padPolicyAbi} / {@link polPolicyAbi} / {@link publicPolicyAbi}
|
|
41
41
|
* the kinds'.
|
|
42
42
|
*/
|
|
43
|
-
export { fPoolManagerAbi,
|
|
43
|
+
export { fPoolManagerAbi, padPolicyAbi, polPolicyAbi, privatePolicyAbi, publicPolicyAbi };
|
|
44
44
|
/**
|
|
45
|
-
* What every protocol
|
|
46
|
-
* `maxHarvest`. `
|
|
47
|
-
* in for the base — a call through it to any of the four
|
|
45
|
+
* What every protocol policy shares (`FPolicyBase`): the roles and their handover, `harvestCut`,
|
|
46
|
+
* `maxHarvest`. `polPolicyAbi` is a superset of it and carries nothing that collides, so it stands
|
|
47
|
+
* in for the base — a call through it to any of the four policies decodes the base's functions.
|
|
48
48
|
*/
|
|
49
|
-
export const
|
|
49
|
+
export const fPolicyBaseAbi = polPolicyAbi;
|
|
50
50
|
import type { LogswapClient } from "./client.js";
|
|
51
51
|
|
|
52
52
|
const WAD = 10n ** 18n;
|
|
53
53
|
const ZERO_ADDRESS = "0x0000000000000000000000000000000000000000" as const;
|
|
54
54
|
|
|
55
|
-
/** The optional questions a
|
|
55
|
+
/** The optional questions a policy declared at birth (decisions 044), as bits of `Pool.asks`. */
|
|
56
56
|
export const ASK_SWAP = 1;
|
|
57
57
|
export const ASK_MINT = 2;
|
|
58
58
|
|
|
@@ -83,10 +83,10 @@ export interface FPoolState {
|
|
|
83
83
|
/**
|
|
84
84
|
* The pool's policy (decisions 041, 044): the contract the key names, holding the pool's roles
|
|
85
85
|
* and answering every question the manager does not decide itself. Read its roles with
|
|
86
|
-
* {@link getFPoolRoles}; one of the deployment's four ({@link
|
|
86
|
+
* {@link getFPoolRoles}; one of the deployment's four ({@link fPoolPolicyFor}) on every protocol pool.
|
|
87
87
|
*/
|
|
88
|
-
|
|
89
|
-
/** Whether the
|
|
88
|
+
policy: Address;
|
|
89
|
+
/** Whether the policy asked to be consulted on swaps ({@link ASK_SWAP}) and mints ({@link ASK_MINT}) — the desk's policy does, no other. */
|
|
90
90
|
asks: number;
|
|
91
91
|
/** The sponsor's own label for this pool, mutable and never identity. "" when unnamed. */
|
|
92
92
|
name: string;
|
|
@@ -120,11 +120,11 @@ export async function getFPool(c: LogswapClient, poolId: Hex): Promise<FPoolStat
|
|
|
120
120
|
} as never) as Promise<T>;
|
|
121
121
|
|
|
122
122
|
// `getPool` returns the whole scalar struct in one call — the singleton's shape makes what used
|
|
123
|
-
// to be a dozen getters a single read. No role in it since F version 15: those are the
|
|
123
|
+
// to be a dozen getters a single read. No role in it since F version 15: those are the policy's.
|
|
124
124
|
const p = await rd<{
|
|
125
125
|
quote: Address;
|
|
126
126
|
phi: bigint;
|
|
127
|
-
|
|
127
|
+
policy: Address;
|
|
128
128
|
kind: number;
|
|
129
129
|
seeded: boolean;
|
|
130
130
|
gen: number;
|
|
@@ -160,7 +160,7 @@ export async function getFPool(c: LogswapClient, poolId: Hex): Promise<FPoolStat
|
|
|
160
160
|
const weights = legs.map((l) => l[1]);
|
|
161
161
|
const x = legs.map((l) => l[2]); // derived by the manager: x_j = ln(w_j·L / R_j) (decisions 026)
|
|
162
162
|
const reserves = legs.map((l) => l[3]); // the state
|
|
163
|
-
const { quote, Q, L, phi, theta0, bigSigma,
|
|
163
|
+
const { quote, Q, L, phi, theta0, bigSigma, policy, restructureBlock, seeded, gen } = p;
|
|
164
164
|
const totalSupply = p.shares;
|
|
165
165
|
|
|
166
166
|
return {
|
|
@@ -176,7 +176,7 @@ export async function getFPool(c: LogswapClient, poolId: Hex): Promise<FPoolStat
|
|
|
176
176
|
theta,
|
|
177
177
|
compositeX,
|
|
178
178
|
bigSigma,
|
|
179
|
-
|
|
179
|
+
policy,
|
|
180
180
|
asks: Number(p.asks),
|
|
181
181
|
name: bytes32ToText(p.name),
|
|
182
182
|
restructureBlock: Number(restructureBlock),
|
|
@@ -190,13 +190,13 @@ export async function getFPool(c: LogswapClient, poolId: Hex): Promise<FPoolStat
|
|
|
190
190
|
|
|
191
191
|
/**
|
|
192
192
|
* The kind of pool (decisions 036, 038, 044) — what the manager promises structurally, whatever
|
|
193
|
-
* the pool's
|
|
194
|
-
* creator; no gate, no operator. `private`, the desk: the one kind whose
|
|
193
|
+
* the pool's policy answers. `public`: the protocol's own pool — its policy requires the collector as
|
|
194
|
+
* creator; no gate, no operator. `private`, the desk: the one kind whose policy may gate swaps and
|
|
195
195
|
* mints, with an operator that reshapes the legs between generations. `pol`, a treasury's pool
|
|
196
196
|
* or a basket: composition and access fixed at birth. `pad`, a launch: a POL whose harvest is
|
|
197
|
-
* income only whatever its
|
|
197
|
+
* income only whatever its policy says and whose seed the contract minted to 0xdead — one asset,
|
|
198
198
|
* open to all. private → pol → pad is the ladder of rigidity; public is a pol held by the
|
|
199
|
-
* protocol. Every rate, role and gate is the
|
|
199
|
+
* protocol. Every rate, role and gate is the policy's.
|
|
200
200
|
*/
|
|
201
201
|
export type FPoolKind = "public" | "private" | "pol" | "pad";
|
|
202
202
|
/** The key's enum values. */
|
|
@@ -208,35 +208,35 @@ export function fPoolKindOf(n: number | bigint): FPoolKind {
|
|
|
208
208
|
return k;
|
|
209
209
|
}
|
|
210
210
|
|
|
211
|
-
/** The deployment's
|
|
212
|
-
export function
|
|
211
|
+
/** The deployment's policy for a kind (decisions 044): what a key of that kind names. */
|
|
212
|
+
export function fPoolPolicyFor(c: LogswapClient, kind: FPoolKind): Address {
|
|
213
213
|
const a = c.addresses;
|
|
214
|
-
const
|
|
215
|
-
if (!
|
|
216
|
-
return
|
|
214
|
+
const policy = kind === "public" ? a.publicPolicy : kind === "private" ? a.privatePolicy : kind === "pol" ? a.polPolicy : a.padPolicy;
|
|
215
|
+
if (!policy) throw new Error(`logswap: this deployment carries no ${kind} policy`);
|
|
216
|
+
return policy;
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
/** The
|
|
220
|
-
export async function
|
|
219
|
+
/** The policy a pool named in its key — one read; {@link getFPool} carries it too. */
|
|
220
|
+
export async function fPoolPolicyOf(c: LogswapClient, poolId: Hex): Promise<Address> {
|
|
221
221
|
const p = (await c.public.readContract({
|
|
222
222
|
address: c.addresses.fPoolManager!,
|
|
223
223
|
abi: fPoolManagerAbi,
|
|
224
224
|
functionName: "getPool",
|
|
225
225
|
args: [poolId],
|
|
226
|
-
} as never)) as { quote: Address;
|
|
226
|
+
} as never)) as { quote: Address; policy: Address };
|
|
227
227
|
if (/^0x0{40}$/i.test(p.quote)) throw new Error(`logswap: no F pool with id ${poolId}`);
|
|
228
|
-
return p.
|
|
228
|
+
return p.policy;
|
|
229
229
|
}
|
|
230
230
|
|
|
231
231
|
/**
|
|
232
|
-
* A pool's roles, as its
|
|
232
|
+
* A pool's roles, as its POLICY holds them (decisions 044). `creator` is immutable identity —
|
|
233
233
|
* who was born holding the pool; `authority` is the cold key that owns it now, the creator until
|
|
234
234
|
* a two-step handover; on the desk's kind the `operator` runs it (zero = the authority itself,
|
|
235
235
|
* {@link fPoolOperatorOf}) and the two gates and the list are its. Every other kind has no
|
|
236
236
|
* operator and no gate: those fields read zero / false.
|
|
237
237
|
*/
|
|
238
238
|
export interface FPoolRoles {
|
|
239
|
-
|
|
239
|
+
policy: Address;
|
|
240
240
|
creator: Address;
|
|
241
241
|
authority: Address;
|
|
242
242
|
/** The proposed next authority, until it accepts — zero when nothing is pending (decisions 020). */
|
|
@@ -245,31 +245,31 @@ export interface FPoolRoles {
|
|
|
245
245
|
pendingOperator: Address;
|
|
246
246
|
gateMint: boolean;
|
|
247
247
|
gateSwap: boolean;
|
|
248
|
-
/** The
|
|
248
|
+
/** The policy's own ceiling on a harvest now — the manager's `harvestable` narrowed by the policy's rule. */
|
|
249
249
|
maxHarvest: bigint;
|
|
250
250
|
}
|
|
251
251
|
|
|
252
|
-
/** Read a pool's roles from its
|
|
253
|
-
export async function getFPoolRoles(c: LogswapClient, poolId: Hex, state?: Pick<FPoolState, "
|
|
252
|
+
/** Read a pool's roles from its policy. `state` saves the `getPool` round trip when already in hand. */
|
|
253
|
+
export async function getFPoolRoles(c: LogswapClient, poolId: Hex, state?: Pick<FPoolState, "policy" | "kind">): Promise<FPoolRoles> {
|
|
254
254
|
const s = state ?? (await getFPool(c, poolId));
|
|
255
|
-
const
|
|
256
|
-
c.public.readContract({ address: s.
|
|
255
|
+
const rdPolicy = <T>(abi: readonly unknown[], functionName: string) =>
|
|
256
|
+
c.public.readContract({ address: s.policy, abi, functionName, args: [poolId] } as never) as Promise<T>;
|
|
257
257
|
const [creator, authority, pendingAuthority, maxHarvest] = await Promise.all([
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
258
|
+
rdPolicy<Address>(fPolicyBaseAbi, "creator"),
|
|
259
|
+
rdPolicy<Address>(fPolicyBaseAbi, "authority"),
|
|
260
|
+
rdPolicy<Address>(fPolicyBaseAbi, "pendingAuthority"),
|
|
261
|
+
rdPolicy<bigint>(fPolicyBaseAbi, "maxHarvest"),
|
|
262
262
|
]);
|
|
263
263
|
const desk = s.kind === "private";
|
|
264
264
|
const [operator, pendingOperator, gateMint, gateSwap] = desk
|
|
265
265
|
? await Promise.all([
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
266
|
+
rdPolicy<Address>(privatePolicyAbi, "operator"),
|
|
267
|
+
rdPolicy<Address>(privatePolicyAbi, "pendingOperator"),
|
|
268
|
+
rdPolicy<boolean>(privatePolicyAbi, "gateMint"),
|
|
269
|
+
rdPolicy<boolean>(privatePolicyAbi, "gateSwap"),
|
|
270
270
|
])
|
|
271
271
|
: [ZERO_ADDRESS, ZERO_ADDRESS, false, false];
|
|
272
|
-
return {
|
|
272
|
+
return { policy: s.policy, creator, authority, pendingAuthority, operator, pendingOperator, gateMint, gateSwap, maxHarvest };
|
|
273
273
|
}
|
|
274
274
|
|
|
275
275
|
/** Which of the two products a pool is configured as — a pad is a launch, anything else a basket/pool. A UI has to choose what to render. */
|
|
@@ -287,7 +287,7 @@ export interface FPoolDescription {
|
|
|
287
287
|
* from the first wei after it.
|
|
288
288
|
*/
|
|
289
289
|
unharvested: bigint;
|
|
290
|
-
/** The most a harvest may take now by the KIND's promise: the bid, and on a pad the income. The
|
|
290
|
+
/** The most a harvest may take now by the KIND's promise: the bid, and on a pad the income. The policy may allow less — {@link FPoolRoles.maxHarvest}. */
|
|
291
291
|
harvestable: bigint;
|
|
292
292
|
/** The dead shares' banked dividend — a pad's pot, the creator's income until a rung is climbed (decisions 041). */
|
|
293
293
|
pot: bigint;
|
|
@@ -870,11 +870,11 @@ export interface FPoolCreateArgs {
|
|
|
870
870
|
kind: FPoolKind;
|
|
871
871
|
/**
|
|
872
872
|
* The pool's policy (decisions 041, 044): the contract the key names, approved by the protocol
|
|
873
|
-
* for the kind. Defaults to the deployment's
|
|
873
|
+
* for the kind. Defaults to the deployment's policy for `kind` ({@link fPoolPolicyFor}) — the one
|
|
874
874
|
* every protocol pool names.
|
|
875
875
|
*/
|
|
876
|
-
|
|
877
|
-
/** Who is born holding the pool: immutable identity, the pool's first authority (the
|
|
876
|
+
policy?: Address;
|
|
877
|
+
/** Who is born holding the pool: immutable identity, the pool's first authority (the policy records it). */
|
|
878
878
|
creator: Address;
|
|
879
879
|
/**
|
|
880
880
|
* The differentiator, and nothing else. Identity is the key's hash, so one sponsor cannot run
|
|
@@ -909,7 +909,7 @@ export function sortFPoolLegs<T>(bases: Address[], ...companions: T[][]): { base
|
|
|
909
909
|
return { bases: idx.map((i) => bases[i]!), companions: companions.map((arr) => idx.map((i) => arr[i]!)) };
|
|
910
910
|
}
|
|
911
911
|
|
|
912
|
-
/** The key as the manager hashes it: legs sorted, the kind's
|
|
912
|
+
/** The key as the manager hashes it: legs sorted, the kind's policy filled in when none is named. */
|
|
913
913
|
export function fPoolKeyOf(c: LogswapClient, a: Omit<FPoolCreateArgs, "account">) {
|
|
914
914
|
const { bases, companions } = sortFPoolLegs(a.bases, a.weights);
|
|
915
915
|
return {
|
|
@@ -918,7 +918,7 @@ export function fPoolKeyOf(c: LogswapClient, a: Omit<FPoolCreateArgs, "account">
|
|
|
918
918
|
weights: companions[0]!,
|
|
919
919
|
phi: a.phi,
|
|
920
920
|
kind: F_KIND[a.kind],
|
|
921
|
-
|
|
921
|
+
policy: a.policy ?? fPoolPolicyFor(c, a.kind),
|
|
922
922
|
creator: a.creator,
|
|
923
923
|
salt: a.salt ?? ZERO_SALT,
|
|
924
924
|
};
|
|
@@ -984,7 +984,7 @@ export async function fPoolSeed(
|
|
|
984
984
|
* operator harvested it out first ({@link fPoolRetire} does both in one transaction). The live
|
|
985
985
|
* (S, Q, R_j) become a frozen bundle every share of this generation redeems ({@link fPoolRedeem})
|
|
986
986
|
* or rolls ({@link fPoolRollIn}), forever; the pool is unseeded until the next {@link fPoolSeed},
|
|
987
|
-
* which opens generation `gen + 1` under the same id,
|
|
987
|
+
* which opens generation `gen + 1` under the same id, policy, roles, gates and lists.
|
|
988
988
|
*/
|
|
989
989
|
export async function fPoolRetireRaw(c: LogswapClient, a: { poolId: Hex; account: Address }) {
|
|
990
990
|
return writeFPool(c, a.poolId, "retire", [], a.account);
|
|
@@ -1010,25 +1010,25 @@ export async function fPoolRetire(c: LogswapClient, a: { poolId: Hex; account: A
|
|
|
1010
1010
|
* `retire` (decisions 035: a retire needs an empty bid on every pool). Since F version 14 one
|
|
1011
1011
|
* `harvest(Q)` does it: the protocol's cut leaves the pool with the amount (with no collector
|
|
1012
1012
|
* there is no cut). `[]` when the bid is already empty. Throws on a pad that still bids (its bid
|
|
1013
|
-
* empties only as the market sells it down), and when the pool's
|
|
1014
|
-
* (`
|
|
1013
|
+
* empties only as the market sells it down), and when the pool's policy keeps a floor on the bid
|
|
1014
|
+
* (`PolPolicy.keepQuote`, decisions 044): a `retire` under that policy waits for the market.
|
|
1015
1015
|
*/
|
|
1016
1016
|
export async function fPoolEmptyingHarvests(c: LogswapClient, poolId: Hex): Promise<bigint[]> {
|
|
1017
1017
|
const s = await getFPool(c, poolId);
|
|
1018
1018
|
if (s.Q === 0n) return [];
|
|
1019
1019
|
if (s.kind === "pad") throw new Error("logswap: a pad is retired only once its bid is empty (decisions 035)");
|
|
1020
1020
|
const { maxHarvest } = await getFPoolRoles(c, poolId, s);
|
|
1021
|
-
if (maxHarvest < s.Q) throw new Error("logswap: the pool's
|
|
1021
|
+
if (maxHarvest < s.Q) throw new Error("logswap: the pool's policy keeps a floor on the bid — it cannot be harvested empty (decisions 044)");
|
|
1022
1022
|
return [s.Q];
|
|
1023
1023
|
}
|
|
1024
1024
|
|
|
1025
1025
|
/**
|
|
1026
|
-
* Release the dead shares' pot under the
|
|
1026
|
+
* Release the dead shares' pot under the policy's terms (decisions 041, 044) — a pad's graduation.
|
|
1027
1027
|
* The manager harvests the generation's income first (the pot then holds everything the dead
|
|
1028
|
-
* shares earned), asks the
|
|
1028
|
+
* shares earned), asks the policy, and applies its answer: a share to the payee it names (the pad's
|
|
1029
1029
|
* creator — the pool's authority), a cut to the collector, the rest bought in as more of the
|
|
1030
|
-
* float the pool owns, a slice of every reserve burned. The
|
|
1031
|
-
* threshold (`
|
|
1030
|
+
* float the pool owns, a slice of every reserve burned. The policy refuses under its rung's
|
|
1031
|
+
* threshold (`PadPolicy.NotGraduated`). Whoever runs the pool.
|
|
1032
1032
|
*/
|
|
1033
1033
|
export async function fPoolRegenerate(c: LogswapClient, a: { poolId: Hex; to?: Address; account: Address }) {
|
|
1034
1034
|
return writeFPool(c, a.poolId, "regenerate", [a.to ?? a.account], a.account);
|
|
@@ -1041,7 +1041,7 @@ export async function fPoolRegenerate(c: LogswapClient, a: { poolId: Hex; to?: A
|
|
|
1041
1041
|
* next generation's composition (`legs`: a private pool's reshape — retire, admit, reweight), and
|
|
1042
1042
|
* `seed` it with what came back — the manager's `multicall`, one sender throughout, the same
|
|
1043
1043
|
* pool. The operator's when one is appointed and the seed's inventory is its own; by default the
|
|
1044
|
-
* authority's. The
|
|
1044
|
+
* authority's. The policy, its roles, gates, allowlist and the name carry over; anything else goes
|
|
1045
1045
|
* in `extra`, pre-encoded against {@link fPoolManagerAbi}. The seed names reserves (decisions
|
|
1046
1046
|
* 029): `r0` outright — one per leg of the NEXT table, 0 for a retired leg — or `x0` marks
|
|
1047
1047
|
* against the next table's `weights`.
|
|
@@ -1120,8 +1120,8 @@ export function fPoolLaunchCalls(
|
|
|
1120
1120
|
const given = "r0" in a ? a.r0 : a.x0.map((x, j) => fPoolReserveAt((a.weights[j]! * a.L0) / WAD, x));
|
|
1121
1121
|
if (given.length !== a.bases.length) throw new Error(`logswap: ${given.length} reserves for ${a.bases.length} legs`);
|
|
1122
1122
|
const { bases, companions } = sortFPoolLegs(a.bases, a.weights, given);
|
|
1123
|
-
if (!a.
|
|
1124
|
-
const key = { quote: a.quote, bases, weights: companions[0]!, phi: a.phi, kind: F_KIND[a.kind],
|
|
1123
|
+
if (!a.policy) throw new Error("logswap: fPoolLaunchCalls needs the key's policy — fPoolLaunch fills it from the deployment");
|
|
1124
|
+
const key = { quote: a.quote, bases, weights: companions[0]!, phi: a.phi, kind: F_KIND[a.kind], policy: a.policy, creator: a.creator, salt: a.salt ?? ZERO_SALT };
|
|
1125
1125
|
const enc = (functionName: string, args: unknown[]) =>
|
|
1126
1126
|
encodeFunctionData({ abi: fPoolManagerAbi, functionName, args } as never) as Hex;
|
|
1127
1127
|
const calls: Hex[] = [];
|
|
@@ -1135,22 +1135,22 @@ export function fPoolLaunchCalls(
|
|
|
1135
1135
|
calls.push(enc("swapQuoteIn", [poolId, BigInt(j), a.openBuy.amountIn, a.openBuy.minOut ?? 0n, a.account]));
|
|
1136
1136
|
}
|
|
1137
1137
|
if (a.name) calls.push(enc("setName", [poolId, textToBytes32(a.name)]));
|
|
1138
|
-
// the desk's gates and list are the
|
|
1138
|
+
// the desk's gates and list are the POLICY's (decisions 044), not the manager's: they cannot ride
|
|
1139
1139
|
// in this batch — `fPoolDeskOpening` sends them right after, as the same creator
|
|
1140
1140
|
calls.push(...(a.extra ?? []));
|
|
1141
1141
|
return calls;
|
|
1142
1142
|
}
|
|
1143
1143
|
|
|
1144
1144
|
/**
|
|
1145
|
-
* The desk's opening posture, on its
|
|
1145
|
+
* The desk's opening posture, on its policy (decisions 044): gated minting, the creator listed. No
|
|
1146
1146
|
* appointment: the authority is the operator until one is appointed (decisions 037). Two writes
|
|
1147
|
-
* to `
|
|
1147
|
+
* to `PrivatePolicy`; the launch batch cannot carry them (they are not manager calls).
|
|
1148
1148
|
*/
|
|
1149
|
-
export async function fPoolDeskOpening(c: LogswapClient, a: { poolId: Hex; account: Address;
|
|
1150
|
-
const
|
|
1149
|
+
export async function fPoolDeskOpening(c: LogswapClient, a: { poolId: Hex; account: Address; policy?: Address }): Promise<Hash[]> {
|
|
1150
|
+
const policy = a.policy ?? fPoolPolicyFor(c, "private");
|
|
1151
1151
|
return [
|
|
1152
|
-
await fPoolSetGates(c, { poolId: a.poolId, gateMint: true, gateSwap: false, account: a.account,
|
|
1153
|
-
await fPoolSetAllowed(c, { poolId: a.poolId, who: [a.account], allowed: true, account: a.account,
|
|
1152
|
+
await fPoolSetGates(c, { poolId: a.poolId, gateMint: true, gateSwap: false, account: a.account, policy }),
|
|
1153
|
+
await fPoolSetAllowed(c, { poolId: a.poolId, who: [a.account], allowed: true, account: a.account, policy }),
|
|
1154
1154
|
];
|
|
1155
1155
|
}
|
|
1156
1156
|
|
|
@@ -1176,22 +1176,22 @@ export async function fPoolLaunch(
|
|
|
1176
1176
|
a: FPoolLaunchArgs & ({ r0: bigint[] } | { x0: bigint[] }),
|
|
1177
1177
|
): Promise<{ hash: Hash; poolId: Hex }> {
|
|
1178
1178
|
const poolId = await fPoolIdOf(c, a);
|
|
1179
|
-
const calls = fPoolLaunchCalls(poolId, { ...a,
|
|
1179
|
+
const calls = fPoolLaunchCalls(poolId, { ...a, policy: a.policy ?? fPoolPolicyFor(c, a.kind) });
|
|
1180
1180
|
const hash = await writeFPool0(c, "multicall", [calls], a.account);
|
|
1181
1181
|
return { hash, poolId };
|
|
1182
1182
|
}
|
|
1183
1183
|
|
|
1184
|
-
// ─── the roles, on the
|
|
1184
|
+
// ─── the roles, on the policy (decisions 044) ─────────────────────────────────────────────
|
|
1185
1185
|
//
|
|
1186
|
-
// Every write here goes to the pool's
|
|
1187
|
-
// may be passed when in hand (`FPoolState.
|
|
1186
|
+
// Every write here goes to the pool's POLICY, not the manager: the manager holds no role. `policy`
|
|
1187
|
+
// may be passed when in hand (`FPoolState.policy`, `FPoolRoles.policy`); otherwise it is read once.
|
|
1188
1188
|
|
|
1189
|
-
/** A write keyed by poolId, on the pool's
|
|
1190
|
-
async function
|
|
1189
|
+
/** A write keyed by poolId, on the pool's policy. */
|
|
1190
|
+
async function writePolicy(c: LogswapClient, abi: readonly unknown[], a: { poolId: Hex; policy?: Address; account: Address }, functionName: string, args: unknown[]) {
|
|
1191
1191
|
const wallet = requireWallet(c);
|
|
1192
|
-
const
|
|
1192
|
+
const policy = a.policy ?? (await fPoolPolicyOf(c, a.poolId));
|
|
1193
1193
|
const { request } = await c.public.simulateContract({
|
|
1194
|
-
address:
|
|
1194
|
+
address: policy,
|
|
1195
1195
|
abi,
|
|
1196
1196
|
functionName,
|
|
1197
1197
|
args: [a.poolId, ...args],
|
|
@@ -1201,50 +1201,50 @@ async function writeHook(c: LogswapClient, abi: readonly unknown[], a: { poolId:
|
|
|
1201
1201
|
}
|
|
1202
1202
|
|
|
1203
1203
|
/**
|
|
1204
|
-
* Propose the next authority — step one of two (decisions 020), on the pool's
|
|
1204
|
+
* Propose the next authority — step one of two (decisions 020), on the pool's policy. Nothing moves
|
|
1205
1205
|
* until `next` accepts; proposing again (the zero address included) cancels a pending proposal.
|
|
1206
|
-
* Indexed on-chain via the
|
|
1206
|
+
* Indexed on-chain via the policy's AuthorityProposed.
|
|
1207
1207
|
*/
|
|
1208
|
-
export async function fPoolProposeAuthority(c: LogswapClient, a: { poolId: Hex; next: Address; account: Address;
|
|
1209
|
-
return
|
|
1208
|
+
export async function fPoolProposeAuthority(c: LogswapClient, a: { poolId: Hex; next: Address; account: Address; policy?: Address }) {
|
|
1209
|
+
return writePolicy(c, fPolicyBaseAbi, a, "proposeAuthority", [a.next]);
|
|
1210
1210
|
}
|
|
1211
1211
|
|
|
1212
1212
|
/**
|
|
1213
|
-
* Accept a pending proposal — step two; only the proposed address can. Indexed via the
|
|
1214
|
-
* AuthoritySet. On the desk's
|
|
1213
|
+
* Accept a pending proposal — step two; only the proposed address can. Indexed via the policy's
|
|
1214
|
+
* AuthoritySet. On the desk's policy this clears the operator (pending and accepted) and voids the
|
|
1215
1215
|
* seller's list (decisions 040, 044).
|
|
1216
1216
|
*/
|
|
1217
|
-
export async function fPoolAcceptAuthority(c: LogswapClient, a: { poolId: Hex; account: Address;
|
|
1218
|
-
return
|
|
1217
|
+
export async function fPoolAcceptAuthority(c: LogswapClient, a: { poolId: Hex; account: Address; policy?: Address }) {
|
|
1218
|
+
return writePolicy(c, fPolicyBaseAbi, a, "acceptAuthority", []);
|
|
1219
1219
|
}
|
|
1220
1220
|
|
|
1221
|
-
// ─── the desk's controls (
|
|
1221
|
+
// ─── the desk's controls (PrivatePolicy; decisions 021, 044) ───────────────────────────────
|
|
1222
1222
|
|
|
1223
|
-
/** Turn the mint / swap gates on or off (the authority, on the desk's
|
|
1223
|
+
/** Turn the mint / swap gates on or off (the authority, on the desk's policy). `burn` is never gated. */
|
|
1224
1224
|
export async function fPoolSetGates(
|
|
1225
1225
|
c: LogswapClient,
|
|
1226
|
-
a: { poolId: Hex; gateMint: boolean; gateSwap: boolean; account: Address;
|
|
1226
|
+
a: { poolId: Hex; gateMint: boolean; gateSwap: boolean; account: Address; policy?: Address },
|
|
1227
1227
|
) {
|
|
1228
|
-
return
|
|
1228
|
+
return writePolicy(c, privatePolicyAbi, a, "setGates", [a.gateMint, a.gateSwap]);
|
|
1229
1229
|
}
|
|
1230
1230
|
|
|
1231
1231
|
/**
|
|
1232
|
-
* Add to or remove from the pool's list, in one call (the authority, on the desk's
|
|
1232
|
+
* Add to or remove from the pool's list, in one call (the authority, on the desk's policy). The list
|
|
1233
1233
|
* is keyed by the authority that granted it: a handover starts the buyer with an empty one.
|
|
1234
1234
|
*/
|
|
1235
1235
|
export async function fPoolSetAllowed(
|
|
1236
1236
|
c: LogswapClient,
|
|
1237
|
-
a: { poolId: Hex; who: readonly Address[]; allowed: boolean; account: Address;
|
|
1237
|
+
a: { poolId: Hex; who: readonly Address[]; allowed: boolean; account: Address; policy?: Address },
|
|
1238
1238
|
) {
|
|
1239
|
-
return
|
|
1239
|
+
return writePolicy(c, privatePolicyAbi, a, "setAllowed", [a.who, a.allowed]);
|
|
1240
1240
|
}
|
|
1241
1241
|
|
|
1242
1242
|
/** Whether `who` is on the pool's list under its current authority (consulted only while a gate is on). */
|
|
1243
|
-
export async function fPoolIsAllowed(c: LogswapClient, poolId: Hex, who: Address,
|
|
1244
|
-
const address =
|
|
1243
|
+
export async function fPoolIsAllowed(c: LogswapClient, poolId: Hex, who: Address, policy?: Address): Promise<boolean> {
|
|
1244
|
+
const address = policy ?? (await fPoolPolicyOf(c, poolId));
|
|
1245
1245
|
return (await c.public.readContract({
|
|
1246
1246
|
address,
|
|
1247
|
-
abi:
|
|
1247
|
+
abi: privatePolicyAbi,
|
|
1248
1248
|
functionName: "isAllowed",
|
|
1249
1249
|
args: [poolId, who],
|
|
1250
1250
|
} as never)) as boolean;
|
|
@@ -1288,17 +1288,17 @@ export function fPoolOperatorOf(s: Pick<FPoolRoles, "authority" | "operator">):
|
|
|
1288
1288
|
}
|
|
1289
1289
|
|
|
1290
1290
|
/**
|
|
1291
|
-
* Propose the operator (the authority, on the desk's
|
|
1291
|
+
* Propose the operator (the authority, on the desk's policy) — the hot key that runs the pool:
|
|
1292
1292
|
* `harvest`, `retire`, `regenerate`, `setLegs`. Two-step: the proposed address takes the role with
|
|
1293
1293
|
* {@link fPoolAcceptOperator}. The zero address clears at once — the role returns to the authority.
|
|
1294
1294
|
*/
|
|
1295
|
-
export async function fPoolAppointOperator(c: LogswapClient, a: { poolId: Hex; operator: Address; account: Address;
|
|
1296
|
-
return
|
|
1295
|
+
export async function fPoolAppointOperator(c: LogswapClient, a: { poolId: Hex; operator: Address; account: Address; policy?: Address }) {
|
|
1296
|
+
return writePolicy(c, privatePolicyAbi, a, "appointOperator", [a.operator]);
|
|
1297
1297
|
}
|
|
1298
1298
|
|
|
1299
1299
|
/** The proposed operator takes the role. */
|
|
1300
|
-
export async function fPoolAcceptOperator(c: LogswapClient, a: { poolId: Hex; account: Address;
|
|
1301
|
-
return
|
|
1300
|
+
export async function fPoolAcceptOperator(c: LogswapClient, a: { poolId: Hex; account: Address; policy?: Address }) {
|
|
1301
|
+
return writePolicy(c, privatePolicyAbi, a, "acceptOperator", []);
|
|
1302
1302
|
}
|
|
1303
1303
|
|
|
1304
1304
|
/**
|
|
@@ -1346,9 +1346,9 @@ export interface DiscoveredFPool {
|
|
|
1346
1346
|
weights: bigint[];
|
|
1347
1347
|
phi: bigint;
|
|
1348
1348
|
kind: FPoolKind;
|
|
1349
|
-
/** The pool's policy — the
|
|
1350
|
-
|
|
1351
|
-
/** Who was born holding the pool (immutable). The LIVE authority is the
|
|
1349
|
+
/** The pool's policy — the contract the key names (decisions 041, 044, 045). */
|
|
1350
|
+
policy: Address;
|
|
1351
|
+
/** Who was born holding the pool (immutable). The LIVE authority is the policy's — {@link getFPoolRoles}. */
|
|
1352
1352
|
creator: Address;
|
|
1353
1353
|
/** The block the pool was initialized in — its age, for annualising realized income. */
|
|
1354
1354
|
block: bigint;
|
|
@@ -1398,7 +1398,7 @@ export async function discoverFPools(
|
|
|
1398
1398
|
return logs.map((l) => {
|
|
1399
1399
|
const a = l.args as {
|
|
1400
1400
|
poolId: Hex; quote: Address; creator: Address; bases: readonly Address[];
|
|
1401
|
-
weights: readonly bigint[]; phi: bigint; kind: number;
|
|
1401
|
+
weights: readonly bigint[]; phi: bigint; kind: number; policy: Address;
|
|
1402
1402
|
};
|
|
1403
1403
|
const q0 = q0Of.get(a.poolId.toLowerCase());
|
|
1404
1404
|
return {
|
|
@@ -1408,7 +1408,7 @@ export async function discoverFPools(
|
|
|
1408
1408
|
weights: [...a.weights],
|
|
1409
1409
|
phi: a.phi,
|
|
1410
1410
|
kind: fPoolKindOf(a.kind),
|
|
1411
|
-
|
|
1411
|
+
policy: a.policy,
|
|
1412
1412
|
creator: a.creator,
|
|
1413
1413
|
block: l.blockNumber ?? 0n,
|
|
1414
1414
|
seeded: q0 !== undefined,
|
|
@@ -1420,49 +1420,49 @@ export async function discoverFPools(
|
|
|
1420
1420
|
|
|
1421
1421
|
// ─── the policy page: what an authority controls (decisions 044) ────────────────────────
|
|
1422
1422
|
//
|
|
1423
|
-
// One
|
|
1424
|
-
// every pool whose
|
|
1425
|
-
// indexer), then one `authority` read per pool on its
|
|
1423
|
+
// One policy serves every pool of its kind, so "the pools I control" is not a policy's list: it is
|
|
1424
|
+
// every pool whose policy answers `authority(poolId) == me`. Discovery from the log stream (or the
|
|
1425
|
+
// indexer), then one `authority` read per pool on its policy — viem multicalls them.
|
|
1426
1426
|
|
|
1427
1427
|
/** A pool an authority controls, with what the policy page shows of it. */
|
|
1428
1428
|
export interface FPoolControlled {
|
|
1429
1429
|
pool: DiscoveredFPool;
|
|
1430
1430
|
roles: FPoolRoles;
|
|
1431
1431
|
state: FPoolState;
|
|
1432
|
-
/** The
|
|
1433
|
-
|
|
1432
|
+
/** The policy's numbers for this pool's kind — a pad's rung and threshold, a POL's floors, the desk's list flags. */
|
|
1433
|
+
rules: FPoolRules;
|
|
1434
1434
|
}
|
|
1435
1435
|
|
|
1436
|
-
/** What a pool's
|
|
1437
|
-
export type
|
|
1436
|
+
/** What a pool's policy says about it, by kind: the terms a buyer reads before entering. */
|
|
1437
|
+
export type FPoolRules =
|
|
1438
1438
|
| { kind: "pad"; harvestCut: bigint; rung: number; threshold: bigint; pot: bigint; creatorShare: bigint; protocolShare: bigint; burnPerRung: bigint }
|
|
1439
1439
|
| { kind: "pol"; harvestCut: bigint; keepQuote: bigint; minQuoteAtSeed: bigint }
|
|
1440
1440
|
| { kind: "public"; harvestCut: bigint; curated: boolean }
|
|
1441
1441
|
| { kind: "private"; harvestCut: bigint; gateMint: boolean; gateSwap: boolean; operator: Address };
|
|
1442
1442
|
|
|
1443
|
-
/** The
|
|
1444
|
-
export async function
|
|
1443
|
+
/** The policy's terms for one pool, by its kind. */
|
|
1444
|
+
export async function getFPoolRules(c: LogswapClient, s: Pick<FPoolState, "poolId" | "policy" | "kind" | "gen">, roles?: FPoolRoles): Promise<FPoolRules> {
|
|
1445
1445
|
const rd = <T>(abi: readonly unknown[], functionName: string, args: readonly unknown[] = []) =>
|
|
1446
|
-
c.public.readContract({ address: s.
|
|
1447
|
-
const harvestCut = await rd<bigint>(
|
|
1446
|
+
c.public.readContract({ address: s.policy, abi, functionName, args } as never) as Promise<T>;
|
|
1447
|
+
const harvestCut = await rd<bigint>(fPolicyBaseAbi, "harvestCut");
|
|
1448
1448
|
switch (s.kind) {
|
|
1449
1449
|
case "pad": {
|
|
1450
1450
|
const [rung, creatorShare, protocolShare, burnPerRung, pot] = await Promise.all([
|
|
1451
|
-
rd<bigint>(
|
|
1452
|
-
rd<bigint>(
|
|
1453
|
-
rd<bigint>(
|
|
1454
|
-
rd<bigint>(
|
|
1451
|
+
rd<bigint>(padPolicyAbi, "rung", [s.poolId]),
|
|
1452
|
+
rd<bigint>(padPolicyAbi, "creatorShare"),
|
|
1453
|
+
rd<bigint>(padPolicyAbi, "protocolShare"),
|
|
1454
|
+
rd<bigint>(padPolicyAbi, "burnPerRung"),
|
|
1455
1455
|
fPoolDividendOf(c, s.poolId, DEAD_ADDRESS, s.gen),
|
|
1456
1456
|
]);
|
|
1457
|
-
const threshold = await rd<bigint>(
|
|
1457
|
+
const threshold = await rd<bigint>(padPolicyAbi, "threshold", [rung]);
|
|
1458
1458
|
return { kind: "pad", harvestCut, rung: Number(rung), threshold, pot, creatorShare, protocolShare, burnPerRung };
|
|
1459
1459
|
}
|
|
1460
1460
|
case "pol": {
|
|
1461
|
-
const [keepQuote, minQuoteAtSeed] = await Promise.all([rd<bigint>(
|
|
1461
|
+
const [keepQuote, minQuoteAtSeed] = await Promise.all([rd<bigint>(polPolicyAbi, "keepQuote"), rd<bigint>(polPolicyAbi, "minQuoteAtSeed")]);
|
|
1462
1462
|
return { kind: "pol", harvestCut, keepQuote, minQuoteAtSeed };
|
|
1463
1463
|
}
|
|
1464
1464
|
case "public": {
|
|
1465
|
-
const curated = await rd<boolean>(
|
|
1465
|
+
const curated = await rd<boolean>(publicPolicyAbi, "curated");
|
|
1466
1466
|
return { kind: "public", harvestCut, curated };
|
|
1467
1467
|
}
|
|
1468
1468
|
case "private": {
|
|
@@ -1473,7 +1473,7 @@ export async function getFPoolPolicy(c: LogswapClient, s: Pick<FPoolState, "pool
|
|
|
1473
1473
|
}
|
|
1474
1474
|
|
|
1475
1475
|
/**
|
|
1476
|
-
* Every F pool `who` is the AUTHORITY of, across the four
|
|
1476
|
+
* Every F pool `who` is the AUTHORITY of, across the four policies — the policy page's list. Pools
|
|
1477
1477
|
* `who` created and handed over are not here (the buyer's page shows them); pools handed TO
|
|
1478
1478
|
* `who` are. `pools` may be passed (from the indexer, say) to skip the log scan.
|
|
1479
1479
|
*/
|
|
@@ -1485,7 +1485,7 @@ export async function fPoolsControlledBy(
|
|
|
1485
1485
|
const pools = opts.pools ?? (await discoverFPools(c, { fromBlock: opts.fromBlock }));
|
|
1486
1486
|
const authorities = await Promise.all(
|
|
1487
1487
|
pools.map((p) =>
|
|
1488
|
-
c.public.readContract({ address: p.
|
|
1488
|
+
c.public.readContract({ address: p.policy, abi: fPolicyBaseAbi, functionName: "authority", args: [p.poolId] } as never) as Promise<Address>,
|
|
1489
1489
|
),
|
|
1490
1490
|
);
|
|
1491
1491
|
const mine = pools.filter((_, i) => authorities[i]!.toLowerCase() === who.toLowerCase());
|
|
@@ -1493,8 +1493,8 @@ export async function fPoolsControlledBy(
|
|
|
1493
1493
|
mine.map(async (pool) => {
|
|
1494
1494
|
const state = await getFPool(c, pool.poolId);
|
|
1495
1495
|
const roles = await getFPoolRoles(c, pool.poolId, state);
|
|
1496
|
-
const
|
|
1497
|
-
return { pool, roles, state,
|
|
1496
|
+
const rules = await getFPoolRules(c, state, roles);
|
|
1497
|
+
return { pool, roles, state, rules };
|
|
1498
1498
|
}),
|
|
1499
1499
|
);
|
|
1500
1500
|
}
|