@logswap/sdk 0.5.68 → 0.5.70

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/src/fpool.ts CHANGED
@@ -30,7 +30,7 @@
30
30
  * `addresses.router`.
31
31
  */
32
32
 
33
- import { encodeAbiParameters, encodeFunctionData, keccak256, toFunctionSelector, type Address, type Hash, type Hex } from "viem";
33
+ import { decodeAbiParameters, encodeAbiParameters, encodeFunctionData, erc20Abi, keccak256, toFunctionSelector, type Address, type Hash, type Hex } from "viem";
34
34
  import { fPoolManagerAbi, logswapRouterAbi, padPolicyAbi, polPolicyAbi, policyRegistryAbi, privatePolicyAbi, publicPolicyAbi } from "./generated.js";
35
35
 
36
36
  /**
@@ -42,8 +42,8 @@ import { fPoolManagerAbi, logswapRouterAbi, padPolicyAbi, polPolicyAbi, policyRe
42
42
  */
43
43
  export { fPoolManagerAbi, padPolicyAbi, polPolicyAbi, privatePolicyAbi, publicPolicyAbi };
44
44
  /**
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
45
+ * What every protocol policy shares (`FPolicyBase`): the roles and their handover and the
46
+ * `maxHarvest` readout (a harvest has no cut, decisions 056). `polPolicyAbi` is a superset of it and carries nothing that collides, so it stands
47
47
  * in for the base — a call through it to any of the four policies decodes the base's functions.
48
48
  */
49
49
  export const fPolicyBaseAbi = polPolicyAbi;
