@secondlayer/subgraphs 3.22.0 → 3.23.1
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/dist/src/index.d.ts +118 -9
- package/dist/src/index.js +159 -14
- package/dist/src/index.js.map +7 -6
- package/dist/src/runtime/block-processor.d.ts +43 -2
- package/dist/src/runtime/block-processor.js +155 -12
- package/dist/src/runtime/block-processor.js.map +7 -6
- package/dist/src/runtime/catchup.d.ts +43 -2
- package/dist/src/runtime/catchup.js +155 -12
- package/dist/src/runtime/catchup.js.map +7 -6
- package/dist/src/runtime/context.d.ts +77 -3
- package/dist/src/runtime/context.js +142 -1
- package/dist/src/runtime/context.js.map +6 -5
- package/dist/src/runtime/processor.js +159 -16
- package/dist/src/runtime/processor.js.map +7 -6
- package/dist/src/runtime/reindex.d.ts +43 -2
- package/dist/src/runtime/reindex.js +155 -12
- package/dist/src/runtime/reindex.js.map +7 -6
- package/dist/src/runtime/reorg.d.ts +43 -2
- package/dist/src/runtime/reorg.js +155 -12
- package/dist/src/runtime/reorg.js.map +7 -6
- package/dist/src/runtime/replay.d.ts +3 -3
- package/dist/src/runtime/replay.js +2 -1
- package/dist/src/runtime/replay.js.map +3 -3
- package/dist/src/runtime/runner.d.ts +102 -5
- package/dist/src/runtime/source-matcher.d.ts +2 -2
- package/dist/src/runtime/stats.d.ts +3 -3
- package/dist/src/schema/index.d.ts +46 -5
- package/dist/src/service.js +159 -16
- package/dist/src/service.js.map +7 -6
- package/dist/src/testing/index.d.ts +52 -5
- package/dist/src/testing/index.js +172 -6
- package/dist/src/testing/index.js.map +6 -5
- package/dist/src/triggers/index.d.ts +2 -2
- package/dist/src/types.d.ts +43 -2
- package/dist/src/validate.d.ts +43 -2
- package/package.json +6 -4
package/dist/src/index.d.ts
CHANGED
|
@@ -1,4 +1,91 @@
|
|
|
1
|
-
import { AbiContract } from "@secondlayer/stacks/clarity";
|
|
1
|
+
import { AbiContract as AbiContract2 } from "@secondlayer/stacks/clarity";
|
|
2
|
+
import { UnwrapResponse } from "@secondlayer/stacks/actions";
|
|
3
|
+
import { AbiContract, ExtractFunctionArgs, ExtractFunctionOutput, ExtractReadOnlyFunctions, ToCamelCase } from "@secondlayer/stacks/clarity";
|
|
4
|
+
/**
|
|
5
|
+
* How long a read stays valid.
|
|
6
|
+
*
|
|
7
|
+
* - `per-block` (default): pinned to the block being processed, cached under
|
|
8
|
+
* its `index_block_hash`. Sound for any read-only function — reindexing the
|
|
9
|
+
* same block always returns the same value, and a reorg-replaced block has a
|
|
10
|
+
* different id so it can't inherit the orphaned answer.
|
|
11
|
+
* - `contract-constant`: the caller asserts the value cannot change for this
|
|
12
|
+
* contract and these args (SIP-010 `get-decimals`/`get-symbol`), so it is
|
|
13
|
+
* resolved ONCE instead of once per block. This is the mode that makes a
|
|
14
|
+
* backfill affordable — under `per-block` every block is a distinct key and
|
|
15
|
+
* therefore a distinct RPC. Declaring it on a value that does change pins the
|
|
16
|
+
* first answer forever, so it is opt-in and never inferred.
|
|
17
|
+
*/
|
|
18
|
+
type ChainReadCacheMode = "per-block" | "contract-constant";
|
|
19
|
+
type ChainReadOptions = {
|
|
20
|
+
cache?: ChainReadCacheMode
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* The chain-read surface, fully typed by an ABI.
|
|
24
|
+
*
|
|
25
|
+
* Shaped like `getContract` from `@secondlayer/stacks` — the same ABI in, the
|
|
26
|
+
* same camelCased read methods out — minus `call`/`buildCall`: a handler
|
|
27
|
+
* indexes the chain, it never writes to it.
|
|
28
|
+
*
|
|
29
|
+
* Reached through {@link readContractAt} rather than off `ctx.client`
|
|
30
|
+
* directly: an ABI-generic method living on the context interface makes every
|
|
31
|
+
* structural comparison of a subgraph definition instantiate it, which exceeds
|
|
32
|
+
* TS's depth budget. At a call site the ABI is already concrete and it costs
|
|
33
|
+
* nothing.
|
|
34
|
+
*/
|
|
35
|
+
interface ChainReadClient {
|
|
36
|
+
/**
|
|
37
|
+
* Read-only methods of a contract, evaluated against the exact block being
|
|
38
|
+
* processed.
|
|
39
|
+
*
|
|
40
|
+
* Every call is pinned to the block's `index_block_hash`, so a handler is a
|
|
41
|
+
* pure function of its block: reindexing a range produces byte-identical
|
|
42
|
+
* rows. Reading at the node's tip instead would make the same reindex
|
|
43
|
+
* return different values — that is the corruption this refuses to allow,
|
|
44
|
+
* so a block whose id was never persisted throws rather than falling back.
|
|
45
|
+
*
|
|
46
|
+
* Results are memoized in Postgres, so a value is fetched once per block
|
|
47
|
+
* rather than once per event (or once per contract with
|
|
48
|
+
* `cache: "contract-constant"`).
|
|
49
|
+
*/
|
|
50
|
+
contract<const C extends AbiContract>(contractId: string, abi: C, options?: ChainReadOptions): {
|
|
51
|
+
read: ChainReadMethods<C>
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* The camelCased read methods of an ABI. Spelled out rather than reused from
|
|
56
|
+
* `ContractInstance<C>["read"]`: that type is a conditional over the codegen
|
|
57
|
+
* brand, and evaluating it wherever a subgraph definition is structurally
|
|
58
|
+
* compared pushes TS past its instantiation depth.
|
|
59
|
+
*/
|
|
60
|
+
type ChainReadMethods<C extends AbiContract> = { [N in ExtractReadOnlyFunctions<C> as ToCamelCase<N>] : (args: ExtractFunctionArgs<C, N>) => Promise<UnwrapResponse<ExtractFunctionOutput<C, N>>> };
|
|
61
|
+
/**
|
|
62
|
+
* `ChainReadClient` with the ABI generic erased. Structurally cheap, so the
|
|
63
|
+
* base `SubgraphContext` can carry it; `TypedSubgraphContext` uses the real
|
|
64
|
+
* one and handlers get full inference.
|
|
65
|
+
*/
|
|
66
|
+
interface ErasedChainReadClient {
|
|
67
|
+
contract(contractId: string, abi: unknown, options?: ChainReadOptions): {
|
|
68
|
+
read: Record<string, (args?: Record<string, unknown>) => Promise<unknown>>
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Typed read methods for one contract, from a handler.
|
|
73
|
+
*
|
|
74
|
+
* ```ts
|
|
75
|
+
* const token = readContractAt(ctx, contractId, SIP010_ABI, {
|
|
76
|
+
* cache: "contract-constant",
|
|
77
|
+
* });
|
|
78
|
+
* const decimals = await token.read.getDecimals({}); // bigint
|
|
79
|
+
* ```
|
|
80
|
+
*/
|
|
81
|
+
declare function readContractAt<const C extends AbiContract>(ctx: {
|
|
82
|
+
client: ErasedChainReadClient
|
|
83
|
+
}, contractId: string, abi: C, options?: ChainReadOptions): {
|
|
84
|
+
read: ChainReadMethods<C>
|
|
85
|
+
};
|
|
86
|
+
declare class ChainReadError extends Error {
|
|
87
|
+
name: string;
|
|
88
|
+
}
|
|
2
89
|
/** Supported column types for subgraph schemas */
|
|
3
90
|
type ColumnType = "text" | "uint" | "int" | "principal" | "boolean" | "timestamp" | "jsonb";
|
|
4
91
|
/** Column definition in a subgraph table */
|
|
@@ -216,7 +303,7 @@ interface ContractCallFilter extends TraitScope, FactoryScope {
|
|
|
216
303
|
* definition and used at runtime to decode args by name. Omit to keep
|
|
217
304
|
* `event.args` as a positional `unknown[]`.
|
|
218
305
|
*/
|
|
219
|
-
abi?:
|
|
306
|
+
abi?: AbiContract2;
|
|
220
307
|
}
|
|
221
308
|
interface ContractDeployFilter {
|
|
222
309
|
type: "contract_deploy";
|
|
@@ -287,8 +374,21 @@ interface SubgraphContext {
|
|
|
287
374
|
hash: string
|
|
288
375
|
timestamp: number
|
|
289
376
|
burnBlockHeight: number
|
|
377
|
+
/** Nakamoto StacksBlockId, when the block row carries one. */
|
|
378
|
+
indexBlockHash?: string | null
|
|
290
379
|
};
|
|
291
380
|
tx: TxMeta;
|
|
381
|
+
/**
|
|
382
|
+
* Read-only contract calls pinned to this block. Cached, so the same
|
|
383
|
+
* (contract, function, args) is fetched once per block rather than once
|
|
384
|
+
* per event.
|
|
385
|
+
*
|
|
386
|
+
* Erased here on purpose: this interface is compared structurally wherever
|
|
387
|
+
* a subgraph definition is passed around, and carrying an ABI-generic
|
|
388
|
+
* method pushes those comparisons past TS's instantiation depth. Handlers
|
|
389
|
+
* receive `TypedSubgraphContext`, which exposes the fully typed version.
|
|
390
|
+
*/
|
|
391
|
+
client: ErasedChainReadClient;
|
|
292
392
|
insert(table: string, row: Record<string, unknown>): void;
|
|
293
393
|
update(table: string, where: Record<string, unknown>, set: Record<string, unknown>): void;
|
|
294
394
|
upsert(table: string, key: Record<string, unknown>, row: Record<string, unknown>): void;
|
|
@@ -397,7 +497,7 @@ declare function backfillSubgraph(def: SubgraphDefinition, opts: {
|
|
|
397
497
|
}): Promise<{
|
|
398
498
|
processed: number
|
|
399
499
|
}>;
|
|
400
|
-
import { AbiContract as
|
|
500
|
+
import { AbiContract as AbiContract3, ExtractFunctionArgs as ExtractFunctionArgs2, ExtractFunctionNames } from "@secondlayer/stacks/clarity";
|
|
401
501
|
/** Maps a ColumnType string literal to its TypeScript equivalent */
|
|
402
502
|
type ColumnToTS<T extends string> = T extends "uint" | "int" ? bigint : T extends "text" | "principal" | "timestamp" ? string : T extends "boolean" ? boolean : T extends "jsonb" ? Record<string, unknown> : unknown;
|
|
403
503
|
/** Infer TS type for a single column definition, respecting nullable */
|
|
@@ -574,8 +674,17 @@ interface TypedSubgraphContext<S extends SubgraphSchema> {
|
|
|
574
674
|
hash: string
|
|
575
675
|
timestamp: number
|
|
576
676
|
burnBlockHeight: number
|
|
677
|
+
/** Nakamoto StacksBlockId, when the block row carries one. */
|
|
678
|
+
indexBlockHash?: string | null
|
|
577
679
|
};
|
|
578
680
|
tx: TxMeta;
|
|
681
|
+
/**
|
|
682
|
+
* Read-only contract calls pinned to this block. Erased here for the same
|
|
683
|
+
* reason as on `SubgraphContext`: an ABI-generic method on a structurally
|
|
684
|
+
* compared interface exceeds TS's instantiation budget. Wrap it with
|
|
685
|
+
* `readContractAt(ctx, id, abi)` for full ABI typing at the call site.
|
|
686
|
+
*/
|
|
687
|
+
client: ErasedChainReadClient;
|
|
579
688
|
insert<T extends TableName<S>>(table: T, row: WriteRow<S[T]>): void;
|
|
580
689
|
update<T extends TableName<S>>(table: T, where: RowWhere<S, T>, set: Partial<WriteRow<S[T]>>): void;
|
|
581
690
|
upsert<
|
|
@@ -690,10 +799,10 @@ interface ContractDeployPayload {
|
|
|
690
799
|
* present for back-compat. Without an `abi`, the payload is {@link ContractCallEvent}.
|
|
691
800
|
*/
|
|
692
801
|
type ContractCallPayload<F> = F extends {
|
|
693
|
-
abi: infer A extends
|
|
802
|
+
abi: infer A extends AbiContract3
|
|
694
803
|
functionName: infer N extends string
|
|
695
804
|
} ? N extends ExtractFunctionNames<A> ? ContractCallEvent & {
|
|
696
|
-
input:
|
|
805
|
+
input: ExtractFunctionArgs2<A, N>
|
|
697
806
|
} : ContractCallEvent : ContractCallEvent;
|
|
698
807
|
/**
|
|
699
808
|
* Print event typed per topic. When a `print_event` source declares a `prints`
|
|
@@ -963,10 +1072,10 @@ declare function generateIndexSchema(target: IndexCodegenTarget, opts?: IndexCod
|
|
|
963
1072
|
/** Index tables available to codegen, for help text and validation. */
|
|
964
1073
|
declare const INDEX_CODEGEN_TABLES: readonly string[];
|
|
965
1074
|
import { pgSchemaName } from "@secondlayer/shared/db/queries/subgraphs";
|
|
966
|
-
import { Database } from "@secondlayer/shared/db";
|
|
1075
|
+
import { Database as Database2 } from "@secondlayer/shared/db";
|
|
967
1076
|
import { ByoBreakingChangeDetails } from "@secondlayer/shared/errors";
|
|
968
|
-
import { Kysely } from "kysely";
|
|
969
|
-
type AnyDb =
|
|
1077
|
+
import { Kysely as Kysely2 } from "kysely";
|
|
1078
|
+
type AnyDb = Kysely2<Database2>;
|
|
970
1079
|
interface TableDiff {
|
|
971
1080
|
/** Tables added to the schema */
|
|
972
1081
|
addedTables: string[];
|
|
@@ -1100,4 +1209,4 @@ type SparseProbeTarget = {
|
|
|
1100
1209
|
declare function canSparseScan(subgraph: SubgraphDefinition): boolean;
|
|
1101
1210
|
/** Probe targets for a subgraph's filters: decoded type + contract scope when\\n* the filter pins one (assetIdentifier "SP….contract::asset" or contractId). */
|
|
1102
1211
|
declare function sparseProbeTargets(subgraph: SubgraphDefinition): SparseProbeTarget[];
|
|
1103
|
-
export { validateSubgraphDefinition, validatePrintPayload, sparseProbeTargets, resumeReindex, renderDeployPlan, reindexSubgraph, pgSchemaName, inferPrintTopics, hasBreakingChanges, generateSubgraphSQL, generatePrismaSchema, generateKyselySchema, generateIndexSchema, generateDrizzleSchema, filterFieldsByType, diffSchema, deploySchema, defineSubgraph, defineSchema, canSparseScan, camelizeDataKey, backfillSubgraph, WriteRow, WhereInput, TypedSubgraphDefinition, TypedSubgraphContext, TypedHandlers, TxMeta, TableDiff, SystemRow, SubscribeOptions, SubgraphTableClient, SubgraphTable, SubgraphSchema, SubgraphHandler, SubgraphFilter, SubgraphDefinition, SubgraphContext, SubgraphColumn, StxTransferPayload, StxTransferFilter, StxMintPayload, StxMintFilter, StxLockPayload, StxLockFilter, StxBurnPayload, StxBurnFilter, SparseProbeTarget, RowValue, ReindexOptions, PrismaGenOptions, PrintValidationResult, PrintSample, PrintFieldToTS, PrintField, PrintEventPayload, PrintEventFor, PrintEventFilter, PrintDataOf, NftTransferPayload, NftTransferFilter, NftMintPayload, NftMintFilter, NftBurnPayload, NftBurnFilter, KyselyGenOptions, InferredTopicSchema, InferredPrintField, InferTableRow, InferSubgraphClient, InferContext, InferColumnType, IndexCodegenTarget, IndexCodegenOptions, INDEX_CODEGEN_TABLES, GeneratedSQL, FtTransferPayload, FtTransferFilter, FtMintPayload, FtMintFilter, FtBurnPayload, FtBurnFilter, FindManyOptions, FactoryScope, EventForFilter, DrizzleGenOptions, DeployPlan, DeployDiff, ContractDeployPayload, ContractDeployFilter, ContractCallPayload, ContractCallFilter, ContractCallEvent, ComputedValue, ComparisonFilter, ColumnType, ColumnToTS, ColumnDiff, ByoMigrationPlan, ByoBreakingChangeError, AnyEvent, AggregateSpec, AggregateResult };
|
|
1212
|
+
export { validateSubgraphDefinition, validatePrintPayload, sparseProbeTargets, resumeReindex, renderDeployPlan, reindexSubgraph, readContractAt, pgSchemaName, inferPrintTopics, hasBreakingChanges, generateSubgraphSQL, generatePrismaSchema, generateKyselySchema, generateIndexSchema, generateDrizzleSchema, filterFieldsByType, diffSchema, deploySchema, defineSubgraph, defineSchema, canSparseScan, camelizeDataKey, backfillSubgraph, WriteRow, WhereInput, TypedSubgraphDefinition, TypedSubgraphContext, TypedHandlers, TxMeta, TableDiff, SystemRow, SubscribeOptions, SubgraphTableClient, SubgraphTable, SubgraphSchema, SubgraphHandler, SubgraphFilter, SubgraphDefinition, SubgraphContext, SubgraphColumn, StxTransferPayload, StxTransferFilter, StxMintPayload, StxMintFilter, StxLockPayload, StxLockFilter, StxBurnPayload, StxBurnFilter, SparseProbeTarget, RowValue, ReindexOptions, PrismaGenOptions, PrintValidationResult, PrintSample, PrintFieldToTS, PrintField, PrintEventPayload, PrintEventFor, PrintEventFilter, PrintDataOf, NftTransferPayload, NftTransferFilter, NftMintPayload, NftMintFilter, NftBurnPayload, NftBurnFilter, KyselyGenOptions, InferredTopicSchema, InferredPrintField, InferTableRow, InferSubgraphClient, InferContext, InferColumnType, IndexCodegenTarget, IndexCodegenOptions, INDEX_CODEGEN_TABLES, GeneratedSQL, FtTransferPayload, FtTransferFilter, FtMintPayload, FtMintFilter, FtBurnPayload, FtBurnFilter, FindManyOptions, FactoryScope, EventForFilter, ErasedChainReadClient, DrizzleGenOptions, DeployPlan, DeployDiff, ContractDeployPayload, ContractDeployFilter, ContractCallPayload, ContractCallFilter, ContractCallEvent, ComputedValue, ComparisonFilter, ColumnType, ColumnToTS, ColumnDiff, ChainReadOptions, ChainReadMethods, ChainReadError, ChainReadClient, ChainReadCacheMode, ByoMigrationPlan, ByoBreakingChangeError, AnyEvent, AggregateSpec, AggregateResult };
|
package/dist/src/index.js
CHANGED
|
@@ -360,6 +360,139 @@ function generateSubgraphSQL(def, schemaNameOverride) {
|
|
|
360
360
|
return { statements, hash };
|
|
361
361
|
}
|
|
362
362
|
|
|
363
|
+
// src/runtime/chain-read.ts
|
|
364
|
+
import { getSourceDb } from "@secondlayer/shared/db";
|
|
365
|
+
import { http, createPublicClient } from "@secondlayer/stacks";
|
|
366
|
+
import {
|
|
367
|
+
ContractResponseError,
|
|
368
|
+
buildFunctionArgs,
|
|
369
|
+
isResponseOutput,
|
|
370
|
+
readContract
|
|
371
|
+
} from "@secondlayer/stacks/actions";
|
|
372
|
+
import {
|
|
373
|
+
clarityValueToJSUntyped,
|
|
374
|
+
deserializeCV,
|
|
375
|
+
normalizeAbi,
|
|
376
|
+
serializeCV,
|
|
377
|
+
toCamelCase
|
|
378
|
+
} from "@secondlayer/stacks/clarity";
|
|
379
|
+
function readContractAt(ctx, contractId, abi, options) {
|
|
380
|
+
return ctx.client.contract(contractId, abi, options);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
class ChainReadError extends Error {
|
|
384
|
+
name = "ChainReadError";
|
|
385
|
+
}
|
|
386
|
+
function nodeClient(rpcUrl) {
|
|
387
|
+
return createPublicClient({
|
|
388
|
+
transport: http(rpcUrl, { retryCount: 0, timeout: 1e4 })
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
class ConcurrencyGate {
|
|
393
|
+
limit;
|
|
394
|
+
active = 0;
|
|
395
|
+
queue = [];
|
|
396
|
+
constructor(limit) {
|
|
397
|
+
this.limit = limit;
|
|
398
|
+
}
|
|
399
|
+
async run(fn) {
|
|
400
|
+
if (this.active >= this.limit) {
|
|
401
|
+
await new Promise((resolve) => this.queue.push(resolve));
|
|
402
|
+
}
|
|
403
|
+
this.active++;
|
|
404
|
+
try {
|
|
405
|
+
return await fn();
|
|
406
|
+
} finally {
|
|
407
|
+
this.active--;
|
|
408
|
+
this.queue.shift()?.();
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
var DEFAULT_MAX_CONCURRENT_READS = 4;
|
|
413
|
+
function maxConcurrentReads() {
|
|
414
|
+
const raw = process.env.SUBGRAPH_CHAIN_READ_CONCURRENCY;
|
|
415
|
+
const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
416
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_MAX_CONCURRENT_READS;
|
|
417
|
+
}
|
|
418
|
+
var gate = new ConcurrencyGate(maxConcurrentReads());
|
|
419
|
+
function hashArgs(args) {
|
|
420
|
+
const serialized = args.map((arg) => serializeCV(arg)).join(",");
|
|
421
|
+
return Bun.hash(serialized).toString(16);
|
|
422
|
+
}
|
|
423
|
+
function findFunction(abi, name) {
|
|
424
|
+
const fn = abi.functions.find((f) => f.name === name);
|
|
425
|
+
if (!fn) {
|
|
426
|
+
throw new ChainReadError(`No function "${name}" in the supplied ABI.`);
|
|
427
|
+
}
|
|
428
|
+
if (fn.access !== "read-only") {
|
|
429
|
+
throw new ChainReadError(`"${name}" is ${fn.access}, not read-only — a handler cannot call it.`);
|
|
430
|
+
}
|
|
431
|
+
return fn;
|
|
432
|
+
}
|
|
433
|
+
function createChainReadClient(ctx) {
|
|
434
|
+
const readValue = async (contractId, abi, functionName, args, options) => {
|
|
435
|
+
const rpcUrl = ctx.rpcUrl ?? process.env.STACKS_NODE_RPC_URL;
|
|
436
|
+
if (!rpcUrl) {
|
|
437
|
+
throw new ChainReadError("ctx.client needs a Stacks node: set STACKS_NODE_RPC_URL on the subgraph processor.");
|
|
438
|
+
}
|
|
439
|
+
const constant = options.cache === "contract-constant";
|
|
440
|
+
if (!constant && !ctx.indexBlockHash) {
|
|
441
|
+
throw new ChainReadError(`Block ${ctx.blockHeight} has no index_block_hash, so a read cannot be pinned to it. Reading at the node's tip would make this handler non-deterministic (the same reindex would produce different rows). Re-ingest the block, or pass cache: "contract-constant" if the value genuinely cannot change.`);
|
|
442
|
+
}
|
|
443
|
+
const fn = findFunction(abi, functionName);
|
|
444
|
+
const clarityArgs = buildFunctionArgs(fn, args);
|
|
445
|
+
const db = ctx.db ?? getSourceDb();
|
|
446
|
+
const argsHash = hashArgs(clarityArgs);
|
|
447
|
+
const pinnedTo = constant ? null : ctx.indexBlockHash;
|
|
448
|
+
const cached = await db.selectFrom("chain_read_cache").select("result_hex").where("contract_id", "=", contractId).where("function_name", "=", fn.name).where("args_hash", "=", argsHash).where((eb) => pinnedTo === null ? eb("index_block_hash", "is", null) : eb("index_block_hash", "=", pinnedTo)).executeTakeFirst();
|
|
449
|
+
const resultHex = cached?.result_hex ?? await gate.run(async () => {
|
|
450
|
+
const value2 = await readContract(nodeClient(rpcUrl), {
|
|
451
|
+
contract: contractId,
|
|
452
|
+
functionName: fn.name,
|
|
453
|
+
args: clarityArgs,
|
|
454
|
+
tip: ctx.indexBlockHash ?? undefined
|
|
455
|
+
});
|
|
456
|
+
return serializeCV(value2);
|
|
457
|
+
});
|
|
458
|
+
if (!cached) {
|
|
459
|
+
await db.insertInto("chain_read_cache").values({
|
|
460
|
+
contract_id: contractId,
|
|
461
|
+
function_name: fn.name,
|
|
462
|
+
args_hash: argsHash,
|
|
463
|
+
index_block_hash: pinnedTo,
|
|
464
|
+
block_height: constant ? null : ctx.blockHeight,
|
|
465
|
+
result_hex: resultHex
|
|
466
|
+
}).onConflict((oc) => oc.doNothing()).execute();
|
|
467
|
+
}
|
|
468
|
+
const value = deserializeCV(resultHex);
|
|
469
|
+
const js = clarityValueToJSUntyped(fn.outputs, value);
|
|
470
|
+
if (isResponseOutput(fn.outputs)) {
|
|
471
|
+
const response = js;
|
|
472
|
+
if ("ok" in response)
|
|
473
|
+
return response.ok;
|
|
474
|
+
throw new ContractResponseError(`${contractId}::${fn.name} returned an err response.`, response.err);
|
|
475
|
+
}
|
|
476
|
+
return js;
|
|
477
|
+
};
|
|
478
|
+
return {
|
|
479
|
+
contract(contractId, rawAbi, options = {}) {
|
|
480
|
+
const abi = normalizeAbi(rawAbi);
|
|
481
|
+
const camelToKebab = new Map;
|
|
482
|
+
for (const fn of abi.functions) {
|
|
483
|
+
camelToKebab.set(toCamelCase(fn.name), fn.name);
|
|
484
|
+
}
|
|
485
|
+
const read = new Proxy({}, {
|
|
486
|
+
get(_target, prop) {
|
|
487
|
+
const fnName = camelToKebab.get(prop) ?? prop;
|
|
488
|
+
return (args = {}) => readValue(contractId, abi, fnName, args, options);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
return { read };
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
}
|
|
495
|
+
|
|
363
496
|
// src/runtime/context.ts
|
|
364
497
|
var JOURNAL_RETENTION_BLOCKS = 300;
|
|
365
498
|
var journalEnsured = new Set;
|
|
@@ -378,6 +511,7 @@ class SubgraphContext {
|
|
|
378
511
|
ops = [];
|
|
379
512
|
byo;
|
|
380
513
|
journal;
|
|
514
|
+
_client;
|
|
381
515
|
constructor(db, pgSchemaName2, subgraphSchema, block, tx, byo = false, journal = false) {
|
|
382
516
|
this.db = db;
|
|
383
517
|
this.pgSchemaName = pgSchemaName2;
|
|
@@ -387,6 +521,13 @@ class SubgraphContext {
|
|
|
387
521
|
this.byo = byo;
|
|
388
522
|
this.journal = journal;
|
|
389
523
|
}
|
|
524
|
+
get client() {
|
|
525
|
+
this._client ??= createChainReadClient({
|
|
526
|
+
blockHeight: this.block.height,
|
|
527
|
+
indexBlockHash: this.block.indexBlockHash
|
|
528
|
+
});
|
|
529
|
+
return this._client;
|
|
530
|
+
}
|
|
390
531
|
get tx() {
|
|
391
532
|
return this._tx;
|
|
392
533
|
}
|
|
@@ -948,11 +1089,11 @@ function buildWhereClause(where) {
|
|
|
948
1089
|
}
|
|
949
1090
|
|
|
950
1091
|
// src/runtime/clarity.ts
|
|
951
|
-
import { cvToValue, deserializeCV } from "@secondlayer/stacks/clarity";
|
|
1092
|
+
import { cvToValue, deserializeCV as deserializeCV2 } from "@secondlayer/stacks/clarity";
|
|
952
1093
|
function decodeClarityValue(hex) {
|
|
953
1094
|
try {
|
|
954
1095
|
const cleanHex = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
955
|
-
const cv =
|
|
1096
|
+
const cv = deserializeCV2(cleanHex);
|
|
956
1097
|
return cvToValue(cv);
|
|
957
1098
|
} catch {
|
|
958
1099
|
return hex;
|
|
@@ -983,8 +1124,8 @@ import { getErrorMessage } from "@secondlayer/shared";
|
|
|
983
1124
|
import { logger as logger2 } from "@secondlayer/shared/logger";
|
|
984
1125
|
import {
|
|
985
1126
|
clarityValueToJS,
|
|
986
|
-
deserializeCV as
|
|
987
|
-
toCamelCase
|
|
1127
|
+
deserializeCV as deserializeCV3,
|
|
1128
|
+
toCamelCase as toCamelCase2
|
|
988
1129
|
} from "@secondlayer/stacks/clarity";
|
|
989
1130
|
|
|
990
1131
|
// src/runtime/print-validate.ts
|
|
@@ -1147,7 +1288,7 @@ function buildContractCallInput(filter, tx) {
|
|
|
1147
1288
|
return;
|
|
1148
1289
|
try {
|
|
1149
1290
|
const clean = hex.startsWith("0x") ? hex.slice(2) : hex;
|
|
1150
|
-
input[
|
|
1291
|
+
input[toCamelCase2(arg.name)] = decodeArg(arg.type, deserializeCV3(clean));
|
|
1151
1292
|
} catch {}
|
|
1152
1293
|
});
|
|
1153
1294
|
return input;
|
|
@@ -1701,7 +1842,7 @@ import { logger as logger5 } from "@secondlayer/shared/logger";
|
|
|
1701
1842
|
import { sql as sql3 } from "kysely";
|
|
1702
1843
|
|
|
1703
1844
|
// src/runtime/block-source.ts
|
|
1704
|
-
import { getSourceDb } from "@secondlayer/shared/db";
|
|
1845
|
+
import { getSourceDb as getSourceDb2 } from "@secondlayer/shared/db";
|
|
1705
1846
|
import {
|
|
1706
1847
|
IndexHttpClient
|
|
1707
1848
|
} from "@secondlayer/shared/index-http";
|
|
@@ -1762,6 +1903,7 @@ function reconstructBlock(b) {
|
|
|
1762
1903
|
parent_hash: b.parent_hash,
|
|
1763
1904
|
burn_block_height: b.burn_block_height,
|
|
1764
1905
|
burn_block_hash: b.burn_block_hash,
|
|
1906
|
+
index_block_hash: b.index_block_hash ?? null,
|
|
1765
1907
|
timestamp: isoToUnixSeconds(b.block_time),
|
|
1766
1908
|
canonical: true,
|
|
1767
1909
|
created_at: new Date(0)
|
|
@@ -1896,11 +2038,11 @@ function sparseProbeTargets(subgraph) {
|
|
|
1896
2038
|
|
|
1897
2039
|
class PostgresBlockSource {
|
|
1898
2040
|
async getTip() {
|
|
1899
|
-
const progress = await
|
|
2041
|
+
const progress = await getSourceDb2().selectFrom("index_progress").selectAll().where("network", "=", process.env.NETWORK ?? "mainnet").executeTakeFirst();
|
|
1900
2042
|
return progress ? Number(progress.highest_seen_block) : 0;
|
|
1901
2043
|
}
|
|
1902
2044
|
loadBlockRange(fromHeight, toHeight) {
|
|
1903
|
-
return loadBlockRange(
|
|
2045
|
+
return loadBlockRange(getSourceDb2(), fromHeight, toHeight);
|
|
1904
2046
|
}
|
|
1905
2047
|
}
|
|
1906
2048
|
var EVENT_FILTER_TO_INDEX_TYPE = {
|
|
@@ -1969,8 +2111,8 @@ class PublicApiBlockSource {
|
|
|
1969
2111
|
eventTypes;
|
|
1970
2112
|
probeTargets;
|
|
1971
2113
|
needsTransactions;
|
|
1972
|
-
constructor(
|
|
1973
|
-
this.http =
|
|
2114
|
+
constructor(http2, eventTypes, probeTargets, needsTransactions = true) {
|
|
2115
|
+
this.http = http2;
|
|
1974
2116
|
this.eventTypes = eventTypes;
|
|
1975
2117
|
this.probeTargets = probeTargets;
|
|
1976
2118
|
this.needsTransactions = needsTransactions;
|
|
@@ -2482,7 +2624,8 @@ async function processBlock(subgraph, subgraphName, blockHeight, opts) {
|
|
|
2482
2624
|
height: block.height,
|
|
2483
2625
|
hash: block.hash,
|
|
2484
2626
|
timestamp: block.timestamp,
|
|
2485
|
-
burnBlockHeight: block.burn_block_height
|
|
2627
|
+
burnBlockHeight: block.burn_block_height,
|
|
2628
|
+
indexBlockHash: block.index_block_hash
|
|
2486
2629
|
};
|
|
2487
2630
|
const initialTx = {
|
|
2488
2631
|
txId: "",
|
|
@@ -3260,7 +3403,7 @@ function defineSchema(schema) {
|
|
|
3260
3403
|
}
|
|
3261
3404
|
// src/print-schema.ts
|
|
3262
3405
|
import {
|
|
3263
|
-
deserializeCV as
|
|
3406
|
+
deserializeCV as deserializeCV4
|
|
3264
3407
|
} from "@secondlayer/stacks/clarity";
|
|
3265
3408
|
function camelizeDataKey(str) {
|
|
3266
3409
|
return str.replace(/-([a-z0-9])/g, (_, c) => c.toUpperCase());
|
|
@@ -3528,7 +3671,7 @@ function inferPrintTopics(samples) {
|
|
|
3528
3671
|
let decoded = 0;
|
|
3529
3672
|
for (const p of picked) {
|
|
3530
3673
|
try {
|
|
3531
|
-
const cv =
|
|
3674
|
+
const cv = deserializeCV4(p.rawHex);
|
|
3532
3675
|
decoded++;
|
|
3533
3676
|
if (cv.type === "tuple")
|
|
3534
3677
|
tuples.push(cv);
|
|
@@ -4527,6 +4670,7 @@ export {
|
|
|
4527
4670
|
resumeReindex,
|
|
4528
4671
|
renderDeployPlan,
|
|
4529
4672
|
reindexSubgraph,
|
|
4673
|
+
readContractAt,
|
|
4530
4674
|
pgSchemaName,
|
|
4531
4675
|
inferPrintTopics,
|
|
4532
4676
|
hasBreakingChanges,
|
|
@@ -4544,8 +4688,9 @@ export {
|
|
|
4544
4688
|
camelizeDataKey,
|
|
4545
4689
|
backfillSubgraph,
|
|
4546
4690
|
INDEX_CODEGEN_TABLES,
|
|
4691
|
+
ChainReadError,
|
|
4547
4692
|
ByoBreakingChangeError
|
|
4548
4693
|
};
|
|
4549
4694
|
|
|
4550
|
-
//# debugId=
|
|
4695
|
+
//# debugId=A7D0F6BE4A80734464756E2164756E21
|
|
4551
4696
|
//# sourceMappingURL=index.js.map
|