Skip to content

Commit

Permalink
Merge pull request #31 from drift-labs/luke/sweepstakes-indexing
Browse files Browse the repository at this point in the history
Luke/sweepstakes indexing
  • Loading branch information
lukecaan authored Nov 22, 2023
2 parents 7f33d3d + 04ae38e commit 4d91983
Show file tree
Hide file tree
Showing 8 changed files with 316 additions and 28 deletions.
1 change: 1 addition & 0 deletions ts/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"@drift-labs/sdk": "2.45.0-beta.5",
"@solana/web3.js": "1.73.2",
"@switchboard-xyz/solana.js": "^2.7.1",
"cerializr": "^3.1.4",
"dotenv": "^16.3.1",
"strict-event-emitter-types": "^2.0.0",
"ts-node": "^10.9.1",
Expand Down
60 changes: 35 additions & 25 deletions ts/sdk/src/competitionClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ import {
import * as anchor from '@coral-xyz/anchor';
import { DRIFT_COMPETITION_PROGRAM_ID } from './constants';
import { LogParser } from './parsers';
import { sleep } from '@drift/common';

export class CompetitionsClient {
driftClient: DriftClient;
Expand Down Expand Up @@ -654,35 +655,44 @@ export class CompetitionsClient {

let earliestPulledSlot = Number.MAX_SAFE_INTEGER;

let loopCount = 0;
// REMOVE LATER : luke/sweepstakes-rpc-patch
const maxLoopCount = 20;

while (!fetchedAllLogs && loopCount < maxLoopCount) {
loopCount++;
const response = await fetchLogs(
this.driftClient.connection,
this.program.programId,
'confirmed',
oldestFetchedTx
);

if (!response?.transactionLogs || response.transactionLogs.length === 0 || response?.earliestSlot >= earliestPulledSlot) {
fetchedAllLogs = true;
break;
while (!fetchedAllLogs) {
try {
const response = await fetchLogs(
this.driftClient.connection,
this.program.programId,
'confirmed',
oldestFetchedTx,
undefined,
undefined,
250,
2,
);

if (!response?.transactionLogs || response.transactionLogs.length === 0 || response?.earliestSlot >= earliestPulledSlot) {
fetchedAllLogs = true;
break;
}

oldestFetchedTx = response.earliestTx;

const newLogs = response.transactionLogs;

logs = logs.concat(newLogs);

earliestPulledSlot = response.earliestSlot;
} catch (e) {
if (e?.includes?.('timed out') || e?.message?.includes?.('timed out')) {
console.log('Handling timeout');
await sleep(2000);
} else {
throw e;
}
}

oldestFetchedTx = response.earliestTx;

const newLogs = response.transactionLogs;

logs = logs.concat(newLogs);

earliestPulledSlot = response.earliestSlot;
}

const logParser = new LogParser(this.program);
const events = logs.map((log) => logParser.parseEventsFromLogs(log)).flat();
const logEvents = logs.map((log) => logParser.parseEventsFromLogs(log));
const events = logEvents.flat();

return events;
}
Expand Down
1 change: 1 addition & 0 deletions ts/sdk/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from './accountSubscribers';
export * from './constants';
export * from './parsers';
export * from './events/eventSubscriber';
export * from './serializer/serializer';
20 changes: 19 additions & 1 deletion ts/sdk/src/parsers/logParser.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Program } from '@coral-xyz/anchor';
import { TransactionSignature } from '@solana/web3.js';
import { TransactionResponse, TransactionSignature, VersionedTransactionResponse } from '@solana/web3.js';
import { WrappedEvents } from '../types/types';

export type EventLog = {
Expand All @@ -8,6 +8,16 @@ export type EventLog = {
logs: string[];
};

function mapTransactionResponseToLog(
transaction: TransactionResponse | VersionedTransactionResponse
): EventLog {
return {
txSig: transaction.transaction.signatures[0],
slot: transaction.slot,
logs: transaction.meta.logMessages,
};
}

export class LogParser {
constructor(private program: Program) {}

Expand All @@ -31,4 +41,12 @@ export class LogParser {

return records;
}

public parseEventsFromTransaction(
transaction: TransactionResponse
): WrappedEvents {
const transactionLogObject = mapTransactionResponseToLog(transaction);

return this.parseEventsFromLogs(transactionLogObject);
}
}
227 changes: 227 additions & 0 deletions ts/sdk/src/serializer/serializer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,227 @@
import { BASE_PRECISION_EXP, BN, BigNum, PERCENTAGE_PRECISION_EXP, PRICE_PRECISION_EXP, PublicKey, QUOTE_PRECISION_EXP } from '@drift-labs/sdk';
import {
Deserialize,
JsonObject,
Serialize,
SetDeserializeKeyTransform,
SetSerializeKeyTransform,
SnakeCase,
autoserializeAs,
autoserializeAsArray,
autoserializeUsing
} from 'cerializr';
import { CompetitionResult, CompetitionRoundSummaryRecord, CompetitionRoundWinnerRecord } from '../types/types';


const BNSerializationFn = (target: BN) =>
target ? target.toString() : undefined;
const BNDeserializationFn = (val: string) => (val ? new BN(val) : undefined);
const BNSerializeAndDeserializeFns = {
Serialize: BNSerializationFn,
Deserialize: BNDeserializationFn,
};

const BNArraySerializationFn = (target: BN[]) =>
target.map((val) => (val ? val.toString() : undefined));
const BNArrayDeserializationFn = (values: string[]) =>
values.map((val) => (val ? new BN(val) : undefined));
const BNArraySerializeAndDeserializeFns = {
Serialize: BNArraySerializationFn,
Deserialize: BNArrayDeserializationFn,
};

const QuoteBigNumSerializationFn = (target: BigNum | BN) =>
target
? target instanceof BigNum
? target.print()
: target.toString()
: undefined;
const QuoteBigNumDeserializationFn = (val: string) =>
val
? BigNum.from(
typeof val === 'string' ? val.replace('.', '') : val,
QUOTE_PRECISION_EXP
)
: undefined;
const QuoteBigNumSerializeAndDeserializeFns = {
Serialize: QuoteBigNumSerializationFn,
Deserialize: QuoteBigNumDeserializationFn,
};

const PctBigNumSerializationFn = (target: BigNum | BN) =>
target
? target instanceof BigNum
? target.print()
: target.toString()
: undefined;

const PctBigNumDeserializationFn = (val: string) =>
val
? BigNum.from(
typeof val === 'string' ? val.replace('.', '') : val,
PERCENTAGE_PRECISION_EXP
)
: undefined;

const PctBigNumSerializeAndDeserializeFns = {
Serialize: PctBigNumSerializationFn,
Deserialize: PctBigNumDeserializationFn,
};

const BaseBigNumSerializationFn = (target: BigNum | BN) =>
target
? target instanceof BigNum
? target.print()
: target.toString()
: undefined;
const BaseBigNumDeserializationFn = (val: string) =>
val
? BigNum.from(
typeof val === 'string' ? val.replace('.', '') : val,
BASE_PRECISION_EXP
)
: undefined;
const BaseBigNumSerializeAndDeserializeFns = {
Serialize: BaseBigNumSerializationFn,
Deserialize: BaseBigNumDeserializationFn,
};

const PriceBigNumSerializationFn = (target: BigNum | BN) =>
target
? target instanceof BigNum
? target.print()
: target.toString()
: undefined;
const PriceBigNumDeserializationFn = (val: string) =>
val
? BigNum.from(
typeof val === 'string' ? val.replace('.', '') : val,
PRICE_PRECISION_EXP
)
: undefined;
const PriceBigNumSerializeAndDeserializeFns = {
Serialize: PriceBigNumSerializationFn,
Deserialize: PriceBigNumDeserializationFn,
};

const PublicKeySerializationFn = (target: PublicKey) =>
target ? target.toString() : undefined;
const PublicKeyDeserializationFn = (val: string) =>
val ? new PublicKey(val) : undefined;
const PublicKeySerializeAndDeserializeFns = {
Serialize: PublicKeySerializationFn,
Deserialize: PublicKeyDeserializationFn,
};

const EnumSerializationFn = (target: Record<string, unknown>) => {
if (!target) return null;

return Object.keys(target)[0];
};
const EnumDeserializationFn = (val: any) => {
if (!val) return null;

{
if (typeof val === 'string') return { [val]: {} };

return val;
}
};
const EnumSerializeAndDeserializeFns = {
Serialize: EnumSerializationFn,
Deserialize: EnumDeserializationFn,
};

const SERIALIZATION_UTILS = {
BNSerializeAndDeserializeFns,
BNArraySerializeAndDeserializeFns,
QuoteBigNumSerializeAndDeserializeFns,
PctBigNumSerializeAndDeserializeFns,
BaseBigNumSerializeAndDeserializeFns,
PriceBigNumSerializeAndDeserializeFns,
PublicKeySerializeAndDeserializeFns,
EnumSerializeAndDeserializeFns,
};

export class SerializableSummaryEvent implements CompetitionRoundSummaryRecord {
@autoserializeUsing(SERIALIZATION_UTILS.PublicKeySerializeAndDeserializeFns) competition: PublicKey;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) roundNumber: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) roundStartTs: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) roundEndTs: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) prizePlacement: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) prizeOddsNumerator: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) prizeRandomness: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) prizeRandomnessMax: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) maxPrizeBucketValue: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) prizeAmount: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) prizeValue: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) prizeBase: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) numberOfWinners: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) numberOfCompetitorsSettled: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) totalScoreSettled: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) insuranceVaultBalance: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) protocolIfShares: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) totalIfShares: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) ts: BN;
@autoserializeAs(String) txSig: string;
@autoserializeAs(Number) slot: number;
}

