-
Notifications
You must be signed in to change notification settings - Fork 370
/
Copy pathSolanaLib.ts
119 lines (95 loc) · 3.19 KB
/
SolanaLib.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import { Keypair, Connection, SendOptions, VersionedTransaction } from '@solana/web3.js'
import bs58 from 'bs58'
import nacl from 'tweetnacl'
import { SOLANA_MAINNET_CHAINS, SOLANA_TEST_CHAINS } from '@/data/SolanaData'
/**
* Types
*/
interface IInitArguments {
secretKey?: Uint8Array
}
/**
* Library
*/
export default class SolanaLib {
keypair: Keypair
constructor(keypair: Keypair) {
this.keypair = keypair
}
static init({ secretKey }: IInitArguments) {
const keypair = secretKey ? Keypair.fromSecretKey(secretKey) : Keypair.generate()
return new SolanaLib(keypair)
}
public async getAddress() {
return await this.keypair.publicKey.toBase58()
}
public getSecretKey() {
return this.keypair.secretKey.toString()
}
public async signMessage(
params: SolanaLib.SignMessage['params']
): Promise<SolanaLib.SignMessage['result']> {
const signature = nacl.sign.detached(bs58.decode(params.message), this.keypair.secretKey)
const bs58Signature = bs58.encode(signature)
return { signature: bs58Signature }
}
public async signTransaction(
params: SolanaLib.SignTransaction['params']
): Promise<SolanaLib.SignTransaction['result']> {
const transaction = this.deserialize(params.transaction)
this.sign(transaction)
const signature = bs58.encode(transaction.signatures[0])
return {
transaction: this.serialize(transaction),
signature
}
}
public async signAndSendTransaction(
params: SolanaLib.SignAndSendTransaction['params'],
chainId: string
): Promise<SolanaLib.SignAndSendTransaction['result']> {
const rpc = { ...SOLANA_TEST_CHAINS, ...SOLANA_MAINNET_CHAINS }[chainId]?.rpc
if (!rpc) {
throw new Error('There is no RPC URL for the provided chain')
}
const connection = new Connection(rpc)
const transaction = this.deserialize(params.transaction)
this.sign(transaction)
const signature = await connection.sendTransaction(transaction, {
maxRetries: 3,
preflightCommitment: 'recent',
...params.sendOptions
})
return { signature }
}
private serialize(transaction: VersionedTransaction): string {
return bs58.encode(transaction.serialize())
}
private deserialize(transaction: string): VersionedTransaction {
/*
* We should consider serializing the transaction to base58 as it is the solana standard.
* But our specs requires base64 right now:
* https://docs.walletconnect.com/advanced/multichain/rpc-reference/solana-rpc#solana_signtransaction
*/
const decodedBase64 = Uint8Array.from(Buffer.from(transaction, 'base64'))
return VersionedTransaction.deserialize(decodedBase64)
}
private sign(transaction: VersionedTransaction) {
transaction.sign([this.keypair])
}
}
export namespace SolanaLib {
type RPCRequest<Params, Result> = {
params: Params
result: Result
}
export type SignMessage = RPCRequest<{ message: string }, { signature: string }>
export type SignTransaction = RPCRequest<
{ transaction: string },
{ transaction: string; signature: string }
>
export type SignAndSendTransaction = RPCRequest<
{ transaction: string; sendOptions?: SendOptions },
{ signature: string }
>
}