@bordrless/shared 0.8.0 → 0.10.0

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/api.js CHANGED
@@ -1,8 +1,3 @@
1
- /**
2
- * The HTTP API between the site and the backend. Amounts in base units travel as decimal strings
3
- * (`Amount`), dollar figures as numbers, times as unix seconds. A figure the backend could not
4
- * read is null, never zero.
5
- */
6
1
  /** "What your hook does": the most an author writes, in characters (the Studio field and what is served). */
7
2
  export const HOOK_ABOUT_CHARS = 280;
8
3
  /** The official summary's limits, in characters. */
@@ -0,0 +1,21 @@
1
+ import type { ExplorerProgram } from './api.ts';
2
+ /** Which group a program id belongs to: the five of Bordrless, or any other program the indexer follows (a token hook). */
3
+ export declare function explorerProgramOf(programId: string): ExplorerProgram;
4
+ /** The groups' names, as the explorer's filter and its chips print them. */
5
+ export declare const EXPLORER_PROGRAM_NAMES: Readonly<Record<ExplorerProgram, string>>;
6
+ /** A program's name: one of the five, a hook Bordrless wrote, or "Hook" for any other. */
7
+ export declare function programLabel(programId: string): string;
8
+ /** The IDL's PascalCase name from whatever case the coder stored (`transferred` -> `Transferred`). */
9
+ export declare const eventName: (raw: string) => string;
10
+ interface EventWords {
11
+ label: string;
12
+ /** The payload key naming the account the event is mostly about; null when it has none worth linking. */
13
+ subject: string | null;
14
+ }
15
+ /** Every event of the IDLs, by its PascalCase name (`ConfigSet` is three programs' and reads the same). */
16
+ export declare const EVENT_WORDS: Readonly<Record<string, EventWords>>;
17
+ /** An event's label in plain words; an unknown event's name with its words spaced (`SomethingNew` -> "Something new"). */
18
+ export declare function eventLabel(name: string): string;
19
+ /** The account an event is mostly about, read from its payload; null when the table names none or the payload lacks it. */
20
+ export declare function eventSubject(name: string, payload: Record<string, unknown>): string | null;
21
+ export {};
@@ -0,0 +1,94 @@
1
+ /**
2
+ * The explorer's words for the chain: each program's name, each IDL event's label in plain words
3
+ * and the account it is mostly about, in one table the backend and the site both read (the backend
4
+ * picks an event's subject with it; the site prints its label with the raw name as a title).
5
+ *
6
+ * Event names are the IDLs' (`packages/sdk/idl/*.json`), PascalCase; the SDK's coder gives them
7
+ * camelCase, which `eventName` undoes. An event the table does not know keeps its raw name, spaced.
8
+ */
9
+ import { PROGRAM_IDS } from "./programs.js";
10
+ /** Which group a program id belongs to: the five of Bordrless, or any other program the indexer follows (a token hook). */
11
+ export function explorerProgramOf(programId) {
12
+ switch (programId) {
13
+ case PROGRAM_IDS.token:
14
+ return 'token';
15
+ case PROGRAM_IDS.swap:
16
+ return 'swap';
17
+ case PROGRAM_IDS.bridge:
18
+ return 'bridge';
19
+ case PROGRAM_IDS.launch:
20
+ return 'launch';
21
+ case PROGRAM_IDS.kit:
22
+ return 'kit';
23
+ default:
24
+ return 'hook';
25
+ }
26
+ }
27
+ /** The groups' names, as the explorer's filter and its chips print them. */
28
+ export const EXPLORER_PROGRAM_NAMES = { token: 'Token', swap: 'DEX', bridge: 'Bridge', launch: 'Launchpad', kit: 'Kit', hook: 'Hook' };
29
+ /** A program's name: one of the five, a hook Bordrless wrote, or "Hook" for any other. */
30
+ export function programLabel(programId) {
31
+ if (programId === PROGRAM_IDS.taxHook)
32
+ return 'Tax hook';
33
+ if (programId === PROGRAM_IDS.halfLife)
34
+ return 'Half-Life';
35
+ return EXPLORER_PROGRAM_NAMES[explorerProgramOf(programId)];
36
+ }
37
+ /** The IDL's PascalCase name from whatever case the coder stored (`transferred` -> `Transferred`). */
38
+ export const eventName = (raw) => (raw ? `${raw.charAt(0).toUpperCase()}${raw.slice(1)}` : raw);
39
+ /** Every event of the IDLs, by its PascalCase name (`ConfigSet` is three programs' and reads the same). */
40
+ export const EVENT_WORDS = {
41
+ // token
42
+ MintCreated: { label: 'Mint created', subject: 'mint' },
43
+ HoldingCreated: { label: 'Holding opened', subject: 'holding' },
44
+ Transferred: { label: 'Transfer', subject: 'mint' },
45
+ Minted: { label: 'Mint', subject: 'mint' },
46
+ Burned: { label: 'Burn', subject: 'mint' },
47
+ HookSet: { label: 'Hook set', subject: 'mint' },
48
+ AuthoritySet: { label: 'Authority set', subject: 'mint' },
49
+ MetadataUpdated: { label: 'Metadata updated', subject: 'mint' },
50
+ HoldingClosed: { label: 'Holding closed', subject: 'holding' },
51
+ DelegateSet: { label: 'Delegate set', subject: 'holding' },
52
+ FrozenSet: { label: 'Freeze changed', subject: 'holding' },
53
+ HookDataWritten: { label: 'Hook data written', subject: 'holding' },
54
+ // swap
55
+ PoolCreated: { label: 'Pool created', subject: 'pool' },
56
+ Swapped: { label: 'Swap', subject: 'pool' },
57
+ LiquidityAdded: { label: 'Liquidity added', subject: 'pool' },
58
+ LiquidityRemoved: { label: 'Liquidity removed', subject: 'pool' },
59
+ CurveFinalized: { label: 'Curve finalized', subject: 'pool' },
60
+ ProtocolFeesCollected: { label: 'Protocol fees collected', subject: 'pool' },
61
+ ConfigSet: { label: 'Config set', subject: 'admin' },
62
+ // launch
63
+ LaunchCreated: { label: 'Launch created', subject: 'mint' },
64
+ Graduated: { label: 'Graduated', subject: 'mint' },
65
+ CreatorFeesClaimed: { label: 'Creator fees claimed', subject: 'mint' },
66
+ LaunchConfigCreated: { label: 'Launch config created', subject: 'config' },
67
+ // kit
68
+ KitInstalled: { label: 'Rules installed', subject: 'mint' },
69
+ KitGraduated: { label: 'Rules graduated', subject: 'mint' },
70
+ RewardsClaimed: { label: 'Rewards claimed', subject: 'mint' },
71
+ RewardsShared: { label: 'Rewards shared', subject: 'mint' },
72
+ // bridge
73
+ WrapperRegistered: { label: 'Wrapper registered', subject: 'wrappedMint' },
74
+ Wrapped: { label: 'Bridged in', subject: 'wrappedMint' },
75
+ Unwrapped: { label: 'Bridged out', subject: 'wrappedMint' },
76
+ // half_life
77
+ Stoked: { label: 'Exit fee burned', subject: 'mint' },
78
+ };
79
+ /** An event's label in plain words; an unknown event's name with its words spaced (`SomethingNew` -> "Something new"). */
80
+ export function eventLabel(name) {
81
+ const known = EVENT_WORDS[eventName(name)];
82
+ if (known)
83
+ return known.label;
84
+ const spaced = eventName(name).replace(/([a-z0-9])([A-Z])/g, '$1 $2');
85
+ return `${spaced.charAt(0)}${spaced.slice(1).toLowerCase()}`;
86
+ }
87
+ /** The account an event is mostly about, read from its payload; null when the table names none or the payload lacks it. */
88
+ export function eventSubject(name, payload) {
89
+ const key = EVENT_WORDS[eventName(name)]?.subject;
90
+ if (!key)
91
+ return null;
92
+ const value = payload[key];
93
+ return typeof value === 'string' && value.length > 0 ? value : null;
94
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,8 @@
1
1
  export * from './api.ts';
2
+ export * from './explorer.ts';
3
+ export * from './keeper.ts';
2
4
  export * from './policy.ts';
3
5
  export * from './programs.ts';
6
+ export * from './rules.ts';
4
7
  export * from './studio.ts';
8
+ export * from './workshop.ts';
package/dist/index.js CHANGED
@@ -1,4 +1,8 @@
1
1
  export * from "./api.js";
2
+ export * from "./explorer.js";
3
+ export * from "./keeper.js";
2
4
  export * from "./policy.js";
3
5
  export * from "./programs.js";
6
+ export * from "./rules.js";
4
7
  export * from "./studio.js";
8
+ export * from "./workshop.js";
@@ -0,0 +1,139 @@
1
+ /** The file, at the root of the submitted source (beside `src/`): a companion script's, or a pair's companion half's. */
2
+ export declare const KEEPER_FILE = "keeper.json";
3
+ /** The schema's version: the only one there is. */
4
+ export declare const KEEPER_VERSION = 1;
5
+ /** The bounds a declaration is held to. */
6
+ export declare const KEEPER_LIMITS: {
7
+ /** The file's bytes (UTF-8). */
8
+ readonly bytes: 4096;
9
+ /** Holdings one step may bring (the keeper brings fewer when a step would not fit one transaction). */
10
+ readonly perStep: 16;
11
+ /** A round or an epoch, seconds: from the shortest step interval to a year. */
12
+ readonly minSecs: 10;
13
+ readonly maxSecs: 31536000;
14
+ /** The unit a hook-data field counts time in, seconds (an epoch's stamp: 300 for five minutes). */
15
+ readonly maxUnitSecs: 31536000;
16
+ /** Wallets a step may bring (about 12 fit one transaction beside the protocol's lookup table on a plain coin; the keeper brings fewer when they do not). */
17
+ readonly wallets: 12;
18
+ };
19
+ /** Bytes of an unsigned little-endian field. */
20
+ export type KeeperWidth = 1 | 2 | 4 | 8;
21
+ /** An unsigned little-endian field of the script's own state account (`PDA(["script-state", mint])` under the companion), read fresh at each step. */
22
+ export interface KeeperStateField {
23
+ offset: number;
24
+ width: KeeperWidth;
25
+ }
26
+ /** A balance or a constant in base units: a string of digits, or a field of the script's state. */
27
+ export type KeeperAmount = string | {
28
+ state: KeeperStateField;
29
+ };
30
+ /** A length of time in seconds: a whole number, or a field of the script's state. */
31
+ export type KeeperSeconds = number | {
32
+ state: KeeperStateField;
33
+ };
34
+ export type KeeperCompare = 'eq' | 'ne' | 'lt' | 'lte' | 'gt' | 'gte';
35
+ export declare const KEEPER_COMPARES: readonly KeeperCompare[];
36
+ /**
37
+ * What a hook-data field is compared with: the pool's price now (the companion's scale, 10^12 per
38
+ * token unit, as `companionSpotPrice` reads it), a constant, or the current epoch, floor(now / secs).
39
+ * With `fieldUnitSecs`, the field counts units of that many seconds (a hook that stamps
40
+ * floor(now / 300)) and is compared in the epoch's own units: floor(field × fieldUnitSecs / secs).
41
+ */
42
+ export type KeeperAgainst = 'poolPrice' | {
43
+ const: string;
44
+ } | {
45
+ epoch: {
46
+ secs: KeeperSeconds;
47
+ fieldUnitSecs: number | null;
48
+ };
49
+ };
50
+ /** A filter on each holding's hook data: the unsigned little-endian field of `width` bytes at `offset`, compared with `against`. A holding passes when `field <compare> against` holds. */
51
+ export interface KeeperHookDataFilter {
52
+ offset: number;
53
+ width: KeeperWidth;
54
+ compare: KeeperCompare;
55
+ against: KeeperAgainst;
56
+ }
57
+ /**
58
+ * Where each holding a step brings is paid: `owner` (its owner's wallet, writable, after the
59
+ * holding), `ownerQuoteHolding` (its owner's bridged-SOL holding under the Bordrless token program,
60
+ * `holdingAddress(BRIDGED_SOL_MINT, owner)`, writable, after the holding; a holder without one is
61
+ * passed over), or `none` (the holding alone).
62
+ */
63
+ export type KeeperPayTo = 'none' | 'owner' | 'ownerQuoteHolding';
64
+ export declare const KEEPER_PAY_TO: readonly KeeperPayTo[];
65
+ export interface KeeperHoldings {
66
+ /** The least balance that counts, base units (at least 1). */
67
+ minBalance: KeeperAmount;
68
+ /** The most balance that counts, base units, inclusive; null: no most. */
69
+ maxBalance: KeeperAmount | null;
70
+ hookData: KeeperHookDataFilter | null;
71
+ payTo: KeeperPayTo;
72
+ /** Holdings a step brings when it carries the swap accounts, and when it does not (the one `swapAccounts` names is required; null: not said). */
73
+ perStep: {
74
+ withSwapAccounts: number | null;
75
+ withoutSwapAccounts: number | null;
76
+ };
77
+ }
78
+ /**
79
+ * A wallet a step brings for the script's wallet answers: a fixed address (base58), or the public key
80
+ * in the 32 bytes at `offset` of the script's state, read fresh at each step (all zeros: none that step).
81
+ */
82
+ export type KeeperWallet = string | {
83
+ state: {
84
+ offset: number;
85
+ };
86
+ };
87
+ /** A declaration in its normal form: what `parseKeeperDeclaration` answers, what the Workshop stores and hashes, what the keeper reads. */
88
+ export interface KeeperDeclaration {
89
+ version: 1;
90
+ /** Which holdings a step brings; null: none (only beside `wallets`: a script that pays wallets alone). */
91
+ holdings: KeeperHoldings | null;
92
+ /** Each step carries the launch pool's swap accounts (what trading the pot needs; they hold the token's hook accounts too). */
93
+ swapAccounts: boolean;
94
+ /** A step without the swap accounts carries the token's custom hook accounts (its program, the token program's signer for it, its registry's extras): what moving the token inside a step needs. */
95
+ hookAccounts: boolean;
96
+ /** Each step carries the companion's payout accounts (`companion.payoutAccounts`): a script whose answers the companion pays and its hook records. */
97
+ payouts: boolean;
98
+ /** The round the keeper's cursor rotates by; null: none (the cursor never starts over). */
99
+ roundSecs: KeeperSeconds | null;
100
+ /**
101
+ * The wallets each step brings (writable, with the payout terms) while the script's terms allow
102
+ * wallet payouts (`allowWalletPayouts`), at most `KEEPER_LIMITS.wallets`. Absent when none: left out
103
+ * of the normal form, so a declaration approved before wallets existed hashes as it did.
104
+ */
105
+ wallets?: KeeperWallet[];
106
+ }
107
+ export type KeeperParse = {
108
+ ok: true;
109
+ declaration: KeeperDeclaration;
110
+ } | {
111
+ ok: false;
112
+ problem: string;
113
+ };
114
+ /** Whether a text is a public key in base58: exactly 32 bytes, as `new PublicKey` reads it (each leading "1" a zero byte). */
115
+ export declare function isBase58Key(text: string): boolean;
116
+ /** The declaration in a parsed JSON value, held to the schema and its bounds: its normal form, or why not, in one sentence. */
117
+ export declare function keeperDeclarationOf(value: unknown): KeeperParse;
118
+ /** Bytes of a text in UTF-8 (a lone surrogate counts as its replacement character, 3). */
119
+ export declare function utf8Length(text: string): number;
120
+ /** A `keeper.json` as submitted: within its size, JSON, then the schema (`keeperDeclarationOf`). */
121
+ export declare function parseKeeperDeclaration(text: string): KeeperParse;
122
+ /** The declaration in a canonical text (keys sorted, no spaces): what its hash is taken of, so the same declaration hashes the same however it was written. */
123
+ export declare function canonicalKeeperJson(d: KeeperDeclaration): string;
124
+ /** Whether a file of a submission is the declaration. */
125
+ export declare const isKeeperFile: (path: string) => boolean;
126
+ /** How many holdings a step brings as declared: with the swap accounts when it carries them, else without; 0 without holdings (a script that pays wallets alone). */
127
+ export declare const keeperPerStep: (d: KeeperDeclaration) => number;
128
+ /**
129
+ * The declaration in one plain sentence, for the item's page, the reading and the operator: "Each
130
+ * step brings up to 3 holders with under 20M tokens not yet paid this 5-minute epoch, paid to their
131
+ * SOL holding." An epoch whose length the script's state sets is "this epoch". The wallets a step
132
+ * brings follow in a sentence of their own.
133
+ */
134
+ export declare function keeperWords(d: KeeperDeclaration, decimals?: number): string;
135
+ /**
136
+ * The rest of the declaration, a sentence each, for the review and the operator: what else each
137
+ * step carries, and where a round's or an epoch's length comes from.
138
+ */
139
+ export declare function keeperDetails(d: KeeperDeclaration): string[];