export class SerializableCompetitionRoundWinner implements CompetitionRoundWinnerRecord {
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) roundNumber: BN;
@autoserializeUsing(SERIALIZATION_UTILS.PublicKeySerializeAndDeserializeFns) competitor: PublicKey;
@autoserializeUsing(SERIALIZATION_UTILS.PublicKeySerializeAndDeserializeFns) competition: PublicKey;
@autoserializeUsing(SERIALIZATION_UTILS.PublicKeySerializeAndDeserializeFns) competitorAuthority: PublicKey;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) minDraw: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) maxDraw: BN;
@autoserializeAs(Number) winnerPlacement: number;
@autoserializeAs(Number) numberOfWinners: number;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) numberOfCompetitorsSettled: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) winnerRandomness: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) totalScoreSettled: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) prizeRandomness: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) prizeRandomnessMax: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) prizeAmount: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) prizeBase: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) prizeValue: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) ts: BN;
@autoserializeAs(String) txSig: string;
@autoserializeAs(Number) slot: number;
}

export class SerializableCompetitionResult implements CompetitionResult {
@autoserializeAs(Number) startTs: number;
@autoserializeAs(Number) endTs: number;
@autoserializeAs(Number) roundNumber: number;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) competitors: BN;
@autoserializeUsing(SERIALIZATION_UTILS.BNSerializeAndDeserializeFns) totalTickets: BN;
// @ts-ignore
@autoserializeAs(SerializableSummaryEvent) summaryEvent: SerializableSummaryEvent;
//@ts-ignore
@autoserializeAsArray(SerializableCompetitionRoundWinner) winners: SerializableCompetitionRoundWinner[];
}

