Skip to content

Commit 8edefea

Browse files
authored
Merge pull request #535 from sevenlabs-hq/v1/versioned-decoders
[V1] Versioned Decoders
2 parents 6bcdac1 + 8fc899a commit 8edefea

File tree

8 files changed

+162
-3
lines changed

8 files changed

+162
-3
lines changed

crates/core/src/filter.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,3 +241,145 @@ impl Filter for DatasourceFilter {
241241
}
242242
}
243243
}
244+
245+
#[derive(Debug, Clone)]
246+
pub struct SlotRangeFilter {
247+
from_slot: Option<u64>,
248+
from_transaction_index: Option<u64>,
249+
to_slot: Option<u64>,
250+
to_transaction_index: Option<u64>,
251+
}
252+
253+
impl SlotRangeFilter {
254+
pub fn from(slot: u64, transaction_index: Option<u64>) -> Self {
255+
Self {
256+
from_slot: Some(slot),
257+
from_transaction_index: transaction_index,
258+
to_slot: None,
259+
to_transaction_index: None,
260+
}
261+
}
262+
263+
pub fn to(slot: u64, transaction_index: Option<u64>) -> Self {
264+
Self {
265+
from_slot: None,
266+
from_transaction_index: None,
267+
to_slot: Some(slot),
268+
to_transaction_index: transaction_index,
269+
}
270+
}
271+
272+
pub fn between(
273+
from_slot: u64,
274+
from_transaction_index: Option<u64>,
275+
to_slot: u64,
276+
to_transaction_index: Option<u64>,
277+
) -> Self {
278+
Self {
279+
from_slot: Some(from_slot),
280+
from_transaction_index,
281+
to_slot: Some(to_slot),
282+
to_transaction_index,
283+
}
284+
}
285+
286+
#[inline(always)]
287+
pub fn contains(&self, slot: u64, index: Option<u64>) -> bool {
288+
if let Some(from) = self.from_slot {
289+
if slot < from {
290+
return false;
291+
}
292+
293+
if slot == from {
294+
if let (Some(from_idx), Some(tx_idx)) = (self.from_transaction_index, index) {
295+
if tx_idx < from_idx {
296+
return false;
297+
}
298+
}
299+
}
300+
}
301+
302+
if let Some(to) = self.to_slot {
303+
if slot > to {
304+
return false;
305+
}
306+
307+
if slot == to {
308+
if let (Some(to_idx), Some(tx_idx)) = (self.to_transaction_index, index) {
309+
if tx_idx >= to_idx {
310+
return false;
311+
}
312+
}
313+
}
314+
}
315+
316+
true
317+
}
318+
}
319+
320+
impl Filter for SlotRangeFilter {
321+
fn filter_instruction(
322+
&self,
323+
_context: &FilterContext,
324+
instruction: &NestedInstruction,
325+
) -> FilterResult {
326+
let slot = instruction.metadata.transaction_metadata.slot;
327+
let index = instruction.metadata.transaction_metadata.index;
328+
329+
if self.contains(slot, index) {
330+
FilterResult::Accept
331+
} else {
332+
FilterResult::Reject
333+
}
334+
}
335+
336+
fn filter_account(
337+
&self,
338+
_context: &FilterContext,
339+
metadata: &AccountMetadata,
340+
_account: &solana_account::Account,
341+
) -> FilterResult {
342+
if self.contains(metadata.slot, None) {
343+
FilterResult::Accept
344+
} else {
345+
FilterResult::Reject
346+
}
347+
}
348+
349+
fn filter_transaction(
350+
&self,
351+
_context: &FilterContext,
352+
metadata: &TransactionMetadata,
353+
_instructions: &NestedInstructions,
354+
) -> FilterResult {
355+
if self.contains(metadata.slot, metadata.index) {
356+
FilterResult::Accept
357+
} else {
358+
FilterResult::Reject
359+
}
360+
}
361+
362+
fn filter_account_deletion(
363+
&self,
364+
_context: &FilterContext,
365+
deletion: &crate::datasource::AccountDeletion,
366+
) -> FilterResult {
367+
if self.contains(deletion.slot, None) {
368+
FilterResult::Accept
369+
} else {
370+
FilterResult::Reject
371+
}
372+
}
373+
374+
fn filter_block_details(
375+
&self,
376+
_context: &FilterContext,
377+
details: &crate::datasource::BlockDetails,
378+
) -> FilterResult {
379+
if self.contains(details.slot, None) {
380+
FilterResult::Accept
381+
} else {
382+
FilterResult::Reject
383+
}
384+
}
385+
}

crates/core/src/pipeline.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -607,7 +607,7 @@ impl PipelineBuilder {
607607
filters: Vec<Box<dyn Filter + Send + Sync + 'static>>,
608608
) -> Self
609609
where
610-
T: Send + Sync + crate::deserialize::ArrangeAccounts + 'static,
610+
T: Send + Sync + 'static,
611611
P: for<'a> Processor<InstructionProcessorInputType<'a, T>> + Send + Sync + 'static,
612612
{
613613
log::trace!(

datasources/jetstreamer-datasource/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ impl JetstreamerDatasource {
279279
meta: transaction.transaction_status_meta,
280280
is_vote: transaction.is_vote,
281281
slot: transaction.slot,
282-
index: None,
282+
index: Some(transaction.transaction_slot_index as u64),
283283
block_time: None,
284284
block_hash: None,
285285
})),

