@velumdotcash/sdk 2.0.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/LICENSE +21 -0
- package/README.md +142 -0
- package/dist/__tests__/paylink.test.d.ts +9 -0
- package/dist/__tests__/paylink.test.js +254 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.js +12 -0
- package/dist/deposit.d.ts +22 -0
- package/dist/deposit.js +445 -0
- package/dist/depositSPL.d.ts +24 -0
- package/dist/depositSPL.js +499 -0
- package/dist/errors.d.ts +78 -0
- package/dist/errors.js +127 -0
- package/dist/exportUtils.d.ts +10 -0
- package/dist/exportUtils.js +10 -0
- package/dist/getUtxos.d.ts +30 -0
- package/dist/getUtxos.js +335 -0
- package/dist/getUtxosSPL.d.ts +34 -0
- package/dist/getUtxosSPL.js +442 -0
- package/dist/index.d.ts +183 -0
- package/dist/index.js +436 -0
- package/dist/models/keypair.d.ts +26 -0
- package/dist/models/keypair.js +43 -0
- package/dist/models/utxo.d.ts +51 -0
- package/dist/models/utxo.js +99 -0
- package/dist/test_paylink_logic.test.d.ts +1 -0
- package/dist/test_paylink_logic.test.js +114 -0
- package/dist/utils/address_lookup_table.d.ts +9 -0
- package/dist/utils/address_lookup_table.js +45 -0
- package/dist/utils/constants.d.ts +27 -0
- package/dist/utils/constants.js +56 -0
- package/dist/utils/debug-logger.d.ts +250 -0
- package/dist/utils/debug-logger.js +688 -0
- package/dist/utils/encryption.d.ts +152 -0
- package/dist/utils/encryption.js +700 -0
- package/dist/utils/logger.d.ts +9 -0
- package/dist/utils/logger.js +35 -0
- package/dist/utils/merkle_tree.d.ts +92 -0
- package/dist/utils/merkle_tree.js +186 -0
- package/dist/utils/node-shim.d.ts +14 -0
- package/dist/utils/node-shim.js +21 -0
- package/dist/utils/prover.d.ts +36 -0
- package/dist/utils/prover.js +169 -0
- package/dist/utils/utils.d.ts +64 -0
- package/dist/utils/utils.js +165 -0
- package/dist/withdraw.d.ts +22 -0
- package/dist/withdraw.js +290 -0
- package/dist/withdrawSPL.d.ts +24 -0
- package/dist/withdrawSPL.js +329 -0
- package/package.json +59 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Velum
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
# @velumdotcash/sdk
|
|
2
|
+
|
|
3
|
+
TypeScript SDK for private payments on Solana using Zero-Knowledge proofs.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @velumdotcash/sdk
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { PrivacyCash } from "@velumdotcash/sdk";
|
|
15
|
+
|
|
16
|
+
// Initialize with wallet signature (browser)
|
|
17
|
+
const sdk = new PrivacyCash({
|
|
18
|
+
RPC_url: "https://api.mainnet-beta.solana.com",
|
|
19
|
+
publicKey: walletPublicKey,
|
|
20
|
+
signature: walletSignature,
|
|
21
|
+
transactionSigner: async (tx) => wallet.signTransaction(tx),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Or with keypair (Node.js / scripts)
|
|
25
|
+
const sdk = new PrivacyCash({
|
|
26
|
+
RPC_url: "https://api.mainnet-beta.solana.com",
|
|
27
|
+
owner: keypair,
|
|
28
|
+
});
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## API Reference
|
|
32
|
+
|
|
33
|
+
### Deposits
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
// Deposit SOL
|
|
37
|
+
await sdk.deposit({ lamports: 10_000_000 });
|
|
38
|
+
|
|
39
|
+
// Deposit USDC
|
|
40
|
+
await sdk.depositUSDC({ base_units: 1_000_000 });
|
|
41
|
+
|
|
42
|
+
// Deposit any SPL token
|
|
43
|
+
await sdk.depositSPL({ base_units: 1_000_000, mintAddress: "..." });
|
|
44
|
+
|
|
45
|
+
// Deposit to a third-party recipient
|
|
46
|
+
await sdk.deposit({
|
|
47
|
+
lamports: 10_000_000,
|
|
48
|
+
recipientUtxoPublicKey: "...",
|
|
49
|
+
recipientEncryptionKey: encryptionKeyBytes,
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Withdrawals
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
// Withdraw SOL
|
|
57
|
+
await sdk.withdraw({ lamports: 10_000_000, recipientAddress: "..." });
|
|
58
|
+
|
|
59
|
+
// Withdraw USDC
|
|
60
|
+
await sdk.withdrawUSDC({ base_units: 1_000_000, recipientAddress: "..." });
|
|
61
|
+
|
|
62
|
+
// Withdraw any SPL token
|
|
63
|
+
await sdk.withdrawSPL({
|
|
64
|
+
base_units: 1_000_000,
|
|
65
|
+
mintAddress: "...",
|
|
66
|
+
recipientAddress: "...",
|
|
67
|
+
});
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Balance
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
const sol = await sdk.getPrivateBalance();
|
|
74
|
+
console.log(sol.lamports);
|
|
75
|
+
|
|
76
|
+
const usdc = await sdk.getPrivateBalanceUSDC();
|
|
77
|
+
console.log(usdc.base_units);
|
|
78
|
+
|
|
79
|
+
const spl = await sdk.getPrivateBalanceSpl(mintAddress);
|
|
80
|
+
console.log(spl.base_units);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Key Derivation
|
|
84
|
+
|
|
85
|
+
```typescript
|
|
86
|
+
// Get public keys for receiving payments
|
|
87
|
+
const encryptionKey = sdk.getAsymmetricPublicKey(); // Uint8Array
|
|
88
|
+
const utxoPubkey = await sdk.getShieldedPublicKey(); // string
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Browser Usage
|
|
92
|
+
|
|
93
|
+
The SDK works in browsers with wallet adapter integration. Circuit files (WASM + zkey) must be served from your application's public directory.
|
|
94
|
+
|
|
95
|
+
```typescript
|
|
96
|
+
const sdk = new PrivacyCash({
|
|
97
|
+
RPC_url: rpcEndpoint,
|
|
98
|
+
publicKey: walletPublicKey,
|
|
99
|
+
signature: walletSignature,
|
|
100
|
+
transactionSigner: async (tx) => wallet.signTransaction(tx),
|
|
101
|
+
circuitPath: "/circuit", // path to circuit files in public/
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Node.js Usage
|
|
106
|
+
|
|
107
|
+
For server-side scripts and testing:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import { Keypair } from "@solana/web3.js";
|
|
111
|
+
|
|
112
|
+
const keypair = Keypair.fromSecretKey(secretKey);
|
|
113
|
+
const sdk = new PrivacyCash({
|
|
114
|
+
RPC_url: process.env.SOLANA_RPC_URL!,
|
|
115
|
+
owner: keypair,
|
|
116
|
+
storage: new LocalStorage("./cache"),
|
|
117
|
+
circuitPath: "./circuits",
|
|
118
|
+
enableDebug: true,
|
|
119
|
+
});
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Error Handling
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
import {
|
|
126
|
+
InsufficientBalanceError,
|
|
127
|
+
ZKProofError,
|
|
128
|
+
NetworkError,
|
|
129
|
+
TransactionTimeoutError,
|
|
130
|
+
} from "@velumdotcash/sdk";
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
await sdk.deposit({ lamports: amount });
|
|
134
|
+
} catch (err) {
|
|
135
|
+
if (err instanceof InsufficientBalanceError) { /* ... */ }
|
|
136
|
+
if (err instanceof ZKProofError) { /* ... */ }
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## License
|
|
141
|
+
|
|
142
|
+
ISC
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests for Privacy Paylink third-party deposit functionality
|
|
3
|
+
*
|
|
4
|
+
* These tests verify that:
|
|
5
|
+
* 1. Utxo can be created with only a public key (no private key)
|
|
6
|
+
* 2. Asymmetric encryption works correctly
|
|
7
|
+
* 3. Third-party deposits work end-to-end
|
|
8
|
+
*/
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Integration tests for Privacy Paylink third-party deposit functionality
|
|
3
|
+
*
|
|
4
|
+
* These tests verify that:
|
|
5
|
+
* 1. Utxo can be created with only a public key (no private key)
|
|
6
|
+
* 2. Asymmetric encryption works correctly
|
|
7
|
+
* 3. Third-party deposits work end-to-end
|
|
8
|
+
*/
|
|
9
|
+
import { describe, it, expect, beforeAll } from "vitest";
|
|
10
|
+
import { Utxo } from "../models/utxo";
|
|
11
|
+
import { Keypair } from "../models/keypair";
|
|
12
|
+
import { EncryptionService } from "../utils/encryption";
|
|
13
|
+
import { WasmFactory } from "@lightprotocol/hasher.rs";
|
|
14
|
+
import BN from "bn.js";
|
|
15
|
+
import nacl from "tweetnacl";
|
|
16
|
+
describe("Paylink Core Functionality", () => {
|
|
17
|
+
let lightWasm;
|
|
18
|
+
beforeAll(async () => {
|
|
19
|
+
// Initialize WASM - required for all crypto operations
|
|
20
|
+
lightWasm = await WasmFactory.getInstance();
|
|
21
|
+
});
|
|
22
|
+
describe("Utxo pubkey-only mode", () => {
|
|
23
|
+
it("should create Utxo with only publicKey", async () => {
|
|
24
|
+
// Generate a keypair to get a valid public key
|
|
25
|
+
const keypair = await Keypair.generateNew(lightWasm);
|
|
26
|
+
const pubkey = keypair.pubkey;
|
|
27
|
+
// Create Utxo with only the public key
|
|
28
|
+
const utxo = new Utxo({
|
|
29
|
+
lightWasm,
|
|
30
|
+
amount: new BN(1000000),
|
|
31
|
+
publicKey: pubkey,
|
|
32
|
+
});
|
|
33
|
+
expect(utxo.pubkey.toString()).toBe(pubkey.toString());
|
|
34
|
+
expect(utxo.amount.toNumber()).toBe(1000000);
|
|
35
|
+
});
|
|
36
|
+
it("should allow getCommitment() with pubkey-only Utxo", async () => {
|
|
37
|
+
const keypair = await Keypair.generateNew(lightWasm);
|
|
38
|
+
const utxo = new Utxo({
|
|
39
|
+
lightWasm,
|
|
40
|
+
amount: new BN(500000),
|
|
41
|
+
publicKey: keypair.pubkey,
|
|
42
|
+
});
|
|
43
|
+
// getCommitment should work without private key (async)
|
|
44
|
+
const commitment = await utxo.getCommitment();
|
|
45
|
+
expect(commitment).toBeDefined();
|
|
46
|
+
expect(typeof commitment).toBe("string");
|
|
47
|
+
});
|
|
48
|
+
it("should throw on getNullifier() with pubkey-only Utxo", async () => {
|
|
49
|
+
const keypair = await Keypair.generateNew(lightWasm);
|
|
50
|
+
const utxo = new Utxo({
|
|
51
|
+
lightWasm,
|
|
52
|
+
amount: new BN(500000),
|
|
53
|
+
publicKey: keypair.pubkey,
|
|
54
|
+
});
|
|
55
|
+
// getNullifier requires private key, should throw
|
|
56
|
+
await expect(utxo.getNullifier()).rejects.toThrow();
|
|
57
|
+
});
|
|
58
|
+
it("should work normally with full keypair", async () => {
|
|
59
|
+
const keypair = await Keypair.generateNew(lightWasm);
|
|
60
|
+
const utxo = new Utxo({
|
|
61
|
+
lightWasm,
|
|
62
|
+
amount: new BN(1000000),
|
|
63
|
+
keypair: keypair,
|
|
64
|
+
});
|
|
65
|
+
// Both should work with full keypair
|
|
66
|
+
const commitment = await utxo.getCommitment();
|
|
67
|
+
const nullifier = await utxo.getNullifier();
|
|
68
|
+
expect(commitment).toBeDefined();
|
|
69
|
+
expect(nullifier).toBeDefined();
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe("Asymmetric Encryption", () => {
|
|
73
|
+
let aliceEncryption;
|
|
74
|
+
let bobEncryption;
|
|
75
|
+
beforeAll(async () => {
|
|
76
|
+
// Simulate two different wallets by deriving from different signatures
|
|
77
|
+
const aliceSignature = nacl.randomBytes(64);
|
|
78
|
+
const bobSignature = nacl.randomBytes(64);
|
|
79
|
+
aliceEncryption = new EncryptionService();
|
|
80
|
+
aliceEncryption.deriveEncryptionKeyFromSignature(aliceSignature);
|
|
81
|
+
bobEncryption = new EncryptionService();
|
|
82
|
+
bobEncryption.deriveEncryptionKeyFromSignature(bobSignature);
|
|
83
|
+
});
|
|
84
|
+
it("should get asymmetric public key", () => {
|
|
85
|
+
const alicePubKey = aliceEncryption.getAsymmetricPublicKey();
|
|
86
|
+
expect(alicePubKey).toBeDefined();
|
|
87
|
+
expect(alicePubKey.length).toBe(32); // X25519 public key is 32 bytes
|
|
88
|
+
});
|
|
89
|
+
it("should encrypt for recipient using asymmetric encryption", () => {
|
|
90
|
+
const alicePubKey = aliceEncryption.getAsymmetricPublicKey();
|
|
91
|
+
const testData = Buffer.from("Hello Alice, this is a secret message!");
|
|
92
|
+
// Bob encrypts for Alice
|
|
93
|
+
const encrypted = bobEncryption.encryptAsymmetric(testData, alicePubKey);
|
|
94
|
+
expect(encrypted).toBeDefined();
|
|
95
|
+
expect(encrypted.length).toBeGreaterThan(testData.length); // Should have nonce + ephemeral key overhead
|
|
96
|
+
});
|
|
97
|
+
it("should decrypt via decryptUtxo for V3 encrypted data", async () => {
|
|
98
|
+
const alicePubKey = aliceEncryption.getAsymmetricPublicKey();
|
|
99
|
+
// Create a simple UTXO to encrypt
|
|
100
|
+
const keypair = await Keypair.generateNew(lightWasm);
|
|
101
|
+
const utxo = new Utxo({
|
|
102
|
+
lightWasm,
|
|
103
|
+
amount: new BN(1_000_000_000),
|
|
104
|
+
publicKey: keypair.pubkey,
|
|
105
|
+
});
|
|
106
|
+
// Bob encrypts UTXO for Alice using asymmetric encryption
|
|
107
|
+
const encrypted = bobEncryption.encryptUtxo(utxo, alicePubKey);
|
|
108
|
+
// Verify it's V3 format (first byte should be 0x03 is at the end of the 8-byte buffer)
|
|
109
|
+
expect(encrypted[7]).toBe(0x03);
|
|
110
|
+
// Alice can decrypt via decryptUtxo (handles V3 internally)
|
|
111
|
+
const decrypted = await aliceEncryption.decryptUtxo(encrypted, lightWasm);
|
|
112
|
+
expect(decrypted).toBeDefined();
|
|
113
|
+
expect(decrypted.amount.toString()).toBe(utxo.amount.toString());
|
|
114
|
+
});
|
|
115
|
+
it("should fail decryption with wrong key", async () => {
|
|
116
|
+
const alicePubKey = aliceEncryption.getAsymmetricPublicKey();
|
|
117
|
+
const keypair = await Keypair.generateNew(lightWasm);
|
|
118
|
+
const utxo = new Utxo({
|
|
119
|
+
lightWasm,
|
|
120
|
+
amount: new BN(1_000_000_000),
|
|
121
|
+
publicKey: keypair.pubkey,
|
|
122
|
+
});
|
|
123
|
+
// Bob encrypts for Alice
|
|
124
|
+
const encrypted = bobEncryption.encryptUtxo(utxo, alicePubKey);
|
|
125
|
+
// Bob tries to decrypt his own encrypted data for Alice - should fail
|
|
126
|
+
try {
|
|
127
|
+
await bobEncryption.decryptUtxo(encrypted, lightWasm);
|
|
128
|
+
expect(true).toBe(false); // Should not reach here
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
expect(e).toBeDefined();
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
describe("UTXO Encryption for Third-Party", () => {
|
|
136
|
+
let senderEncryption;
|
|
137
|
+
let recipientEncryption;
|
|
138
|
+
let recipientKeypair;
|
|
139
|
+
beforeAll(async () => {
|
|
140
|
+
const senderSignature = nacl.randomBytes(64);
|
|
141
|
+
const recipientSignature = nacl.randomBytes(64);
|
|
142
|
+
senderEncryption = new EncryptionService();
|
|
143
|
+
senderEncryption.deriveEncryptionKeyFromSignature(senderSignature);
|
|
144
|
+
recipientEncryption = new EncryptionService();
|
|
145
|
+
recipientEncryption.deriveEncryptionKeyFromSignature(recipientSignature);
|
|
146
|
+
recipientKeypair = await Keypair.generateNew(lightWasm);
|
|
147
|
+
});
|
|
148
|
+
it("should encrypt UTXO for recipient", () => {
|
|
149
|
+
const recipientEncKey = recipientEncryption.getAsymmetricPublicKey();
|
|
150
|
+
// Create UTXO owned by recipient
|
|
151
|
+
const utxo = new Utxo({
|
|
152
|
+
lightWasm,
|
|
153
|
+
amount: new BN(1_000_000_000), // 1 SOL
|
|
154
|
+
publicKey: recipientKeypair.pubkey,
|
|
155
|
+
});
|
|
156
|
+
// Sender encrypts UTXO data for recipient
|
|
157
|
+
const encrypted = senderEncryption.encryptUtxo(utxo, recipientEncKey);
|
|
158
|
+
expect(encrypted).toBeDefined();
|
|
159
|
+
expect(encrypted.length).toBeGreaterThan(0);
|
|
160
|
+
// First byte should be V3 version marker (0x03 is at the end of the 8-byte buffer)
|
|
161
|
+
expect(encrypted[7]).toBe(0x03);
|
|
162
|
+
});
|
|
163
|
+
it("should allow recipient to decrypt UTXO", async () => {
|
|
164
|
+
// For this test to work, recipientKeypair must match what EncryptionService derives
|
|
165
|
+
const recipientPrivKey = recipientEncryption.getUtxoPrivateKeyV2();
|
|
166
|
+
// Re-instantiate the keypair that matches the encryption service
|
|
167
|
+
const derivedRecipientKeypair = new Keypair(recipientPrivKey, lightWasm);
|
|
168
|
+
const recipientEncKey = recipientEncryption.getAsymmetricPublicKey();
|
|
169
|
+
// Create UTXO owned by recipient
|
|
170
|
+
const originalAmount = new BN(2_500_000_000); // 2.5 SOL
|
|
171
|
+
const utxo = new Utxo({
|
|
172
|
+
lightWasm,
|
|
173
|
+
amount: originalAmount,
|
|
174
|
+
publicKey: derivedRecipientKeypair.pubkey,
|
|
175
|
+
});
|
|
176
|
+
// Sender encrypts for recipient
|
|
177
|
+
const encrypted = senderEncryption.encryptUtxo(utxo, recipientEncKey);
|
|
178
|
+
// Recipient decrypts
|
|
179
|
+
const decryptedUtxo = await recipientEncryption.decryptUtxo(encrypted, lightWasm);
|
|
180
|
+
expect(decryptedUtxo).toBeDefined();
|
|
181
|
+
expect(decryptedUtxo.amount.toString()).toBe(originalAmount.toString());
|
|
182
|
+
expect(decryptedUtxo.pubkey.toString()).toBe(derivedRecipientKeypair.pubkey.toString());
|
|
183
|
+
});
|
|
184
|
+
it("should not allow sender to decrypt their own encrypted UTXO for recipient", async () => {
|
|
185
|
+
const recipientEncKey = recipientEncryption.getAsymmetricPublicKey();
|
|
186
|
+
const utxo = new Utxo({
|
|
187
|
+
lightWasm,
|
|
188
|
+
amount: new BN(1_000_000_000),
|
|
189
|
+
publicKey: recipientKeypair.pubkey,
|
|
190
|
+
});
|
|
191
|
+
// Sender encrypts for recipient
|
|
192
|
+
const encrypted = senderEncryption.encryptUtxo(utxo, recipientEncKey);
|
|
193
|
+
// Sender tries to decrypt - should fail
|
|
194
|
+
try {
|
|
195
|
+
await senderEncryption.decryptUtxo(encrypted, lightWasm);
|
|
196
|
+
// If we get here, it failed to throw
|
|
197
|
+
expect(true).toBe(false);
|
|
198
|
+
}
|
|
199
|
+
catch (error) {
|
|
200
|
+
expect(error).toBeDefined();
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
describe("End-to-End Paylink Flow (Unit)", () => {
|
|
205
|
+
it("should support complete paylink flow", async () => {
|
|
206
|
+
// === RECIPIENT GENERATES PAYLINK ===
|
|
207
|
+
const recipientSignature = nacl.randomBytes(64);
|
|
208
|
+
const recipientEncryption = new EncryptionService();
|
|
209
|
+
recipientEncryption.deriveEncryptionKeyFromSignature(recipientSignature);
|
|
210
|
+
const recipientUtxoKeypair = await Keypair.generateNew(lightWasm);
|
|
211
|
+
// These would be encoded in the paylink URL
|
|
212
|
+
const paylinkData = {
|
|
213
|
+
recipientUtxoPubkey: recipientUtxoKeypair.pubkey.toString(),
|
|
214
|
+
recipientEncryptionKey: Buffer.from(recipientEncryption.getAsymmetricPublicKey()).toString("base64"),
|
|
215
|
+
token: "SOL",
|
|
216
|
+
amount: null, // Open amount
|
|
217
|
+
};
|
|
218
|
+
// === SENDER PAYS VIA PAYLINK ===
|
|
219
|
+
const senderSignature = nacl.randomBytes(64);
|
|
220
|
+
const senderEncryption = new EncryptionService();
|
|
221
|
+
senderEncryption.deriveEncryptionKeyFromSignature(senderSignature);
|
|
222
|
+
// Sender decodes paylink data
|
|
223
|
+
const recipientPubkey = new BN(paylinkData.recipientUtxoPubkey);
|
|
224
|
+
const recipientEncKey = new Uint8Array(Buffer.from(paylinkData.recipientEncryptionKey, "base64"));
|
|
225
|
+
// Sender creates UTXO for recipient
|
|
226
|
+
const paymentAmount = new BN(5_000_000_000); // 5 SOL
|
|
227
|
+
const outputUtxo = new Utxo({
|
|
228
|
+
lightWasm,
|
|
229
|
+
amount: paymentAmount,
|
|
230
|
+
publicKey: recipientPubkey,
|
|
231
|
+
});
|
|
232
|
+
// Sender encrypts UTXO for recipient
|
|
233
|
+
const encryptedOutput = senderEncryption.encryptUtxo(outputUtxo, recipientEncKey);
|
|
234
|
+
// Verify commitment can be computed (needed for on-chain)
|
|
235
|
+
const commitment = await outputUtxo.getCommitment();
|
|
236
|
+
expect(commitment).toBeDefined();
|
|
237
|
+
// === RECIPIENT CLAIMS ===
|
|
238
|
+
// Recipient scans encrypted outputs and tries to decrypt
|
|
239
|
+
const decryptedUtxo = await recipientEncryption.decryptUtxo(encryptedOutput, lightWasm);
|
|
240
|
+
expect(decryptedUtxo).toBeDefined();
|
|
241
|
+
expect(decryptedUtxo.amount.toString()).toBe(paymentAmount.toString());
|
|
242
|
+
// Recipient can now use their keypair to spend (would need full keypair for nullifier)
|
|
243
|
+
// In real flow, recipient would reconstruct UTXO with their full keypair
|
|
244
|
+
const spendableUtxo = new Utxo({
|
|
245
|
+
lightWasm,
|
|
246
|
+
amount: decryptedUtxo.amount,
|
|
247
|
+
keypair: recipientUtxoKeypair, // Full keypair for spending
|
|
248
|
+
});
|
|
249
|
+
// Now recipient can compute nullifier for spending
|
|
250
|
+
const nullifier = await spendableUtxo.getNullifier();
|
|
251
|
+
expect(nullifier).toBeDefined();
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
});
|
package/dist/config.d.ts
ADDED
package/dist/config.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { RELAYER_API_URL } from "./utils/constants.js";
|
|
2
|
+
let config;
|
|
3
|
+
export async function getConfig(key) {
|
|
4
|
+
if (!config) {
|
|
5
|
+
const res = await fetch(RELAYER_API_URL + '/config');
|
|
6
|
+
config = await res.json();
|
|
7
|
+
}
|
|
8
|
+
if (typeof config[key] == 'undefined') {
|
|
9
|
+
throw new Error(`can not get ${key} from ${RELAYER_API_URL}/config`);
|
|
10
|
+
}
|
|
11
|
+
return config[key];
|
|
12
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Connection, PublicKey, VersionedTransaction } from "@solana/web3.js";
|
|
2
|
+
import BN from "bn.js";
|
|
3
|
+
import * as hasher from "@lightprotocol/hasher.rs";
|
|
4
|
+
import { EncryptionService } from "./utils/encryption.js";
|
|
5
|
+
type DepositParams = {
|
|
6
|
+
publicKey: PublicKey;
|
|
7
|
+
connection: Connection;
|
|
8
|
+
amount_in_lamports: number;
|
|
9
|
+
storage: Storage;
|
|
10
|
+
encryptionService: EncryptionService;
|
|
11
|
+
keyBasePath: string;
|
|
12
|
+
lightWasm: hasher.LightWasm;
|
|
13
|
+
referrer?: string;
|
|
14
|
+
signer?: PublicKey;
|
|
15
|
+
transactionSigner: (tx: VersionedTransaction) => Promise<VersionedTransaction>;
|
|
16
|
+
recipientUtxoPublicKey?: BN | string;
|
|
17
|
+
recipientEncryptionKey?: Uint8Array;
|
|
18
|
+
};
|
|
19
|
+
export declare function deposit({ lightWasm, storage, keyBasePath, publicKey, connection, amount_in_lamports, encryptionService, transactionSigner, referrer, signer, recipientUtxoPublicKey, recipientEncryptionKey, }: DepositParams): Promise<{
|
|
20
|
+
tx: string;
|
|
21
|
+
}>;
|
|
22
|
+
export {};
|