@sip-protocol/sdk 0.1.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/dist/index.d.mts +3640 -0
- package/dist/index.d.ts +3640 -0
- package/dist/index.js +5725 -0
- package/dist/index.mjs +5606 -0
- package/package.json +61 -0
- package/src/adapters/index.ts +19 -0
- package/src/adapters/near-intents.ts +475 -0
- package/src/adapters/oneclick-client.ts +367 -0
- package/src/commitment.ts +470 -0
- package/src/crypto.ts +93 -0
- package/src/errors.ts +471 -0
- package/src/index.ts +369 -0
- package/src/intent.ts +488 -0
- package/src/privacy.ts +382 -0
- package/src/proofs/index.ts +52 -0
- package/src/proofs/interface.ts +228 -0
- package/src/proofs/mock.ts +258 -0
- package/src/proofs/noir.ts +233 -0
- package/src/sip.ts +299 -0
- package/src/solver/index.ts +25 -0
- package/src/solver/mock-solver.ts +278 -0
- package/src/stealth.ts +414 -0
- package/src/validation.ts +401 -0
- package/src/wallet/base-adapter.ts +407 -0
- package/src/wallet/errors.ts +106 -0
- package/src/wallet/ethereum/adapter.ts +655 -0
- package/src/wallet/ethereum/index.ts +48 -0
- package/src/wallet/ethereum/mock.ts +505 -0
- package/src/wallet/ethereum/types.ts +364 -0
- package/src/wallet/index.ts +116 -0
- package/src/wallet/registry.ts +207 -0
- package/src/wallet/solana/adapter.ts +533 -0
- package/src/wallet/solana/index.ts +40 -0
- package/src/wallet/solana/mock.ts +522 -0
- package/src/wallet/solana/types.ts +253 -0
- package/src/zcash/index.ts +53 -0
- package/src/zcash/rpc-client.ts +623 -0
- package/src/zcash/shielded-service.ts +641 -0
|
@@ -0,0 +1,533 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solana Wallet Adapter
|
|
3
|
+
*
|
|
4
|
+
* Implementation of WalletAdapter for Solana chain.
|
|
5
|
+
* Supports Phantom, Solflare, Backpack, and generic Solana wallets.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
HexString,
|
|
10
|
+
Asset,
|
|
11
|
+
Signature,
|
|
12
|
+
UnsignedTransaction,
|
|
13
|
+
SignedTransaction,
|
|
14
|
+
TransactionReceipt,
|
|
15
|
+
} from '@sip-protocol/types'
|
|
16
|
+
import { WalletErrorCode } from '@sip-protocol/types'
|
|
17
|
+
import { BaseWalletAdapter } from '../base-adapter'
|
|
18
|
+
import { WalletError } from '../errors'
|
|
19
|
+
import type {
|
|
20
|
+
SolanaWalletProvider,
|
|
21
|
+
SolanaAdapterConfig,
|
|
22
|
+
SolanaWalletName,
|
|
23
|
+
SolanaCluster,
|
|
24
|
+
SolanaTransaction,
|
|
25
|
+
SolanaVersionedTransaction,
|
|
26
|
+
SolanaConnection,
|
|
27
|
+
SolanaSendOptions,
|
|
28
|
+
} from './types'
|
|
29
|
+
import {
|
|
30
|
+
getSolanaProvider,
|
|
31
|
+
solanaPublicKeyToHex,
|
|
32
|
+
} from './types'
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Default RPC endpoints for Solana clusters
|
|
36
|
+
*/
|
|
37
|
+
const DEFAULT_RPC_ENDPOINTS: Record<SolanaCluster, string> = {
|
|
38
|
+
'mainnet-beta': 'https://api.mainnet-beta.solana.com',
|
|
39
|
+
'testnet': 'https://api.testnet.solana.com',
|
|
40
|
+
'devnet': 'https://api.devnet.solana.com',
|
|
41
|
+
'localnet': 'http://localhost:8899',
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Solana wallet adapter
|
|
46
|
+
*
|
|
47
|
+
* Provides SIP-compatible wallet interface for Solana.
|
|
48
|
+
* Works with Phantom, Solflare, Backpack, and other Solana wallets.
|
|
49
|
+
*
|
|
50
|
+
* @example Browser usage with Phantom
|
|
51
|
+
* ```typescript
|
|
52
|
+
* const wallet = new SolanaWalletAdapter({ wallet: 'phantom' })
|
|
53
|
+
* await wallet.connect()
|
|
54
|
+
*
|
|
55
|
+
* const balance = await wallet.getBalance()
|
|
56
|
+
* console.log(`Balance: ${balance} lamports`)
|
|
57
|
+
*
|
|
58
|
+
* // Sign a message
|
|
59
|
+
* const sig = await wallet.signMessage(new TextEncoder().encode('Hello'))
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @example With custom RPC endpoint
|
|
63
|
+
* ```typescript
|
|
64
|
+
* const wallet = new SolanaWalletAdapter({
|
|
65
|
+
* wallet: 'phantom',
|
|
66
|
+
* cluster: 'devnet',
|
|
67
|
+
* rpcEndpoint: 'https://my-rpc.example.com',
|
|
68
|
+
* })
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
export class SolanaWalletAdapter extends BaseWalletAdapter {
|
|
72
|
+
readonly chain = 'solana' as const
|
|
73
|
+
readonly name: string
|
|
74
|
+
|
|
75
|
+
private provider: SolanaWalletProvider | undefined
|
|
76
|
+
private connection: SolanaConnection | undefined
|
|
77
|
+
private walletName: SolanaWalletName
|
|
78
|
+
private cluster: SolanaCluster
|
|
79
|
+
private rpcEndpoint: string
|
|
80
|
+
|
|
81
|
+
// Event handler references for cleanup
|
|
82
|
+
private connectHandler?: () => void
|
|
83
|
+
private disconnectHandler?: () => void
|
|
84
|
+
private accountChangedHandler?: (pubkey: unknown) => void
|
|
85
|
+
|
|
86
|
+
constructor(config: SolanaAdapterConfig = {}) {
|
|
87
|
+
super()
|
|
88
|
+
this.walletName = config.wallet ?? 'phantom'
|
|
89
|
+
this.name = `solana-${this.walletName}`
|
|
90
|
+
this.cluster = config.cluster ?? 'mainnet-beta'
|
|
91
|
+
this.rpcEndpoint = config.rpcEndpoint ?? DEFAULT_RPC_ENDPOINTS[this.cluster]
|
|
92
|
+
|
|
93
|
+
// Allow injecting provider/connection for testing
|
|
94
|
+
if (config.provider) {
|
|
95
|
+
this.provider = config.provider
|
|
96
|
+
}
|
|
97
|
+
if (config.connection) {
|
|
98
|
+
this.connection = config.connection
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get the current Solana cluster
|
|
104
|
+
*/
|
|
105
|
+
getCluster(): SolanaCluster {
|
|
106
|
+
return this.cluster
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Get the RPC endpoint
|
|
111
|
+
*/
|
|
112
|
+
getRpcEndpoint(): string {
|
|
113
|
+
return this.rpcEndpoint
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Set the RPC endpoint
|
|
118
|
+
*/
|
|
119
|
+
setRpcEndpoint(endpoint: string): void {
|
|
120
|
+
this.rpcEndpoint = endpoint
|
|
121
|
+
// Clear connection so it's recreated with new endpoint
|
|
122
|
+
this.connection = undefined
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Get or create a connection for RPC calls
|
|
127
|
+
*/
|
|
128
|
+
private async getConnection(): Promise<SolanaConnection> {
|
|
129
|
+
if (this.connection) return this.connection
|
|
130
|
+
|
|
131
|
+
// Create a minimal fetch-based connection
|
|
132
|
+
this.connection = createMinimalConnection(this.rpcEndpoint)
|
|
133
|
+
return this.connection
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Connect to the wallet
|
|
138
|
+
*/
|
|
139
|
+
async connect(): Promise<void> {
|
|
140
|
+
this._connectionState = 'connecting'
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
// Get provider if not already set
|
|
144
|
+
if (!this.provider) {
|
|
145
|
+
this.provider = getSolanaProvider(this.walletName)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (!this.provider) {
|
|
149
|
+
this.setError(
|
|
150
|
+
WalletErrorCode.NOT_INSTALLED,
|
|
151
|
+
`${this.walletName} wallet is not installed`
|
|
152
|
+
)
|
|
153
|
+
throw new WalletError(
|
|
154
|
+
`${this.walletName} wallet is not installed`,
|
|
155
|
+
WalletErrorCode.NOT_INSTALLED
|
|
156
|
+
)
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Connect to wallet
|
|
160
|
+
const { publicKey } = await this.provider.connect()
|
|
161
|
+
|
|
162
|
+
if (!publicKey) {
|
|
163
|
+
throw new WalletError(
|
|
164
|
+
'No public key returned from wallet',
|
|
165
|
+
WalletErrorCode.CONNECTION_FAILED
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Set up event handlers
|
|
170
|
+
this.setupEventHandlers()
|
|
171
|
+
|
|
172
|
+
// Update state
|
|
173
|
+
const address = publicKey.toBase58()
|
|
174
|
+
const hexPubKey = solanaPublicKeyToHex(publicKey)
|
|
175
|
+
this.setConnected(address, hexPubKey)
|
|
176
|
+
} catch (error) {
|
|
177
|
+
const message = error instanceof Error ? error.message : 'Connection failed'
|
|
178
|
+
|
|
179
|
+
// Check if user rejected
|
|
180
|
+
if (message.includes('User rejected') || message.includes('rejected')) {
|
|
181
|
+
this.setError(WalletErrorCode.CONNECTION_REJECTED, message)
|
|
182
|
+
throw new WalletError(message, WalletErrorCode.CONNECTION_REJECTED)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
this.setError(WalletErrorCode.CONNECTION_FAILED, message)
|
|
186
|
+
throw error instanceof WalletError
|
|
187
|
+
? error
|
|
188
|
+
: new WalletError(message, WalletErrorCode.CONNECTION_FAILED, { cause: error as Error })
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Disconnect from the wallet
|
|
194
|
+
*/
|
|
195
|
+
async disconnect(): Promise<void> {
|
|
196
|
+
this.cleanupEventHandlers()
|
|
197
|
+
|
|
198
|
+
if (this.provider?.disconnect) {
|
|
199
|
+
try {
|
|
200
|
+
await this.provider.disconnect()
|
|
201
|
+
} catch {
|
|
202
|
+
// Ignore disconnect errors
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
this.setDisconnected('User disconnected')
|
|
207
|
+
this.provider = undefined
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Sign a message
|
|
212
|
+
*/
|
|
213
|
+
async signMessage(message: Uint8Array): Promise<Signature> {
|
|
214
|
+
this.requireConnected()
|
|
215
|
+
|
|
216
|
+
if (!this.provider) {
|
|
217
|
+
throw new WalletError('Provider not available', WalletErrorCode.NOT_CONNECTED)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
try {
|
|
221
|
+
const { signature } = await this.provider.signMessage(message)
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
signature: ('0x' + Buffer.from(signature).toString('hex')) as HexString,
|
|
225
|
+
publicKey: this._publicKey as HexString,
|
|
226
|
+
}
|
|
227
|
+
} catch (error) {
|
|
228
|
+
const message = error instanceof Error ? error.message : 'Signing failed'
|
|
229
|
+
|
|
230
|
+
if (message.includes('User rejected') || message.includes('rejected')) {
|
|
231
|
+
throw new WalletError(message, WalletErrorCode.SIGNING_REJECTED)
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
throw new WalletError(message, WalletErrorCode.SIGNING_FAILED, {
|
|
235
|
+
cause: error as Error,
|
|
236
|
+
})
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Sign a transaction
|
|
242
|
+
*
|
|
243
|
+
* The transaction data should be a SolanaTransaction or SolanaVersionedTransaction
|
|
244
|
+
*/
|
|
245
|
+
async signTransaction(tx: UnsignedTransaction): Promise<SignedTransaction> {
|
|
246
|
+
this.requireConnected()
|
|
247
|
+
|
|
248
|
+
if (!this.provider) {
|
|
249
|
+
throw new WalletError('Provider not available', WalletErrorCode.NOT_CONNECTED)
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
const solTx = tx.data as SolanaTransaction | SolanaVersionedTransaction
|
|
254
|
+
const signed = await this.provider.signTransaction(solTx)
|
|
255
|
+
const serialized = signed.serialize()
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
unsigned: tx,
|
|
259
|
+
signatures: [
|
|
260
|
+
{
|
|
261
|
+
signature: ('0x' + Buffer.from(serialized).toString('hex')) as HexString,
|
|
262
|
+
publicKey: this._publicKey as HexString,
|
|
263
|
+
},
|
|
264
|
+
],
|
|
265
|
+
serialized: ('0x' + Buffer.from(serialized).toString('hex')) as HexString,
|
|
266
|
+
}
|
|
267
|
+
} catch (error) {
|
|
268
|
+
const message = error instanceof Error ? error.message : 'Signing failed'
|
|
269
|
+
|
|
270
|
+
if (message.includes('User rejected') || message.includes('rejected')) {
|
|
271
|
+
throw new WalletError(message, WalletErrorCode.SIGNING_REJECTED)
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
throw new WalletError(message, WalletErrorCode.SIGNING_FAILED, {
|
|
275
|
+
cause: error as Error,
|
|
276
|
+
})
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Sign and send a transaction
|
|
282
|
+
*/
|
|
283
|
+
async signAndSendTransaction(tx: UnsignedTransaction): Promise<TransactionReceipt> {
|
|
284
|
+
this.requireConnected()
|
|
285
|
+
|
|
286
|
+
if (!this.provider) {
|
|
287
|
+
throw new WalletError('Provider not available', WalletErrorCode.NOT_CONNECTED)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
try {
|
|
291
|
+
const solTx = tx.data as SolanaTransaction | SolanaVersionedTransaction
|
|
292
|
+
const sendOptions = tx.metadata?.sendOptions as SolanaSendOptions | undefined
|
|
293
|
+
|
|
294
|
+
const { signature } = await this.provider.signAndSendTransaction(solTx, sendOptions)
|
|
295
|
+
|
|
296
|
+
return {
|
|
297
|
+
txHash: ('0x' + Buffer.from(signature).toString('hex')) as HexString,
|
|
298
|
+
status: 'pending', // Transaction is sent but not confirmed yet
|
|
299
|
+
timestamp: Date.now(),
|
|
300
|
+
}
|
|
301
|
+
} catch (error) {
|
|
302
|
+
const message = error instanceof Error ? error.message : 'Transaction failed'
|
|
303
|
+
|
|
304
|
+
if (message.includes('User rejected') || message.includes('rejected')) {
|
|
305
|
+
throw new WalletError(message, WalletErrorCode.TRANSACTION_REJECTED)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
if (message.includes('insufficient') || message.includes('Insufficient')) {
|
|
309
|
+
throw new WalletError(message, WalletErrorCode.INSUFFICIENT_FUNDS)
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
throw new WalletError(message, WalletErrorCode.TRANSACTION_FAILED, {
|
|
313
|
+
cause: error as Error,
|
|
314
|
+
})
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Sign multiple transactions at once
|
|
320
|
+
*
|
|
321
|
+
* Solana-specific method for batch signing
|
|
322
|
+
*/
|
|
323
|
+
async signAllTransactions<T extends SolanaTransaction | SolanaVersionedTransaction>(
|
|
324
|
+
transactions: T[]
|
|
325
|
+
): Promise<T[]> {
|
|
326
|
+
this.requireConnected()
|
|
327
|
+
|
|
328
|
+
if (!this.provider) {
|
|
329
|
+
throw new WalletError('Provider not available', WalletErrorCode.NOT_CONNECTED)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
try {
|
|
333
|
+
return await this.provider.signAllTransactions(transactions)
|
|
334
|
+
} catch (error) {
|
|
335
|
+
const message = error instanceof Error ? error.message : 'Signing failed'
|
|
336
|
+
|
|
337
|
+
if (message.includes('User rejected') || message.includes('rejected')) {
|
|
338
|
+
throw new WalletError(message, WalletErrorCode.SIGNING_REJECTED)
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
throw new WalletError(message, WalletErrorCode.SIGNING_FAILED, {
|
|
342
|
+
cause: error as Error,
|
|
343
|
+
})
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Get native SOL balance
|
|
349
|
+
*/
|
|
350
|
+
async getBalance(): Promise<bigint> {
|
|
351
|
+
this.requireConnected()
|
|
352
|
+
|
|
353
|
+
try {
|
|
354
|
+
const connection = await this.getConnection()
|
|
355
|
+
const balance = await connection.getBalance({
|
|
356
|
+
toBase58: () => this._address,
|
|
357
|
+
toBytes: () => new Uint8Array(32),
|
|
358
|
+
toString: () => this._address,
|
|
359
|
+
})
|
|
360
|
+
|
|
361
|
+
return BigInt(balance)
|
|
362
|
+
} catch (error) {
|
|
363
|
+
throw new WalletError(
|
|
364
|
+
'Failed to get balance',
|
|
365
|
+
WalletErrorCode.UNKNOWN,
|
|
366
|
+
{ cause: error as Error }
|
|
367
|
+
)
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Get SPL token balance
|
|
373
|
+
*/
|
|
374
|
+
async getTokenBalance(asset: Asset): Promise<bigint> {
|
|
375
|
+
this.requireConnected()
|
|
376
|
+
|
|
377
|
+
if (asset.chain !== 'solana') {
|
|
378
|
+
throw new WalletError(
|
|
379
|
+
`Asset chain ${asset.chain} not supported by Solana adapter`,
|
|
380
|
+
WalletErrorCode.UNSUPPORTED_CHAIN
|
|
381
|
+
)
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Native SOL
|
|
385
|
+
if (!asset.address) {
|
|
386
|
+
return this.getBalance()
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
const connection = await this.getConnection()
|
|
391
|
+
// For SPL tokens, we need to find the associated token account
|
|
392
|
+
// This is a simplified implementation - real implementation would use
|
|
393
|
+
// getAssociatedTokenAddress and handle missing accounts
|
|
394
|
+
const result = await connection.getTokenAccountBalance({
|
|
395
|
+
toBase58: () => asset.address as string,
|
|
396
|
+
toBytes: () => new Uint8Array(32),
|
|
397
|
+
toString: () => asset.address as string,
|
|
398
|
+
})
|
|
399
|
+
|
|
400
|
+
return BigInt(result.value.amount)
|
|
401
|
+
} catch {
|
|
402
|
+
// Token account might not exist
|
|
403
|
+
return 0n
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Set up wallet event handlers
|
|
409
|
+
*/
|
|
410
|
+
private setupEventHandlers(): void {
|
|
411
|
+
if (!this.provider) return
|
|
412
|
+
|
|
413
|
+
this.connectHandler = () => {
|
|
414
|
+
// Wallet reconnected
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
this.disconnectHandler = () => {
|
|
418
|
+
this.setDisconnected('Wallet disconnected')
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
this.accountChangedHandler = (pubkey: unknown) => {
|
|
422
|
+
if (pubkey && typeof (pubkey as { toBase58?: () => string }).toBase58 === 'function') {
|
|
423
|
+
const newAddress = (pubkey as { toBase58: () => string }).toBase58()
|
|
424
|
+
const previousAddress = this._address
|
|
425
|
+
this._address = newAddress
|
|
426
|
+
this._publicKey = solanaPublicKeyToHex(pubkey as { toBase58: () => string; toBytes: () => Uint8Array; toString: () => string })
|
|
427
|
+
this.emitAccountChanged(previousAddress, newAddress)
|
|
428
|
+
} else {
|
|
429
|
+
// Account changed to null = disconnected
|
|
430
|
+
this.setDisconnected('Account changed')
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
this.provider.on('connect', this.connectHandler)
|
|
435
|
+
this.provider.on('disconnect', this.disconnectHandler)
|
|
436
|
+
this.provider.on('accountChanged', this.accountChangedHandler)
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Clean up wallet event handlers
|
|
441
|
+
*/
|
|
442
|
+
private cleanupEventHandlers(): void {
|
|
443
|
+
if (!this.provider) return
|
|
444
|
+
|
|
445
|
+
if (this.connectHandler) {
|
|
446
|
+
this.provider.off('connect', this.connectHandler)
|
|
447
|
+
}
|
|
448
|
+
if (this.disconnectHandler) {
|
|
449
|
+
this.provider.off('disconnect', this.disconnectHandler)
|
|
450
|
+
}
|
|
451
|
+
if (this.accountChangedHandler) {
|
|
452
|
+
this.provider.off('accountChanged', this.accountChangedHandler)
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
this.connectHandler = undefined
|
|
456
|
+
this.disconnectHandler = undefined
|
|
457
|
+
this.accountChangedHandler = undefined
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* Create a minimal Solana RPC connection using fetch
|
|
463
|
+
*
|
|
464
|
+
* This allows basic RPC calls without requiring @solana/web3.js
|
|
465
|
+
*/
|
|
466
|
+
function createMinimalConnection(endpoint: string): SolanaConnection {
|
|
467
|
+
const rpc = async (method: string, params: unknown[]): Promise<unknown> => {
|
|
468
|
+
const response = await fetch(endpoint, {
|
|
469
|
+
method: 'POST',
|
|
470
|
+
headers: { 'Content-Type': 'application/json' },
|
|
471
|
+
body: JSON.stringify({
|
|
472
|
+
jsonrpc: '2.0',
|
|
473
|
+
id: Date.now(),
|
|
474
|
+
method,
|
|
475
|
+
params,
|
|
476
|
+
}),
|
|
477
|
+
})
|
|
478
|
+
|
|
479
|
+
const data = await response.json()
|
|
480
|
+
|
|
481
|
+
if (data.error) {
|
|
482
|
+
throw new Error(data.error.message || 'RPC error')
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return data.result
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
return {
|
|
489
|
+
async getBalance(publicKey) {
|
|
490
|
+
const result = await rpc('getBalance', [publicKey.toBase58()])
|
|
491
|
+
return (result as { value: number }).value
|
|
492
|
+
},
|
|
493
|
+
|
|
494
|
+
async getTokenAccountBalance(publicKey) {
|
|
495
|
+
const result = await rpc('getTokenAccountBalance', [publicKey.toBase58()])
|
|
496
|
+
return result as { value: { amount: string; decimals: number } }
|
|
497
|
+
},
|
|
498
|
+
|
|
499
|
+
async getLatestBlockhash() {
|
|
500
|
+
const result = await rpc('getLatestBlockhash', [])
|
|
501
|
+
return result as { blockhash: string; lastValidBlockHeight: number }
|
|
502
|
+
},
|
|
503
|
+
|
|
504
|
+
async sendRawTransaction(rawTransaction, options) {
|
|
505
|
+
const base64Tx = Buffer.from(rawTransaction).toString('base64')
|
|
506
|
+
const result = await rpc('sendTransaction', [
|
|
507
|
+
base64Tx,
|
|
508
|
+
{
|
|
509
|
+
encoding: 'base64',
|
|
510
|
+
skipPreflight: options?.skipPreflight ?? false,
|
|
511
|
+
preflightCommitment: options?.preflightCommitment ?? 'confirmed',
|
|
512
|
+
maxRetries: options?.maxRetries,
|
|
513
|
+
},
|
|
514
|
+
])
|
|
515
|
+
return result as string
|
|
516
|
+
},
|
|
517
|
+
|
|
518
|
+
async confirmTransaction(signature, commitment = 'confirmed') {
|
|
519
|
+
const result = await rpc('getSignatureStatuses', [[signature]])
|
|
520
|
+
const statuses = result as { value: Array<{ err: unknown } | null> }
|
|
521
|
+
return { value: { err: statuses.value[0]?.err ?? null } }
|
|
522
|
+
},
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* Create a Solana wallet adapter with default configuration
|
|
528
|
+
*/
|
|
529
|
+
export function createSolanaAdapter(
|
|
530
|
+
config: SolanaAdapterConfig = {}
|
|
531
|
+
): SolanaWalletAdapter {
|
|
532
|
+
return new SolanaWalletAdapter(config)
|
|
533
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Solana Wallet Module
|
|
3
|
+
*
|
|
4
|
+
* Provides Solana-specific wallet adapter for SIP Protocol.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
// Main adapter
|
|
8
|
+
export { SolanaWalletAdapter, createSolanaAdapter } from './adapter'
|
|
9
|
+
|
|
10
|
+
// Mock adapter for testing
|
|
11
|
+
export {
|
|
12
|
+
MockSolanaAdapter,
|
|
13
|
+
createMockSolanaAdapter,
|
|
14
|
+
createMockSolanaProvider,
|
|
15
|
+
createMockSolanaConnection,
|
|
16
|
+
} from './mock'
|
|
17
|
+
|
|
18
|
+
export type { MockSolanaAdapterConfig } from './mock'
|
|
19
|
+
|
|
20
|
+
// Types
|
|
21
|
+
export {
|
|
22
|
+
getSolanaProvider,
|
|
23
|
+
detectSolanaWallets,
|
|
24
|
+
solanaPublicKeyToHex,
|
|
25
|
+
base58ToHex,
|
|
26
|
+
} from './types'
|
|
27
|
+
|
|
28
|
+
export type {
|
|
29
|
+
SolanaPublicKey,
|
|
30
|
+
SolanaTransaction,
|
|
31
|
+
SolanaVersionedTransaction,
|
|
32
|
+
SolanaWalletProvider,
|
|
33
|
+
SolanaWalletName,
|
|
34
|
+
SolanaCluster,
|
|
35
|
+
SolanaAdapterConfig,
|
|
36
|
+
SolanaConnection,
|
|
37
|
+
SolanaSendOptions,
|
|
38
|
+
SolanaUnsignedTransaction,
|
|
39
|
+
SolanaSignature,
|
|
40
|
+
} from './types'
|