@drift-labs/sdk-browser
Version:
SDK for Drift Protocol
374 lines (348 loc) • 11.3 kB
text/typescript
import { DriftClient } from '../src/driftClient';
import { grpcDriftClientAccountSubscriberV2 } from '../src/accounts/grpcDriftClientAccountSubscriberV2';
import { grpcDriftClientAccountSubscriber } from '../src/accounts/grpcDriftClientAccountSubscriber';
import { Connection, Keypair, PublicKey } from '@solana/web3.js';
import { DriftClientConfig } from '../src/driftClientConfig';
import {
DRIFT_PROGRAM_ID,
PerpMarketAccount,
SpotMarketAccount,
Wallet,
OracleInfo,
} from '../src';
import { CommitmentLevel } from '@triton-one/yellowstone-grpc';
import dotenv from 'dotenv';
import {
AnchorProvider,
Idl,
Program,
ProgramAccount,
} from '@coral-xyz/anchor';
import driftIDL from '../src/idl/drift.json';
import assert from 'assert';
const GRPC_ENDPOINT = process.env.GRPC_ENDPOINT;
const TOKEN = process.env.TOKEN;
const RPC_ENDPOINT = process.env.RPC_ENDPOINT;
async function initializeGrpcDriftClientV2VersusV1() {
const connection = new Connection(RPC_ENDPOINT);
const wallet = new Wallet(new Keypair());
dotenv.config({ path: '../' });
const programId = new PublicKey(DRIFT_PROGRAM_ID);
const provider = new AnchorProvider(
connection,
// @ts-ignore
wallet,
{
commitment: 'processed',
}
);
const program = new Program(driftIDL as Idl, programId, provider);
const allPerpMarketProgramAccounts =
(await program.account.perpMarket.all()) as ProgramAccount<PerpMarketAccount>[];
const perpMarketProgramAccounts = allPerpMarketProgramAccounts.filter((val) =>
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15].includes(
val.account.marketIndex
)
);
const perpMarketIndexes = perpMarketProgramAccounts.map(
(val) => val.account.marketIndex
);
const allSpotMarketProgramAccounts =
(await program.account.spotMarket.all()) as ProgramAccount<SpotMarketAccount>[];
const spotMarketProgramAccounts = allSpotMarketProgramAccounts.filter((val) =>
[0, 1, 2, 3, 4, 5].includes(val.account.marketIndex)
);
const spotMarketIndexes = spotMarketProgramAccounts.map(
(val) => val.account.marketIndex
);
const seen = new Set<string>();
const oracleInfos: OracleInfo[] = [];
for (const acct of perpMarketProgramAccounts) {
const key = `${acct.account.amm.oracle.toBase58()}-${
Object.keys(acct.account.amm.oracleSource)[0]
}`;
if (!seen.has(key)) {
seen.add(key);
oracleInfos.push({
publicKey: acct.account.amm.oracle,
source: acct.account.amm.oracleSource,
});
}
}
for (const acct of spotMarketProgramAccounts) {
const key = `${acct.account.oracle.toBase58()}-${
Object.keys(acct.account.oracleSource)[0]
}`;
if (!seen.has(key)) {
seen.add(key);
oracleInfos.push({
publicKey: acct.account.oracle,
source: acct.account.oracleSource,
});
}
}
const baseAccountSubscription = {
type: 'grpc' as const,
grpcConfigs: {
endpoint: GRPC_ENDPOINT,
token: TOKEN,
commitmentLevel: CommitmentLevel.PROCESSED,
channelOptions: {
'grpc.keepalive_time_ms': 10_000,
'grpc.keepalive_timeout_ms': 1_000,
'grpc.keepalive_permit_without_calls': 1,
},
},
};
const configV2: DriftClientConfig = {
connection,
wallet,
programID: new PublicKey(DRIFT_PROGRAM_ID),
accountSubscription: {
...baseAccountSubscription,
driftClientAccountSubscriber: grpcDriftClientAccountSubscriberV2,
},
perpMarketIndexes,
spotMarketIndexes,
oracleInfos,
};
const configV1: DriftClientConfig = {
connection,
wallet,
programID: new PublicKey(DRIFT_PROGRAM_ID),
accountSubscription: {
...baseAccountSubscription,
driftClientAccountSubscriber: grpcDriftClientAccountSubscriber,
},
perpMarketIndexes,
spotMarketIndexes,
oracleInfos,
};
const clientV2 = new DriftClient(configV2);
const clientV1 = new DriftClient(configV1);
await Promise.all([clientV1.subscribe(), clientV2.subscribe()]);
const compare = () => {
try {
// 1. Test getStateAccountAndSlot
const state1 = clientV1.accountSubscriber.getStateAccountAndSlot();
const state2 = clientV2.accountSubscriber.getStateAccountAndSlot();
assert.deepStrictEqual(
state1.data,
state2.data,
'State accounts should match'
);
if (
state1.slot !== undefined &&
state2.slot !== undefined &&
state2.slot < state1.slot
) {
console.error(
`State account slot regression: v2 slot ${state2.slot} < v1 slot ${state1.slot}`
);
}
// 2. Test getMarketAccountsAndSlots (all perp markets) - sorted comparison
const allPerpMarkets1 = clientV1.accountSubscriber
.getMarketAccountsAndSlots()
.sort((a, b) => a.data.marketIndex - b.data.marketIndex);
const allPerpMarkets2 = clientV2.accountSubscriber
.getMarketAccountsAndSlots()
.sort((a, b) => a.data.marketIndex - b.data.marketIndex);
assert.strictEqual(
allPerpMarkets1.length,
allPerpMarkets2.length,
'Number of perp markets should match'
);
// Compare each perp market in the sorted arrays
for (let i = 0; i < allPerpMarkets1.length; i++) {
const market1 = allPerpMarkets1[i];
const market2 = allPerpMarkets2[i];
assert.strictEqual(
market1.data.marketIndex,
market2.data.marketIndex,
`Perp market at position ${i} should have same marketIndex`
);
// assert.deepStrictEqual(
// market1.data,
// market2.data,
// `Perp market ${market1.data.marketIndex} (from getMarketAccountsAndSlots) should match`
// );
}
// 3. Test getMarketAccountAndSlot for each perp market
for (const idx of perpMarketIndexes) {
const market1 = clientV1.accountSubscriber.getMarketAccountAndSlot(idx);
const market2 = clientV2.accountSubscriber.getMarketAccountAndSlot(idx);
// assert.deepStrictEqual(
// market1?.data,
// market2?.data,
// `Perp market ${idx} data should match`
// );
// assert.strictEqual(
// market1?.slot,
// market2?.slot,
// `Perp market ${idx} slot should match`
// );
if (
market1?.slot !== undefined &&
market2?.slot !== undefined &&
market2.slot < market1.slot
) {
console.error(
`Perp market ${idx} slot regression: v2 slot ${market2.slot} < v1 slot ${market1.slot}`
);
} else if (
market1?.slot !== undefined &&
market2?.slot !== undefined &&
market2.slot > market1.slot
) {
console.info(
`Perp market ${idx} slot is FASTER! v2: ${market2.slot}, v1: ${market1.slot}`
);
}
}
// 4. Test getSpotMarketAccountsAndSlots (all spot markets) - sorted comparison
const allSpotMarkets1 = clientV1.accountSubscriber
.getSpotMarketAccountsAndSlots()
.sort((a, b) => a.data.marketIndex - b.data.marketIndex);
const allSpotMarkets2 = clientV2.accountSubscriber
.getSpotMarketAccountsAndSlots()
.sort((a, b) => a.data.marketIndex - b.data.marketIndex);
assert.strictEqual(
allSpotMarkets1.length,
allSpotMarkets2.length,
'Number of spot markets should match'
);
// Compare each spot market in the sorted arrays
for (let i = 0; i < allSpotMarkets1.length; i++) {
const market1 = allSpotMarkets1[i];
const market2 = allSpotMarkets2[i];
assert.strictEqual(
market1.data.marketIndex,
market2.data.marketIndex,
`Spot market at position ${i} should have same marketIndex`
);
// assert.deepStrictEqual(
// market1.data,
// market2.data,
// `Spot market ${market1.data.marketIndex} (from getSpotMarketAccountsAndSlots) should match`
// );
}
// 5. Test getSpotMarketAccountAndSlot for each spot market
for (const idx of spotMarketIndexes) {
const market1 =
clientV1.accountSubscriber.getSpotMarketAccountAndSlot(idx);
const market2 =
clientV2.accountSubscriber.getSpotMarketAccountAndSlot(idx);
// assert.deepStrictEqual(
// market1?.data,
// market2?.data,
// `Spot market ${idx} data should match`
// );
// assert.strictEqual(
// market1?.slot,
// market2?.slot,
// `Spot market ${idx} slot should match`
// );
if (
market1?.slot !== undefined &&
market2?.slot !== undefined &&
market2.slot < market1.slot
) {
console.error(
`Spot market ${idx} slot regression: v2 slot ${market2.slot} < v1 slot ${market1.slot}`
);
} else if (
market1?.slot !== undefined &&
market2?.slot !== undefined &&
market2.slot > market1.slot
) {
console.info(
`Spot market ${idx} slot is FASTER! v2: ${market2.slot}, v1: ${market1.slot}`
);
}
}
// 6. Test getOraclePriceDataAndSlotForPerpMarket
for (const idx of perpMarketIndexes) {
const oracle1 =
clientV1.accountSubscriber.getOraclePriceDataAndSlotForPerpMarket(
idx
);
const oracle2 =
clientV2.accountSubscriber.getOraclePriceDataAndSlotForPerpMarket(
idx
);
// assert.deepStrictEqual(
// oracle1?.data,
// oracle2?.data,
// `Perp market ${idx} oracle data should match`
// );
// Note: slots might differ slightly due to timing, so we can optionally skip this check or be lenient
// assert.strictEqual(oracle1?.slot, oracle2?.slot, `Perp market ${idx} oracle slot should match`);
if (
oracle1?.slot !== undefined &&
oracle2?.slot !== undefined &&
oracle2.slot < oracle1.slot
) {
console.error(
`Perp market ${idx} oracle slot regression: v2 slot ${oracle2.slot} < v1 slot ${oracle1.slot}`
);
} else if (
oracle1?.slot !== undefined &&
oracle2?.slot !== undefined &&
oracle2.slot > oracle1.slot
) {
console.info(
`Perp market ${idx} oracle slot is FASTER! v2: ${oracle2.slot}, v1: ${oracle1.slot}`
);
}
}
// 7. Test getOraclePriceDataAndSlotForSpotMarket
for (const idx of spotMarketIndexes) {
const oracle1 =
clientV1.accountSubscriber.getOraclePriceDataAndSlotForSpotMarket(
idx
);
const oracle2 =
clientV2.accountSubscriber.getOraclePriceDataAndSlotForSpotMarket(
idx
);
// assert.deepStrictEqual(
// oracle1?.data,
// oracle2?.data,
// `Spot market ${idx} oracle data should match`
// );
// Note: slots might differ slightly due to timing
// assert.strictEqual(oracle1?.slot, oracle2?.slot, `Spot market ${idx} oracle slot should match`);
if (
oracle1?.slot !== undefined &&
oracle2?.slot !== undefined &&
oracle2.slot < oracle1.slot
) {
console.error(
`Spot market ${idx} oracle slot regression: v2 slot ${oracle2.slot} < v1 slot ${oracle1.slot}`
);
} else if (
oracle1?.slot !== undefined &&
oracle2?.slot !== undefined &&
oracle2.slot > oracle1.slot
) {
console.info(
`Spot market ${idx} oracle slot is FASTER! v2: ${oracle2.slot}, v1: ${oracle1.slot}`
);
}
}
console.log('✓ All comparisons passed');
} catch (error) {
console.error('✗ Comparison failed:', error);
}
};
compare();
const interval = setInterval(compare, 1000);
const cleanup = async () => {
clearInterval(interval);
await Promise.all([clientV1.unsubscribe(), clientV2.unsubscribe()]);
process.exit(0);
};
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);
}
initializeGrpcDriftClientV2VersusV1().catch(console.error);