@1upmonster/duel 0.2.1 → 0.2.3

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.
Files changed (39) hide show
  1. package/README.md +120 -59
  2. package/dist/admin.d.ts +8 -1
  3. package/dist/admin.js +52 -9
  4. package/dist/generated/duel/errors/duel.d.ts +1 -3
  5. package/dist/generated/duel/errors/duel.js +2 -4
  6. package/dist/generated/duel/instructions/index.d.ts +2 -0
  7. package/dist/generated/duel/instructions/index.js +3 -1
  8. package/dist/generated/duel/instructions/joinQueue.d.ts +1 -1
  9. package/dist/generated/duel/instructions/joinQueue.js +3 -3
  10. package/dist/generated/duel/instructions/setupQueuePermission.d.ts +51 -0
  11. package/dist/generated/duel/instructions/setupQueuePermission.js +63 -0
  12. package/dist/generated/duel/instructions/setupTicketPermission.d.ts +54 -0
  13. package/dist/generated/duel/instructions/setupTicketPermission.js +63 -0
  14. package/dist/generated/duel/programs/duel.d.ts +11 -3
  15. package/dist/generated/duel/programs/duel.js +19 -3
  16. package/dist/player.d.ts +8 -1
  17. package/dist/player.js +56 -5
  18. package/dist/tee.d.ts +4 -3
  19. package/dist/tee.js +17 -23
  20. package/dist/transaction.d.ts +2 -0
  21. package/dist/transaction.js +30 -19
  22. package/dist/utils.d.ts +10 -0
  23. package/dist/utils.js +28 -1
  24. package/package.json +1 -1
  25. package/src/admin.ts +67 -7
  26. package/src/duel.json +57 -5
  27. package/src/generated/duel/errors/duel.ts +2 -4
  28. package/src/generated/duel/instructions/index.ts +3 -1
  29. package/src/generated/duel/instructions/joinQueue.ts +3 -3
  30. package/src/generated/duel/instructions/setupQueuePermission.ts +112 -0
  31. package/src/generated/duel/instructions/setupTicketPermission.ts +115 -0
  32. package/src/generated/duel/programs/duel.ts +12 -4
  33. package/src/player.ts +72 -1
  34. package/src/tee.ts +18 -22
  35. package/src/transaction.ts +33 -18
  36. package/src/utils.ts +34 -0
  37. package/src/encryption.ts +0 -154
  38. package/src/idl/private_matchmaking.json +0 -1027
  39. package/src/idl/private_matchmaking.ts +0 -1033
@@ -15,8 +15,34 @@ import {
15
15
 
16
16
  type SolanaRpc = Rpc<SolanaRpcApi>;
17
17
 
18
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
+ const bigIntReplacer = (_: string, v: unknown) => typeof v === 'bigint' ? v.toString() : v;
20
+
21
+ /**
22
+ * Poll for transaction confirmation and throw if it failed.
23
+ * This ensures callers always know immediately when a transaction is rejected.
24
+ */
25
+ async function confirmTransaction(rpc: SolanaRpc, sig: string): Promise<void> {
26
+ for (let i = 0; i < 8; i++) {
27
+ await new Promise(r => setTimeout(r, 1000));
28
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
29
+ const statuses = await (rpc as any).getSignatureStatuses([sig], { searchTransactionHistory: false }).send().catch(() => null);
30
+ const status = statuses?.value?.[0];
31
+ if (status) {
32
+ if (status.err) {
33
+ throw new Error(`[TX] ${sig.slice(0, 16)}... FAILED: ${JSON.stringify(status.err, bigIntReplacer)}`);
34
+ }
35
+ console.log(`[TX] ${sig.slice(0, 16)}... ${status.confirmationStatus}`);
36
+ return;
37
+ }
38
+ }
39
+ // No status after 8s — treat as timeout rather than silently succeeding
40
+ throw new Error(`[TX] ${sig.slice(0, 16)}... confirmation timeout (no status after 8s)`);
41
+ }
42
+
18
43
  /**
19
44
  * Build, sign with a Kit keypair signer, and send a single instruction.
45
+ * Throws if the transaction is rejected or times out.
20
46
  */
21
47
  export async function sendInstruction(
22
48
  rpc: SolanaRpc,
@@ -42,27 +68,13 @@ export async function sendInstruction(
42
68
  skipPreflight: true,
43
69
  }).send() as Promise<string>);
44
70
 
