@microslop/ping-directory-sdk 0.1.5

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.

Potentially problematic release.


This version of @microslop/ping-directory-sdk might be problematic. Click here for more details.

@@ -0,0 +1,351 @@
1
+ // Admin / config / owner / blocklist / pause / treasury.
2
+
3
+ import { TransactionInstruction, SystemProgram, PublicKey } from '@solana/web3.js';
4
+ import { PROGRAM_ID } from '../constants.js';
5
+ import { borshString, u8, u16LE, u64LE, i64LE, concat, pkBytes } from '../encoding.js';
6
+ import { ixDisc } from '../disc.js';
7
+ import {
8
+ findConfigPDA, findAdminPDA, findBlocklistPDA, findGracePeriodPDA, findUsernamePDA,
9
+ findEd25519AccountPDA, findUidMapPDA, findUserIndexPDA, findSaleListingPDA,
10
+ findInventoryPDA, findProfilePhotoPDA, findReferralBalancePDA,
11
+ findTreasuryWalletPDA,
12
+ } from '../pda.js';
13
+
14
+ const k = (pubkey, isSigner, isWritable) => ({ pubkey, isSigner, isWritable });
15
+
16
+ // ── initialize ──────────────────────────────────────────────────────
17
+ export function initialize({ payer }) {
18
+ const data = ixDisc('initialize');
19
+ const [configPda] = findConfigPDA();
20
+ const ix = new TransactionInstruction({
21
+ programId: PROGRAM_ID,
22
+ keys: [
23
+ k(configPda, false, true),
24
+ k(payer.publicKey, true, true),
25
+ k(SystemProgram.programId, false, false),
26
+ ],
27
+ data,
28
+ });
29
+ return { instructions: [ix], signers: [payer] };
30
+ }
31
+
32
+ // ── add_admin (owner-only) ──────────────────────────────────────────
33
+ export function addAdmin({ adminPubkey, owner }) {
34
+ const data = concat(ixDisc('add_admin'), pkBytes(adminPubkey));
35
+ const [configPda] = findConfigPDA();
36
+ const [adminPda] = findAdminPDA(adminPubkey);
37
+ const ix = new TransactionInstruction({
38
+ programId: PROGRAM_ID,
39
+ keys: [
40
+ k(configPda, false, false),
41
+ k(adminPda, false, true),
42
+ k(owner.publicKey, true, true),
43
+ k(SystemProgram.programId, false, false),
44
+ ],
45
+ data,
46
+ });
47
+ return { instructions: [ix], signers: [owner] };
48
+ }
49
+
50
+ export function removeAdmin({ adminPubkey, owner }) {
51
+ const data = concat(ixDisc('remove_admin'), pkBytes(adminPubkey));
52
+ const [configPda] = findConfigPDA();
53
+ const [adminPda] = findAdminPDA(adminPubkey);
54
+ const ix = new TransactionInstruction({
55
+ programId: PROGRAM_ID,
56
+ keys: [
57
+ k(configPda, false, false),
58
+ k(adminPda, false, true),
59
+ k(owner.publicKey, true, true),
60
+ ],
61
+ data,
62
+ });
63
+ return { instructions: [ix], signers: [owner] };
64
+ }
65
+
66
+ // ── two-step ownership ──────────────────────────────────────────────
67
+ export function proposeOwner({ newOwner, owner }) {
68
+ const data = concat(ixDisc('propose_owner'), pkBytes(newOwner));
69
+ const [configPda] = findConfigPDA();
70
+ const ix = new TransactionInstruction({
71
+ programId: PROGRAM_ID,
72
+ keys: [
73
+ k(configPda, false, true),
74
+ k(owner.publicKey, true, false),
75
+ ],
76
+ data,
77
+ });
78
+ return { instructions: [ix], signers: [owner] };
79
+ }
80
+
81
+ export function acceptOwner({ newOwner }) {
82
+ const data = ixDisc('accept_owner');
83
+ const [configPda] = findConfigPDA();
84
+ const ix = new TransactionInstruction({
85
+ programId: PROGRAM_ID,
86
+ keys: [
87
+ k(configPda, false, true),
88
+ k(newOwner.publicKey, true, false),
89
+ ],
90
+ data,
91
+ });
92
+ return { instructions: [ix], signers: [newOwner] };
93
+ }
94
+
95
+ // `owner` is the on-chain field name. The constraint accepts EITHER
96
+ // the current owner OR the pending owner (LOW-26): standard two-step
97
+ // ownership pattern letting either side abort the proposal.
98
+ export function cancelProposeOwner({ owner, signer }) {
99
+ const s = owner ?? signer;
100
+ const data = ixDisc('cancel_propose_owner');
101
+ const [configPda] = findConfigPDA();
102
+ const ix = new TransactionInstruction({
103
+ programId: PROGRAM_ID,
104
+ keys: [
105
+ k(configPda, false, true),
106
+ k(s.publicKey, true, false),
107
+ ],
108
+ data,
109
+ });
110
+ return { instructions: [ix], signers: [s] };
111
+ }
112
+
113
+ // ── config setters (owner-only) ─────────────────────────────────────
114
+ function configSetterIx(name, payload, owner) {
115
+ const data = concat(ixDisc(name), payload);
116
+ const [configPda] = findConfigPDA();
117
+ const ix = new TransactionInstruction({
118
+ programId: PROGRAM_ID,
119
+ keys: [
120
+ k(configPda, false, true),
121
+ k(owner.publicKey, true, false),
122
+ ],
123
+ data,
124
+ });
125
+ return { instructions: [ix], signers: [owner] };
126
+ }
127
+
128
+ export const setRegistrationFee = ({ tier, fee, owner }) => configSetterIx('set_registration_fee', concat(u8(tier), u64LE(fee)), owner);
129
+ export const setProPriceMonthly = ({ price, owner }) => configSetterIx('set_pro_price_monthly', u64LE(price), owner);
130
+ export const setProPriceLifetime= ({ price, owner }) => configSetterIx('set_pro_price_lifetime', u64LE(price), owner);
131
+ export const setSaleFee = ({ feeBps, owner }) => configSetterIx('set_sale_fee', u16LE(feeBps), owner);
132
+ export const setMinSalePrice = ({ price, owner }) => configSetterIx('set_min_sale_price', u64LE(price), owner);
133
+ export const setGracePeriod = ({ duration, owner }) => configSetterIx('set_grace_period', i64LE(duration), owner);
134
+ export const setUnlockDelay = ({ delay, owner }) => configSetterIx('set_unlock_delay', i64LE(delay), owner);
135
+ export const setUnlockWindow = ({ window, owner }) => configSetterIx('set_unlock_window', i64LE(window), owner);
136
+ export const setAutoUnfreezeDelay = ({ delay, owner }) => configSetterIx('set_auto_unfreeze_delay', i64LE(delay), owner);
137
+ // Per-MIME profile-photo fee (lamports). `mime` ∈ {0=jpeg,1=png,2=webp,3=gif};
138
+ // fee capped at Config::MAX_PHOTO_FEE (100 SOL). Charged at init_profile_photo;
139
+ // non-refundable on clear. Lands in Config lamports — sweepable via withdraw_treasury.
140
+ export const setProfilePhotoFee = ({ mime, fee, owner }) => configSetterIx('set_profile_photo_fee', concat(u8(mime), u64LE(fee)), owner);
141
+
142
+ // ── pause (owner OR admin) ──────────────────────────────────────────
143
+ function pauseIx(name, paused, signer, admin) {
144
+ const data = concat(ixDisc(name), u8(paused ? 1 : 0));
145
+ const [configPda] = findConfigPDA();
146
+ const adminPda = admin ? findAdminPDA(admin)[0] : null;
147
+ const ix = new TransactionInstruction({
148
+ programId: PROGRAM_ID,
149
+ keys: [
150
+ k(configPda, false, true),
151
+ k(signer.publicKey, true, false),
152
+ k(adminPda ?? PROGRAM_ID, false, false),
153
+ ],
154
+ data,
155
+ });
156
+ return { instructions: [ix], signers: [signer] };
157
+ }
158
+
159
+ export const pauseRegistration = ({ paused, signer, admin = null }) =>
160
+ pauseIx('pause_registration', paused, signer, admin);
161
+ export const pausePro = ({ paused, signer, admin = null }) =>
162
+ pauseIx('pause_pro', paused, signer, admin);
163
+
164
+ // ── blocklist (owner OR admin) ──────────────────────────────────────
165
+ export function blocklistAdd({ username, signer, admin = null, payer }) {
166
+ const p = payer ?? signer; // rent payer defaults to the signing owner/admin
167
+ const data = concat(ixDisc('blocklist_add'), borshString(username));
168
+ const [configPda] = findConfigPDA();
169
+ const [blocklistPda] = findBlocklistPDA(username);
170
+ const [usernamePda] = findUsernamePDA(username);
171
+ const [gracePda] = findGracePeriodPDA(username);
172
+ const adminPda = admin ? findAdminPDA(admin)[0] : null;
173
+ const ix = new TransactionInstruction({
174
+ programId: PROGRAM_ID,
175
+ keys: [
176
+ k(configPda, false, false),
177
+ k(blocklistPda, false, true),
178
+ k(usernamePda, false, false),
179
+ k(gracePda, false, false),
180
+ k(signer.publicKey, true, false),
181
+ k(adminPda ?? PROGRAM_ID, false, false),
182
+ k(p.publicKey, true, true),
183
+ k(SystemProgram.programId, false, false),
184
+ ],
185
+ data,
186
+ });
187
+ // signer + payer may be the same wallet
188
+ const signers = signer.publicKey.equals(p.publicKey) ? [signer] : [signer, p];
189
+ return { instructions: [ix], signers };
190
+ }
191
+
192
+ export function blocklistRemove({ username, signer, admin = null }) {
193
+ const data = concat(ixDisc('blocklist_remove'), borshString(username));
194
+ const [configPda] = findConfigPDA();
195
+ const [blocklistPda] = findBlocklistPDA(username);
196
+ const adminPda = admin ? findAdminPDA(admin)[0] : null;
197
+ const ix = new TransactionInstruction({
198
+ programId: PROGRAM_ID,
199
+ keys: [
200
+ k(configPda, false, false),
201
+ k(blocklistPda, false, true),
202
+ k(signer.publicKey, true, true),
203
+ k(adminPda ?? PROGRAM_ID, false, false),
204
+ ],
205
+ data,
206
+ });
207
+ return { instructions: [ix], signers: [signer] };
208
+ }
209
+
210
+ // ── auto_unfreeze_username (permissionless time-gated unfreeze) ─────
211
+ //
212
+ // When an ed25519 key is `mark_compromised`'d, every ed25519-gated handler
213
+ // on its bound username refuses to operate (auto-freeze). After
214
+ // `Config.auto_unfreeze_delay` (default 1 year) elapses since the
215
+ // `mark_compromised` call, anyone may call this handler — no owner gate,
216
+ // no ed25519 sig — to flip the frozen username's UsernameAccount state
217
+ // to UNREGISTERED in place (PDA preserved as a permanent slot) and reset
218
+ // every personal field (premium, equipped cosmetics, account_type,
219
+ // referrer) to a fresh-claim baseline. Side PDAs (Inventory, ProfilePhoto,
220
+ // SaleListing) are closed in place if present; their rent goes to the
221
+ // caller. UidMap is closed → caller. UserIndex is tombstoned. The
222
+ // compromised key's `compromised_at` mark is preserved forever.
223
+ //
224
+ // Caller must supply:
225
+ // - `username`: the frozen username to release
226
+ // - `frozenEd25519Pubkey`: the bound (compromised) key — read from
227
+ // `UsernameAccount.ed25519Pubkey` before calling
228
+ // - `userId`: u64/BigInt — read from UidMap before calling
229
+ // - `caller`: signer paying tx + receiving every closed-PDA rent
230
+ //
231
+ // The high-level `PingDirectory.autoUnfreezeUsername` facade resolves
232
+ // `frozenEd25519Pubkey` and `userId` from chain state automatically.
233
+ export function autoUnfreezeUsername({
234
+ username, frozenEd25519Pubkey, userId, caller,
235
+ }) {
236
+ if (frozenEd25519Pubkey == null) {
237
+ throw new Error('autoUnfreezeUsername: frozenEd25519Pubkey required');
238
+ }
239
+ if (userId == null) {
240
+ throw new Error('autoUnfreezeUsername: userId required');
241
+ }
242
+ if (caller == null) {
243
+ throw new Error('autoUnfreezeUsername: caller required');
244
+ }
245
+ const data = concat(ixDisc('auto_unfreeze_username'), borshString(username));
246
+ const [configPda] = findConfigPDA();
247
+ const [usernamePda] = findUsernamePDA(username);
248
+ const [edPda] = findEd25519AccountPDA(frozenEd25519Pubkey);
249
+ const [uidMapPda] = findUidMapPDA(username);
250
+ const [userIdxPda] = findUserIndexPDA(userId);
251
+ const [inventoryPda] = findInventoryPDA(username);
252
+ const [photoPda] = findProfilePhotoPDA(username);
253
+ const [salePda] = findSaleListingPDA(username);
254
+ const ix = new TransactionInstruction({
255
+ programId: PROGRAM_ID,
256
+ keys: [
257
+ k(configPda, false, true),
258
+ k(usernamePda, false, true),
259
+ k(edPda, false, true),
260
+ k(uidMapPda, false, true),
261
+ k(userIdxPda, false, true),
262
+ k(inventoryPda, false, true),
263
+ k(photoPda, false, true),
264
+ k(salePda, false, true),
265
+ k(caller.publicKey, true, true),
266
+ k(SystemProgram.programId, false, false),
267
+ ],
268
+ data,
269
+ });
270
+ return { instructions: [ix], signers: [caller] };
271
+ }
272
+
273
+ // ── withdraw_treasury (owner OR treasury wallet) ────────────────────
274
+ // `signer` (alias `owner` for back-compat) sweeps the surplus to itself.
275
+ // Pass `treasuryWalletPda` when the signer is a treasury wallet (Some);
276
+ // omit it for the owner path (None ⇒ program id sentinel). The high-level
277
+ // `PingDirectory.withdrawTreasury` resolves this from chain automatically.
278
+ export function withdrawTreasury({ signer, owner, treasuryWalletPda = null }) {
279
+ const s = signer ?? owner;
280
+ const data = ixDisc('withdraw_treasury');
281
+ const [configPda] = findConfigPDA();
282
+ const ix = new TransactionInstruction({
283
+ programId: PROGRAM_ID,
284
+ keys: [
285
+ k(configPda, false, true),
286
+ k(s.publicKey, true, true),
287
+ k(treasuryWalletPda ?? PROGRAM_ID, false, false),
288
+ ],
289
+ data,
290
+ });
291
+ return { instructions: [ix], signers: [s] };
292
+ }
293
+
294
+ // ── add_treasury_wallet / remove_treasury_wallet (owner-only) ───────
295
+ // The owner designates / revokes addresses allowed to withdraw the
296
+ // treasury. Mirrors add_admin / remove_admin.
297
+ export function addTreasuryWallet({ walletPubkey, owner }) {
298
+ const data = concat(ixDisc('add_treasury_wallet'), pkBytes(walletPubkey));
299
+ const [configPda] = findConfigPDA();
300
+ const [twPda] = findTreasuryWalletPDA(walletPubkey);
301
+ const ix = new TransactionInstruction({
302
+ programId: PROGRAM_ID,
303
+ keys: [
304
+ k(configPda, false, false),
305
+ k(twPda, false, true),
306
+ k(owner.publicKey, true, true),
307
+ k(SystemProgram.programId, false, false),
308
+ ],
309
+ data,
310
+ });
311
+ return { instructions: [ix], signers: [owner] };
312
+ }
313
+
314
+ export function removeTreasuryWallet({ walletPubkey, owner }) {
315
+ const data = concat(ixDisc('remove_treasury_wallet'), pkBytes(walletPubkey));
316
+ const [configPda] = findConfigPDA();
317
+ const [twPda] = findTreasuryWalletPDA(walletPubkey);
318
+ const ix = new TransactionInstruction({
319
+ programId: PROGRAM_ID,
320
+ keys: [
321
+ k(configPda, false, false),
322
+ k(twPda, false, true),
323
+ k(owner.publicKey, true, true),
324
+ ],
325
+ data,
326
+ });
327
+ return { instructions: [ix], signers: [owner] };
328
+ }
329
+
330
+ // ── admin_close_zero_referral (owner-only) ─────────────────────────
331
+ // One-shot sweep of the dust ReferralBalance PDA at the zero-pubkey
332
+ // seed. Holds ~0.001 SOL rent that no other handler can recover
333
+ // (nobody can sign as `Pubkey::default()`). Reverts if balance != 0.
334
+ // Will fail with `AccountNotInitialized` if the dust PDA was never
335
+ // created (i.e. no no-referrer register / subscribe / shop call has
336
+ // happened yet) — caller can interpret that as "nothing to sweep".
337
+ export function adminCloseZeroReferral({ owner }) {
338
+ const data = ixDisc('admin_close_zero_referral');
339
+ const [configPda] = findConfigPDA();
340
+ const [referralPda] = findReferralBalancePDA(PublicKey.default);
341
+ const ix = new TransactionInstruction({
342
+ programId: PROGRAM_ID,
343
+ keys: [
344
+ k(configPda, false, false),
345
+ k(referralPda, false, true),
346
+ k(owner.publicKey, true, true),
347
+ ],
348
+ data,
349
+ });
350
+ return { instructions: [ix], signers: [owner] };
351
+ }