@logswap/sdk 0.5.49 → 0.5.51

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/fpool.js CHANGED
@@ -30,7 +30,7 @@
30
30
  * `addresses.router`.
31
31
  */
32
32
  import { encodeFunctionData, toFunctionSelector } from "viem";
33
- import { fPoolManagerAbi, logswapRouterAbi, padPolicyAbi, polPolicyAbi, privatePolicyAbi, publicPolicyAbi } from "./generated.js";
33
+ import { fPoolManagerAbi, logswapRouterAbi, padPolicyAbi, polPolicyAbi, policyRegistryAbi, privatePolicyAbi, publicPolicyAbi } from "./generated.js";
34
34
  /**
35
35
  * One contract, one ABI (decisions 039, F version 12): `harvest`, `claim`, `retire`, `regenerate`,
36
36
  * `setLegs`, the names, the fee parameters are `FPoolManager`'s own code. The roles left it for
@@ -61,7 +61,8 @@ export async function getFPool(c, poolId) {
61
61
  args,
62
62
  });
63
63
  // `getPool` returns the whole scalar struct in one call — the singleton's shape makes what used
64
- // to be a dozen getters a single read. No role in it since F version 15: those are the policy's.
64
+ // to be a dozen getters a single read. No role in it since F version 15, no kind and no name
65
+ // since 18: those are the policy's.
65
66
  const p = await rd("getPool", [poolId]);
66
67
  // A pool that was never created reads as an all-zero struct rather than reverting, so the
67
68
  // absence has to be named here or every caller mistakes "no such pool" for "a pool holding
@@ -72,12 +73,14 @@ export async function getFPool(c, poolId) {
72
73
  throw new Error(`logswap: no F pool with id ${poolId}`);
73
74
  const n = Number(p.n);
74
75
  const idx = Array.from({ length: n }, (_, i) => BigInt(i));
75
- const [theta, compositeX, legs] = await Promise.all([
76
+ const [theta, compositeX, legs, kind, name] = await Promise.all([
76
77
  // before the seed there is no L and no floor: θ is X by convention (the manager says so since
77
78
  // bd3dd87's successor; older deployments revert with DivWadFailed, so do not ask them)
78
79
  p.L > 0n ? rd("theta", [poolId]) : rd("compositeX", [poolId]),
79
80
  rd("compositeX", [poolId]),
80
81
  Promise.all(idx.map((i) => rd("legOf", [poolId, i]))),
82
+ fPoolKindOfPolicy(c, p.policy),
83
+ fPoolNameOf(c, poolId, p.policy),
81
84
  ]);
82
85
  const bases = legs.map((l) => l[0]);
83
86
  const weights = legs.map((l) => l[1]);
@@ -100,25 +103,46 @@ export async function getFPool(c, poolId) {
100
103
  bigSigma,
101
104
  policy,
102
105
  asks: Number(p.asks),
103
- name: bytes32ToText(p.name),
106
+ name,
104
107
  restructureBlock: Number(restructureBlock),
105
- kind: fPoolKindOf(p.kind),
108
+ kind,
106
109
  seeded,
107
110
  gen: Number(gen),
108
111
  totalSupply,
109
112
  reserves,
110
113
  };
111
114
  }
112
- /** The key's enum values. */
113
- export const F_KIND = { public: 0, private: 1, pol: 2, pad: 3 };
114
115
  const F_KIND_NAMES = ["public", "private", "pol", "pad"];
