@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.
- package/LICENSE +201 -0
- package/README.md +152 -0
- package/package.json +51 -0
- package/src/PingDirectory.js +1110 -0
- package/src/constants.js +103 -0
- package/src/deserialize.js +322 -0
- package/src/disc.js +76 -0
- package/src/encoding.js +130 -0
- package/src/fees.js +102 -0
- package/src/format.js +149 -0
- package/src/identicon.js +364 -0
- package/src/index.js +58 -0
- package/src/ix/admin.js +351 -0
- package/src/ix/identity.js +418 -0
- package/src/ix/index.js +10 -0
- package/src/ix/lock.js +63 -0
- package/src/ix/marketplace.js +198 -0
- package/src/ix/photo.js +173 -0
- package/src/ix/pro.js +91 -0
- package/src/ix/revoke.js +41 -0
- package/src/ix/shop.js +322 -0
- package/src/pda.js +75 -0
- package/src/wallet.js +189 -0
package/src/pda.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
// PDA finders. Each returns [PublicKey, bump].
|
|
2
|
+
|
|
3
|
+
import { PublicKey } from '@solana/web3.js';
|
|
4
|
+
import { PROGRAM_ID, Seeds } from './constants.js';
|
|
5
|
+
import { u32LE, u64LE } from './encoding.js';
|
|
6
|
+
|
|
7
|
+
const seedBytes = (s) => new TextEncoder().encode(s);
|
|
8
|
+
|
|
9
|
+
export function findPDA(seeds) {
|
|
10
|
+
return PublicKey.findProgramAddressSync(seeds, PROGRAM_ID);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const findConfigPDA = () =>
|
|
14
|
+
findPDA([seedBytes(Seeds.CONFIG)]);
|
|
15
|
+
|
|
16
|
+
export const findUsernamePDA = (username) =>
|
|
17
|
+
findPDA([seedBytes(Seeds.USERNAME), new TextEncoder().encode(username)]);
|
|
18
|
+
|
|
19
|
+
// Combined ed25519 metadata PDA — replaces ReverseLookup + CompromisedKey.
|
|
20
|
+
// Holds both `bound_username` (current binding) and `compromised_at`
|
|
21
|
+
// (revocation marker), in one PDA per ed25519 key.
|
|
22
|
+
export const findEd25519AccountPDA = (ed25519Pubkey) => {
|
|
23
|
+
const bytes = ed25519Pubkey instanceof PublicKey ? ed25519Pubkey.toBytes() : ed25519Pubkey;
|
|
24
|
+
return findPDA([seedBytes(Seeds.ED25519), bytes]);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export const findNoncePDA = (nonce) =>
|
|
28
|
+
findPDA([seedBytes(Seeds.NONCE), nonce]);
|
|
29
|
+
|
|
30
|
+
export const findGracePeriodPDA = (username) =>
|
|
31
|
+
findPDA([seedBytes(Seeds.GRACE), new TextEncoder().encode(username)]);
|
|
32
|
+
|
|
33
|
+
export const findSaleListingPDA = (username) =>
|
|
34
|
+
findPDA([seedBytes(Seeds.SALE), new TextEncoder().encode(username)]);
|
|
35
|
+
|
|
36
|
+
export const findReferralBalancePDA = (referrerPubkey) => {
|
|
37
|
+
const key = referrerPubkey ?? PublicKey.default;
|
|
38
|
+
const bytes = key instanceof PublicKey ? key.toBytes() : new Uint8Array(key);
|
|
39
|
+
return findPDA([seedBytes(Seeds.REFERRAL), bytes]);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const findUserIndexPDA = (userId) =>
|
|
43
|
+
findPDA([seedBytes(Seeds.USER_INDEX), u64LE(userId)]);
|
|
44
|
+
|
|
45
|
+
export const findUidMapPDA = (username) =>
|
|
46
|
+
findPDA([seedBytes(Seeds.UID_MAP), new TextEncoder().encode(username)]);
|
|
47
|
+
|
|
48
|
+
export const findAdminPDA = (adminPubkey) =>
|
|
49
|
+
findPDA([seedBytes(Seeds.ADMIN), adminPubkey instanceof PublicKey ? adminPubkey.toBytes() : adminPubkey]);
|
|
50
|
+
|
|
51
|
+
// Treasury-wallet role PDA — existence at [b"treasury_wallet", pubkey]
|
|
52
|
+
// authorizes that address to withdraw the treasury (to itself).
|
|
53
|
+
export const findTreasuryWalletPDA = (walletPubkey) =>
|
|
54
|
+
findPDA([seedBytes(Seeds.TREASURY_WALLET), walletPubkey instanceof PublicKey ? walletPubkey.toBytes() : walletPubkey]);
|
|
55
|
+
|
|
56
|
+
export const findBlocklistPDA = (username) =>
|
|
57
|
+
findPDA([seedBytes(Seeds.BLOCKED), new TextEncoder().encode(username)]);
|
|
58
|
+
|
|
59
|
+
// Companion visibility PDA — [b"moderation", username]. Absence = visible.
|
|
60
|
+
export const findModerationPDA = (username) =>
|
|
61
|
+
findPDA([seedBytes(Seeds.MODERATION), new TextEncoder().encode(username)]);
|
|
62
|
+
|
|
63
|
+
export const findShopItemPDA = (itemId) =>
|
|
64
|
+
findPDA([seedBytes(Seeds.SHOP_ITEM), u32LE(itemId)]);
|
|
65
|
+
|
|
66
|
+
// Inventory lives at [b"inventory", username] — items travel with the
|
|
67
|
+
// username (cosmetics survive key rotation / sale).
|
|
68
|
+
export const findInventoryPDA = (username) =>
|
|
69
|
+
findPDA([seedBytes(Seeds.INVENTORY), new TextEncoder().encode(username)]);
|
|
70
|
+
|
|
71
|
+
// ProfilePhoto lives at [b"profile_photo", username] — photo travels
|
|
72
|
+
// with the username.
|
|
73
|
+
export const findProfilePhotoPDA = (username) =>
|
|
74
|
+
findPDA([seedBytes(Seeds.PROFILE_PHOTO), new TextEncoder().encode(username)]);
|
|
75
|
+
|
package/src/wallet.js
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
// Wallet-adapter helper — sign + send a Transaction via a connected
|
|
2
|
+
// browser wallet (Phantom, Solflare, Backpack, …).
|
|
3
|
+
//
|
|
4
|
+
// Ported from the legacy SDK (`tx.js#walletSignAndSend`) and adapted to
|
|
5
|
+
// v1.0.0's Connection-based world. The legacy helper operated on raw
|
|
6
|
+
// serialized tx bytes and broadcast through a module-level `sendTx`
|
|
7
|
+
// (which used a custom proxied fetch); v1.0.0 instead takes a
|
|
8
|
+
// `Connection` so we can use `connection.sendRawTransaction` /
|
|
9
|
+
// `connection.confirmTransaction` directly, and `Connection` itself is
|
|
10
|
+
// the integration point for proxied fetch + beforeRequest headers
|
|
11
|
+
// (configured at construction time on PingDirectory).
|
|
12
|
+
//
|
|
13
|
+
// Tries three signing paths in order, mirroring the legacy SDK:
|
|
14
|
+
// 1. Wallet Standard `solana:signTransaction` — wallet returns
|
|
15
|
+
// signed bytes; we broadcast via `connection.sendRawTransaction`.
|
|
16
|
+
// 2. Wallet Standard `solana:signAndSendTransaction` — wallet
|
|
17
|
+
// broadcasts itself and returns a signature.
|
|
18
|
+
// 3. Legacy `provider.signTransaction(tx)` — the wallet object has
|
|
19
|
+
// a top-level `signTransaction` (older Phantom / wallet-adapter
|
|
20
|
+
// shims). We sign, then `sendRawTransaction`.
|
|
21
|
+
//
|
|
22
|
+
// Pure-JS, no extra deps. Uses only @solana/web3.js (already a SDK
|
|
23
|
+
// dep) for the Transaction wire format.
|
|
24
|
+
|
|
25
|
+
import { VersionedTransaction, Transaction } from '@solana/web3.js';
|
|
26
|
+
|
|
27
|
+
// ── base58 encode (for signature bytes returned by Wallet Standard) ──
|
|
28
|
+
// Tiny inline impl — avoids pulling in `bs58`. Identical alphabet to
|
|
29
|
+
// the one Solana uses; signatures are always 64 bytes.
|
|
30
|
+
const B58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
|
|
31
|
+
function b58encode(bytes) {
|
|
32
|
+
if (!bytes || bytes.length === 0) return '';
|
|
33
|
+
let zeros = 0;
|
|
34
|
+
while (zeros < bytes.length && bytes[zeros] === 0) zeros++;
|
|
35
|
+
const digits = [0];
|
|
36
|
+
for (let i = zeros; i < bytes.length; i++) {
|
|
37
|
+
let carry = bytes[i];
|
|
38
|
+
for (let j = 0; j < digits.length; j++) {
|
|
39
|
+
carry += digits[j] << 8;
|
|
40
|
+
digits[j] = carry % 58;
|
|
41
|
+
carry = (carry / 58) | 0;
|
|
42
|
+
}
|
|
43
|
+
while (carry > 0) {
|
|
44
|
+
digits.push(carry % 58);
|
|
45
|
+
carry = (carry / 58) | 0;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
let out = '';
|
|
49
|
+
for (let i = 0; i < zeros; i++) out += '1';
|
|
50
|
+
for (let i = digits.length - 1; i >= 0; i--) out += B58_ALPHABET[digits[i]];
|
|
51
|
+
return out;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Serialize a Transaction or VersionedTransaction to wire bytes.
|
|
56
|
+
* Both classes expose `.serialize()`; legacy Transactions need
|
|
57
|
+
* `requireAllSignatures: false` because the wallet hasn't signed yet.
|
|
58
|
+
*/
|
|
59
|
+
function serializeTx(tx) {
|
|
60
|
+
if (tx instanceof VersionedTransaction) return tx.serialize();
|
|
61
|
+
if (tx instanceof Transaction) return tx.serialize({ requireAllSignatures: false, verifySignatures: false });
|
|
62
|
+
if (tx instanceof Uint8Array) return tx;
|
|
63
|
+
throw new Error('walletSignAndSend: transaction must be a Transaction, VersionedTransaction, or Uint8Array');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Best-effort detection of an account object suitable for Wallet
|
|
68
|
+
* Standard `solana:signTransaction` calls. Wallet Standard expects
|
|
69
|
+
* `{ address, publicKey, chains, features }`. If the wallet object
|
|
70
|
+
* already has an `account` field (the case when produced by our
|
|
71
|
+
* legacy-style `connectSolanaWallet`), use it directly. Otherwise we
|
|
72
|
+
* synthesize one from `pubkeyBytes`.
|
|
73
|
+
*/
|
|
74
|
+
function resolveAccount(wallet, chainId) {
|
|
75
|
+
if (wallet.account) return wallet.account;
|
|
76
|
+
if (wallet.pubkeyBytes) {
|
|
77
|
+
return {
|
|
78
|
+
address: b58encode(wallet.pubkeyBytes),
|
|
79
|
+
publicKey: wallet.pubkeyBytes,
|
|
80
|
+
chains: [chainId],
|
|
81
|
+
features: ['solana:signTransaction', 'solana:signAndSendTransaction'],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Sign and send a transaction via a connected wallet, then await
|
|
89
|
+
* `confirmed` commitment.
|
|
90
|
+
*
|
|
91
|
+
* @param {Connection} connection — the SDK Connection to broadcast on.
|
|
92
|
+
* @param {object} wallet — wallet adapter:
|
|
93
|
+
* - Wallet Standard: `{ provider: { features }, account?, pubkeyBytes?, type? }`
|
|
94
|
+
* - Legacy: `{ provider: { signTransaction(tx) }, pubkeyBytes? }`
|
|
95
|
+
* @param {Transaction|VersionedTransaction|Uint8Array} transaction
|
|
96
|
+
* @param {object} [opts]
|
|
97
|
+
* @param {string} [opts.chainId='solana:mainnet'] — Wallet Standard
|
|
98
|
+
* chain identifier passed to `signTransaction` so wallets on a
|
|
99
|
+
* mismatched cluster can refuse the prompt instead of silently
|
|
100
|
+
* broadcasting on the wrong network.
|
|
101
|
+
* @param {string} [opts.commitment='confirmed']
|
|
102
|
+
* @returns {Promise<string>} the transaction signature (base58).
|
|
103
|
+
*/
|
|
104
|
+
export async function walletSignAndSend(connection, wallet, transaction, opts = {}) {
|
|
105
|
+
if (!connection || typeof connection.sendRawTransaction !== 'function') {
|
|
106
|
+
throw new Error('walletSignAndSend: connection (with sendRawTransaction) is required');
|
|
107
|
+
}
|
|
108
|
+
if (!wallet) throw new Error('walletSignAndSend: wallet is required');
|
|
109
|
+
|
|
110
|
+
const chainId = opts.chainId || 'solana:mainnet';
|
|
111
|
+
const commitment = opts.commitment || 'confirmed';
|
|
112
|
+
const provider = wallet.provider || wallet; // some adapters ARE the provider
|
|
113
|
+
const account = resolveAccount(wallet, chainId);
|
|
114
|
+
|
|
115
|
+
// ── Path 1: Wallet Standard `solana:signTransaction` ────────────────
|
|
116
|
+
// Returns signed bytes; we broadcast.
|
|
117
|
+
const stFeat = provider?.features?.['solana:signTransaction'];
|
|
118
|
+
if (stFeat && account) {
|
|
119
|
+
const txBytes = serializeTx(transaction);
|
|
120
|
+
let signedBytes;
|
|
121
|
+
try {
|
|
122
|
+
const [output] = await stFeat.signTransaction({
|
|
123
|
+
account,
|
|
124
|
+
transaction: txBytes,
|
|
125
|
+
chain: chainId,
|
|
126
|
+
});
|
|
127
|
+
signedBytes = output.signedTransaction;
|
|
128
|
+
} catch (e) {
|
|
129
|
+
// Fall through to next path on signing error (mirrors the legacy SDK).
|
|
130
|
+
console.warn('[wallet] WS signTransaction failed:', e?.message ?? e);
|
|
131
|
+
}
|
|
132
|
+
if (signedBytes) {
|
|
133
|
+
const sig = await connection.sendRawTransaction(signedBytes, {
|
|
134
|
+
skipPreflight: false,
|
|
135
|
+
preflightCommitment: commitment,
|
|
136
|
+
});
|
|
137
|
+
await connection.confirmTransaction(sig, commitment);
|
|
138
|
+
return sig;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ── Path 2: Wallet Standard `solana:signAndSendTransaction` ─────────
|
|
143
|
+
// Wallet broadcasts; returns a 64-byte signature.
|
|
144
|
+
const saFeat = provider?.features?.['solana:signAndSendTransaction'];
|
|
145
|
+
if (saFeat && account) {
|
|
146
|
+
const txBytes = serializeTx(transaction);
|
|
147
|
+
try {
|
|
148
|
+
const [output] = await saFeat.signAndSendTransaction({
|
|
149
|
+
account,
|
|
150
|
+
transaction: txBytes,
|
|
151
|
+
chain: chainId,
|
|
152
|
+
});
|
|
153
|
+
const sig = b58encode(output.signature);
|
|
154
|
+
await connection.confirmTransaction(sig, commitment);
|
|
155
|
+
return sig;
|
|
156
|
+
} catch (e) {
|
|
157
|
+
console.warn('[wallet] WS signAndSendTransaction failed:', e?.message ?? e);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ── Path 3: Legacy `provider.signTransaction(tx)` ───────────────────
|
|
162
|
+
// Older Phantom / wallet-adapter shims — pass a real Transaction
|
|
163
|
+
// instance, get a signed one back, then broadcast its bytes.
|
|
164
|
+
if (typeof provider?.signTransaction === 'function') {
|
|
165
|
+
let signed;
|
|
166
|
+
try {
|
|
167
|
+
// If caller gave us raw bytes, deserialize so the legacy adapter
|
|
168
|
+
// sees a Transaction object. Try VersionedTransaction first
|
|
169
|
+
// (modern format), fall back to legacy Transaction.
|
|
170
|
+
let tx = transaction;
|
|
171
|
+
if (tx instanceof Uint8Array) {
|
|
172
|
+
try { tx = VersionedTransaction.deserialize(tx); }
|
|
173
|
+
catch { tx = Transaction.from(tx); }
|
|
174
|
+
}
|
|
175
|
+
signed = await provider.signTransaction(tx);
|
|
176
|
+
} catch (e) {
|
|
177
|
+
throw new Error('Wallet signing failed: ' + (e?.message ?? 'unknown'));
|
|
178
|
+
}
|
|
179
|
+
const signedBytes = signed instanceof Uint8Array ? signed : signed.serialize();
|
|
180
|
+
const sig = await connection.sendRawTransaction(signedBytes, {
|
|
181
|
+
skipPreflight: false,
|
|
182
|
+
preflightCommitment: commitment,
|
|
183
|
+
});
|
|
184
|
+
await connection.confirmTransaction(sig, commitment);
|
|
185
|
+
return sig;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
throw new Error('walletSignAndSend: no compatible signing method found on wallet');
|
|
189
|
+
}
|