@drift-labs/sdk 2.85.0-beta.1 → 2.85.0-beta.10
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/VERSION +1 -1
- package/bun.lockb +0 -0
- package/lib/accounts/bulkAccountLoader.d.ts +3 -3
- package/lib/accounts/pollingDriftClientAccountSubscriber.js +10 -2
- package/lib/accounts/pollingUserAccountSubscriber.d.ts +4 -3
- package/lib/accounts/pollingUserAccountSubscriber.js +7 -6
- package/lib/accounts/testBulkAccountLoader.d.ts +4 -0
- package/lib/accounts/testBulkAccountLoader.js +45 -0
- package/lib/bankrun/bankrunConnection.d.ts +71 -0
- package/lib/bankrun/bankrunConnection.js +285 -0
- package/lib/blockhashSubscriber/BlockhashSubscriber.js +22 -15
- package/lib/constants/perpMarkets.js +10 -0
- package/lib/constants/spotMarkets.js +10 -0
- package/lib/driftClient.d.ts +1 -1
- package/lib/driftClient.js +8 -2
- package/lib/events/eventSubscriber.js +12 -4
- package/lib/idl/drift.json +28 -8
- package/lib/testClient.js +1 -2
- package/lib/tokenFaucet.d.ts +3 -1
- package/lib/tokenFaucet.js +41 -8
- package/lib/tx/txParamProcessor.d.ts +2 -1
- package/lib/tx/txParamProcessor.js +7 -3
- package/lib/types.d.ts +1 -0
- package/lib/user.js +1 -1
- package/package.json +3 -1
- package/src/accounts/bulkAccountLoader.ts +3 -2
- package/src/accounts/pollingDriftClientAccountSubscriber.ts +16 -3
- package/src/accounts/pollingUserAccountSubscriber.ts +13 -12
- package/src/accounts/testBulkAccountLoader.ts +53 -0
- package/src/bankrun/bankrunConnection.ts +466 -0
- package/src/blockhashSubscriber/BlockhashSubscriber.ts +24 -19
- package/src/constants/perpMarkets.ts +10 -0
- package/src/constants/spotMarkets.ts +10 -0
- package/src/driftClient.ts +12 -3
- package/src/events/eventSubscriber.ts +5 -0
- package/src/idl/drift.json +28 -8
- package/src/testClient.ts +1 -2
- package/src/tokenFaucet.ts +49 -12
- package/src/tx/txParamProcessor.ts +11 -2
- package/src/types.ts +1 -0
- package/src/user.ts +5 -2
package/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
2.85.0-beta.
|
|
1
|
+
2.85.0-beta.10
|
package/bun.lockb
CHANGED
|
Binary file
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { Commitment,
|
|
2
|
+
import { Commitment, PublicKey } from '@solana/web3.js';
|
|
3
3
|
import { BufferAndSlot } from './types';
|
|
4
|
-
|
|
4
|
+
import { Connection } from '../bankrun/bankrunConnection';
|
|
5
|
+
export type AccountToLoad = {
|
|
5
6
|
publicKey: PublicKey;
|
|
6
7
|
callbacks: Map<string, (buffer: Buffer, slot: number) => void>;
|
|
7
8
|
};
|
|
@@ -33,4 +34,3 @@ export declare class BulkAccountLoader {
|
|
|
33
34
|
log(msg: string): void;
|
|
34
35
|
updatePollingFrequency(pollingFrequency: number): void;
|
|
35
36
|
}
|
|
36
|
-
export {};
|
|
@@ -178,7 +178,11 @@ class PollingDriftClientAccountSubscriber {
|
|
|
178
178
|
async fetch() {
|
|
179
179
|
await this.accountLoader.load();
|
|
180
180
|
for (const [_, accountToPoll] of this.accountsToPoll) {
|
|
181
|
-
const
|
|
181
|
+
const bufferAndSlot = this.accountLoader.getBufferAndSlot(accountToPoll.publicKey);
|
|
182
|
+
if (!bufferAndSlot) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
const { buffer, slot } = bufferAndSlot;
|
|
182
186
|
if (buffer) {
|
|
183
187
|
const account = this.program.account[accountToPoll.key].coder.accounts.decodeUnchecked((0, utils_1.capitalize)(accountToPoll.key), buffer);
|
|
184
188
|
if (accountToPoll.mapKey != undefined) {
|
|
@@ -196,7 +200,11 @@ class PollingDriftClientAccountSubscriber {
|
|
|
196
200
|
}
|
|
197
201
|
}
|
|
198
202
|
for (const [_, oracleToPoll] of this.oraclesToPoll) {
|
|
199
|
-
const
|
|
203
|
+
const bufferAndSlot = this.accountLoader.getBufferAndSlot(oracleToPoll.publicKey);
|
|
204
|
+
if (!bufferAndSlot) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
const { buffer, slot } = bufferAndSlot;
|
|
200
208
|
if (buffer) {
|
|
201
209
|
const oracleClient = this.oracleClientCache.get(oracleToPoll.source, this.program.provider.connection, this.program);
|
|
202
210
|
const oraclePriceData = oracleClient.getOraclePriceDataFromBuffer(buffer);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { DataAndSlot, UserAccountEvents, UserAccountSubscriber } from './types';
|
|
3
|
-
import {
|
|
3
|
+
import { Connection } from '../bankrun/bankrunConnection';
|
|
4
4
|
import StrictEventEmitter from 'strict-event-emitter-types';
|
|
5
5
|
import { EventEmitter } from 'events';
|
|
6
6
|
import { PublicKey } from '@solana/web3.js';
|
|
@@ -8,14 +8,15 @@ import { UserAccount } from '../types';
|
|
|
8
8
|
import { BulkAccountLoader } from './bulkAccountLoader';
|
|
9
9
|
export declare class PollingUserAccountSubscriber implements UserAccountSubscriber {
|
|
10
10
|
isSubscribed: boolean;
|
|
11
|
-
|
|
11
|
+
connection: Connection;
|
|
12
12
|
eventEmitter: StrictEventEmitter<EventEmitter, UserAccountEvents>;
|
|
13
13
|
userAccountPublicKey: PublicKey;
|
|
14
14
|
accountLoader: BulkAccountLoader;
|
|
15
15
|
callbackId?: string;
|
|
16
16
|
errorCallbackId?: string;
|
|
17
|
+
decode: (name: any, buffer: any) => UserAccount;
|
|
17
18
|
user?: DataAndSlot<UserAccount>;
|
|
18
|
-
constructor(
|
|
19
|
+
constructor(connection: Connection, userAccountPublicKey: PublicKey, accountLoader: BulkAccountLoader, decode: (name: any, buffer: any) => UserAccount);
|
|
19
20
|
subscribe(userAccount?: UserAccount): Promise<boolean>;
|
|
20
21
|
addToAccountLoader(): Promise<void>;
|
|
21
22
|
fetchIfUnloaded(): Promise<void>;
|
|
@@ -4,12 +4,13 @@ exports.PollingUserAccountSubscriber = void 0;
|
|
|
4
4
|
const types_1 = require("./types");
|
|
5
5
|
const events_1 = require("events");
|
|
6
6
|
class PollingUserAccountSubscriber {
|
|
7
|
-
constructor(
|
|
7
|
+
constructor(connection, userAccountPublicKey, accountLoader, decode) {
|
|
8
8
|
this.isSubscribed = false;
|
|
9
|
-
this.
|
|
9
|
+
this.connection = connection;
|
|
10
10
|
this.accountLoader = accountLoader;
|
|
11
11
|
this.eventEmitter = new events_1.EventEmitter();
|
|
12
12
|
this.userAccountPublicKey = userAccountPublicKey;
|
|
13
|
+
this.decode = decode;
|
|
13
14
|
}
|
|
14
15
|
async subscribe(userAccount) {
|
|
15
16
|
if (this.isSubscribed) {
|
|
@@ -37,7 +38,7 @@ class PollingUserAccountSubscriber {
|
|
|
37
38
|
if (this.user && this.user.slot > slot) {
|
|
38
39
|
return;
|
|
39
40
|
}
|
|
40
|
-
const account = this.
|
|
41
|
+
const account = this.decode('User', buffer);
|
|
41
42
|
this.user = { data: account, slot };
|
|
42
43
|
this.eventEmitter.emit('userAccountUpdate', account);
|
|
43
44
|
this.eventEmitter.emit('update');
|
|
@@ -54,16 +55,16 @@ class PollingUserAccountSubscriber {
|
|
|
54
55
|
async fetch() {
|
|
55
56
|
var _a, _b;
|
|
56
57
|
try {
|
|
57
|
-
const dataAndContext = await this.
|
|
58
|
+
const dataAndContext = await this.connection.getAccountInfoAndContext(this.userAccountPublicKey, this.accountLoader.commitment);
|
|
58
59
|
if (dataAndContext.context.slot > ((_b = (_a = this.user) === null || _a === void 0 ? void 0 : _a.slot) !== null && _b !== void 0 ? _b : 0)) {
|
|
59
60
|
this.user = {
|
|
60
|
-
data: dataAndContext.data,
|
|
61
|
+
data: this.decode('User', dataAndContext.value.data),
|
|
61
62
|
slot: dataAndContext.context.slot,
|
|
62
63
|
};
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
66
|
catch (e) {
|
|
66
|
-
console.log(`PollingUserAccountSubscriber.fetch() UserAccount does not exist: ${e.message}`);
|
|
67
|
+
console.log(`PollingUserAccountSubscriber.fetch() UserAccount does not exist: ${e.message}-${e.stack}`);
|
|
67
68
|
}
|
|
68
69
|
}
|
|
69
70
|
doesAccountExist() {
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TestBulkAccountLoader = void 0;
|
|
4
|
+
const bulkAccountLoader_1 = require("./bulkAccountLoader");
|
|
5
|
+
class TestBulkAccountLoader extends bulkAccountLoader_1.BulkAccountLoader {
|
|
6
|
+
async loadChunk(accountsToLoadChunks) {
|
|
7
|
+
if (accountsToLoadChunks.length === 0) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
const accounts = [];
|
|
11
|
+
for (const accountsToLoadChunk of accountsToLoadChunks) {
|
|
12
|
+
for (const accountToLoad of accountsToLoadChunk) {
|
|
13
|
+
const account = await this.connection.getAccountInfoAndContext(accountToLoad.publicKey, this.commitment);
|
|
14
|
+
accounts.push(account);
|
|
15
|
+
const newSlot = account.context.slot;
|
|
16
|
+
if (newSlot > this.mostRecentSlot) {
|
|
17
|
+
this.mostRecentSlot = newSlot;
|
|
18
|
+
}
|
|
19
|
+
if (accountToLoad.callbacks.size === 0) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const key = accountToLoad.publicKey.toBase58();
|
|
23
|
+
const prev = this.bufferAndSlotMap.get(key);
|
|
24
|
+
if (prev && newSlot < prev.slot) {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
let newBuffer = undefined;
|
|
28
|
+
if (account.value) {
|
|
29
|
+
newBuffer = account.value.data;
|
|
30
|
+
}
|
|
31
|
+
if (!prev) {
|
|
32
|
+
this.bufferAndSlotMap.set(key, { slot: newSlot, buffer: newBuffer });
|
|
33
|
+
this.handleAccountCallbacks(accountToLoad, newBuffer, newSlot);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const oldBuffer = prev.buffer;
|
|
37
|
+
if (newBuffer && (!oldBuffer || !newBuffer.equals(oldBuffer))) {
|
|
38
|
+
this.bufferAndSlotMap.set(key, { slot: newSlot, buffer: newBuffer });
|
|
39
|
+
this.handleAccountCallbacks(accountToLoad, newBuffer, newSlot);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
exports.TestBulkAccountLoader = TestBulkAccountLoader;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
/// <reference types="bn.js" />
|
|
3
|
+
import { AccountInfo, Keypair, PublicKey, Transaction, RpcResponseAndContext, Commitment, TransactionSignature, SignatureStatusConfig, SignatureStatus, GetVersionedTransactionConfig, GetTransactionConfig, VersionedTransaction, SimulateTransactionConfig, SimulatedTransactionResponse, TransactionError, SignatureResultCallback, ClientSubscriptionId, Connection as SolanaConnection, Blockhash, LogsFilter, LogsCallback, AccountChangeCallback } from '@solana/web3.js';
|
|
4
|
+
import { ProgramTestContext, BanksClient } from 'solana-bankrun';
|
|
5
|
+
import { BankrunProvider } from 'anchor-bankrun';
|
|
6
|
+
import { BN, Wallet } from '@coral-xyz/anchor';
|
|
7
|
+
import { Account } from '@solana/spl-token';
|
|
8
|
+
export type Connection = SolanaConnection | BankrunConnection;
|
|
9
|
+
type BankrunTransactionMetaNormalized = {
|
|
10
|
+
logMessages: string[];
|
|
11
|
+
err: TransactionError;
|
|
12
|
+
};
|
|
13
|
+
type BankrunTransactionRespose = {
|
|
14
|
+
slot: number;
|
|
15
|
+
meta: BankrunTransactionMetaNormalized;
|
|
16
|
+
};
|
|
17
|
+
export declare class BankrunContextWrapper {
|
|
18
|
+
readonly connection: BankrunConnection;
|
|
19
|
+
readonly context: ProgramTestContext;
|
|
20
|
+
readonly provider: BankrunProvider;
|
|
21
|
+
readonly commitment: Commitment;
|
|
22
|
+
constructor(context: ProgramTestContext);
|
|
23
|
+
sendTransaction(tx: Transaction, additionalSigners?: Keypair[]): Promise<TransactionSignature>;
|
|
24
|
+
getMinimumBalanceForRentExemption(_: number): Promise<number>;
|
|
25
|
+
fundKeypair(keypair: Keypair | Wallet, lamports: number | bigint): Promise<TransactionSignature>;
|
|
26
|
+
getLatestBlockhash(): Promise<Blockhash>;
|
|
27
|
+
printTxLogs(signature: string): void;
|
|
28
|
+
moveTimeForward(increment: number): Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
export declare class BankrunConnection {
|
|
31
|
+
private readonly _banksClient;
|
|
32
|
+
private readonly context;
|
|
33
|
+
private transactionToMeta;
|
|
34
|
+
private clock;
|
|
35
|
+
private nextClientSubscriptionId;
|
|
36
|
+
private onLogCallbacks;
|
|
37
|
+
private onAccountChangeCallbacks;
|
|
38
|
+
constructor(banksClient: BanksClient, context: ProgramTestContext);
|
|
39
|
+
getSlot(): Promise<bigint>;
|
|
40
|
+
toConnection(): SolanaConnection;
|
|
41
|
+
getTokenAccount(publicKey: PublicKey): Promise<Account>;
|
|
42
|
+
getMultipleAccountsInfo(publicKeys: PublicKey[], _commitmentOrConfig?: Commitment): Promise<AccountInfo<Buffer>[]>;
|
|
43
|
+
getAccountInfo(publicKey: PublicKey): Promise<null | AccountInfo<Buffer>>;
|
|
44
|
+
getAccountInfoAndContext(publicKey: PublicKey, _commitment?: Commitment): Promise<RpcResponseAndContext<null | AccountInfo<Buffer>>>;
|
|
45
|
+
sendRawTransaction(rawTransaction: Buffer | Uint8Array | Array<number>, _options?: any): Promise<TransactionSignature>;
|
|
46
|
+
sendTransaction(tx: Transaction): Promise<TransactionSignature>;
|
|
47
|
+
private updateSlotAndClock;
|
|
48
|
+
getTime(): number;
|
|
49
|
+
getParsedAccountInfo(publicKey: PublicKey): Promise<RpcResponseAndContext<AccountInfo<Buffer>>>;
|
|
50
|
+
getLatestBlockhash(commitment?: Commitment): Promise<Readonly<{
|
|
51
|
+
blockhash: string;
|
|
52
|
+
lastValidBlockHeight: number;
|
|
53
|
+
}>>;
|
|
54
|
+
getSignatureStatus(signature: string, _config?: SignatureStatusConfig): Promise<RpcResponseAndContext<null | SignatureStatus>>;
|
|
55
|
+
/**
|
|
56
|
+
* There's really no direct equivalent to getTransaction exposed by SolanaProgramTest, so we do the best that we can here - it's a little hacky.
|
|
57
|
+
*/
|
|
58
|
+
getTransaction(signature: string, _rawConfig?: GetTransactionConfig | GetVersionedTransactionConfig): Promise<BankrunTransactionRespose | null>;
|
|
59
|
+
findComputeUnitConsumption(signature: string): bigint;
|
|
60
|
+
printTxLogs(signature: string): void;
|
|
61
|
+
simulateTransaction(transaction: Transaction | VersionedTransaction, _config?: SimulateTransactionConfig): Promise<RpcResponseAndContext<SimulatedTransactionResponse>>;
|
|
62
|
+
onSignature(signature: string, callback: SignatureResultCallback, commitment?: Commitment): ClientSubscriptionId;
|
|
63
|
+
removeSignatureListener(_clientSubscriptionId: number): Promise<void>;
|
|
64
|
+
onLogs(filter: LogsFilter, callback: LogsCallback, _commitment?: Commitment): ClientSubscriptionId;
|
|
65
|
+
removeOnLogsListener(clientSubscriptionId: ClientSubscriptionId): Promise<void>;
|
|
66
|
+
onAccountChange(publicKey: PublicKey, callback: AccountChangeCallback, _commitment?: Commitment): ClientSubscriptionId;
|
|
67
|
+
removeAccountChangeListener(clientSubscriptionId: ClientSubscriptionId): Promise<void>;
|
|
68
|
+
getMinimumBalanceForRentExemption(_: number): Promise<number>;
|
|
69
|
+
}
|
|
70
|
+
export declare function asBN(value: number | bigint): BN;
|
|
71
|
+
export {};
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.asBN = exports.BankrunConnection = exports.BankrunContextWrapper = void 0;
|
|
7
|
+
const web3_js_1 = require("@solana/web3.js");
|
|
8
|
+
const solana_bankrun_1 = require("solana-bankrun");
|
|
9
|
+
const anchor_bankrun_1 = require("anchor-bankrun");
|
|
10
|
+
const bs58_1 = __importDefault(require("bs58"));
|
|
11
|
+
const anchor_1 = require("@coral-xyz/anchor");
|
|
12
|
+
const spl_token_1 = require("@solana/spl-token");
|
|
13
|
+
class BankrunContextWrapper {
|
|
14
|
+
constructor(context) {
|
|
15
|
+
this.commitment = 'confirmed';
|
|
16
|
+
this.context = context;
|
|
17
|
+
this.provider = new anchor_bankrun_1.BankrunProvider(context);
|
|
18
|
+
this.connection = new BankrunConnection(this.context.banksClient, this.context);
|
|
19
|
+
}
|
|
20
|
+
async sendTransaction(tx, additionalSigners) {
|
|
21
|
+
tx.recentBlockhash = (await this.getLatestBlockhash()).toString();
|
|
22
|
+
tx.feePayer = this.context.payer.publicKey;
|
|
23
|
+
if (!additionalSigners) {
|
|
24
|
+
additionalSigners = [];
|
|
25
|
+
}
|
|
26
|
+
tx.sign(this.context.payer, ...additionalSigners);
|
|
27
|
+
return await this.connection.sendTransaction(tx);
|
|
28
|
+
}
|
|
29
|
+
async getMinimumBalanceForRentExemption(_) {
|
|
30
|
+
return 10 * web3_js_1.LAMPORTS_PER_SOL;
|
|
31
|
+
}
|
|
32
|
+
async fundKeypair(keypair, lamports) {
|
|
33
|
+
const ixs = [
|
|
34
|
+
web3_js_1.SystemProgram.transfer({
|
|
35
|
+
fromPubkey: this.context.payer.publicKey,
|
|
36
|
+
toPubkey: keypair.publicKey,
|
|
37
|
+
lamports,
|
|
38
|
+
}),
|
|
39
|
+
];
|
|
40
|
+
const tx = new web3_js_1.Transaction().add(...ixs);
|
|
41
|
+
return await this.sendTransaction(tx);
|
|
42
|
+
}
|
|
43
|
+
async getLatestBlockhash() {
|
|
44
|
+
const blockhash = await this.connection.getLatestBlockhash('finalized');
|
|
45
|
+
return blockhash.blockhash;
|
|
46
|
+
}
|
|
47
|
+
printTxLogs(signature) {
|
|
48
|
+
this.connection.printTxLogs(signature);
|
|
49
|
+
}
|
|
50
|
+
async moveTimeForward(increment) {
|
|
51
|
+
const currentClock = await this.context.banksClient.getClock();
|
|
52
|
+
const newUnixTimestamp = currentClock.unixTimestamp + BigInt(increment);
|
|
53
|
+
const newClock = new solana_bankrun_1.Clock(currentClock.slot, currentClock.epochStartTimestamp, currentClock.epoch, currentClock.leaderScheduleEpoch, newUnixTimestamp);
|
|
54
|
+
await this.context.setClock(newClock);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.BankrunContextWrapper = BankrunContextWrapper;
|
|
58
|
+
class BankrunConnection {
|
|
59
|
+
constructor(banksClient, context) {
|
|
60
|
+
this.transactionToMeta = new Map();
|
|
61
|
+
this.nextClientSubscriptionId = 0;
|
|
62
|
+
this.onLogCallbacks = new Map();
|
|
63
|
+
this.onAccountChangeCallbacks = new Map();
|
|
64
|
+
this._banksClient = banksClient;
|
|
65
|
+
this.context = context;
|
|
66
|
+
}
|
|
67
|
+
getSlot() {
|
|
68
|
+
return this._banksClient.getSlot();
|
|
69
|
+
}
|
|
70
|
+
toConnection() {
|
|
71
|
+
return this;
|
|
72
|
+
}
|
|
73
|
+
async getTokenAccount(publicKey) {
|
|
74
|
+
const info = await this.getAccountInfo(publicKey);
|
|
75
|
+
return (0, spl_token_1.unpackAccount)(publicKey, info, spl_token_1.TOKEN_PROGRAM_ID);
|
|
76
|
+
}
|
|
77
|
+
async getMultipleAccountsInfo(publicKeys, _commitmentOrConfig) {
|
|
78
|
+
const accountInfos = [];
|
|
79
|
+
for (const publicKey of publicKeys) {
|
|
80
|
+
const accountInfo = await this.getAccountInfo(publicKey);
|
|
81
|
+
accountInfos.push(accountInfo);
|
|
82
|
+
}
|
|
83
|
+
return accountInfos;
|
|
84
|
+
}
|
|
85
|
+
async getAccountInfo(publicKey) {
|
|
86
|
+
const parsedAccountInfo = await this.getParsedAccountInfo(publicKey);
|
|
87
|
+
return parsedAccountInfo ? parsedAccountInfo.value : null;
|
|
88
|
+
}
|
|
89
|
+
async getAccountInfoAndContext(publicKey, _commitment) {
|
|
90
|
+
return await this.getParsedAccountInfo(publicKey);
|
|
91
|
+
}
|
|
92
|
+
async sendRawTransaction(rawTransaction,
|
|
93
|
+
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
|
|
94
|
+
_options) {
|
|
95
|
+
const tx = web3_js_1.Transaction.from(rawTransaction);
|
|
96
|
+
const signature = await this.sendTransaction(tx);
|
|
97
|
+
return signature;
|
|
98
|
+
}
|
|
99
|
+
async sendTransaction(tx) {
|
|
100
|
+
const banksTransactionMeta = await this._banksClient.tryProcessTransaction(tx);
|
|
101
|
+
if (banksTransactionMeta.result) {
|
|
102
|
+
throw new Error(banksTransactionMeta.result);
|
|
103
|
+
}
|
|
104
|
+
const signature = bs58_1.default.encode(tx.signatures[0].signature);
|
|
105
|
+
this.transactionToMeta.set(signature, banksTransactionMeta);
|
|
106
|
+
let finalizedCount = 0;
|
|
107
|
+
while (finalizedCount < 10) {
|
|
108
|
+
const signatureStatus = (await this.getSignatureStatus(signature)).value
|
|
109
|
+
.confirmationStatus;
|
|
110
|
+
if (signatureStatus.toString() == '"finalized"') {
|
|
111
|
+
finalizedCount += 1;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// update the clock slot/timestamp
|
|
115
|
+
// sometimes race condition causes failures so we retry
|
|
116
|
+
try {
|
|
117
|
+
await this.updateSlotAndClock();
|
|
118
|
+
}
|
|
119
|
+
catch (e) {
|
|
120
|
+
await this.updateSlotAndClock();
|
|
121
|
+
}
|
|
122
|
+
if (this.onLogCallbacks.size > 0) {
|
|
123
|
+
const transaction = await this.getTransaction(signature);
|
|
124
|
+
const context = { slot: transaction.slot };
|
|
125
|
+
const logs = {
|
|
126
|
+
logs: transaction.meta.logMessages,
|
|
127
|
+
err: transaction.meta.err,
|
|
128
|
+
signature,
|
|
129
|
+
};
|
|
130
|
+
for (const logCallback of this.onLogCallbacks.values()) {
|
|
131
|
+
logCallback(logs, context);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
for (const [publicKey, callback,] of this.onAccountChangeCallbacks.values()) {
|
|
135
|
+
const accountInfo = await this.getParsedAccountInfo(publicKey);
|
|
136
|
+
callback(accountInfo.value, accountInfo.context);
|
|
137
|
+
}
|
|
138
|
+
return signature;
|
|
139
|
+
}
|
|
140
|
+
async updateSlotAndClock() {
|
|
141
|
+
const currentSlot = await this.getSlot();
|
|
142
|
+
const nextSlot = currentSlot + BigInt(1);
|
|
143
|
+
this.context.warpToSlot(nextSlot);
|
|
144
|
+
const currentClock = await this._banksClient.getClock();
|
|
145
|
+
const newClock = new solana_bankrun_1.Clock(nextSlot, currentClock.epochStartTimestamp, currentClock.epoch, currentClock.leaderScheduleEpoch, currentClock.unixTimestamp + BigInt(1));
|
|
146
|
+
this.context.setClock(newClock);
|
|
147
|
+
this.clock = newClock;
|
|
148
|
+
}
|
|
149
|
+
getTime() {
|
|
150
|
+
return Number(this.clock.unixTimestamp);
|
|
151
|
+
}
|
|
152
|
+
async getParsedAccountInfo(publicKey) {
|
|
153
|
+
const accountInfoBytes = await this._banksClient.getAccount(publicKey);
|
|
154
|
+
if (accountInfoBytes === null) {
|
|
155
|
+
return {
|
|
156
|
+
context: { slot: Number(await this._banksClient.getSlot()) },
|
|
157
|
+
value: null,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
accountInfoBytes.data = Buffer.from(accountInfoBytes.data);
|
|
161
|
+
const accountInfoBuffer = accountInfoBytes;
|
|
162
|
+
return {
|
|
163
|
+
context: { slot: Number(await this._banksClient.getSlot()) },
|
|
164
|
+
value: accountInfoBuffer,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
async getLatestBlockhash(commitment) {
|
|
168
|
+
const blockhashAndBlockheight = await this._banksClient.getLatestBlockhash(commitment);
|
|
169
|
+
return {
|
|
170
|
+
blockhash: blockhashAndBlockheight[0],
|
|
171
|
+
lastValidBlockHeight: Number(blockhashAndBlockheight[1]),
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
async getSignatureStatus(signature, _config) {
|
|
175
|
+
const transactionStatus = await this._banksClient.getTransactionStatus(signature);
|
|
176
|
+
if (transactionStatus === null) {
|
|
177
|
+
return {
|
|
178
|
+
context: { slot: Number(await this._banksClient.getSlot()) },
|
|
179
|
+
value: null,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return {
|
|
183
|
+
context: { slot: Number(await this._banksClient.getSlot()) },
|
|
184
|
+
value: {
|
|
185
|
+
slot: Number(transactionStatus.slot),
|
|
186
|
+
confirmations: Number(transactionStatus.confirmations),
|
|
187
|
+
err: transactionStatus.err,
|
|
188
|
+
confirmationStatus: transactionStatus.confirmationStatus,
|
|
189
|
+
},
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* There's really no direct equivalent to getTransaction exposed by SolanaProgramTest, so we do the best that we can here - it's a little hacky.
|
|
194
|
+
*/
|
|
195
|
+
async getTransaction(signature, _rawConfig) {
|
|
196
|
+
const txMeta = this.transactionToMeta.get(signature);
|
|
197
|
+
if (txMeta === undefined) {
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
const transactionStatus = await this._banksClient.getTransactionStatus(signature);
|
|
201
|
+
const meta = {
|
|
202
|
+
logMessages: txMeta.meta.logMessages,
|
|
203
|
+
err: txMeta.result,
|
|
204
|
+
};
|
|
205
|
+
return {
|
|
206
|
+
slot: Number(transactionStatus.slot),
|
|
207
|
+
meta,
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
findComputeUnitConsumption(signature) {
|
|
211
|
+
const txMeta = this.transactionToMeta.get(signature);
|
|
212
|
+
if (txMeta === undefined) {
|
|
213
|
+
throw new Error('Transaction not found');
|
|
214
|
+
}
|
|
215
|
+
return txMeta.meta.computeUnitsConsumed;
|
|
216
|
+
}
|
|
217
|
+
printTxLogs(signature) {
|
|
218
|
+
const txMeta = this.transactionToMeta.get(signature);
|
|
219
|
+
if (txMeta === undefined) {
|
|
220
|
+
throw new Error('Transaction not found');
|
|
221
|
+
}
|
|
222
|
+
console.log(txMeta.meta.logMessages);
|
|
223
|
+
}
|
|
224
|
+
async simulateTransaction(transaction, _config) {
|
|
225
|
+
var _a, _b, _c, _d;
|
|
226
|
+
const simulationResult = await this._banksClient.simulateTransaction(transaction);
|
|
227
|
+
const returnDataProgramId = (_b = (_a = simulationResult.meta) === null || _a === void 0 ? void 0 : _a.returnData) === null || _b === void 0 ? void 0 : _b.programId.toBase58();
|
|
228
|
+
const returnDataNormalized = Buffer.from((_d = (_c = simulationResult.meta) === null || _c === void 0 ? void 0 : _c.returnData) === null || _d === void 0 ? void 0 : _d.data).toString('base64');
|
|
229
|
+
const returnData = {
|
|
230
|
+
programId: returnDataProgramId,
|
|
231
|
+
data: [returnDataNormalized, 'base64'],
|
|
232
|
+
};
|
|
233
|
+
return {
|
|
234
|
+
context: { slot: Number(await this._banksClient.getSlot()) },
|
|
235
|
+
value: {
|
|
236
|
+
err: simulationResult.result,
|
|
237
|
+
logs: simulationResult.meta.logMessages,
|
|
238
|
+
accounts: undefined,
|
|
239
|
+
unitsConsumed: Number(simulationResult.meta.computeUnitsConsumed),
|
|
240
|
+
returnData,
|
|
241
|
+
},
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
onSignature(signature, callback, commitment) {
|
|
245
|
+
const txMeta = this.transactionToMeta.get(signature);
|
|
246
|
+
this._banksClient.getSlot(commitment).then((slot) => {
|
|
247
|
+
if (txMeta) {
|
|
248
|
+
callback({ err: txMeta.result }, { slot: Number(slot) });
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
return 0;
|
|
252
|
+
}
|
|
253
|
+
async removeSignatureListener(_clientSubscriptionId) {
|
|
254
|
+
// Nothing actually has to happen here! Pretty cool, huh?
|
|
255
|
+
// This function signature only exists to match the web3js interface
|
|
256
|
+
}
|
|
257
|
+
onLogs(filter, callback, _commitment) {
|
|
258
|
+
const subscriptId = this.nextClientSubscriptionId;
|
|
259
|
+
this.onLogCallbacks.set(subscriptId, callback);
|
|
260
|
+
this.nextClientSubscriptionId += 1;
|
|
261
|
+
return subscriptId;
|
|
262
|
+
}
|
|
263
|
+
async removeOnLogsListener(clientSubscriptionId) {
|
|
264
|
+
this.onLogCallbacks.delete(clientSubscriptionId);
|
|
265
|
+
}
|
|
266
|
+
onAccountChange(publicKey, callback,
|
|
267
|
+
// @ts-ignore
|
|
268
|
+
_commitment) {
|
|
269
|
+
const subscriptId = this.nextClientSubscriptionId;
|
|
270
|
+
this.onAccountChangeCallbacks.set(subscriptId, [publicKey, callback]);
|
|
271
|
+
this.nextClientSubscriptionId += 1;
|
|
272
|
+
return subscriptId;
|
|
273
|
+
}
|
|
274
|
+
async removeAccountChangeListener(clientSubscriptionId) {
|
|
275
|
+
this.onAccountChangeCallbacks.delete(clientSubscriptionId);
|
|
276
|
+
}
|
|
277
|
+
async getMinimumBalanceForRentExemption(_) {
|
|
278
|
+
return 10 * web3_js_1.LAMPORTS_PER_SOL;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
exports.BankrunConnection = BankrunConnection;
|
|
282
|
+
function asBN(value) {
|
|
283
|
+
return new anchor_1.BN(Number(value));
|
|
284
|
+
}
|
|
285
|
+
exports.asBN = asBN;
|
|
@@ -45,23 +45,30 @@ class BlockhashSubscriber {
|
|
|
45
45
|
}
|
|
46
46
|
}
|
|
47
47
|
async updateBlockhash() {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
if (
|
|
59
|
-
|
|
60
|
-
|
|
48
|
+
try {
|
|
49
|
+
const [resp, lastConfirmedBlockHeight] = await Promise.all([
|
|
50
|
+
this.connection.getLatestBlockhashAndContext({
|
|
51
|
+
commitment: this.commitment,
|
|
52
|
+
}),
|
|
53
|
+
this.connection.getBlockHeight({ commitment: this.commitment }),
|
|
54
|
+
]);
|
|
55
|
+
this.latestBlockHeight = lastConfirmedBlockHeight;
|
|
56
|
+
this.latestBlockHeightContext = resp.context;
|
|
57
|
+
// avoid caching duplicate blockhashes
|
|
58
|
+
if (this.blockhashes.length > 0) {
|
|
59
|
+
if (resp.value.blockhash ===
|
|
60
|
+
this.blockhashes[this.blockhashes.length - 1].blockhash) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
61
63
|
}
|
|
64
|
+
this.blockhashes.push(resp.value);
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
console.error('Error updating blockhash:\n', e);
|
|
68
|
+
}
|
|
69
|
+
finally {
|
|
70
|
+
this.pruneBlockhashes();
|
|
62
71
|
}
|
|
63
|
-
this.blockhashes.push(resp.value);
|
|
64
|
-
this.pruneBlockhashes();
|
|
65
72
|
}
|
|
66
73
|
async subscribe() {
|
|
67
74
|
if (this.isSubscribed) {
|
|
@@ -586,6 +586,16 @@ exports.MainnetPerpMarkets = [
|
|
|
586
586
|
launchTs: 1718021389000,
|
|
587
587
|
oracleSource: __1.OracleSource.SWITCHBOARD,
|
|
588
588
|
},
|
|
589
|
+
{
|
|
590
|
+
fullName: 'ZEX',
|
|
591
|
+
category: ['DEX', 'Solana'],
|
|
592
|
+
symbol: 'ZEX-PERP',
|
|
593
|
+
baseAssetSymbol: 'ZEX',
|
|
594
|
+
marketIndex: 33,
|
|
595
|
+
oracle: new web3_js_1.PublicKey('4gdbqxkMrF1bYVeEJKRmTqCCvJjRCZrRhxvriGY6SwLj'),
|
|
596
|
+
launchTs: 1719415157000,
|
|
597
|
+
oracleSource: __1.OracleSource.SWITCHBOARD,
|
|
598
|
+
},
|
|
589
599
|
];
|
|
590
600
|
exports.PerpMarkets = {
|
|
591
601
|
devnet: exports.DevnetPerpMarkets,
|
|
@@ -238,6 +238,16 @@ exports.MainnetSpotMarkets = [
|
|
|
238
238
|
precisionExp: numericConstants_1.SIX,
|
|
239
239
|
launchTs: 1718811089000,
|
|
240
240
|
},
|
|
241
|
+
{
|
|
242
|
+
symbol: 'JLP',
|
|
243
|
+
marketIndex: 19,
|
|
244
|
+
oracle: new web3_js_1.PublicKey('pmHEXBam7kbmCCg5ED5V7RNMN8e34sKu338KeuFAGof'),
|
|
245
|
+
oracleSource: __1.OracleSource.SWITCHBOARD,
|
|
246
|
+
mint: new web3_js_1.PublicKey('27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4'),
|
|
247
|
+
precision: new __1.BN(10).pow(numericConstants_1.SIX),
|
|
248
|
+
precisionExp: numericConstants_1.SIX,
|
|
249
|
+
launchTs: 1719415157000,
|
|
250
|
+
},
|
|
241
251
|
];
|
|
242
252
|
exports.SpotMarkets = {
|
|
243
253
|
devnet: exports.DevnetSpotMarkets,
|
package/lib/driftClient.d.ts
CHANGED
|
@@ -293,7 +293,7 @@ export declare class DriftClient {
|
|
|
293
293
|
* @deprecated use {@link placePerpOrder} or {@link placeAndTakePerpOrder} instead
|
|
294
294
|
*/
|
|
295
295
|
openPosition(direction: PositionDirection, amount: BN, marketIndex: number, limitPrice?: BN, subAccountId?: number): Promise<TransactionSignature>;
|
|
296
|
-
sendSignedTx(tx: Transaction, opts?: ConfirmOptions): Promise<TransactionSignature>;
|
|
296
|
+
sendSignedTx(tx: Transaction | VersionedTransaction, opts?: ConfirmOptions): Promise<TransactionSignature>;
|
|
297
297
|
prepareMarketOrderTxs(orderParams: OptionalOrderParams, userAccountPublicKey: PublicKey, userAccount: UserAccount, makerInfo?: MakerInfo | MakerInfo[], txParams?: TxParams, bracketOrdersParams?: OptionalOrderParams[], referrerInfo?: ReferrerInfo, cancelExistingOrders?: boolean, settlePnl?: boolean): Promise<{
|
|
298
298
|
cancelExistingOrdersTx?: Transaction | VersionedTransaction;
|
|
299
299
|
settlePnlTx?: Transaction | VersionedTransaction;
|
package/lib/driftClient.js
CHANGED
|
@@ -2267,6 +2267,9 @@ class DriftClient {
|
|
|
2267
2267
|
async getJupiterSwapIxV6({ jupiterClient, outMarketIndex, inMarketIndex, outAssociatedTokenAccount, inAssociatedTokenAccount, amount, slippageBps, swapMode, onlyDirectRoutes, quote, reduceOnly, userAccountPublicKey, }) {
|
|
2268
2268
|
const outMarket = this.getSpotMarketAccount(outMarketIndex);
|
|
2269
2269
|
const inMarket = this.getSpotMarketAccount(inMarketIndex);
|
|
2270
|
+
const isExactOut = swapMode === 'ExactOut' || quote.swapMode === 'ExactOut';
|
|
2271
|
+
const amountIn = new anchor_1.BN(quote.inAmount);
|
|
2272
|
+
const exactOutBufferedAmountIn = amountIn.muln(1001).divn(1000); // Add 10bp buffer
|
|
2270
2273
|
if (!quote) {
|
|
2271
2274
|
const fetchedQuote = await jupiterClient.getQuote({
|
|
2272
2275
|
inputMint: inMarket.mint,
|
|
@@ -2312,7 +2315,7 @@ class DriftClient {
|
|
|
2312
2315
|
const { beginSwapIx, endSwapIx } = await this.getSwapIx({
|
|
2313
2316
|
outMarketIndex,
|
|
2314
2317
|
inMarketIndex,
|
|
2315
|
-
amountIn:
|
|
2318
|
+
amountIn: isExactOut ? exactOutBufferedAmountIn : amountIn,
|
|
2316
2319
|
inTokenAccount: inAssociatedTokenAccount,
|
|
2317
2320
|
outTokenAccount: outAssociatedTokenAccount,
|
|
2318
2321
|
reduceOnly,
|
|
@@ -2544,7 +2547,7 @@ class DriftClient {
|
|
|
2544
2547
|
};
|
|
2545
2548
|
if (shouldUseSimulationComputeUnits || shouldExitIfSimulationFails) {
|
|
2546
2549
|
const placeAndTakeTxToSim = (await this.buildTransaction(placeAndTakeIxs, txParams, undefined, undefined, true, recentBlockHash));
|
|
2547
|
-
const simulationResult = await txParamProcessor_1.TransactionParamProcessor.getTxSimComputeUnits(placeAndTakeTxToSim, this.connection, (_a = txParams.computeUnitsBufferMultiplier) !== null && _a !== void 0 ? _a : 1.2);
|
|
2550
|
+
const simulationResult = await txParamProcessor_1.TransactionParamProcessor.getTxSimComputeUnits(placeAndTakeTxToSim, this.connection, (_a = txParams.computeUnitsBufferMultiplier) !== null && _a !== void 0 ? _a : 1.2, txParams.lowerBoundCu);
|
|
2548
2551
|
if (shouldExitIfSimulationFails && !simulationResult.success) {
|
|
2549
2552
|
earlyExitFailedPlaceAndTakeSim = true;
|
|
2550
2553
|
return;
|
|
@@ -2586,6 +2589,9 @@ class DriftClient {
|
|
|
2586
2589
|
}
|
|
2587
2590
|
async placeAndTakePerpWithAdditionalOrders(orderParams, makerInfo, referrerInfo, bracketOrdersParams = new Array(), txParams, subAccountId, cancelExistingOrders, settlePnl, exitEarlyIfSimFails) {
|
|
2588
2591
|
const txsToSign = await this.preparePlaceAndTakePerpOrderWithAdditionalOrders(orderParams, makerInfo, referrerInfo, bracketOrdersParams, txParams, subAccountId, cancelExistingOrders, settlePnl, exitEarlyIfSimFails);
|
|
2592
|
+
if (!txsToSign) {
|
|
2593
|
+
return null;
|
|
2594
|
+
}
|
|
2589
2595
|
const signedTxs = (await this.txHandler.getSignedTransactionMap(txsToSign, this.provider.wallet)).signedTxMap;
|
|
2590
2596
|
const { txSig, slot } = await this.sendTransaction(signedTxs.placeAndTakeTx, [], this.opts, true);
|
|
2591
2597
|
this.perpMarketLastSlotCache.set(orderParams.marketIndex, slot);
|