@streamflow/common 6.3.8 → 7.0.0-alpha.1
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/dist/{index.js → cjs/index.js} +2 -2
- package/dist/{solana → cjs/solana}/index.js +3 -3
- package/dist/{solana → cjs/solana}/utils.js +8 -8
- package/dist/{utils.js → cjs/utils.js} +3 -3
- package/dist/esm/index.d.ts +2 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/solana/index.d.ts +3 -0
- package/dist/esm/solana/index.js +3 -0
- package/dist/esm/solana/instructions.js +18 -0
- package/dist/esm/solana/types.js +7 -0
- package/dist/{solana → esm/solana}/utils.d.ts +1 -1
- package/dist/esm/solana/utils.js +415 -0
- package/dist/{types.d.ts → esm/types.d.ts} +1 -2
- package/dist/esm/types.js +38 -0
- package/dist/esm/utils.js +50 -0
- package/package.json +19 -18
- package/dist/index.d.ts +0 -2
- package/dist/solana/index.d.ts +0 -3
- /package/dist/{solana → cjs/solana}/instructions.js +0 -0
- /package/dist/{solana → cjs/solana}/types.js +0 -0
- /package/dist/{types.js → cjs/types.js} +0 -0
- /package/dist/{solana → esm/solana}/instructions.d.ts +0 -0
- /package/dist/{solana → esm/solana}/types.d.ts +0 -0
- /package/dist/{utils.d.ts → esm/utils.d.ts} +0 -0
|
@@ -14,5 +14,5 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./types"), exports);
|
|
18
|
-
__exportStar(require("./utils"), exports);
|
|
17
|
+
__exportStar(require("./types.js"), exports);
|
|
18
|
+
__exportStar(require("./utils.js"), exports);
|
|
@@ -14,6 +14,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
__exportStar(require("./instructions"), exports);
|
|
18
|
-
__exportStar(require("./types"), exports);
|
|
19
|
-
__exportStar(require("./utils"), exports);
|
|
17
|
+
__exportStar(require("./instructions.js"), exports);
|
|
18
|
+
__exportStar(require("./types.js"), exports);
|
|
19
|
+
__exportStar(require("./utils.js"), exports);
|
|
@@ -55,8 +55,8 @@ var spl_token_1 = require("@solana/spl-token");
|
|
|
55
55
|
var web3_js_1 = require("@solana/web3.js");
|
|
56
56
|
var bs58_1 = __importDefault(require("bs58"));
|
|
57
57
|
var p_queue_1 = __importDefault(require("p-queue"));
|
|
58
|
-
var
|
|
59
|
-
var
|
|
58
|
+
var types_js_1 = require("./types.js");
|
|
59
|
+
var utils_js_1 = require("../utils.js");
|
|
60
60
|
var SIMULATE_TRIES = 3;
|
|
61
61
|
var buildSendThrottler = function (sendRate) {
|
|
62
62
|
return new p_queue_1.default({ concurrency: sendRate, intervalCap: 1, interval: 1000 });
|
|
@@ -323,12 +323,12 @@ function sendAndConfirmTransaction(connection, tx, _a, _b) {
|
|
|
323
323
|
e_1 = _d.sent();
|
|
324
324
|
if (!(transactionSent ||
|
|
325
325
|
(e_1 instanceof web3_js_1.SendTransactionError && e_1.message.includes("Minimum context slot has not been reached")))) return [3 /*break*/, 8];
|
|
326
|
-
return [4 /*yield*/, (0,
|
|
326
|
+
return [4 /*yield*/, (0, utils_js_1.sleep)(500)];
|
|
327
327
|
case 7:
|
|
328
328
|
_d.sent();
|
|
329
329
|
return [3 /*break*/, 2];
|
|
330
330
|
case 8: throw e_1;
|
|
331
|
-
case 9: return [4 /*yield*/, (0,
|
|
331
|
+
case 9: return [4 /*yield*/, (0, utils_js_1.sleep)(500)];
|
|
332
332
|
case 10:
|
|
333
333
|
_d.sent();
|
|
334
334
|
_d.label = 11;
|
|
@@ -343,10 +343,10 @@ function sendAndConfirmTransaction(connection, tx, _a, _b) {
|
|
|
343
343
|
return [3 /*break*/, 15];
|
|
344
344
|
case 13:
|
|
345
345
|
e_2 = _d.sent();
|
|
346
|
-
if (e_2 instanceof
|
|
346
|
+
if (e_2 instanceof types_js_1.TransactionFailedError) {
|
|
347
347
|
throw e_2;
|
|
348
348
|
}
|
|
349
|
-
return [4 /*yield*/, (0,
|
|
349
|
+
return [4 /*yield*/, (0, utils_js_1.sleep)(500)];
|
|
350
350
|
case 14:
|
|
351
351
|
_d.sent();
|
|
352
352
|
return [3 /*break*/, 15];
|
|
@@ -358,7 +358,7 @@ function sendAndConfirmTransaction(connection, tx, _a, _b) {
|
|
|
358
358
|
return [3 /*break*/, 19];
|
|
359
359
|
case 17:
|
|
360
360
|
_e_1 = _d.sent();
|
|
361
|
-
return [4 /*yield*/, (0,
|
|
361
|
+
return [4 /*yield*/, (0, utils_js_1.sleep)(500)];
|
|
362
362
|
case 18:
|
|
363
363
|
_d.sent();
|
|
364
364
|
return [3 /*break*/, 19];
|
|
@@ -430,7 +430,7 @@ function confirmAndEnsureTransaction(connection, signature, ignoreError) {
|
|
|
430
430
|
}
|
|
431
431
|
if (!ignoreError && value.err) {
|
|
432
432
|
// That's how solana-web3js does it, `err` here is an object that won't really be handled
|
|
433
|
-
throw new
|
|
433
|
+
throw new types_js_1.TransactionFailedError("Raw transaction ".concat(signature, " failed (").concat(JSON.stringify({ err: value.err }), ")"));
|
|
434
434
|
}
|
|
435
435
|
switch (connection.commitment) {
|
|
436
436
|
case "confirmed":
|
|
@@ -41,7 +41,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
41
41
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
42
42
|
exports.sleep = exports.handleContractError = exports.getNumberFromBN = exports.getBN = void 0;
|
|
43
43
|
var bn_js_1 = __importDefault(require("bn.js"));
|
|
44
|
-
var
|
|
44
|
+
var types_js_1 = require("./types.js");
|
|
45
45
|
/**
|
|
46
46
|
* Used for conversion of token amounts to their Big Number representation.
|
|
47
47
|
* Get Big Number representation in the smallest units from the same value in the highest units.
|
|
@@ -86,9 +86,9 @@ function handleContractError(func, callback) {
|
|
|
86
86
|
err_1 = _a.sent();
|
|
87
87
|
if (err_1 instanceof Error) {
|
|
88
88
|
if (callback) {
|
|
89
|
-
throw new
|
|
89
|
+
throw new types_js_1.ContractError(err_1, callback(err_1));
|
|
90
90
|
}
|
|
91
|
-
throw new
|
|
91
|
+
throw new types_js_1.ContractError(err_1);
|
|
92
92
|
}
|
|
93
93
|
throw err_1;
|
|
94
94
|
case 3: return [2 /*return*/];
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { getAssociatedTokenAddress, NATIVE_MINT, createAssociatedTokenAccountInstruction, createSyncNativeInstruction, } from "@solana/spl-token";
|
|
2
|
+
import { SystemProgram } from "@solana/web3.js";
|
|
3
|
+
export const prepareWrappedAccount = async (connection, senderAddress, amount) => {
|
|
4
|
+
const tokenAccount = await getAssociatedTokenAddress(NATIVE_MINT, senderAddress, true);
|
|
5
|
+
const accInfo = await connection.getParsedAccountInfo(tokenAccount);
|
|
6
|
+
const instructions = (accInfo.value?.lamports ?? 0) > 0
|
|
7
|
+
? []
|
|
8
|
+
: [createAssociatedTokenAccountInstruction(senderAddress, tokenAccount, senderAddress, NATIVE_MINT)];
|
|
9
|
+
return [
|
|
10
|
+
...instructions,
|
|
11
|
+
SystemProgram.transfer({
|
|
12
|
+
fromPubkey: senderAddress,
|
|
13
|
+
toPubkey: tokenAccount,
|
|
14
|
+
lamports: amount.toNumber(),
|
|
15
|
+
}),
|
|
16
|
+
createSyncNativeInstruction(tokenAccount),
|
|
17
|
+
];
|
|
18
|
+
};
|
|
@@ -2,7 +2,7 @@ import { Mint } from "@solana/spl-token";
|
|
|
2
2
|
import { SignerWalletAdapter } from "@solana/wallet-adapter-base";
|
|
3
3
|
import { BlockhashWithExpiryBlockHeight, Commitment, Connection, Keypair, PublicKey, Transaction, TransactionInstruction, SignatureStatus, VersionedTransaction, Context, RpcResponseAndContext, SimulatedTransactionResponse } from "@solana/web3.js";
|
|
4
4
|
import PQueue from "p-queue";
|
|
5
|
-
import { Account, AtaParams, ConfirmationParams, ITransactionSolanaExt, ThrottleParams } from "./types";
|
|
5
|
+
import { Account, AtaParams, ConfirmationParams, ITransactionSolanaExt, ThrottleParams } from "./types.js";
|
|
6
6
|
export declare const buildSendThrottler: (sendRate: number) => PQueue;
|
|
7
7
|
/**
|
|
8
8
|
* Wrapper function for Solana web3 getProgramAccounts with slightly better call interface
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
import { createAssociatedTokenAccountInstruction, getAssociatedTokenAddress, unpackMint, TOKEN_PROGRAM_ID, TOKEN_2022_PROGRAM_ID, } from "@solana/spl-token";
|
|
2
|
+
import { ComputeBudgetProgram, Keypair, TransactionMessage, VersionedTransaction, SendTransactionError, } from "@solana/web3.js";
|
|
3
|
+
import bs58 from "bs58";
|
|
4
|
+
import PQueue from "p-queue";
|
|
5
|
+
import { TransactionFailedError, } from "./types.js";
|
|
6
|
+
import { sleep } from "../utils.js";
|
|
7
|
+
const SIMULATE_TRIES = 3;
|
|
8
|
+
export const buildSendThrottler = (sendRate) => {
|
|
9
|
+
return new PQueue({ concurrency: sendRate, intervalCap: 1, interval: 1000 });
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Wrapper function for Solana web3 getProgramAccounts with slightly better call interface
|
|
13
|
+
* @param {Connection} connection - Solana web3 connection object.
|
|
14
|
+
* @param {PublicKey} wallet - PublicKey to compare against.
|
|
15
|
+
* @param {number} offset - Offset of bits of the PublicKey in the account binary.
|
|
16
|
+
* @param {PublicKey} programId - Solana program ID.
|
|
17
|
+
* @return {Promise<Account[]>} - Array of resulting accounts.
|
|
18
|
+
*/
|
|
19
|
+
export async function getProgramAccounts(connection, wallet, offset, programId) {
|
|
20
|
+
return connection?.getProgramAccounts(programId, {
|
|
21
|
+
filters: [
|
|
22
|
+
{
|
|
23
|
+
memcmp: {
|
|
24
|
+
offset,
|
|
25
|
+
bytes: wallet.toBase58(),
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Utility function to check if the transaction initiator is a Wallet object
|
|
33
|
+
* @param {Keypair | SignerWalletAdapter} walletOrKeypair - Wallet or Keypair in question
|
|
34
|
+
* @return {boolean} - Returns true if parameter is a Wallet.
|
|
35
|
+
*/
|
|
36
|
+
export function isSignerWallet(walletOrKeypair) {
|
|
37
|
+
return walletOrKeypair.signTransaction !== undefined;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Utility function to check if the transaction initiator a Keypair object, tries to mitigate version mismatch issues
|
|
41
|
+
* @param walletOrKeypair {Keypair | SignerWalletAdapter} walletOrKeypair - Wallet or Keypair in question
|
|
42
|
+
* @returns {boolean} - Returns true if parameter is a Keypair.
|
|
43
|
+
*/
|
|
44
|
+
export function isSignerKeypair(walletOrKeypair) {
|
|
45
|
+
return (walletOrKeypair instanceof Keypair ||
|
|
46
|
+
walletOrKeypair.constructor === Keypair ||
|
|
47
|
+
walletOrKeypair.constructor.name === Keypair.prototype.constructor.name);
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Utility function to check whether given transaction is Versioned
|
|
51
|
+
* @param tx {Transaction | VersionedTransaction} - Transaction to check
|
|
52
|
+
* @returns {boolean} - Returns true if transaction is Versioned.
|
|
53
|
+
*/
|
|
54
|
+
export function isTransactionVersioned(tx) {
|
|
55
|
+
return "message" in tx;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Creates a Transaction with given instructions and optionally signs it.
|
|
59
|
+
* @param connection - Solana client connection
|
|
60
|
+
* @param ixs - Instructions to add to the Transaction
|
|
61
|
+
* @param payer - PublicKey of payer
|
|
62
|
+
* @param commitment - optional Commitment that will be used to fetch latest blockhash
|
|
63
|
+
* @param partialSigners - optional signers that will be used to partially sign a Transaction
|
|
64
|
+
* @returns Transaction and Blockhash
|
|
65
|
+
*/
|
|
66
|
+
export async function prepareTransaction(connection, ixs, payer, commitment, ...partialSigners) {
|
|
67
|
+
if (!payer) {
|
|
68
|
+
throw new Error("Payer public key is not provided!");
|
|
69
|
+
}
|
|
70
|
+
const { value: hash, context } = await connection.getLatestBlockhashAndContext(commitment);
|
|
71
|
+
const messageV0 = new TransactionMessage({
|
|
72
|
+
payerKey: payer,
|
|
73
|
+
recentBlockhash: hash.blockhash,
|
|
74
|
+
instructions: ixs,
|
|
75
|
+
}).compileToV0Message();
|
|
76
|
+
const tx = new VersionedTransaction(messageV0);
|
|
77
|
+
const signers = partialSigners.filter((item) => !!item);
|
|
78
|
+
tx.sign(signers);
|
|
79
|
+
return { tx, context, hash };
|
|
80
|
+
}
|
|
81
|
+
export async function signTransaction(invoker, tx) {
|
|
82
|
+
let signedTx;
|
|
83
|
+
if (isSignerWallet(invoker)) {
|
|
84
|
+
signedTx = await invoker.signTransaction(tx);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
if (isTransactionVersioned(tx)) {
|
|
88
|
+
tx.sign([invoker]);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
tx.partialSign(invoker);
|
|
92
|
+
}
|
|
93
|
+
signedTx = tx;
|
|
94
|
+
}
|
|
95
|
+
return signedTx;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Signs, sends and confirms Transaction
|
|
99
|
+
* @param connection - Solana client connection
|
|
100
|
+
* @param invoker - Keypair used as signer
|
|
101
|
+
* @param tx - Transaction instance
|
|
102
|
+
* @param confirmationParams - Confirmation Params that will be used for execution
|
|
103
|
+
* @param throttleParams - rate or throttler instance to throttle TX sending - to not spam the blockchain too much
|
|
104
|
+
* @returns Transaction signature
|
|
105
|
+
*/
|
|
106
|
+
export async function signAndExecuteTransaction(connection, invoker, tx, confirmationParams, throttleParams) {
|
|
107
|
+
const signedTx = await signTransaction(invoker, tx);
|
|
108
|
+
return executeTransaction(connection, signedTx, confirmationParams, throttleParams);
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Sends and confirms Transaction
|
|
112
|
+
* Uses custom confirmation logic that:
|
|
113
|
+
* - simulates tx before sending separately
|
|
114
|
+
* - sends transaction without preFlight checks but with some valuable flags https://twitter.com/jordaaash/status/1774892862049800524?s=46&t=bhZ10V0r7IX5Lk5kKzxfGw
|
|
115
|
+
* - rebroadcasts a tx every 500 ms
|
|
116
|
+
* - after broadcasting check whether tx has executed once
|
|
117
|
+
* - catch errors for every actionable item, throw only the ones that signal that tx has failed
|
|
118
|
+
* - otherwise there is a chance of marking a landed tx as failed if it was broadcasted at least once
|
|
119
|
+
* @param connection - Solana client connection
|
|
120
|
+
* @param tx - Transaction instance
|
|
121
|
+
* @param confirmationParams - Confirmation Params that will be used for execution
|
|
122
|
+
* @param throttleParams - rate or throttler instance to throttle TX sending - to not spam the blockchain too much
|
|
123
|
+
* @returns Transaction signature
|
|
124
|
+
*/
|
|
125
|
+
export async function executeTransaction(connection, tx, confirmationParams, throttleParams) {
|
|
126
|
+
if (tx.signatures.length === 0) {
|
|
127
|
+
throw Error("Error with transaction parameters.");
|
|
128
|
+
}
|
|
129
|
+
await simulateTransaction(connection, tx);
|
|
130
|
+
return sendAndConfirmTransaction(connection, tx, confirmationParams, throttleParams);
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Launches a PromisePool with all transaction being executed at the same time, allows to throttle all TXs through one Queue
|
|
134
|
+
* @param connection - Solana client connection
|
|
135
|
+
* @param txs - Transactions
|
|
136
|
+
* @param confirmationParams - Confirmation Params that will be used for execution
|
|
137
|
+
* @param throttleParams - rate or throttler instance to throttle TX sending - to not spam the blockchain too much
|
|
138
|
+
* @param throttleParams.sendRate - rate
|
|
139
|
+
* @param throttleParams.sendThrottler - throttler instance
|
|
140
|
+
* @returns Raw Promise Results - should be handled by the consumer and unwrapped accordingly
|
|
141
|
+
*/
|
|
142
|
+
export async function executeMultipleTransactions(connection, txs, confirmationParams, { sendRate = 1, sendThrottler }) {
|
|
143
|
+
if (!sendThrottler) {
|
|
144
|
+
sendThrottler = buildSendThrottler(sendRate);
|
|
145
|
+
}
|
|
146
|
+
return Promise.allSettled(txs.map((tx) => executeTransaction(connection, tx, confirmationParams, { sendRate: sendRate, sendThrottler: sendThrottler })));
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Sends and confirm transaction in a loop, constantly re-broadcsting the tx until Blockheight expires.
|
|
150
|
+
* - we add additional 30 bocks to account for validators in an PRC pool divergence
|
|
151
|
+
* @param connection - Solana client connection
|
|
152
|
+
* @param tx - Transaction instance
|
|
153
|
+
* @param confirmationParams - Confirmation Params that will be used for execution
|
|
154
|
+
* @param confirmationParams.hash - blockhash information, the same hash should be used in the Transaction
|
|
155
|
+
* @param confirmationParams.context - context at which blockhash has been retrieve
|
|
156
|
+
* @param confirmationParams.commitment - optional commitment that will be used for simulation and confirmation
|
|
157
|
+
* @param throttleParams - rate or throttler instance to throttle TX sending - to not spam the blockchain too much
|
|
158
|
+
* @param throttleParams.sendRate - rate
|
|
159
|
+
* @param throttleParams.sendThrottler - throttler instance
|
|
160
|
+
*/
|
|
161
|
+
export async function sendAndConfirmTransaction(connection, tx, { hash, context, commitment }, { sendRate = 1, sendThrottler }) {
|
|
162
|
+
const isVersioned = isTransactionVersioned(tx);
|
|
163
|
+
let signature;
|
|
164
|
+
if (isVersioned) {
|
|
165
|
+
signature = bs58.encode(tx.signatures[0]);
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
signature = bs58.encode(tx.signature);
|
|
169
|
+
}
|
|
170
|
+
if (!sendThrottler) {
|
|
171
|
+
sendThrottler = buildSendThrottler(sendRate);
|
|
172
|
+
}
|
|
173
|
+
let blockheight = await connection.getBlockHeight(commitment);
|
|
174
|
+
let transactionSent = false;
|
|
175
|
+
const rawTransaction = tx.serialize();
|
|
176
|
+
while (blockheight < hash.lastValidBlockHeight + 15) {
|
|
177
|
+
try {
|
|
178
|
+
if (blockheight < hash.lastValidBlockHeight || !transactionSent) {
|
|
179
|
+
await sendThrottler.add(async () => connection.sendRawTransaction(rawTransaction, {
|
|
180
|
+
maxRetries: 0,
|
|
181
|
+
minContextSlot: context.slot,
|
|
182
|
+
preflightCommitment: commitment,
|
|
183
|
+
skipPreflight: true,
|
|
184
|
+
}));
|
|
185
|
+
transactionSent = true;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch (e) {
|
|
189
|
+
if (transactionSent ||
|
|
190
|
+
(e instanceof SendTransactionError && e.message.includes("Minimum context slot has not been reached"))) {
|
|
191
|
+
await sleep(500);
|
|
192
|
+
continue;
|
|
193
|
+
}
|
|
194
|
+
throw e;
|
|
195
|
+
}
|
|
196
|
+
await sleep(500);
|
|
197
|
+
try {
|
|
198
|
+
const value = await confirmAndEnsureTransaction(connection, signature);
|
|
199
|
+
if (value) {
|
|
200
|
+
return signature;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
catch (e) {
|
|
204
|
+
if (e instanceof TransactionFailedError) {
|
|
205
|
+
throw e;
|
|
206
|
+
}
|
|
207
|
+
await sleep(500);
|
|
208
|
+
}
|
|
209
|
+
try {
|
|
210
|
+
blockheight = await connection.getBlockHeight(commitment);
|
|
211
|
+
}
|
|
212
|
+
catch (_e) {
|
|
213
|
+
await sleep(500);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
throw new Error(`Transaction ${signature} expired.`);
|
|
217
|
+
}
|
|
218
|
+
export async function simulateTransaction(connection, tx) {
|
|
219
|
+
let res;
|
|
220
|
+
for (let i = 0; i < SIMULATE_TRIES; i++) {
|
|
221
|
+
if (isTransactionVersioned(tx)) {
|
|
222
|
+
res = await connection.simulateTransaction(tx);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
res = await connection.simulateTransaction(tx);
|
|
226
|
+
}
|
|
227
|
+
if (res.value.err) {
|
|
228
|
+
const errMessage = JSON.stringify(res.value.err);
|
|
229
|
+
if (!errMessage.includes("BlockhashNotFound") || i === SIMULATE_TRIES - 1) {
|
|
230
|
+
throw new SendTransactionError("failed to simulate transaction: " + errMessage, res.value.logs || undefined);
|
|
231
|
+
}
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
return res;
|
|
235
|
+
}
|
|
236
|
+
throw new SendTransactionError("failed to simulate transaction");
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Confirms and validates transaction success once
|
|
240
|
+
* @param connection - Solana client connection
|
|
241
|
+
* @param signature - Transaction signature
|
|
242
|
+
* @param ignoreError - return status even if tx failed
|
|
243
|
+
* @returns Transaction Status
|
|
244
|
+
*/
|
|
245
|
+
export async function confirmAndEnsureTransaction(connection, signature, ignoreError) {
|
|
246
|
+
const response = await connection.getSignatureStatus(signature);
|
|
247
|
+
if (!response) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
const { value } = response;
|
|
251
|
+
if (!value) {
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
if (!ignoreError && value.err) {
|
|
255
|
+
// That's how solana-web3js does it, `err` here is an object that won't really be handled
|
|
256
|
+
throw new TransactionFailedError(`Raw transaction ${signature} failed (${JSON.stringify({ err: value.err })})`);
|
|
257
|
+
}
|
|
258
|
+
switch (connection.commitment) {
|
|
259
|
+
case "confirmed":
|
|
260
|
+
case "single":
|
|
261
|
+
case "singleGossip": {
|
|
262
|
+
if (value.confirmationStatus === "processed") {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
break;
|
|
266
|
+
}
|
|
267
|
+
case "finalized":
|
|
268
|
+
case "max":
|
|
269
|
+
case "root": {
|
|
270
|
+
if (value.confirmationStatus === "processed" || value.confirmationStatus === "confirmed") {
|
|
271
|
+
return null;
|
|
272
|
+
}
|
|
273
|
+
break;
|
|
274
|
+
}
|
|
275
|
+
// exhaust enums to ensure full coverage
|
|
276
|
+
case "processed":
|
|
277
|
+
case "recent":
|
|
278
|
+
}
|
|
279
|
+
return value;
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Shorthand call signature for getAssociatedTokenAddress, with allowance for address to be offCurve
|
|
283
|
+
* @param {PublicKey} mint - SPL token Mint address.
|
|
284
|
+
* @param {PublicKey} owner - Owner of the Associated Token Address
|
|
285
|
+
* @param {PublicKey} programId - Program ID of the mint
|
|
286
|
+
* @return {Promise<PublicKey>} - Associated Token Address
|
|
287
|
+
*/
|
|
288
|
+
export function ata(mint, owner, programId) {
|
|
289
|
+
return getAssociatedTokenAddress(mint, owner, true, programId);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* Function that checks whether ATA exists for each provided owner
|
|
293
|
+
* @param connection - Solana client connection
|
|
294
|
+
* @param paramsBatch - Array of Params for each ATA account: {mint, owner}
|
|
295
|
+
* @returns Array of boolean where each member corresponds to an owner
|
|
296
|
+
*/
|
|
297
|
+
export async function ataBatchExist(connection, paramsBatch) {
|
|
298
|
+
const tokenAccounts = await Promise.all(paramsBatch.map(async ({ mint, owner, programId }) => {
|
|
299
|
+
return ata(mint, owner, programId);
|
|
300
|
+
}));
|
|
301
|
+
const response = await connection.getMultipleAccountsInfo(tokenAccounts);
|
|
302
|
+
return response.map((accInfo) => !!accInfo);
|
|
303
|
+
}
|
|
304
|
+
export async function enrichAtaParams(connection, paramsBatch) {
|
|
305
|
+
const programIdByMint = {};
|
|
306
|
+
return Promise.all(paramsBatch.map(async (params) => {
|
|
307
|
+
if (params.programId) {
|
|
308
|
+
return params;
|
|
309
|
+
}
|
|
310
|
+
const mintStr = params.mint.toString();
|
|
311
|
+
if (!(mintStr in programIdByMint)) {
|
|
312
|
+
const { tokenProgramId } = await getMintAndProgram(connection, params.mint);
|
|
313
|
+
programIdByMint[mintStr] = tokenProgramId;
|
|
314
|
+
}
|
|
315
|
+
params.programId = programIdByMint[mintStr];
|
|
316
|
+
return params;
|
|
317
|
+
}));
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Generates a Transaction to create ATA for an array of owners
|
|
321
|
+
* @param connection - Solana client connection
|
|
322
|
+
* @param payer - Transaction invoker, should be a signer
|
|
323
|
+
* @param paramsBatch - Array of Params for an each ATA account: {mint, owner}
|
|
324
|
+
* @param commitment - optional commitment that will be used to fetch Blockhash
|
|
325
|
+
* @returns Unsigned Transaction with create ATA instructions
|
|
326
|
+
*/
|
|
327
|
+
export async function generateCreateAtaBatchTx(connection, payer, paramsBatch, commitment) {
|
|
328
|
+
paramsBatch = await enrichAtaParams(connection, paramsBatch);
|
|
329
|
+
const ixs = await Promise.all(paramsBatch.map(async ({ mint, owner, programId }) => {
|
|
330
|
+
return createAssociatedTokenAccountInstruction(payer, await ata(mint, owner), owner, mint, programId);
|
|
331
|
+
}));
|
|
332
|
+
const { value: hash, context } = await connection.getLatestBlockhashAndContext({ commitment });
|
|
333
|
+
const messageV0 = new TransactionMessage({
|
|
334
|
+
payerKey: payer,
|
|
335
|
+
recentBlockhash: hash.blockhash,
|
|
336
|
+
instructions: ixs,
|
|
337
|
+
}).compileToV0Message();
|
|
338
|
+
const tx = new VersionedTransaction(messageV0);
|
|
339
|
+
return { tx, hash, context };
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Creates ATA for an array of owners
|
|
343
|
+
* @param connection - Solana client connection
|
|
344
|
+
* @param invoker - Transaction invoker and payer
|
|
345
|
+
* @param paramsBatch - Array of Params for an each ATA account: {mint, owner}
|
|
346
|
+
* @param commitment - optional commitment that will be used to fetch Blockhash
|
|
347
|
+
* @param rate - throttle rate for tx sending
|
|
348
|
+
* @returns Transaction signature
|
|
349
|
+
*/
|
|
350
|
+
export async function createAtaBatch(connection, invoker, paramsBatch, commitment, rate) {
|
|
351
|
+
const { tx, hash, context } = await generateCreateAtaBatchTx(connection, invoker.publicKey, await enrichAtaParams(connection, paramsBatch), commitment);
|
|
352
|
+
return signAndExecuteTransaction(connection, invoker, tx, { hash, context, commitment }, { sendRate: rate });
|
|
353
|
+
}
|
|
354
|
+
/**
|
|
355
|
+
* Utility function that checks whether associated token accounts exist and return instructions to populate them if not
|
|
356
|
+
* @param connection - Solana client connection
|
|
357
|
+
* @param owners - Array of ATA owners
|
|
358
|
+
* @param mint - Mint for which ATA will be checked
|
|
359
|
+
* @param invoker - Transaction invoker and payer
|
|
360
|
+
* @param programId - Program ID of the Mint
|
|
361
|
+
* @returns Array of Transaction Instructions that should be added to a transaction
|
|
362
|
+
*/
|
|
363
|
+
export async function checkOrCreateAtaBatch(connection, owners, mint, invoker, programId) {
|
|
364
|
+
const ixs = [];
|
|
365
|
+
if (!programId) {
|
|
366
|
+
programId = (await getMintAndProgram(connection, mint)).tokenProgramId;
|
|
367
|
+
}
|
|
368
|
+
// TODO: optimize fetching and maps/arrays
|
|
369
|
+
const atas = [];
|
|
370
|
+
for (const owner of owners) {
|
|
371
|
+
atas.push(await ata(mint, owner, programId));
|
|
372
|
+
}
|
|
373
|
+
const response = await connection.getMultipleAccountsInfo(atas);
|
|
374
|
+
for (let i = 0; i < response.length; i++) {
|
|
375
|
+
if (!response[i]) {
|
|
376
|
+
ixs.push(createAssociatedTokenAccountInstruction(invoker.publicKey, atas[i], owners[i], mint, programId));
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return ixs;
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Create Base instructions for Solana
|
|
383
|
+
* - sets compute price if `computePrice` is provided
|
|
384
|
+
* - sets compute limit if `computeLimit` is provided
|
|
385
|
+
*/
|
|
386
|
+
export function prepareBaseInstructions(connection, { computePrice, computeLimit }) {
|
|
387
|
+
const ixs = [];
|
|
388
|
+
if (computePrice) {
|
|
389
|
+
ixs.push(ComputeBudgetProgram.setComputeUnitPrice({ microLamports: computePrice }));
|
|
390
|
+
}
|
|
391
|
+
if (computeLimit) {
|
|
392
|
+
ixs.push(ComputeBudgetProgram.setComputeUnitLimit({ units: computeLimit }));
|
|
393
|
+
}
|
|
394
|
+
return ixs;
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Retrieve information about a mint and its program ID, support all Token Programs.
|
|
398
|
+
*
|
|
399
|
+
* @param connection Connection to use
|
|
400
|
+
* @param address Mint account
|
|
401
|
+
* @param commitment Desired level of commitment for querying the state
|
|
402
|
+
*
|
|
403
|
+
* @return Mint information
|
|
404
|
+
*/
|
|
405
|
+
export async function getMintAndProgram(connection, address, commitment) {
|
|
406
|
+
const accountInfo = await connection.getAccountInfo(address, commitment);
|
|
407
|
+
let programId = accountInfo?.owner;
|
|
408
|
+
if (!programId?.equals(TOKEN_PROGRAM_ID) && !programId?.equals(TOKEN_2022_PROGRAM_ID)) {
|
|
409
|
+
programId = TOKEN_PROGRAM_ID;
|
|
410
|
+
}
|
|
411
|
+
return {
|
|
412
|
+
mint: unpackMint(address, accountInfo, programId),
|
|
413
|
+
tokenProgramId: programId,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { TransactionInstruction } from "@solana/web3.js";
|
|
2
|
-
import { Types } from "aptos";
|
|
3
2
|
export interface ITransactionResult {
|
|
4
|
-
ixs:
|
|
3
|
+
ixs: TransactionInstruction[];
|
|
5
4
|
txId: string;
|
|
6
5
|
}
|
|
7
6
|
export declare enum ICluster {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Utility types
|
|
2
|
+
export var ICluster;
|
|
3
|
+
(function (ICluster) {
|
|
4
|
+
ICluster["Mainnet"] = "mainnet";
|
|
5
|
+
ICluster["Devnet"] = "devnet";
|
|
6
|
+
ICluster["Testnet"] = "testnet";
|
|
7
|
+
ICluster["Local"] = "local";
|
|
8
|
+
})(ICluster || (ICluster = {}));
|
|
9
|
+
export var IChain;
|
|
10
|
+
(function (IChain) {
|
|
11
|
+
IChain["Solana"] = "Solana";
|
|
12
|
+
IChain["Aptos"] = "Aptos";
|
|
13
|
+
IChain["Ethereum"] = "Ethereum";
|
|
14
|
+
IChain["BNB"] = "BNB";
|
|
15
|
+
IChain["Polygon"] = "Polygon";
|
|
16
|
+
IChain["Sui"] = "Sui";
|
|
17
|
+
})(IChain || (IChain = {}));
|
|
18
|
+
/**
|
|
19
|
+
* Error wrapper for calls made to the contract on chain
|
|
20
|
+
*/
|
|
21
|
+
export class ContractError extends Error {
|
|
22
|
+
contractErrorCode;
|
|
23
|
+
description;
|
|
24
|
+
/**
|
|
25
|
+
* Constructs the Error Wrapper
|
|
26
|
+
* @param error Original error raised probably by the chain SDK
|
|
27
|
+
* @param code extracted code from the error if managed to parse it
|
|
28
|
+
*/
|
|
29
|
+
constructor(error, code, description) {
|
|
30
|
+
super(error.message); // Call the base class constructor with the error message
|
|
31
|
+
this.contractErrorCode = code ?? null;
|
|
32
|
+
this.description = description ?? null;
|
|
33
|
+
// Copy properties from the original error
|
|
34
|
+
Object.setPrototypeOf(this, ContractError.prototype);
|
|
35
|
+
this.name = "ContractError"; // Set the name property
|
|
36
|
+
this.stack = error.stack;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import BN from "bn.js";
|
|
2
|
+
import { ContractError } from "./types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Used for conversion of token amounts to their Big Number representation.
|
|
5
|
+
* Get Big Number representation in the smallest units from the same value in the highest units.
|
|
6
|
+
* @param {number} value - Number of tokens you want to convert to its BN representation.
|
|
7
|
+
* @param {number} decimals - Number of decimals the token has.
|
|
8
|
+
*/
|
|
9
|
+
export const getBN = (value, decimals) => {
|
|
10
|
+
const decimalPart = value - Math.trunc(value);
|
|
11
|
+
const integerPart = new BN(Math.trunc(value));
|
|
12
|
+
const decimalE = new BN(decimalPart * 1e9);
|
|
13
|
+
const sum = integerPart.mul(new BN(1e9)).add(decimalE);
|
|
14
|
+
const resultE = sum.mul(new BN(10).pow(new BN(decimals)));
|
|
15
|
+
return resultE.div(new BN(1e9));
|
|
16
|
+
};
|
|
17
|
+
/**
|
|
18
|
+
* Used for token amounts conversion from their Big Number representation to number.
|
|
19
|
+
* Get value in the highest units from BN representation of the same value in the smallest units.
|
|
20
|
+
* @param {BN} value - Big Number representation of value in the smallest units.
|
|
21
|
+
* @param {number} decimals - Number of decimals the token has.
|
|
22
|
+
*/
|
|
23
|
+
export const getNumberFromBN = (value, decimals) => value.gt(new BN(2 ** 53 - 1)) ? value.div(new BN(10 ** decimals)).toNumber() : value.toNumber() / 10 ** decimals;
|
|
24
|
+
/**
|
|
25
|
+
* Used to make on chain calls to the contract and wrap raised errors if any
|
|
26
|
+
* @param func function that interacts with the contract
|
|
27
|
+
* @param callback callback that may be used to extract error code
|
|
28
|
+
* @returns {T}
|
|
29
|
+
*/
|
|
30
|
+
export async function handleContractError(func, callback) {
|
|
31
|
+
try {
|
|
32
|
+
return await func();
|
|
33
|
+
}
|
|
34
|
+
catch (err) {
|
|
35
|
+
if (err instanceof Error) {
|
|
36
|
+
if (callback) {
|
|
37
|
+
throw new ContractError(err, callback(err));
|
|
38
|
+
}
|
|
39
|
+
throw new ContractError(err);
|
|
40
|
+
}
|
|
41
|
+
throw err;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Pause async function execution for given amount of milliseconds
|
|
46
|
+
* @param ms millisecond to sleep for
|
|
47
|
+
*/
|
|
48
|
+
export function sleep(ms) {
|
|
49
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
50
|
+
}
|
package/package.json
CHANGED
|
@@ -1,36 +1,37 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamflow/common",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "7.0.0-alpha.1",
|
|
4
4
|
"description": "Common utilities and types used by streamflow packages.",
|
|
5
5
|
"homepage": "https://github.com/streamflow-finance/js-sdk/",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
6
|
+
"main": "./dist/esm/index.js",
|
|
7
|
+
"types": "./dist/esm/index.d.ts",
|
|
8
|
+
"type": "module",
|
|
8
9
|
"exports": {
|
|
9
|
-
".":
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/esm/index.js",
|
|
12
|
+
"require": "./dist/cjs/index.js",
|
|
13
|
+
"types": "./dist/esm/index.d.ts"
|
|
14
|
+
},
|
|
15
|
+
"./solana": {
|
|
16
|
+
"import": "./dist/esm/solana/index.js",
|
|
17
|
+
"require": "./dist/cjs/solana/index.js",
|
|
18
|
+
"types": "./dist/esm/solana/index.d.ts"
|
|
17
19
|
}
|
|
18
20
|
},
|
|
19
21
|
"scripts": {
|
|
20
|
-
"build": "rm -rf dist; tsc -p tsconfig.json",
|
|
22
|
+
"build:cjs": "rm -rf dist/cjs; tsc -p tsconfig.cjs.json",
|
|
23
|
+
"build:esm": "rm -rf dist/esm; tsc -p tsconfig.esm.json",
|
|
24
|
+
"build": "rm -rf dist; pnpm run build:cjs && pnpm run build:esm",
|
|
21
25
|
"pack": "pnpm build && pnpm pack",
|
|
22
26
|
"lint": "eslint --fix .",
|
|
23
27
|
"lint-config": "eslint --print-config",
|
|
24
28
|
"prepublishOnly": "npm run lint && npm run build"
|
|
25
29
|
},
|
|
26
|
-
"gitHead": "
|
|
30
|
+
"gitHead": "8b2be143ac1bfeaca3bcecc11eb5fa1e81f2ffb7",
|
|
27
31
|
"devDependencies": {
|
|
28
|
-
"@streamflow/eslint-config": "
|
|
32
|
+
"@streamflow/eslint-config": "7.0.0-alpha.1",
|
|
29
33
|
"@types/bn.js": "5.1.1",
|
|
30
|
-
"@types/jest": "29.2.4",
|
|
31
34
|
"date-fns": "2.28.0",
|
|
32
|
-
"jest": "29.3.1",
|
|
33
|
-
"ts-jest": "29.0.3",
|
|
34
35
|
"typescript": "^4.9.5"
|
|
35
36
|
},
|
|
36
37
|
"dependencies": {
|
|
@@ -43,6 +44,6 @@
|
|
|
43
44
|
"bn.js": "5.2.1",
|
|
44
45
|
"borsh": "^2.0.0",
|
|
45
46
|
"bs58": "5.0.0",
|
|
46
|
-
"p-queue": "^
|
|
47
|
+
"p-queue": "^8.0.1"
|
|
47
48
|
}
|
|
48
49
|
}
|
package/dist/index.d.ts
DELETED
package/dist/solana/index.d.ts
DELETED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|