45
- // Poll for status to detect runtime errors
46
- for (let i = 0; i < 8; i++) {
47
- await new Promise(r => setTimeout(r, 1000));
48
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
49
- const statuses = await (rpc as any).getSignatureStatuses([sig], { searchTransactionHistory: false }).send().catch(() => null);
50
- const status = statuses?.value?.[0];
51
- if (status) {
52
- if (status.err) {
53
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
- console.error(`[TX] ${sig.slice(0, 16)}... FAILED:`, JSON.stringify(status.err, (_, v) => typeof v === 'bigint' ? v.toString() : v));
55
- } else if (status.confirmationStatus) {
56
- console.log(`[TX] ${sig.slice(0, 16)}... ${status.confirmationStatus}`);
57
- }
58
- break;
59
- }
60
- }
71
+ await confirmTransaction(rpc, sig);
61
72
  return sig;
62
73
  }
63
74
 
64
75
  /**
65
76
  * Build, sign, and send multiple instructions in a single transaction.
77
+ * Throws if the transaction is rejected or times out.
66
78
  */
67
79
  export async function sendInstructions(
68
80
  rpc: SolanaRpc,
@@ -83,8 +95,11 @@ export async function sendInstructions(
83
95
  const encoded = getBase64EncodedWireTransaction(signedTx);
84
96
 
85
97
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
86
- return rpc.sendTransaction(encoded as any, {
98
+ const sig = await (rpc.sendTransaction(encoded as any, {
87
99
  encoding: "base64",
88
100
  skipPreflight: true,
89
- }).send() as Promise<string>;
101
+ }).send() as Promise<string>);
102
+
103
+ await confirmTransaction(rpc, sig);
104
+ return sig;
90
105
  }
package/src/utils.ts CHANGED
@@ -8,6 +8,40 @@ import {
8
8
  const addressEncoder = getAddressEncoder();
9
9
  const utf8Encoder = getUtf8Encoder();
10
10
 
11
+ export const PERMISSION_PROGRAM = "ACLseoPoyC3cBqoUtkbjZ4aDrkurZW86v19pXz2XQnp1" as Address;
12
+ export const DELEGATION_PROGRAM = "DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh" as Address;
13
+
14
+ /** Derives the Permission PDA for a given permissioned account. */
15
+ export async function derivePermissionPda(accountAddress: Address): Promise<Address> {
16
+ const [pda] = await getProgramDerivedAddress({
17
+ programAddress: PERMISSION_PROGRAM,
18
+ seeds: [utf8Encoder.encode("permission:"), addressEncoder.encode(accountAddress)],
19
+ });
20
+ return pda;
21
+ }
22
+
23
+ /** Derives the DELeGG delegation PDAs for the Permission PDA (used in DelegatePermission ix). */
24
+ export async function derivePermissionDelegationPdas(permissionPda: Address): Promise<{
25
+ delegationBuffer: Address;
26
+ delegationRecord: Address;
27
+ delegationMetadata: Address;
28
+ }> {
29
+ const [delegationRecord] = await getProgramDerivedAddress({
30
+ programAddress: DELEGATION_PROGRAM,
31
+ seeds: [utf8Encoder.encode("delegation"), addressEncoder.encode(permissionPda)],
32
+ });
33
+ const [delegationMetadata] = await getProgramDerivedAddress({
34
+ programAddress: DELEGATION_PROGRAM,
35
+ seeds: [utf8Encoder.encode("delegation-metadata"), addressEncoder.encode(permissionPda)],
36
+ });
37
+ // Buffer PDA is under the ownerProgram (PERMISSION_PROGRAM for the Permission PDA)
38
+ const [delegationBuffer] = await getProgramDerivedAddress({
39
+ programAddress: PERMISSION_PROGRAM,
40
+ seeds: [utf8Encoder.encode("buffer"), addressEncoder.encode(permissionPda)],
41
+ });
42
+ return { delegationBuffer, delegationRecord, delegationMetadata };
43
+ }
44
+
11
45
  export const QUEUE_SEED = "queue";
12
46
  export const TENANT_SEED = "tenant";
13
47
  export const TICKET_SEED = "ticket";
package/src/encryption.ts DELETED
@@ -1,154 +0,0 @@
1
- export class EncryptionProvider {
2
- private keyPair: CryptoKeyPair | null = null;
3
-
4
- // Check for crypto availability
5
- private get crypto(): Crypto {
6
- if (typeof globalThis.crypto !== 'undefined') {
7
- return globalThis.crypto;
8
- }
9
- // Fallback for Node < 19 if global crypto not set (though SDK likely targets modern envs)
10
- try {
11
- return require('crypto').webcrypto;
12
- } catch (e) {
13
- throw new Error("WebCrypto API not available.");
14
- }
15
- }
16
-
17
- /**
18
- * Generate a fresh ephemeral keypair for the session (X25519/P-256).
19
- */
20
- async generateSessionKey(): Promise<CryptoKey> {
21
- this.keyPair = await this.crypto.subtle.generateKey(
22
- {
23
- name: "ECDH",
24
- namedCurve: "P-256",
25
- },
26
- true,
27
- ["deriveKey", "deriveBits"]
28
- ) as CryptoKeyPair;
29
-
30
- return this.keyPair.publicKey;
31
- }
32
-
33
- /**
34
- * Derive shared secret and encrypt payload.
35
- */
36
- async encryptPayload(
37
- data: Uint8Array,
38
- teePublicKeyBytes: Uint8Array
39
- ): Promise<{ encrypted: Uint8Array, clientPublicKey: Uint8Array }> {
40
- if (!this.keyPair) {
41
- await this.generateSessionKey();
42
- }
43
-
44
- // Import TEE Public Key
45
- // Fix: Explicitly cast to BufferSource/any because TS gets confused with SharedArrayBuffer in some envs
46
- const teeKey = await this.crypto.subtle.importKey(
47
- "raw",
48
- teePublicKeyBytes as unknown as BufferSource,
49
- { name: "ECDH", namedCurve: "P-256" },
50
- false,
51
- []
52
- );
53
-
54
- // Derive Shared Secret (AES-GCM Key)
55
- const sharedKey = await this.crypto.subtle.deriveKey(
56
- {
57
- name: "ECDH",
58
- public: teeKey,
59
- },
60
- this.keyPair!.privateKey,
61
- {
62
- name: "AES-GCM",
63
- length: 256,
64
- },
65
- false,
66
- ["encrypt", "decrypt"]
67
- );
68
-
69
- // Encrypt Data
70
- const iv = this.crypto.getRandomValues(new Uint8Array(12));
71
- const encryptedBuffer = await this.crypto.subtle.encrypt(
72
- {
73
- name: "AES-GCM",
74
- iv: iv,
75
- },
76
- sharedKey,
77
- data as unknown as BufferSource
78
- );
79
-
80
- // Concatenate IV + CipherText
81
- const encrypted = new Uint8Array(iv.length + encryptedBuffer.byteLength);
82
- encrypted.set(iv);
83
- encrypted.set(new Uint8Array(encryptedBuffer), iv.length);
84
-
85
- // Export Client Public Key
86
- const clientPubRaw = await this.crypto.subtle.exportKey("raw", this.keyPair!.publicKey);
87
-
88
- return {
89
- encrypted,
90
- clientPublicKey: new Uint8Array(clientPubRaw)
91
- };
92
- }
93
-
94
- /**
95
- * Decrypt a response from the TEE.
96
- */
97
- async decryptResponse(
98
- encryptedData: Uint8Array,
99
- teePublicKeyBytes: Uint8Array
100
- ): Promise<Uint8Array> {
101
- if (!this.keyPair) throw new Error("No session key");
102
-
103
- // Import TEE Public Key
104
- const teeKey = await this.crypto.subtle.importKey(
105
- "raw",
106
- teePublicKeyBytes as unknown as BufferSource,
107
- { name: "ECDH", namedCurve: "P-256" },
108
- false,
109
- []
110
- );
111
-
112
- // Derive Shared Secret
113
- const sharedKey = await this.crypto.subtle.deriveKey(
114
- {
115
- name: "ECDH",
116
- public: teeKey,
117
- },
118
- this.keyPair!.privateKey,
119
- {
120
- name: "AES-GCM",
121
- length: 256,
122
- },
123
- false,
124
- ["encrypt", "decrypt"]
125
- );
126
-
127
- const iv = encryptedData.slice(0, 12);
128
- const ciphertext = encryptedData.slice(12);
129
-
130
- const decrypted = await this.crypto.subtle.decrypt(
131
- {
132
- name: "AES-GCM",
133
- iv: iv,
134
- },
135
- sharedKey,
136
- ciphertext as unknown as BufferSource
137
- );
138
-
139
- return new Uint8Array(decrypted);
140
- }
141
-
142
- /**
143
- * Helper to generate a valid random P-256 Public Key (65 bytes) for testing.
144
- */
145
- async createMockValidatorKey(): Promise<Uint8Array> {
146
- const pair = await this.crypto.subtle.generateKey(
147
- { name: "ECDH", namedCurve: "P-256" },
148
- true,
149
- ["deriveKey"]
150
- ) as CryptoKeyPair;
151
- const raw = await this.crypto.subtle.exportKey("raw", pair.publicKey);
152
- return new Uint8Array(raw);
153
- }
154
- }