/
AccountsStore.ts
238 lines (215 loc) · 8.93 KB
/
AccountsStore.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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
import * as solanaWeb3 from "@solana/web3.js";
import { Connection, Keypair } from "@solana/web3.js";
import { BridgeTokenConfig } from "../../common";
import { PublicKey } from "@solana/web3.js";
import { solanaMnemonicToSecretkey } from "./utils";
import * as bip39 from "bip39"
import BigNumber from "bignumber.js";
import { getAssociatedTokenAccount } from "./transactions";
/**
* Represents a Solana account.
*
* @typedef {Object} SolanaAccount
* @property {string} addr - The account address.
* @property {Uint8Array} sk - The secret key.
* @property {PublicKey} pk - The public key.
* @property {number} [balance] - The account balance (optional).
* @property {string} [mnemonic] - The mnemonic associated with the account (optional).
*/
export type SolanaAccount = {
addr: string;
sk: Uint8Array;
pk: PublicKey;
balance?: number;
mnemonic?: string;
};
/**
* Represents a store for Solana accounts.
*
* @class SolanaAccountsStore
*/
export class SolanaAccountsStore {
SOL_DECIMALS = 9
private __accounts: Map<string, SolanaAccount>;
private __connection: Connection;
/**
* Represents a store for Solana accounts.
*
* @class SolanaAccountsStore
* @constructor
* @param {Connection} connection - The Solana connection object to use for interacting with the Solana network.
*/
public constructor(connection: Connection) {
this.__connection = connection;
this.__accounts = new Map();
}
/**
* Adds a new Solana account to the store.
*
* @method add
* @async
* @param {Uint8Array|undefined|string|undefined} skOrMnemonic - The secret key as Uint8Array or the mnemonic as a string (optional).
* @returns {Promise<SolanaAccount>} - A Promise that resolves to the added Solana account.
*/
public async add(...args: [sk: Uint8Array | undefined] | [mnemonic: string | undefined]): Promise<SolanaAccount> {
let sk: Uint8Array | undefined = undefined;
if (typeof args[0] == "undefined") {
return Promise.reject(new Error("solana add args not set"));
} else if (typeof args[0] == "string") {
const mnemonic = args[0] as string;
sk = await solanaMnemonicToSecretkey(mnemonic);
} else if (typeof args[0] == "object") {
sk = args[0] as Uint8Array;
}
if (!sk) return Promise.reject(new Error("Solana Key not found"));
const keyPair = solanaWeb3.Keypair.fromSecretKey(sk);
const solAccount: SolanaAccount = {
addr: keyPair.publicKey.toString(),
sk: sk,
pk: keyPair.publicKey
};
this.__accounts.set(keyPair.publicKey.toString(), solAccount)
return solAccount;
}
/**
* Retrieves the SOL (Solana) token balance for a given account address.
*
* @method getSOLBalance
* @async
* @param {string} accountAddress - The account address for which to retrieve the SOL balance.
* @param {Connection} [connection] - The Solana connection object to use for retrieving the balance (optional, defaults to the class's connection).
* @returns {Promise<{ balanceBn: BigNumber, balanceHuman: BigNumber }>} - A Promise that resolves to an object containing the SOL balance in both BigNumber and human-readable format.
*/
public async getSOLBalance(accountAddress: string, connection?: Connection): Promise<{
balanceBn: BigNumber;
balanceHuman: BigNumber;
}> {
const balance = connection ? await connection.getBalance(
new PublicKey(accountAddress)
) : await this.__connection.getBalance(
new PublicKey(accountAddress)
);
return {
balanceBn: new BigNumber(balance),
balanceHuman: new BigNumber(balance).div(10 ** this.SOL_DECIMALS)
}
}
/**
* Creates a new Solana account and adds it to the store.
*
* @method createNew
* @async
* @returns {Promise<SolanaAccount>} - A Promise that resolves to the newly created Solana account.
*/
public async createNew(): Promise<SolanaAccount> {
const mnemonic = bip39.generateMnemonic();
const keyPair = await solanaMnemonicToSecretkey(mnemonic);
const wallet = Keypair.fromSecretKey(keyPair);
const solAccount: SolanaAccount = {
addr: wallet.publicKey.toString(),
sk: wallet.secretKey,
pk: wallet.publicKey,
mnemonic: mnemonic,
};
this.add(solAccount.sk);
return solAccount;
}
/**
* Creates a new Solana account with a prefix and adds it to the store.
*
* @method createNewWithPrefix
* @async
* @param {string} prefix - The prefix to use for generating the account address.
* @param {number} [tries=10000] - The maximum number of tries to generate a unique account address with the given prefix (optional, defaults to 10000).
* @returns {Promise<SolanaAccount|undefined>} - A Promise that resolves to the newly created Solana account with the specified prefix, or undefined if a unique account address could not be generated within the specified number of tries.
*/
public async createNewWithPrefix(prefix: string, tries = 10000): Promise<SolanaAccount | undefined> {
for (let i = 0; i < tries; i++) {
const mnemonic = bip39.generateMnemonic();
const keyPair = await solanaMnemonicToSecretkey(mnemonic);
const wallet = Keypair.fromSecretKey(keyPair);
const address = wallet.publicKey.toString();
if (address.startsWith(prefix)) {
const accountToAdd: SolanaAccount = {
addr: wallet.publicKey.toString(),
sk: wallet.secretKey,
pk: wallet.publicKey
};
this.add(accountToAdd.sk);
return accountToAdd
}
}
console.log(`Could not find a wallet with prefix: ${prefix}`);
return undefined;
}
/**
* Retrieves a Solana account from the store based on the account address.
*
* @method get
* @param {string} account - The account address for which to retrieve the Solana account.
* @returns {SolanaAccount|undefined} - The retrieved Solana account, or undefined if the account is not found in the store.
*/
get (account: string): SolanaAccount | undefined {
return this.__accounts.get(account)
}
/**
* Retrieves the SPL token balance for a given owner's account.
*
* @method getSPLTokenBalance
* @async
* @param {string} owner - The owner's address for which to retrieve the SPL token balance.
* @param {BridgeTokenConfig} tokenConfig - The configuration for the SPL token.
* @param {Connection} connection - The Solana connection object to use for retrieving the balance.
* @returns {Promise<{ balanceBn: BigNumber, balanceHuman: BigNumber }>} - A Promise that resolves to an object containing the SPL token balance in both BigNumber and human-readable format.
*/
async getSPLTokenBalance(
owner: string,
tokenConfig: BridgeTokenConfig,
connection : Connection
): Promise<{
balanceBn: BigNumber;
balanceHuman: BigNumber;
}> {
let tokenAccount;
try {
tokenAccount = await getAssociatedTokenAccount(
owner,
tokenConfig,
connection
);
} catch (error) {
console.error(error)
}
if (!tokenAccount) {
return {
balanceBn: new BigNumber(0),
balanceHuman: new BigNumber(0)
}
}
const unitsContext = connection ? await connection.getTokenAccountBalance(
tokenAccount.address
) : await this.__connection.getTokenAccountBalance(
tokenAccount.address
);
return {
balanceBn: new BigNumber(unitsContext.value.amount),
balanceHuman: new BigNumber(unitsContext.value.amount).div(
10 ** unitsContext.value.decimals
)
}
}
/**
* Requests an airdrop of SOL tokens to a given account.
*
* @method requestAirDrop
* @async
* @param {SolanaAccount} signer - The Solana account requesting the airdrop.
* @param {number} [amount=1000000000] - The amount of SOL tokens to request for the airdrop (optional, defaults to 1,000,000,000).
* @param {Connection} connection - The Solana connection object to use for requesting the airdrop.
* @returns {Promise<string>} - A Promise that resolves to the transaction signature of the airdrop request.
*/
async requestAirDrop(signer: SolanaAccount, amount = 1_000_000_000, connection : Connection): Promise<string> {
const airdropTx = connection ? await connection.requestAirdrop(signer.pk, amount) : await this.__connection.requestAirdrop(signer.pk, amount);
return airdropTx
}
}