@secondlayer/subgraphs 7.1.0 → 7.3.0
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/index.d.ts +16 -2
- package/dist/index.js +198 -128
- package/dist/index.js.map +7 -7
- package/dist/runtime/replay.js +279 -26
- package/dist/runtime/replay.js.map +10 -8
- package/dist/schema/index.d.ts +6 -0
- package/dist/schema/index.js +6 -4
- package/dist/schema/index.js.map +3 -3
- package/dist/testing/index.d.ts +7 -0
- package/dist/testing/index.js +1738 -1631
- package/dist/testing/index.js.map +9 -9
- package/dist/types.d.ts +6 -0
- package/dist/validate.d.ts +6 -0
- package/dist/validate.js +3 -1
- package/dist/validate.js.map +3 -3
- package/dist/verification.d.ts +6 -0
- package/dist/verification.js +4 -4
- package/dist/verification.js.map +1 -1
- package/dist/verify/index.d.ts +6 -0
- package/dist/verify/index.js +138 -41
- package/dist/verify/index.js.map +6 -6
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -108,6 +108,12 @@ declare function readContractAt<const C extends AbiContract>(ctx: {
|
|
|
108
108
|
}, contractId: string, abi: C, options?: ChainReadOptions): {
|
|
109
109
|
read: ChainReadMethods<C>;
|
|
110
110
|
};
|
|
111
|
+
/**
|
|
112
|
+
* A handler asked for something no node can answer: a function missing from
|
|
113
|
+
* the ABI, or one that isn't read-only. Deterministic, so it fails only the
|
|
114
|
+
* event. A read the processor couldn't complete (no node, node down, cache
|
|
115
|
+
* error) throws {@link HandlerReadError} instead and aborts the block.
|
|
116
|
+
*/
|
|
111
117
|
declare class ChainReadError extends Error {
|
|
112
118
|
name: string;
|
|
113
119
|
}
|
|
@@ -390,6 +396,12 @@ type PrintEventPrints = Record<string, Record<string, PrintField>>;
|
|
|
390
396
|
type PrintEventFields = FactoryScope & {
|
|
391
397
|
type: "print_event";
|
|
392
398
|
topic?: string;
|
|
399
|
+
/** Equality on top-level keys of the decoded print tuple, spelled as the
|
|
400
|
+
* contract prints them (`signer-manager`). A value matches the field's
|
|
401
|
+
* JSON form: principals `SP…`, uints as decimal strings, bools
|
|
402
|
+
* `"true"`/`"false"`, buffers `0x…`. An array is any-of; keys AND.
|
|
403
|
+
* Nested tuples and lists never match. */
|
|
404
|
+
where?: Record<string, string | readonly string[]>;
|
|
393
405
|
};
|
|
394
406
|
/**
|
|
395
407
|
* Pinned to one or more contract ids with no trait — `prints` is required so
|
|
@@ -1200,9 +1212,11 @@ declare class NondeterminismError extends Error {
|
|
|
1200
1212
|
name: string;
|
|
1201
1213
|
}
|
|
1202
1214
|
/**
|
|
1203
|
-
* A ctx read
|
|
1215
|
+
* A ctx read failed for a reason outside the handler: the database under a
|
|
1216
|
+
* verifiable subgraph, or a chain read (`ctx.client`) on any subgraph that
|
|
1217
|
+
* had no node, a node that didn't answer, or a cache error. Infra-class like
|
|
1204
1218
|
* {@link NondeterminismError}: skipping the event would make its rows depend
|
|
1205
|
-
* on whether the database was reachable.
|
|
1219
|
+
* on whether the database or node was reachable.
|
|
1206
1220
|
*/
|
|
1207
1221
|
declare class HandlerReadError extends Error {
|
|
1208
1222
|
name: string;
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@ function defineSchema(schema) {
|
|
|
6
6
|
return schema;
|
|
7
7
|
}
|
|
8
8
|
// src/validate.ts
|
|
9
|
+
import { PrintWhereSchema } from "@secondlayer/shared/schemas/webhooks";
|
|
9
10
|
import { z } from "zod";
|
|
10
11
|
var SubgraphNameSchema = z.string().min(1).max(63).regex(/^[a-z][a-z0-9-]*$/, "Must start with lowercase letter, contain only lowercase alphanumeric and hyphens");
|
|
11
12
|
var SqlIdentifierSchema = z.string().min(1).max(63).regex(/^[a-z_][a-z0-9_]*$/i, "Must be a valid SQL identifier: start with a letter or underscore, then letters/digits/underscores only");
|
|
@@ -182,6 +183,7 @@ var SubgraphFilterUnion = z.discriminatedUnion("type", [
|
|
|
182
183
|
type: z.literal("print_event"),
|
|
183
184
|
contractId: contractIdField.optional(),
|
|
184
185
|
topic: z.string().optional(),
|
|
186
|
+
where: PrintWhereSchema.optional(),
|
|
185
187
|
prints: z.record(z.string(), z.record(z.string(), PrintFieldSchema)).optional(),
|
|
186
188
|
...traitScope,
|
|
187
189
|
...factoryScope
|
|
@@ -646,9 +648,17 @@ function inferPrintTopics(samples) {
|
|
|
646
648
|
return out.sort((a, b) => b.count - a.count);
|
|
647
649
|
}
|
|
648
650
|
// src/runtime/chain-read.ts
|
|
651
|
+
import { getErrorMessage } from "@secondlayer/shared";
|
|
652
|
+
import { chainReadArgsHash } from "@secondlayer/shared/chain-read";
|
|
649
653
|
import { getSourceDb } from "@secondlayer/shared/db";
|
|
654
|
+
import { hasNoLocalChain } from "@secondlayer/shared/db/queries/instance";
|
|
650
655
|
import { logger } from "@secondlayer/shared/logger";
|
|
651
|
-
import {
|
|
656
|
+
import {
|
|
657
|
+
http,
|
|
658
|
+
HttpRequestError,
|
|
659
|
+
ReadContractError,
|
|
660
|
+
createPublicClient
|
|
661
|
+
} from "@secondlayer/stacks";
|
|
652
662
|
import {
|
|
653
663
|
ContractResponseError,
|
|
654
664
|
buildFunctionArgs,
|
|
@@ -662,136 +672,16 @@ import {
|
|
|
662
672
|
serializeCV,
|
|
663
673
|
toCamelCase
|
|
664
674
|
} from "@secondlayer/stacks/clarity";
|
|
665
|
-
function readContractAt(ctx, contractId, abi, options) {
|
|
666
|
-
return ctx.client.contract(contractId, abi, options);
|
|
667
|
-
}
|
|
668
675
|
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
}
|
|
672
|
-
function nodeClient(rpcUrl) {
|
|
673
|
-
return createPublicClient({
|
|
674
|
-
transport: http(rpcUrl, { retryCount: 0, timeout: 1e4 })
|
|
675
|
-
});
|
|
676
|
-
}
|
|
676
|
+
// src/runtime/realm.ts
|
|
677
|
+
import vm from "node:vm";
|
|
677
678
|
|
|
678
|
-
class ConcurrencyGate {
|
|
679
|
-
limit;
|
|
680
|
-
active = 0;
|
|
681
|
-
queue = [];
|
|
682
|
-
constructor(limit) {
|
|
683
|
-
this.limit = limit;
|
|
684
|
-
}
|
|
685
|
-
async run(fn) {
|
|
686
|
-
if (this.active >= this.limit) {
|
|
687
|
-
await new Promise((resolve) => this.queue.push(resolve));
|
|
688
|
-
}
|
|
689
|
-
this.active++;
|
|
690
|
-
try {
|
|
691
|
-
return await fn();
|
|
692
|
-
} finally {
|
|
693
|
-
this.active--;
|
|
694
|
-
this.queue.shift()?.();
|
|
695
|
-
}
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
var DEFAULT_MAX_CONCURRENT_READS = 4;
|
|
699
|
-
function maxConcurrentReads() {
|
|
700
|
-
const raw = process.env.SUBGRAPH_CHAIN_READ_CONCURRENCY;
|
|
701
|
-
const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
702
|
-
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_MAX_CONCURRENT_READS;
|
|
703
|
-
}
|
|
704
|
-
var gate = new ConcurrencyGate(maxConcurrentReads());
|
|
705
|
-
function hashArgs(args) {
|
|
706
|
-
const serialized = args.map((arg) => serializeCV(arg)).join(",");
|
|
707
|
-
return Bun.hash(serialized).toString(16);
|
|
708
|
-
}
|
|
709
|
-
function findFunction(abi, name) {
|
|
710
|
-
const fn = abi.functions.find((f) => f.name === name);
|
|
711
|
-
if (!fn) {
|
|
712
|
-
throw new ChainReadError(`No function "${name}" in the supplied ABI.`);
|
|
713
|
-
}
|
|
714
|
-
if (fn.access !== "read-only") {
|
|
715
|
-
throw new ChainReadError(`"${name}" is ${fn.access}, not read-only — a handler cannot call it.`);
|
|
716
|
-
}
|
|
717
|
-
return fn;
|
|
718
|
-
}
|
|
719
|
-
function createChainReadClient(ctx) {
|
|
720
|
-
const readValue = async (contractId, abi, functionName, args, options) => {
|
|
721
|
-
const rpcUrl = ctx.rpcUrl ?? process.env.STACKS_NODE_RPC_URL;
|
|
722
|
-
if (!rpcUrl) {
|
|
723
|
-
throw new ChainReadError("ctx.client needs a Stacks node: set STACKS_NODE_RPC_URL on the subgraph processor.");
|
|
724
|
-
}
|
|
725
|
-
const constant = options.cache === "contract-constant";
|
|
726
|
-
if (!constant && !ctx.indexBlockHash) {
|
|
727
|
-
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.`);
|
|
728
|
-
}
|
|
729
|
-
const fn = findFunction(abi, functionName);
|
|
730
|
-
const clarityArgs = buildFunctionArgs(fn, args);
|
|
731
|
-
const db = ctx.db ?? getSourceDb();
|
|
732
|
-
const argsHash = hashArgs(clarityArgs);
|
|
733
|
-
const pinnedTo = constant ? null : ctx.indexBlockHash;
|
|
734
|
-
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();
|
|
735
|
-
const resultHex = cached?.result_hex ?? await gate.run(async () => {
|
|
736
|
-
if (!ctx.indexBlockHash) {
|
|
737
|
-
logger.warn("Chain read is not pinned to a block (no index_block_hash)", {
|
|
738
|
-
contractId,
|
|
739
|
-
functionName: fn.name,
|
|
740
|
-
blockHeight: ctx.blockHeight,
|
|
741
|
-
note: 'cache: "contract-constant" asserts this value cannot change; the node answered at its own tip'
|
|
742
|
-
});
|
|
743
|
-
}
|
|
744
|
-
const value = await readContract(nodeClient(rpcUrl), {
|
|
745
|
-
contract: contractId,
|
|
746
|
-
functionName: fn.name,
|
|
747
|
-
args: clarityArgs,
|
|
748
|
-
tip: ctx.indexBlockHash ?? undefined
|
|
749
|
-
});
|
|
750
|
-
return serializeCV(value);
|
|
751
|
-
});
|
|
752
|
-
if (!cached) {
|
|
753
|
-
await db.insertInto("chain_read_cache").values({
|
|
754
|
-
contract_id: contractId,
|
|
755
|
-
function_name: fn.name,
|
|
756
|
-
args_hash: argsHash,
|
|
757
|
-
index_block_hash: pinnedTo,
|
|
758
|
-
block_height: constant ? null : ctx.blockHeight,
|
|
759
|
-
result_hex: resultHex
|
|
760
|
-
}).onConflict((oc) => oc.doNothing()).execute();
|
|
761
|
-
}
|
|
762
|
-
const value = deserializeCV2(resultHex);
|
|
763
|
-
const js = clarityValueToJSUntyped(fn.outputs, value);
|
|
764
|
-
if (isResponseOutput(fn.outputs)) {
|
|
765
|
-
const response = js;
|
|
766
|
-
if ("ok" in response)
|
|
767
|
-
return response.ok;
|
|
768
|
-
throw new ContractResponseError(`${contractId}::${fn.name} returned an err response.`, response.err);
|
|
769
|
-
}
|
|
770
|
-
return js;
|
|
771
|
-
};
|
|
772
|
-
return {
|
|
773
|
-
contract(contractId, rawAbi, options = {}) {
|
|
774
|
-
const abi = normalizeAbi(rawAbi);
|
|
775
|
-
const camelToKebab = new Map;
|
|
776
|
-
for (const fn of abi.functions) {
|
|
777
|
-
camelToKebab.set(toCamelCase(fn.name), fn.name);
|
|
778
|
-
}
|
|
779
|
-
const read = new Proxy({}, {
|
|
780
|
-
get(_target, prop) {
|
|
781
|
-
const fnName = camelToKebab.get(prop) ?? prop;
|
|
782
|
-
return (args = {}) => readValue(contractId, abi, fnName, args, options);
|
|
783
|
-
}
|
|
784
|
-
});
|
|
785
|
-
return { read };
|
|
786
|
-
}
|
|
787
|
-
};
|
|
788
|
-
}
|
|
789
679
|
// src/verification.ts
|
|
790
680
|
import { createHash } from "node:crypto";
|
|
791
681
|
// package.json
|
|
792
682
|
var package_default = {
|
|
793
683
|
name: "@secondlayer/subgraphs",
|
|
794
|
-
version: "7.
|
|
684
|
+
version: "7.3.0",
|
|
795
685
|
description: "Describe the Stacks data you want as one TypeScript file; it fills itself and survives reorgs.",
|
|
796
686
|
license: "MIT",
|
|
797
687
|
repository: {
|
|
@@ -851,8 +741,8 @@ var package_default = {
|
|
|
851
741
|
prepublishOnly: "bun run build"
|
|
852
742
|
},
|
|
853
743
|
dependencies: {
|
|
854
|
-
"@secondlayer/shared": "^13.
|
|
855
|
-
"@secondlayer/stacks": "^6.
|
|
744
|
+
"@secondlayer/shared": "^13.2.0",
|
|
745
|
+
"@secondlayer/stacks": "^6.4.0",
|
|
856
746
|
"@secondlayer/verify": "^0.6.0",
|
|
857
747
|
esbuild: "^0.28.2",
|
|
858
748
|
kysely: "0.28.17",
|
|
@@ -1054,8 +944,8 @@ function pinPreimage(input) {
|
|
|
1054
944
|
function computePin(input) {
|
|
1055
945
|
return sha256(pinPreimage(input));
|
|
1056
946
|
}
|
|
947
|
+
|
|
1057
948
|
// src/runtime/realm.ts
|
|
1058
|
-
import vm from "node:vm";
|
|
1059
949
|
class NondeterminismError extends Error {
|
|
1060
950
|
name = "NondeterminismError";
|
|
1061
951
|
}
|
|
@@ -1270,6 +1160,186 @@ ${code}
|
|
|
1270
1160
|
realmDefinitions.add(loaded);
|
|
1271
1161
|
return loaded;
|
|
1272
1162
|
}
|
|
1163
|
+
|
|
1164
|
+
// src/runtime/chain-read.ts
|
|
1165
|
+
function readContractAt(ctx, contractId, abi, options) {
|
|
1166
|
+
return ctx.client.contract(contractId, abi, options);
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
class ChainReadError extends Error {
|
|
1170
|
+
name = "ChainReadError";
|
|
1171
|
+
}
|
|
1172
|
+
var NO_NODE_MESSAGE = "ctx.client needs a Stacks node: set STACKS_NODE_RPC_URL on the subgraph processor.";
|
|
1173
|
+
async function resolveReadTarget(rpcUrl, db) {
|
|
1174
|
+
if (rpcUrl)
|
|
1175
|
+
return { kind: "node", rpcUrl };
|
|
1176
|
+
const apiUrl = process.env.SUBGRAPH_INDEX_API_URL;
|
|
1177
|
+
if (!apiUrl || !await hasNoLocalChain(db)) {
|
|
1178
|
+
throw new HandlerReadError(NO_NODE_MESSAGE);
|
|
1179
|
+
}
|
|
1180
|
+
const apiKey = process.env.INDEX_INTERNAL_API_KEY?.trim();
|
|
1181
|
+
if (!apiKey) {
|
|
1182
|
+
throw new HandlerReadError("Hosted contract reads need the processor's key (INDEX_INTERNAL_API_KEY), and none is set. Set STACKS_NODE_RPC_URL to read a node directly instead.");
|
|
1183
|
+
}
|
|
1184
|
+
return { kind: "hosted", apiUrl, apiKey };
|
|
1185
|
+
}
|
|
1186
|
+
function hostedReadError(err, label) {
|
|
1187
|
+
if (!(err instanceof HttpRequestError))
|
|
1188
|
+
return;
|
|
1189
|
+
if (err.status === 400) {
|
|
1190
|
+
return new ChainReadError(`${label} was refused as invalid: ${err.details ?? err.message}`);
|
|
1191
|
+
}
|
|
1192
|
+
if (err.status === 403) {
|
|
1193
|
+
return new HandlerReadError(`${label}: hosted contract reads serve hosted subgraphs only. ${NO_NODE_MESSAGE}`, { cause: err });
|
|
1194
|
+
}
|
|
1195
|
+
return;
|
|
1196
|
+
}
|
|
1197
|
+
function isChainAnswer(err) {
|
|
1198
|
+
return err instanceof ContractResponseError || err instanceof ReadContractError;
|
|
1199
|
+
}
|
|
1200
|
+
function readClient(target) {
|
|
1201
|
+
if (target.kind === "node") {
|
|
1202
|
+
return createPublicClient({
|
|
1203
|
+
transport: http(target.rpcUrl, { retryCount: 0, timeout: 1e4 })
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
return createPublicClient({
|
|
1207
|
+
transport: http(`${target.apiUrl.replace(/\/+$/, "")}/v1/chain-read`, {
|
|
1208
|
+
retryCount: 3,
|
|
1209
|
+
timeout: 1e4,
|
|
1210
|
+
fetchOptions: {
|
|
1211
|
+
headers: { authorization: `Bearer ${target.apiKey}` }
|
|
1212
|
+
}
|
|
1213
|
+
})
|
|
1214
|
+
});
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
class ConcurrencyGate {
|
|
1218
|
+
limit;
|
|
1219
|
+
active = 0;
|
|
1220
|
+
queue = [];
|
|
1221
|
+
constructor(limit) {
|
|
1222
|
+
this.limit = limit;
|
|
1223
|
+
}
|
|
1224
|
+
async run(fn) {
|
|
1225
|
+
if (this.active >= this.limit) {
|
|
1226
|
+
await new Promise((resolve) => this.queue.push(resolve));
|
|
1227
|
+
}
|
|
1228
|
+
this.active++;
|
|
1229
|
+
try {
|
|
1230
|
+
return await fn();
|
|
1231
|
+
} finally {
|
|
1232
|
+
this.active--;
|
|
1233
|
+
this.queue.shift()?.();
|
|
1234
|
+
}
|
|
1235
|
+
}
|
|
1236
|
+
}
|
|
1237
|
+
var DEFAULT_MAX_CONCURRENT_READS = 4;
|
|
1238
|
+
function maxConcurrentReads() {
|
|
1239
|
+
const raw = process.env.SUBGRAPH_CHAIN_READ_CONCURRENCY;
|
|
1240
|
+
const parsed = raw ? Number.parseInt(raw, 10) : Number.NaN;
|
|
1241
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : DEFAULT_MAX_CONCURRENT_READS;
|
|
1242
|
+
}
|
|
1243
|
+
var gate = new ConcurrencyGate(maxConcurrentReads());
|
|
1244
|
+
function hashArgs(args) {
|
|
1245
|
+
return chainReadArgsHash(args.map((arg) => serializeCV(arg)));
|
|
1246
|
+
}
|
|
1247
|
+
function findFunction(abi, name) {
|
|
1248
|
+
const fn = abi.functions.find((f) => f.name === name);
|
|
1249
|
+
if (!fn) {
|
|
1250
|
+
throw new ChainReadError(`No function "${name}" in the supplied ABI.`);
|
|
1251
|
+
}
|
|
1252
|
+
if (fn.access !== "read-only") {
|
|
1253
|
+
throw new ChainReadError(`"${name}" is ${fn.access}, not read-only — a handler cannot call it.`);
|
|
1254
|
+
}
|
|
1255
|
+
return fn;
|
|
1256
|
+
}
|
|
1257
|
+
function createChainReadClient(ctx) {
|
|
1258
|
+
const readValue = async (contractId, abi, functionName, args, options) => {
|
|
1259
|
+
const db = ctx.db ?? getSourceDb();
|
|
1260
|
+
const rpcUrl = ctx.rpcUrl || process.env.STACKS_NODE_RPC_URL || undefined;
|
|
1261
|
+
if (!rpcUrl && !process.env.SUBGRAPH_INDEX_API_URL) {
|
|
1262
|
+
throw new HandlerReadError(NO_NODE_MESSAGE);
|
|
1263
|
+
}
|
|
1264
|
+
const constant = options.cache === "contract-constant";
|
|
1265
|
+
if (!constant && !ctx.indexBlockHash) {
|
|
1266
|
+
throw new HandlerReadError(`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.`);
|
|
1267
|
+
}
|
|
1268
|
+
const fn = findFunction(abi, functionName);
|
|
1269
|
+
const clarityArgs = buildFunctionArgs(fn, args);
|
|
1270
|
+
const argsHash = hashArgs(clarityArgs);
|
|
1271
|
+
const pinnedTo = constant ? null : ctx.indexBlockHash;
|
|
1272
|
+
let resultHex;
|
|
1273
|
+
try {
|
|
1274
|
+
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();
|
|
1275
|
+
resultHex = cached?.result_hex ?? await gate.run(async () => {
|
|
1276
|
+
const target = await resolveReadTarget(rpcUrl, db);
|
|
1277
|
+
if (!ctx.indexBlockHash && target.kind === "hosted") {
|
|
1278
|
+
throw new HandlerReadError(`Block ${ctx.blockHeight} has no index_block_hash, so a hosted contract read cannot be pinned to it. Reindex the subgraph from this block once the block is re-ingested.`);
|
|
1279
|
+
}
|
|
1280
|
+
if (!ctx.indexBlockHash) {
|
|
1281
|
+
logger.warn("Chain read is not pinned to a block (no index_block_hash)", {
|
|
1282
|
+
contractId,
|
|
1283
|
+
functionName: fn.name,
|
|
1284
|
+
blockHeight: ctx.blockHeight,
|
|
1285
|
+
note: 'cache: "contract-constant" asserts this value cannot change; the node answered at its own tip'
|
|
1286
|
+
});
|
|
1287
|
+
}
|
|
1288
|
+
try {
|
|
1289
|
+
const value = await readContract(readClient(target), {
|
|
1290
|
+
contract: contractId,
|
|
1291
|
+
functionName: fn.name,
|
|
1292
|
+
args: clarityArgs,
|
|
1293
|
+
tip: ctx.indexBlockHash ?? undefined
|
|
1294
|
+
});
|
|
1295
|
+
return serializeCV(value);
|
|
1296
|
+
} catch (err) {
|
|
1297
|
+
throw target.kind === "hosted" && hostedReadError(err, `Chain read ${contractId}::${fn.name}`) || err;
|
|
1298
|
+
}
|
|
1299
|
+
});
|
|
1300
|
+
if (!cached) {
|
|
1301
|
+
await db.insertInto("chain_read_cache").values({
|
|
1302
|
+
contract_id: contractId,
|
|
1303
|
+
function_name: fn.name,
|
|
1304
|
+
args_hash: argsHash,
|
|
1305
|
+
index_block_hash: pinnedTo,
|
|
1306
|
+
block_height: constant ? null : ctx.blockHeight,
|
|
1307
|
+
result_hex: resultHex
|
|
1308
|
+
}).onConflict((oc) => oc.doNothing()).execute();
|
|
1309
|
+
}
|
|
1310
|
+
} catch (err) {
|
|
1311
|
+
if (isChainAnswer(err) || err instanceof ChainReadError || err instanceof HandlerReadError) {
|
|
1312
|
+
throw err;
|
|
1313
|
+
}
|
|
1314
|
+
throw new HandlerReadError(`Chain read ${contractId}::${fn.name} at block ${ctx.blockHeight} failed: ${getErrorMessage(err)}`, { cause: err });
|
|
1315
|
+
}
|
|
1316
|
+
const value = deserializeCV2(resultHex);
|
|
1317
|
+
const js = clarityValueToJSUntyped(fn.outputs, value);
|
|
1318
|
+
if (isResponseOutput(fn.outputs)) {
|
|
1319
|
+
const response = js;
|
|
1320
|
+
if ("ok" in response)
|
|
1321
|
+
return response.ok;
|
|
1322
|
+
throw new ContractResponseError(`${contractId}::${fn.name} returned an err response.`, response.err);
|
|
1323
|
+
}
|
|
1324
|
+
return js;
|
|
1325
|
+
};
|
|
1326
|
+
return {
|
|
1327
|
+
contract(contractId, rawAbi, options = {}) {
|
|
1328
|
+
const abi = normalizeAbi(rawAbi);
|
|
1329
|
+
const camelToKebab = new Map;
|
|
1330
|
+
for (const fn of abi.functions) {
|
|
1331
|
+
camelToKebab.set(toCamelCase(fn.name), fn.name);
|
|
1332
|
+
}
|
|
1333
|
+
const read = new Proxy({}, {
|
|
1334
|
+
get(_target, prop) {
|
|
1335
|
+
const fnName = camelToKebab.get(prop) ?? prop;
|
|
1336
|
+
return (args = {}) => readValue(contractId, abi, fnName, args, options);
|
|
1337
|
+
}
|
|
1338
|
+
});
|
|
1339
|
+
return { read };
|
|
1340
|
+
}
|
|
1341
|
+
};
|
|
1342
|
+
}
|
|
1273
1343
|
// src/schema/generator.ts
|
|
1274
1344
|
import { createHash as createHash2 } from "node:crypto";
|
|
1275
1345
|
|
|
@@ -2953,5 +3023,5 @@ export {
|
|
|
2953
3023
|
validateSubgraphDefinition
|
|
2954
3024
|
};
|
|
2955
3025
|
|
|
2956
|
-
//# debugId=
|
|
3026
|
+
//# debugId=B682843D48E7715C64756E2164756E21
|
|
2957
3027
|
//# sourceMappingURL=index.js.map
|