export const Serializer = {
Serialize: {
SerializableSummaryEvent: (cls: SerializableSummaryEvent) => Serialize(cls, SerializableSummaryEvent),
SerializableCompetitionRoundWinner: (cls: SerializableCompetitionRoundWinner) => Serialize(cls, SerializableCompetitionRoundWinner),
SerializableCompetitionResult: (cls: SerializableCompetitionResult) => Serialize(cls, SerializableCompetitionResult),
},
Deserialize: {
SerializableSummaryEvent: (cls: Record<string, unknown>) =>
// @ts-ignore
Deserialize(cls as JsonObject, SerializableSummaryEvent) as SerializableSummaryEvent,
SerializableCompetitionRoundWinner: (cls: Record<string, unknown>) =>
// @ts-ignore
Deserialize(cls as JsonObject, SerializableCompetitionRoundWinner) as SerializableCompetitionRoundWinner,
SerializableCompetitionResult: (cls: Record<string, unknown>) =>
// @ts-ignore
Deserialize(cls as JsonObject, SerializableCompetitionResult) as SerializableCompetitionResult,
},
setDeserializeFromSnakeCase: () => {
SetDeserializeKeyTransform(SnakeCase);
},
setSerializeFromSnakeCase: () => {
SetSerializeKeyTransform(SnakeCase);
},
};
17 changes: 17 additions & 0 deletions ts/sdk/src/types/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
BN,
BigNum,
DataAndSlot,
Event,
EventSubscriptionOrderBy,
Expand Down Expand Up @@ -231,3 +232,19 @@ export type CompetitorAccountSubscriber =
Competitor,
CompetitorAccountEvents
>;

export type CompetitionResult = {
startTs: number;
endTs: number;
roundNumber: number;
competitors: BN;
totalTickets: BN;
summaryEvent: Event<CompetitionRoundSummaryRecord>;
winners: {
authority: PublicKey;
prize: BigNum;
tickets: BN;
placement: number;
winnerEvent: Event<CompetitionRoundWinnerRecord>;
}[];
};
3 changes: 2 additions & 1 deletion ts/sdk/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"resolveJsonModule": true,
"skipLibCheck": true,
"moduleResolution": "node",
"baseUrl": "./"
"baseUrl": "./",
"experimentalDecorators" : true
},
"include": ["src"],
"exclude": ["node_modules", "tests"]
Expand Down
Loading

0 comments on commit 4d91983

Please sign in to comment.