packages/cli/src/cli.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ program
6161
.option('--with-graphql <boolean>', 'Include GraphQL wiring and deps (default: true)')
6262
.option('--with-serde <boolean>', 'Include serde feature for decoder (default: false)')
6363
.option('--with-base58 <boolean>', 'Include base58 feature for decoder (default: false)')
64+
.option('--version-name <name>', 'Decoder version name (e.g., "v1", "v2")')
6465
.option('--no-clean', 'Do not delete output directory before rendering', false)
6566
.action(async opts => {
6667
showBanner();
@@ -102,6 +103,7 @@ program
102103
withSerde,
103104
withBase58,
104105
standalone: true,
106+
versionName: opts.versionName,
105107
});
106108

107109
logger.succeedSpinner('Decoder generated');
@@ -146,6 +148,7 @@ program
146148
.option('--with-graphql <boolean>', 'Include GraphQL wiring and deps (default: true)')
147149
.option('--with-serde <boolean>', 'Include serde feature for decoder (default: false)')
148150
.option('--with-base58 <boolean>', 'Include base58 feature for decoder (default: false)')
151+
.option('--version-name <name>', 'Decoder version name (e.g., "v1", "v2")')
149152
.option('--postgres-mode <generic|typed>', 'Postgres table storage mode', 'typed')
150153
.option('--force', 'Overwrite output directory if it exists', false)
151154
.action(async opts => {
@@ -246,6 +249,7 @@ program
246249
withSerde,
247250
withBase58,
248251
standalone: false,
252+
versionName: opts.versionName,
249253
});
250254

251255
logger.succeedSpinner('Decoder generated successfully');

packages/cli/src/lib/decoder.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,7 @@ export type DecoderGenerationOptions = {
7878
withSerde?: boolean;
7979
withBase58?: boolean;
8080
standalone?: boolean;
81+
versionName?: string;
8182
};
8283

8384
export type IdlMetadata = {
@@ -287,6 +288,7 @@ export async function generateDecoder(options: DecoderGenerationOptions): Promis
287288
withSerde,
288289
withBase58,
289290
standalone,
291+
versionName: options.versionName,
290292
}),
291293
);
292294
return;
@@ -351,6 +353,7 @@ export async function generateDecoder(options: DecoderGenerationOptions): Promis
351353
withSerde,
352354
withBase58,
353355
standalone,
356+
versionName: options.versionName,
354357
}),
355358
);
356359
} else {
@@ -365,6 +368,7 @@ export async function generateDecoder(options: DecoderGenerationOptions): Promis
365368
withSerde,
366369
withBase58,
367370
standalone,
371+
versionName: options.versionName,
368372
}),
369373
);
370374
}

packages/renderer/src/cargoTomlGenerator.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export type DecoderCargoTomlOptions = {
66
packageName?: string;
77
programName: string;
88
originalProgramName?: string; // Original program name from IDL (for token-2022 checks)
9+
versionName?: string;
910
withPostgres: boolean;
1011
withGraphQL: boolean;
1112
withSerde: boolean;
@@ -19,6 +20,7 @@ export function generateDecoderCargoToml(options: DecoderCargoTomlOptions): stri
1920
packageName,
2021
programName,
2122
originalProgramName,
23+
versionName,
2224
withPostgres,
2325
withGraphQL,
2426
withSerde,
@@ -27,13 +29,17 @@ export function generateDecoderCargoToml(options: DecoderCargoTomlOptions): stri
2729
standalone = true,
2830
} = options;
2931

30-
const decoderPackageName =
32+
let decoderPackageName =
3133
packageName && packageName.trim()
3234
? `carbon-${kebabCase(packageName)}-decoder`
3335
: programName && programName.trim()
3436
? `carbon-${kebabCase(programName)}-decoder`
3537
: 'carbon-decoder';
3638

39+
if (versionName && versionName.trim()) {
40+
decoderPackageName = `${decoderPackageName}-${kebabCase(versionName)}`;
41+
}
42+
3743
const carbonCoreDep = getCrateDependencyString('carbon-core', VERSIONS['carbon-core'], ['macros']);
3844
const carbonTestUtilsDep = getCrateDependencyString('carbon-test-utils', VERSIONS['carbon-test-utils']);
3945
const borshDep = getCrateDependencyString('borsh', VERSIONS['borsh'], ['derive']);

packages/renderer/src/getRenderMapVisitor.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ export type GetRenderMapOptions = {
4141
withSerde?: boolean;
4242
withBase58?: boolean;
4343
standalone?: boolean;
44+
versionName?: string;
4445
};
4546

4647
type FlattenedField = {
@@ -892,6 +893,7 @@ export function getRenderMapVisitor(options: GetRenderMapOptions = {}) {
892893
packageName: options.packageName,
893894
programName: programName,
894895
originalProgramName: originalProgramName, // Pass original program name for token-2022 checks
896+
versionName: options.versionName,
895897
withPostgres: options.withPostgres !== false,
896898
withGraphQL: options.withGraphql !== false,
897899
withSerde: options.withSerde ?? false,

packages/renderer/src/renderVisitor.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ export type RenderOptions = GetRenderMapOptions & {
1010
discriminator: number[];
1111
}[];
1212
postgresMode?: 'generic' | 'typed';
13+
versionName?: string;
1314
};
1415

1516
export function renderVisitor(path: string, options: RenderOptions = {}) {

0 commit comments

Comments
 (0)