@@ -1079,7 +1079,7 @@ export async function fPoolIdOf(c: LogswapClient, a: Omit<FPoolCreateArgs, "acco
1079
1079
  /**
1080
1080
  * Rebuild a live pool's KEY from its id: the `FInitialize` log carries every field but the salt,
1081
1081
  * which is taken from `opts.salt` (zero by default — every pool the SDK's create flow makes
1082
- * without naming one). The result is checked against the id, so a wrong salt throws rather than
1082
+ * without naming one; a pad's carries its split, so it is rebuilt from the policy's record with nonce 0 — pass `opts.salt` for another nonce). The result is checked against the id, so a wrong salt throws rather than
1083
1083
  * returning a key that names another pool. What {@link fPoolMigrate} needs when handed an id.
1084
1084
  */
1085
1085
  export async function fPoolKeyOfPool(c: LogswapClient, poolId: Hex, opts: { salt?: Hex; fromBlock?: bigint } = {}): Promise<FPoolKey> {
@@ -1104,6 +1104,16 @@ export async function fPoolKeyOfPool(c: LogswapClient, poolId: Hex, opts: { salt
1104
1104
  creator: a.creator,
1105
1105
  salt: opts.salt ?? ZERO_SALT,
1106
1106
  };
1107
+ if (fPoolIdOfKey(key).toLowerCase() !== poolId.toLowerCase() && opts.salt === undefined) {
1108
+ // a pad's salt carries its split (decisions 056): rebuild it from the policy's record with the default nonce
1109
+ try {
1110
+ const [creatorShare, burnPerRung] = (await c.public.readContract({ address: a.policy, abi: padPolicyAbi, functionName: "splitOf", args: [poolId] } as never)) as readonly [bigint, bigint, bigint];
1111
+ const padKey = { ...key, salt: padSalt({ creatorShare, burnPerRung }) };
1112
+ if (fPoolIdOfKey(padKey).toLowerCase() === poolId.toLowerCase()) return padKey;
1113
+ } catch {
1114
+ /* not a pad: fall through to the error */
1115
+ }
1116
+ }
1107
1117
  if (fPoolIdOfKey(key).toLowerCase() !== poolId.toLowerCase()) {
1108
1118
  throw new Error(`logswap: F pool ${poolId} was not born with salt ${key.salt} — pass its salt, or its whole key`);
1109
1119
  }
@@ -1252,6 +1262,29 @@ export const DEAD_ADDRESS: Address = "0x000000000000000000000000000000000000dEaD
1252
1262
  /** The manager's `MIN_SHARES`: the seed mints this much to 0xdead on every pool, the rest to the seeder — or, on a pad, to 0xdead too. */
1253
1263
  export const F_MIN_SHARES = 1000n;
1254
1264
 
1265
+ /**
1266
+ * One genesis transaction: a swap the creator sends right after the seed, inside the launch's
1267
+ * `multicall`. A `buy` spends `amountIn` of quote for leg `j` (an index into `bases` AS GIVEN); a
1268
+ * `sell` spends `amountIn` of that leg for quote. The creator pays and is paid — the manager settles
1269
+ * to its sender — so a `recipient` is paid by an ERC-20 transfer of the row's output
1270
+ * ({@link fPoolGenesisTransfers}); without one the output stays with the creator.
1271
+ */
1272
+ export interface FPoolGenesisRow {
1273
+ side: "buy" | "sell";
1274
+ j: number;
1275
+ amountIn: bigint;
1276
+ /** Defaults to 0. With a `recipient`, {@link fPoolLaunch} pins it to the simulated output, which is what the transfer pays. */
1277
+ minOut?: bigint;
1278
+ recipient?: Address;
1279
+ }
1280
+
1281
+ /** An ERC-20 transfer owed to a genesis recipient: one per (token, recipient), rows summed. */
1282
+ export interface FPoolGenesisTransfer {
1283
+ token: Address;
1284
+ to: Address;
1285
+ amount: bigint;
1286
+ }
1287
+
1255
1288
  export interface FPoolLaunchArgs extends Omit<FPoolCreateArgs, "account"> {
1256
1289
  L0: bigint;
1257
1290
  /** Quote the seed pulls. A pad seeds at 0 — pure float, a resting ask, nothing raised. */
@@ -1264,6 +1297,18 @@ export interface FPoolLaunchArgs extends Omit<FPoolCreateArgs, "account"> {
1264
1297
  * this buy — they are one transaction — so `minOut` is safe at its default of 0.
1265
1298
  */
1266
1299
  openBuy?: { j: number; amountIn: bigint; minOut?: bigint };
1300
+ /**
1301
+ * The genesis transactions: an ordered list of swaps right after the seed, each with an optional
1302
+ * recipient ({@link FPoolGenesisRow}). The same batch as `openBuy`, which stays as the one-buy
1303
+ * case and runs first when both are given. See `specs/f-pools/opening.md`.
1304
+ */
1305
+ openBatch?: FPoolGenesisRow[];
1306
+ /**
1307
+ * How a row's recipient is paid. `auto` (default): in the launch's own transaction when the
1308
+ * wallet batches atomically (EIP-5792), else left to {@link fPoolSendGenesisTransfers};
1309
+ * `followup`: always left to it. Ignored when no row names a recipient.
1310
+ */
1311
+ deliver?: "auto" | "followup";
1267
1312
  /** The pool's label (≤ 31 bytes of UTF-8) — the policy's word (049), written right after the batch by {@link fPoolLaunch}; not in the batch. */
1268
1313
  name?: string;
1269
1314
  /**
@@ -1301,10 +1346,10 @@ export function fPoolLaunchCalls(
1301
1346
  calls.push(enc("seed", [poolId, a.L0, companions[1]!, a.Q0]));
1302
1347
  // gen 0 by construction: a launch is the pool's FIRST seed — a relaunch is `fPoolRelaunch`.
1303
1348
  // No transfer to 0xdead: on a pad `seed` mints every share there itself.
1304
- if (a.openBuy) {
1305
- const j = bases.indexOf(a.bases[a.openBuy.j]!);
1306
- if (j < 0) throw new Error(`logswap: openBuy.j = ${a.openBuy.j} is not a leg`);
1307
- calls.push(enc("swapQuoteIn", [poolId, BigInt(j), a.openBuy.amountIn, a.openBuy.minOut ?? 0n, a.account]));
1349
+ for (const row of fPoolGenesisRows(a)) {
1350
+ const j = bases.indexOf(a.bases[row.j]!);
1351
+ if (j < 0) throw new Error(`logswap: genesis row j = ${row.j} is not a leg`);
1352
+ calls.push(enc(row.side === "buy" ? "swapQuoteIn" : "swapBaseIn", [poolId, BigInt(j), row.amountIn, row.minOut ?? 0n, a.account]));
1308
1353
  }
1309
1354
  // the name, the desk's gates and its list are the POLICY's, not the
1310
1355
  // manager's: they cannot ride in this batch — `fPoolLaunch` and `fPoolDeskOpening` send them
@@ -1313,6 +1358,87 @@ export function fPoolLaunchCalls(
1313
1358
  return calls;
1314
1359
  }
1315
1360
 
1361
+ /** The genesis rows in the order they run: `openBuy` first when given, then `openBatch`. Legs as given. */
1362
+ export function fPoolGenesisRows(a: Pick<FPoolLaunchArgs, "openBuy" | "openBatch">): FPoolGenesisRow[] {
1363
+ return [...(a.openBuy ? [{ side: "buy" as const, ...a.openBuy }] : []), ...(a.openBatch ?? [])];
1364
+ }
1365
+
1366
+ /**
1367
+ * What each genesis row returned, read by simulating the launch's `multicall` on the chain — the
1368
+ * contract's own rounding, no re-implementation. Exact at send time: the pool is new, and nothing
1369
+ * can trade between the seed and the rows. Needs the creator's funds and allowances, as the launch
1370
+ * does. One output per row, in {@link fPoolGenesisRows} order: base for a buy, quote for a sell.
1371
+ */
1372
+ export async function fPoolGenesisOutputs(
1373
+ c: LogswapClient,
1374
+ poolId: Hex,
1375
+ a: FPoolLaunchArgs & ({ r0: bigint[] } | { x0: bigint[] }),
1376
+ ): Promise<bigint[]> {
1377
+ const rows = fPoolGenesisRows(a);
1378
+ if (rows.length === 0) return [];
1379
+ const calls = fPoolLaunchCalls(poolId, a);
1380
+ const { result } = await c.public.simulateContract({
1381
+ address: c.addresses.fPoolManager!,
1382
+ abi: fPoolManagerAbi,
1383
+ functionName: "multicall",
1384
+ args: [calls],
1385
+ account: signerFor(c, a.account),
1386
+ } as never);
1387
+ const results = result as readonly Hex[];
1388
+ const first = (a.initialized ? 0 : 1) + 1; // [initialize] · seed · rows
1389
+ return rows.map((_, i) => decodeAbiParameters([{ type: "uint256" }], results[first + i]!)[0] as bigint);
1390
+ }
1391
+
1392
+ /**
1393
+ * The transfers that pay the recipients: each row's output (`outs`, aligned with
1394
+ * {@link fPoolGenesisRows}) to its `recipient`, summed per (token, recipient), zero amounts dropped.
1395
+ * The token is the leg's base for a buy and the quote for a sell. Pure.
1396
+ */
1397
+ export function fPoolGenesisTransfers(
1398
+ a: Pick<FPoolLaunchArgs, "quote" | "bases" | "openBuy" | "openBatch">,
1399
+ outs: bigint[],
1400
+ ): FPoolGenesisTransfer[] {
1401
+ const out = new Map<string, FPoolGenesisTransfer>();
1402
+ fPoolGenesisRows(a).forEach((row, i) => {
1403
+ if (!row.recipient || !outs[i]) return;
1404
+ const token = row.side === "buy" ? a.bases[row.j]! : a.quote;
1405
+ const k = `${token.toLowerCase()}:${row.recipient.toLowerCase()}`;
1406
+ const t = out.get(k);
1407
+ if (t) t.amount += outs[i]!;
1408
+ else out.set(k, { token, to: row.recipient, amount: outs[i]! });
1409
+ });
1410
+ return [...out.values()];
1411
+ }
1412
+
1413
+ /** Pay genesis recipients with plain ERC-20 transfers, one transaction each, in order. For a launch that did not deliver itself. */
1414
+ export async function fPoolSendGenesisTransfers(c: LogswapClient, a: { transfers: FPoolGenesisTransfer[]; account: Address }): Promise<Hash[]> {
1415
+ const wallet = requireWallet(c);
1416
+ const hashes: Hash[] = [];
1417
+ for (const t of a.transfers) {
1418
+ const { request } = await c.public.simulateContract({
1419
+ address: t.token,
1420
+ abi: erc20Abi,
1421
+ functionName: "transfer",
1422
+ args: [t.to, t.amount],
1423
+ account: signerFor(c, a.account),
1424
+ } as never);
1425
+ hashes.push(await wallet.writeContract(request as never));
1426
+ }
1427
+ return hashes;
1428
+ }
1429
+
1430
+ /** Whether the wallet runs a batch of calls atomically (EIP-5792 `atomic` capability), on the client's chain. A wallet without it, or one that errors, says no. */
1431
+ export async function fPoolWalletBatchesAtomically(c: LogswapClient, account: Address): Promise<boolean> {
1432
+ try {
1433
+ const wallet = requireWallet(c);
1434
+ const chainId = wallet.chain?.id;
1435
+ const caps = (await wallet.getCapabilities({ account, chainId })) as { atomic?: { status?: string } };
1436
+ return caps?.atomic?.status === "supported" || caps?.atomic?.status === "ready";
1437
+ } catch {
1438
+ return false;
1439
+ }
1440
+ }
1441
+
1316
1442
  /**
1317
1443
  * The desk's opening posture, on its policy: gated minting, the creator a
1318
1444
  * minter. No appointment: the authority is the operator until one is appointed.
@@ -1346,13 +1472,42 @@ export async function fPoolDeskOpening(c: LogswapClient, a: { poolId: Hex; accou
1346
1472
  export async function fPoolLaunch(
1347
1473
  c: LogswapClient,
1348
1474
  a: FPoolLaunchArgs & ({ r0: bigint[] } | { x0: bigint[] }),
1349
- ): Promise<{ hash: Hash; poolId: Hex }> {
1475
+ ): Promise<{ hash: Hash; poolId: Hex; transfers: FPoolGenesisTransfer[]; delivered: boolean }> {
1350
1476
  const poolId = await fPoolIdOf(c, a);
1351
1477
  const policy = a.policy ?? fPoolPolicyFor(c, a.kind);
1352
- const calls = fPoolLaunchCalls(poolId, { ...a, policy });
1353
- const hash = await writeFPool0(c, "multicall", [calls], a.account);
1478
+ let args = { ...a, policy };
1479
+ // With recipients, each row's minOut is pinned to its simulated output and the transfers pay
1480
+ // exactly that: a row that returns less reverts the launch, a wei of surplus stays with the creator.
1481
+ let transfers: FPoolGenesisTransfer[] = [];
1482
+ const rows = fPoolGenesisRows(a);
1483
+ if (rows.some((r) => r.recipient)) {
1484
+ const outs = await fPoolGenesisOutputs(c, poolId, args);
1485
+ args = { ...args, openBuy: undefined, openBatch: rows.map((r, i) => ({ ...r, minOut: outs[i]! })) };
1486
+ transfers = fPoolGenesisTransfers(args, outs);
1487
+ }
1488
+ const calls = fPoolLaunchCalls(poolId, args);
1489
+ let hash: Hash;
1490
+ let delivered = transfers.length === 0;
1491
+ if (transfers.length > 0 && a.deliver !== "followup" && (await fPoolWalletBatchesAtomically(c, a.account))) {
1492
+ const wallet = requireWallet(c);
1493
+ const { id } = await wallet.sendCalls({
1494
+ account: signerFor(c, a.account),
1495
+ forceAtomic: true,
1496
+ calls: [
1497
+ { to: c.addresses.fPoolManager!, data: encodeFunctionData({ abi: fPoolManagerAbi, functionName: "multicall", args: [calls] } as never) },
1498
+ ...transfers.map((t) => ({ to: t.token, data: encodeFunctionData({ abi: erc20Abi, functionName: "transfer", args: [t.to, t.amount] }) })),
1499
+ ],
1500
+ } as never);
1501
+ const status = await wallet.waitForCallsStatus({ id });
1502
+ const receipt = status.receipts?.[0];
1503
+ if (status.status !== "success" || !receipt) throw new Error("logswap: the launch batch did not succeed — nothing was created");
1504
+ hash = receipt.transactionHash;
1505
+ delivered = true;
1506
+ } else {
1507
+ hash = await writeFPool0(c, "multicall", [calls], a.account);
1508
+ }
1354
1509
  if (a.name) await fPoolSetName(c, { poolId, name: a.name, account: a.account, policy });
1355
- return { hash, poolId };
1510
+ return { hash, poolId, transfers, delivered };
1356
1511
  }
1357
1512
 
1358
1513
  // ─── the roles, on the policy ─────────────────────────────────────────────
@@ -1640,7 +1795,6 @@ export interface FPoolControlled {
1640
1795
  export type FPoolRules =
1641
1796
  | {
1642
1797
  kind: "pad";
1643
- harvestCut: bigint;
1644
1798
  /** The pad's graduation count: the pool's generation (F version 21 — `PadPolicy.rung` is gone). */
1645
1799
  gen: number;
1646
1800
  /** @deprecated Equal to {@link gen} since F version 21, kept for callers; read `gen`. */
@@ -1648,43 +1802,81 @@ export type FPoolRules =
1648
1802
  /** The pot the next graduation needs: `PadPolicy.threshold(gen)`. */
1649
1803
  threshold: bigint;
1650
1804
  pot: bigint;
1805
+ /** This pad's split, recorded at its birth from the key's salt and never changed (decisions 056): the creator's cut of the pot, the protocol's, and the burn per rung. The rest of the pot is bought in. */
1651
1806
  creatorShare: bigint;
1652
1807
  protocolShare: bigint;
1653
1808
  burnPerRung: bigint;
1809
+ /** The ranges the collector's policy holds NOW for the pads born after this read: what a new creator may choose. */
1810
+ ranges: PadRanges;
1654
1811
  }
1655
- | { kind: "pol"; harvestCut: bigint; keepQuote: bigint; minQuoteAtSeed: bigint }
1656
- | { kind: "public"; harvestCut: bigint; curated: boolean }
1657
- | { kind: "private"; harvestCut: bigint; gateMint: boolean; gateSwap: boolean; operator: Address };
1812
+ | { kind: "pol"; keepQuote: bigint; minQuoteAtSeed: bigint }
1813
+ | { kind: "public"; curated: boolean }
1814
+ | { kind: "private"; gateMint: boolean; gateSwap: boolean; operator: Address };
1815
+
1816
+ /** The collector's ranges on a pad's creator choices (`PadPolicy`, decisions 056), WAD fractions: of the pot, and of every reserve per rung. */
1817
+ export interface PadRanges {
1818
+ /** The protocol's share of the pot, recorded into each pad at birth. */
1819
+ protocolShare: bigint;
1820
+ creatorShareMin: bigint;
1821
+ creatorShareMax: bigint;
1822
+ burnMin: bigint;
1823
+ burnMax: bigint;
1824
+ }
1825
+
1826
+ const MAX_U64 = (1n << 64n) - 1n;
1827
+ const MAX_U128 = (1n << 128n) - 1n;
1828
+
1829
+ /**
1830
+ * The salt that carries a pad's split (`PadPolicy.pack`, decisions 056): `creatorShare` in the top 64 bits, `burnPerRung` in
1831
+ * the next 64 (WAD fractions), a nonce in the low 128 that tells two otherwise identical pads apart. It is part of the pool's
1832
+ * identity, so it is chosen at launch (`FPoolLaunchArgs.salt`) and never changes; the policy refuses a split outside its ranges.
1833
+ */
1834
+ export function padSalt(s: { creatorShare: bigint; burnPerRung: bigint; nonce?: bigint }): Hex {
1835
+ const nonce = s.nonce ?? 0n;
1836
+ if (s.creatorShare < 0n || s.creatorShare > MAX_U64 || s.burnPerRung < 0n || s.burnPerRung > MAX_U64) throw new Error("logswap: a pad's split is a WAD fraction that fits 64 bits");
1837
+ if (nonce < 0n || nonce > MAX_U128) throw new Error("logswap: a pad salt's nonce fits 128 bits");
1838
+ return `0x${((s.creatorShare << 192n) | (s.burnPerRung << 128n) | nonce).toString(16).padStart(64, "0")}` as Hex;
1839
+ }
1840
+
1841
+ /** The inverse of {@link padSalt}. */
1842
+ export function padSplitOfSalt(salt: Hex): { creatorShare: bigint; burnPerRung: bigint; nonce: bigint } {
1843
+ const v = BigInt(salt);
1844
+ return { creatorShare: v >> 192n, burnPerRung: (v >> 128n) & MAX_U64, nonce: v & MAX_U128 };
1845
+ }
1658
1846
 
1659
1847
  /** The policy's terms for one pool, by its kind. */
1660
1848
  export async function getFPoolRules(c: LogswapClient, s: Pick<FPoolState, "poolId" | "policy" | "kind" | "gen">, roles?: FPoolRoles): Promise<FPoolRules> {
1661
1849
  const rd = <T>(abi: readonly unknown[], functionName: string, args: readonly unknown[] = []) =>
1662
1850
  c.public.readContract({ address: s.policy, abi, functionName, args } as never) as Promise<T>;
1663
- const harvestCut = await rd<bigint>(fPolicyBaseAbi, "harvestCut");
1664
1851
  switch (s.kind) {
1665
1852
  case "pad": {
1666
1853
  // the rung is the manager's generation (F version 21): a move carries it, nothing to ask the policy
1667
1854
  const gen = s.gen;
1668
- const [creatorShare, protocolShare, burnPerRung, pot, threshold] = await Promise.all([
1669
- rd<bigint>(padPolicyAbi, "creatorShare"),
1670
- rd<bigint>(padPolicyAbi, "protocolShare"),
1671
- rd<bigint>(padPolicyAbi, "burnPerRung"),
1855
+ const [split, pot, threshold, protocolNow, creatorShareMin, creatorShareMax, burnMin, burnMax] = await Promise.all([
1856
+ rd<readonly [bigint, bigint, bigint]>(padPolicyAbi, "splitOf", [s.poolId]),
1672
1857
  fPoolDividendOf(c, s.poolId, DEAD_ADDRESS, gen),
1673
1858
  rd<bigint>(padPolicyAbi, "threshold", [BigInt(gen)]),
1859
+ rd<bigint>(padPolicyAbi, "protocolShare"),
1860
+ rd<bigint>(padPolicyAbi, "creatorShareMin"),
1861
+ rd<bigint>(padPolicyAbi, "creatorShareMax"),
1862
+ rd<bigint>(padPolicyAbi, "burnMin"),
1863
+ rd<bigint>(padPolicyAbi, "burnMax"),
1674
1864
  ]);
1675
- return { kind: "pad", harvestCut, gen, rung: gen, threshold, pot, creatorShare, protocolShare, burnPerRung };
1865
+ const [creatorShare, burnPerRung, protocolShare] = split;
1866
+ const ranges: PadRanges = { protocolShare: protocolNow, creatorShareMin, creatorShareMax, burnMin, burnMax };
1867
+ return { kind: "pad", gen, rung: gen, threshold, pot, creatorShare, protocolShare, burnPerRung, ranges };
1676
1868
  }
1677
1869
  case "pol": {
1678
1870
  const [keepQuote, minQuoteAtSeed] = await Promise.all([rd<bigint>(polPolicyAbi, "keepQuote"), rd<bigint>(polPolicyAbi, "minQuoteAtSeed")]);
1679
- return { kind: "pol", harvestCut, keepQuote, minQuoteAtSeed };
1871
+ return { kind: "pol", keepQuote, minQuoteAtSeed };
1680
1872
  }
1681
1873
  case "public": {
1682
1874
  const curated = await rd<boolean>(publicPolicyAbi, "curated");
1683
- return { kind: "public", harvestCut, curated };
1875
+ return { kind: "public", curated };
1684
1876
  }
1685
1877
  case "private": {
1686
1878
  const r = roles ?? (await getFPoolRoles(c, s.poolId, s));
1687
- return { kind: "private", harvestCut, gateMint: r.gateMint, gateSwap: r.gateSwap, operator: fPoolOperatorOf(r) };
1879
+ return { kind: "private", gateMint: r.gateMint, gateSwap: r.gateSwap, operator: fPoolOperatorOf(r) };
1688
1880
  }
1689
1881
  }
1690
1882
  }
@@ -1753,8 +1945,7 @@ export async function fPoolShareHolders(
1753
1945
  }
1754
1946
 
1755
1947
  /**
1756
- * What a policy answers `onRegenerate` with (and `onHarvest`'s split, the first four words):
1757
- * `IFPolicy.Terms`. `carried` is F version 21's fifth word — the holder the next generation
1948
+ * What a policy answers `onRegenerate` with: `IFPolicy.Terms`. `carried` is F version 21's fifth word — the holder the next generation
1758
1949
  * carries beside the dead shares; the reply is exactly 160 bytes.
1759
1950
  */
1760
1951
  export interface FPolicyTerms {