115
- export function fPoolKindOf(n) {
116
- const k = F_KIND_NAMES[Number(n)];
117
- if (!k)
118
- throw new Error(`logswap: unknown pool kind ${n}`);
119
- return k;
116
+ /** The policy's birth-time declaration that the seed's shares are the pool's (a pad's), in `asks`. */
117
+ export const SEED_TO_DEAD = 4;
118
+ const kindCache = new Map();
119
+ /**
120
+ * What a policy IS — its `kind()` tag, read once per policy address and cached for the client's
121
+ * chain: a pool's kind is its policy's word (decisions 049), never the manager's. An unknown tag
122
+ * (a policy that is not one of the protocol's four shapes) throws; the app has no screen for it.
123
+ */
124
+ export function fPoolKindOfPolicy(c, policy) {
125
+ const key = `${c.public.chain?.id ?? 0}:${policy.toLowerCase()}`;
126
+ let hit = kindCache.get(key);
127
+ if (!hit) {
128
+ hit = c.public.readContract({ address: policy, abi: fPolicyBaseAbi, functionName: "kind" }).then((word) => {
129
+ const tag = bytes32ToText(word);
130
+ if (!F_KIND_NAMES.includes(tag))
131
+ throw new Error(`logswap: policy ${policy} says it is "${tag}", which is no kind this SDK knows`);
132
+ return tag;
133
+ });
134
+ kindCache.set(key, hit);
135
+ hit.catch(() => kindCache.delete(key)); // a failed read is not a fact
136
+ }
137
+ return hit;
120
138
  }
121
- /** The deployment's policy for a kind (decisions 044): what a key of that kind names. */
139
+ /** The pool's label, as its policy holds it (decisions 049). "" when unnamed. */
140
+ export async function fPoolNameOf(c, poolId, policy) {
141
+ const p = policy ?? (await fPoolPolicyOf(c, poolId));
142
+ const word = (await c.public.readContract({ address: p, abi: fPolicyBaseAbi, functionName: "nameOf", args: [poolId] }));
143
+ return bytes32ToText(word);
144
+ }
145
+ /** The deployment's policy for a kind (decisions 044, 049): the protocol's policy that IS that kind. */
122
146
  export function fPoolPolicyFor(c, kind) {
123
147
  const a = c.addresses;
124
148
  const policy = kind === "public" ? a.publicPolicy : kind === "private" ? a.privatePolicy : kind === "pol" ? a.polPolicy : a.padPolicy;
@@ -541,8 +565,8 @@ export var FPoolQuoteKind;
541
565
  export async function fPoolQuoteSwap(c, a) {
542
566
  try {
543
567
  await c.public.simulateContract({
544
- address: c.addresses.fPoolManager,
545
- abi: fPoolManagerAbi,
568
+ address: c.addresses.router,
569
+ abi: logswapRouterAbi,
546
570
  functionName: "quoteSwap",
547
571
  // the taker matters only on a gated pool: the connected account, or the zero address
548
572
  args: [a.poolId, a.kind, BigInt(a.j), BigInt(a.k ?? 0), a.amountIn, a.taker ?? c.wallet?.account?.address ?? ZERO_ADDRESS],
@@ -606,7 +630,7 @@ export function sortFPoolLegs(bases, ...companions) {
606
630
  const idx = bases.map((_, i) => i).sort((a, b) => (bases[a].toLowerCase() < bases[b].toLowerCase() ? -1 : 1));
607
631
  return { bases: idx.map((i) => bases[i]), companions: companions.map((arr) => idx.map((i) => arr[i])) };
608
632
  }
609
- /** The key as the manager hashes it: legs sorted, the kind's policy filled in when none is named. */
633
+ /** The key as the manager hashes it: legs sorted, the kind's policy filled in when none is named; no kind in it (049). */
610
634
  export function fPoolKeyOf(c, a) {
611
635
  const { bases, companions } = sortFPoolLegs(a.bases, a.weights);
612
636
  return {
@@ -614,7 +638,6 @@ export function fPoolKeyOf(c, a) {
614
638
  bases,
615
639
  weights: companions[0],
616
640
  phi: a.phi,
617
- kind: F_KIND[a.kind],
618
641
  policy: a.policy ?? fPoolPolicyFor(c, a.kind),
619
642
  creator: a.creator,
620
643
  salt: a.salt ?? ZERO_SALT,
@@ -769,7 +792,7 @@ export function fPoolLaunchCalls(poolId, a) {
769
792
  const { bases, companions } = sortFPoolLegs(a.bases, a.weights, given);
770
793
  if (!a.policy)
771
794
  throw new Error("logswap: fPoolLaunchCalls needs the key's policy — fPoolLaunch fills it from the deployment");
772
- 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 };
795
+ const key = { quote: a.quote, bases, weights: companions[0], phi: a.phi, policy: a.policy, creator: a.creator, salt: a.salt ?? ZERO_SALT };
773
796
  const enc = (functionName, args) => encodeFunctionData({ abi: fPoolManagerAbi, functionName, args });
774
797
  const calls = [];
775
798
  if (!a.initialized)
@@ -783,23 +806,22 @@ export function fPoolLaunchCalls(poolId, a) {
783
806
  throw new Error(`logswap: openBuy.j = ${a.openBuy.j} is not a leg`);
784
807
  calls.push(enc("swapQuoteIn", [poolId, BigInt(j), a.openBuy.amountIn, a.openBuy.minOut ?? 0n, a.account]));
785
808
  }
786
- if (a.name)
787
- calls.push(enc("setName", [poolId, textToBytes32(a.name)]));
788
- // the desk's gates and list are the POLICY's (decisions 044), not the manager's: they cannot ride
789
- // in this batch — `fPoolDeskOpening` sends them right after, as the same creator
809
+ // the name, the desk's gates and its list are the POLICY's (decisions 044, 049), not the
810
+ // manager's: they cannot ride in this batch — `fPoolLaunch` and `fPoolDeskOpening` send them
811
+ // right after, as the same creator
790
812
  calls.push(...(a.extra ?? []));
791
813
  return calls;
792
814
  }
793
815
  /**
794
- * The desk's opening posture, on its policy (decisions 044): gated minting, the creator listed. No
795
- * appointment: the authority is the operator until one is appointed (decisions 037). Two writes
796
- * to `PrivatePolicy`; the launch batch cannot carry them (they are not manager calls).
816
+ * The desk's opening posture, on its policy (decisions 044, 048): gated minting, the creator a
817
+ * minter. No appointment: the authority is the operator until one is appointed (decisions 037).
818
+ * Two writes to `PrivatePolicy`; the launch batch cannot carry them (they are not manager calls).
797
819
  */
798
820
  export async function fPoolDeskOpening(c, a) {
799
821
  const policy = a.policy ?? fPoolPolicyFor(c, "private");
800
822
  return [
801
823
  await fPoolSetGates(c, { poolId: a.poolId, gateMint: true, gateSwap: false, account: a.account, policy }),
802
- await fPoolSetAllowed(c, { poolId: a.poolId, who: [a.account], allowed: true, account: a.account, policy }),
824
+ await fPoolSetMinters(c, { poolId: a.poolId, who: [a.account], allowed: true, account: a.account, policy }),
803
825
  ];
804
826
  }
805
827
  /**
@@ -821,8 +843,11 @@ export async function fPoolDeskOpening(c, a) {
821
843
  */
822
844
  export async function fPoolLaunch(c, a) {
823
845
  const poolId = await fPoolIdOf(c, a);
824
- const calls = fPoolLaunchCalls(poolId, { ...a, policy: a.policy ?? fPoolPolicyFor(c, a.kind) });
846
+ const policy = a.policy ?? fPoolPolicyFor(c, a.kind);
847
+ const calls = fPoolLaunchCalls(poolId, { ...a, policy });
825
848
  const hash = await writeFPool0(c, "multicall", [calls], a.account);
849
+ if (a.name)
850
+ await fPoolSetName(c, { poolId, name: a.name, account: a.account, policy });
826
851
  return { hash, poolId };
827
852
  }
828
853
  // ─── the roles, on the policy (decisions 044) ─────────────────────────────────────────────
@@ -864,51 +889,75 @@ export async function fPoolSetGates(c, a) {
864
889
  return writePolicy(c, privatePolicyAbi, a, "setGates", [a.gateMint, a.gateSwap]);
865
890
  }
866
891
  /**
867
- * Add to or remove from the pool's list, in one call (the authority, on the desk's policy). The list
868
- * is keyed by the authority that granted it: a handover starts the buyer with an empty one.
892
+ * Add to or remove from the pool's MINTERS — who may mint, and swap (a zap in is both under one
893
+ * lock) — in one call (the authority, on the desk's policy; decisions 048). The list is keyed by
894
+ * the authority that granted it: a handover starts the buyer with an empty one. Stamps the
895
+ * block: no swap lands in it (047).
869
896
  */
870
- export async function fPoolSetAllowed(c, a) {
871
- return writePolicy(c, privatePolicyAbi, a, "setAllowed", [a.who, a.allowed]);
897
+ export async function fPoolSetMinters(c, a) {
898
+ return writePolicy(c, privatePolicyAbi, a, "setMinters", [a.who, a.allowed]);
872
899
  }
873
- /** Whether `who` is on the pool's list under its current authority (consulted only while a gate is on). */
874
- export async function fPoolIsAllowed(c, poolId, who, policy) {
900
+ /** Add to or remove from the pool's TAKERS — who may swap only — in one call (the authority; decisions 048). Stamps the block. */
901
+ export async function fPoolSetTakers(c, a) {
902
+ return writePolicy(c, privatePolicyAbi, a, "setTakers", [a.who, a.allowed]);
903
+ }
904
+ async function readDesk(c, poolId, who, functionName, policy) {
875
905
  const address = policy ?? (await fPoolPolicyOf(c, poolId));
876
- return (await c.public.readContract({
877
- address,
878
- abi: privatePolicyAbi,
879
- functionName: "isAllowed",
880
- args: [poolId, who],
881
- }));
906
+ return (await c.public.readContract({ address, abi: privatePolicyAbi, functionName, args: [poolId, who] }));
907
+ }
908
+ /** On the pool's minters under its current authority: may mint, and swap. */
909
+ export async function fPoolIsMinter(c, poolId, who, policy) {
910
+ return readDesk(c, poolId, who, "isMinter", policy);
911
+ }
912
+ /** On the pool's takers under its current authority: may swap only. */
913
+ export async function fPoolIsTaker(c, poolId, who, policy) {
914
+ return readDesk(c, poolId, who, "isTaker", policy);
882
915
  }
883
- /** Label the pool — the sponsor's own words, mutable, never identity. At most 31 bytes of UTF-8. */
916
+ /** May swap: a minter or a taker (consulted only while the swap gate is on). */
917
+ export async function fPoolIsAllowed(c, poolId, who, policy) {
918
+ return readDesk(c, poolId, who, "isAllowed", policy);
919
+ }
920
+ /** Label the pool — the authority's own words on the pool's POLICY (decisions 049), mutable, never identity. At most 31 bytes of UTF-8. */
884
921
  export async function fPoolSetName(c, a) {
885
- return writeFPool(c, a.poolId, "setName", [textToBytes32(a.name)], a.account);
922
+ return writePolicy(c, fPolicyBaseAbi, a, "setName", [textToBytes32(a.name)]);
886
923
  }
887
924
  /**
888
- * What the PROTOCOL says an address is — "Keyrock". Only `feeCollector` may write it, which is
889
- * what makes it an attestation rather than a claim anyone could make about themselves.
925
+ * What the PROTOCOL says an address is — "Keyrock" — on the REGISTRY (decisions 049). Only its
926
+ * owner, the LOG authority, may write it, which is what makes it an attestation rather than a
927
+ * claim anyone could make about themselves.
890
928
  */
891
929
  export async function fPoolSetSponsorName(c, a) {
892
930
  const wallet = requireWallet(c);
931
+ const registry = await registryAddress(c);
893
932
  const { request } = await c.public.simulateContract({
894
- address: c.addresses.fPoolManager,
895
- abi: fPoolManagerAbi,
933
+ address: registry,
934
+ abi: policyRegistryAbi,
896
935
  functionName: "setSponsorName",
897
936
  args: [a.who, textToBytes32(a.name)],
898
937
  account: a.account,
899
938
  });
900
939
  return wallet.writeContract(request);
901
940
  }
902
- /** The protocol's attestation for an address, or "" when unattested — which most addresses are. */
941
+ /** The protocol's attestation for an address, or "" when unattested — which most addresses are, and every address on a deployment with no registry. */
903
942
  export async function fPoolSponsorName(c, who) {
904
- const word = (await c.public.readContract({
905
- address: c.addresses.fPoolManager,
906
- abi: fPoolManagerAbi,
907
- functionName: "sponsorName",
908
- args: [who],
909
- }));
943
+ const registry = c.addresses.policyRegistry ?? (await fPoolRegistryOf(c));
944
+ if (registry === ZERO_ADDRESS)
945
+ return "";
946
+ const word = (await c.public.readContract({ address: registry, abi: policyRegistryAbi, functionName: "sponsorName", args: [who] }));
910
947
  return bytes32ToText(word);
911
948
  }
949
+ /** The registry the manager asks at every birth — `addresses.policyRegistry`, else the manager's word; zero on a bare deployment. */
950
+ export async function fPoolRegistryOf(c) {
951
+ if (c.addresses.policyRegistry)
952
+ return c.addresses.policyRegistry;
953
+ return (await c.public.readContract({ address: c.addresses.fPoolManager, abi: fPoolManagerAbi, functionName: "registry" }));
954
+ }
955
+ async function registryAddress(c) {
956
+ const registry = await fPoolRegistryOf(c);
957
+ if (registry === ZERO_ADDRESS)
958
+ throw new Error("logswap: this deployment carries no policy registry");
959
+ return registry;
960
+ }
912
961
  /** Who runs the pool (decisions 037): the appointed operator, or the authority until one is. */
913
962
  export function fPoolOperatorOf(s) {
914
963
  return s.operator === ZERO_ADDRESS ? s.authority : s.operator;
@@ -954,9 +1003,10 @@ export async function fPoolTransferShares(c, a) {
954
1003
  return wallet.writeContract(request);
955
1004
  }
956
1005
  /**
957
- * Every F pool ever created, from `Initialize` logs alone. Possible only because the event
958
- * carries the full key preimage (bases, weights, phi, pad) — the poolId is a hash and could
959
- * never be inverted; contracts PR #17 exists for exactly this call.
1006
+ * Every F pool ever created, from `Initialize` logs alone — plus one `kind()` read per distinct
1007
+ * policy, cached. Possible only because the event carries the full key preimage (bases,
1008
+ * weights, phi, policy) — the poolId is a hash and could never be inverted; contracts PR #17
1009
+ * exists for exactly this call.
960
1010
  */
961
1011
  export async function discoverFPools(c, opts = {}) {
962
1012
  const ev = fPoolManagerAbi.find((x) => x.type === "event" && x.name === "Initialize");
@@ -979,24 +1029,25 @@ export async function discoverFPools(c, opts = {}) {
979
1029
  const a = l.args;
980
1030
  q0Of.set(a.poolId.toLowerCase(), a.Q0);
981
1031
  }
982
- return logs.map((l) => {
1032
+ return Promise.all(logs.map(async (l) => {
983
1033
  const a = l.args;
984
1034
  const q0 = q0Of.get(a.poolId.toLowerCase());
1035
+ const kind = await fPoolKindOfPolicy(c, a.policy); // one read per distinct policy, cached
985
1036
  return {
986
1037
  poolId: a.poolId,
987
1038
  quote: a.quote,
988
1039
  bases: [...a.bases],
989
1040
  weights: [...a.weights],
990
1041
  phi: a.phi,
991
- kind: fPoolKindOf(a.kind),
1042
+ kind,
992
1043
  policy: a.policy,
993
1044
  creator: a.creator,
994
1045
  block: l.blockNumber ?? 0n,
995
1046
  seeded: q0 !== undefined,
996
1047
  q0: q0 ?? 0n,
997
- shape: a.kind === F_KIND.pad ? "launch" : "basket", // the kind, not n = 1: a mono POL is a pool (decisions 036)
1048
+ shape: kind === "pad" ? "launch" : "basket", // the policy's word, not n = 1: a mono POL is a pool (decisions 036, 049)
998
1049
  };
999
- });
1050
+ }));
1000
1051
  }
1001
1052
  /** The policy's terms for one pool, by its kind. */
1002
1053
  export async function getFPoolRules(c, s, roles) {
@@ -1035,14 +1086,19 @@ export async function getFPoolRules(c, s, roles) {
1035
1086
  */
1036
1087
  export async function fPoolsControlledBy(c, who, opts = {}) {
1037
1088
  const pools = opts.pools ?? (await discoverFPools(c, { fromBlock: opts.fromBlock }));
1038
- const authorities = await Promise.all(pools.map((p) => c.public.readContract({ address: p.policy, abi: fPolicyBaseAbi, functionName: "authority", args: [p.poolId] })));
1039
- const mine = pools.filter((_, i) => authorities[i].toLowerCase() === who.toLowerCase());
1040
- return Promise.all(mine.map(async (pool) => {
1041
- const state = await getFPool(c, pool.poolId);
1042
- const roles = await getFPoolRoles(c, pool.poolId, state);
1089
+ // the LIVE policy answers for the authority: a moved pool (046) is on another one than its birth's
1090
+ const states = await Promise.all(pools.map((p) => getFPool(c, p.poolId)));
1091
+ const authorities = await Promise.all(states.map((s) => c.public.readContract({ address: s.policy, abi: fPolicyBaseAbi, functionName: "authority", args: [s.poolId] })));
1092
+ const out = [];
1093
+ for (let i = 0; i < pools.length; ++i) {
1094
+ if (authorities[i].toLowerCase() !== who.toLowerCase())
1095
+ continue;
1096
+ const state = states[i];
1097
+ const roles = await getFPoolRoles(c, state.poolId, state);
1043
1098
  const rules = await getFPoolRules(c, state, roles);
1044
- return { pool, roles, state, rules };
1045
- }));
1099
+ out.push({ pool: pools[i], roles, state, rules });
1100
+ }
1101
+ return out;
1046
1102
  }
1047
1103
  /**
1048
1104
  * Every holder of a pool's shares, from the ERC-6909 Transfer stream — the id is an indexed