@logrix/sdk 0.3.1 → 0.3.2
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/README.md +26 -6
- package/dist/commands/build.js +14 -3
- package/dist/commands/codegen.js +22 -14
- package/dist/commands/status.js +140 -0
- package/dist/index.js +3 -1
- package/index.ts +2 -1
- package/package.json +2 -1
- package/src/runtime/index.ts +52 -0
- package/src/testing/index.ts +29 -0
package/README.md
CHANGED
|
@@ -24,11 +24,11 @@ npm test
|
|
|
24
24
|
|
|
25
25
|
### `logrix init [name]`
|
|
26
26
|
|
|
27
|
-
Scaffolds a new indexer project. Run interactively or pass flags for CI/CD automation.
|
|
27
|
+
Scaffolds a new indexer project with built-in presets for 26+ popular mainnets and testnets. Run interactively or pass flags for CI/CD automation.
|
|
28
28
|
|
|
29
29
|
Options:
|
|
30
30
|
- `-y, --yes` -- non-interactive mode using sensible defaults
|
|
31
|
-
- `--network <name>` -- network preset (`arbitrum-one`, `ethereum`, `base`, `polygon`, `sepolia
|
|
31
|
+
- `--network <name>` -- network preset (`arbitrum-one`, `ethereum`, `base`, `optimism`, `polygon`, `bsc`, `avalanche`, `sepolia`, etc.)
|
|
32
32
|
- `--rpc <url>` -- custom RPC endpoint
|
|
33
33
|
- `--contract-name <name>` -- contract name
|
|
34
34
|
- `--address <address>` -- target contract 0x address
|
|
@@ -51,6 +51,23 @@ my-indexer/
|
|
|
51
51
|
|
|
52
52
|
---
|
|
53
53
|
|
|
54
|
+
### `logrix status`
|
|
55
|
+
|
|
56
|
+
Checks live JSON-RPC connectivity, measures round-trip latency, verifies chain ID match, checks latest block height, and verifies that target smart contracts are deployed with on-chain bytecode.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
npx logrix status
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Output includes:
|
|
63
|
+
- Network Name & Chain ID verification
|
|
64
|
+
- RPC reachability and response latency (ms)
|
|
65
|
+
- Latest chain block number
|
|
66
|
+
- Contract deployment verification (`eth_getCode` bytecode inspection)
|
|
67
|
+
- Sync scope and remaining block delta
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
54
71
|
### `logrix add contract <name>`
|
|
55
72
|
|
|
56
73
|
Adds an additional smart contract to an existing `logrix.yaml` project for multi-contract indexing.
|
|
@@ -62,7 +79,7 @@ npx logrix add contract SecondaryToken \
|
|
|
62
79
|
--abi ./my-abis/SecondaryToken.json \
|
|
63
80
|
--start-block 1000000
|
|
64
81
|
|
|
65
|
-
# Add with automatic ABI fetch from
|
|
82
|
+
# Add with automatic ABI fetch from block explorers / Sourcify
|
|
66
83
|
npx logrix add contract DaiToken \
|
|
67
84
|
--address 0x6b175474e89094c44da98b954eedeac495271d0f \
|
|
68
85
|
--fetch-abi
|
|
@@ -71,8 +88,8 @@ npx logrix add contract DaiToken \
|
|
|
71
88
|
Options:
|
|
72
89
|
- `--address <address>` -- contract address
|
|
73
90
|
- `--abi <path>` -- path to local ABI JSON
|
|
74
|
-
- `--fetch-abi` -- auto-fetch verified contract ABI from Etherscan
|
|
75
|
-
- `--etherscan-key <key>` -- optional
|
|
91
|
+
- `--fetch-abi` -- auto-fetch verified contract ABI from block explorers (Etherscan, Arbiscan, Basescan, Polygonscan, etc.) and Sourcify
|
|
92
|
+
- `--etherscan-key <key>` -- optional block explorer API key for rate limits
|
|
76
93
|
- `--start-block <block>` -- initial indexing block
|
|
77
94
|
|
|
78
95
|
---
|
|
@@ -96,7 +113,7 @@ npx logrix codegen
|
|
|
96
113
|
```
|
|
97
114
|
|
|
98
115
|
Outputs:
|
|
99
|
-
- `src/generated/events.ts` -- typed event and parameter classes (namespaced per contract), multi-contract address constants, and
|
|
116
|
+
- `src/generated/events.ts` -- typed event and parameter classes (namespaced per contract), multi-contract address constants, router helpers, and dynamic ABI decoding (`string`, `bytes`)
|
|
100
117
|
- `src/generated/schema.ts` -- entity classes with static `.load(id)`, `.remove(id)`, and `.save()`, handling `@derivedFrom` relationships
|
|
101
118
|
|
|
102
119
|
---
|
|
@@ -165,6 +182,9 @@ Imported via `import { ... } from "@logrix/sdk"` in AssemblyScript handlers:
|
|
|
165
182
|
| `Bytes` | Hex byte wrapper: `.fromHexString()`, `.fromUTF8()`, `.toHexString()`, `.toByteArray()`, `.length` |
|
|
166
183
|
| `crypto.keccak256(data)` | Computes standard EVM Keccak-256 hash |
|
|
167
184
|
| `formatUnits(val, decimals)` | Formats raw BigInt value into human-readable decimal string (default: 18) |
|
|
185
|
+
| `decodeDynamicString(data, idx)` | Decodes dynamic string parameter from raw ABI event data |
|
|
186
|
+
| `decodeDynamicBytes(data, idx)` | Decodes dynamic bytes parameter from raw ABI event data |
|
|
187
|
+
| `createMockEventLog(config)` | Creates mock EventLog instance for testing mapping logic |
|
|
168
188
|
| `log` | Structured logging: `log.info()`, `log.warning()`, `log.error()`, `log.debug()` |
|
|
169
189
|
| `allocate(size)` | Guest memory allocator (must be re-exported by `mapping.ts`) |
|
|
170
190
|
|
package/dist/commands/build.js
CHANGED
|
@@ -55,6 +55,13 @@ function buildCommand() {
|
|
|
55
55
|
console.error(`Error: entry file not found: ${opts.entry}`);
|
|
56
56
|
process.exit(1);
|
|
57
57
|
}
|
|
58
|
+
// Check if generated bindings exist
|
|
59
|
+
const generatedSchema = path.join(projectDir, "src", "generated", "schema.ts");
|
|
60
|
+
const generatedEvents = path.join(projectDir, "src", "generated", "events.ts");
|
|
61
|
+
if (!fs.existsSync(generatedSchema) || !fs.existsSync(generatedEvents)) {
|
|
62
|
+
console.warn("Notice: Generated bindings (src/generated/) not found or incomplete.");
|
|
63
|
+
console.warn("Tip: Run `logrix codegen` before `logrix build` to generate typed classes.\n");
|
|
64
|
+
}
|
|
58
65
|
// Ensure output directory exists
|
|
59
66
|
fs.mkdirSync(path.dirname(outFile), { recursive: true });
|
|
60
67
|
// Prefer logrix.yaml mapping.file if present
|
|
@@ -81,7 +88,7 @@ function buildCommand() {
|
|
|
81
88
|
ascBin = "asc"; // global
|
|
82
89
|
}
|
|
83
90
|
const optimizeFlag = opts.debug ? "" : "--optimize";
|
|
84
|
-
const
|
|
91
|
+
const ascCmd = [
|
|
85
92
|
ascBin,
|
|
86
93
|
resolvedEntry,
|
|
87
94
|
"--config", "tsconfig.json",
|
|
@@ -91,14 +98,18 @@ function buildCommand() {
|
|
|
91
98
|
]
|
|
92
99
|
.filter(Boolean)
|
|
93
100
|
.join(" ");
|
|
94
|
-
console.log(`Running: ${
|
|
101
|
+
console.log(`Running: ${ascCmd}\n`);
|
|
95
102
|
try {
|
|
96
|
-
(0, child_process_1.execSync)(
|
|
103
|
+
(0, child_process_1.execSync)(ascCmd, { cwd: projectDir, stdio: "inherit" });
|
|
97
104
|
const stat = fs.statSync(outFile);
|
|
98
105
|
console.log(`\nBuild complete: ${path.relative(projectDir, outFile)} (${stat.size} bytes)`);
|
|
99
106
|
}
|
|
100
107
|
catch {
|
|
101
108
|
console.error("\nBuild failed.");
|
|
109
|
+
console.error("Troubleshooting tips:");
|
|
110
|
+
console.error(" 1. Verify AssemblyScript types in src/mapping.ts (e.g. use BigInt / Address / Bytes from @logrix/sdk)");
|
|
111
|
+
console.error(" 2. Run `logrix codegen` to refresh generated bindings");
|
|
112
|
+
console.error(" 3. Run `logrix validate` to check configuration syntax\n");
|
|
102
113
|
process.exit(1);
|
|
103
114
|
}
|
|
104
115
|
});
|
package/dist/commands/codegen.js
CHANGED
|
@@ -57,7 +57,7 @@ function solTypeToAs(solType) {
|
|
|
57
57
|
function generateEventsTs(dataSources, projectDir) {
|
|
58
58
|
const lines = [
|
|
59
59
|
"// Auto-generated by `logrix codegen`. Do not edit.",
|
|
60
|
-
'import { EventLog, BigInt, Address, Bytes } from "@logrix/sdk";',
|
|
60
|
+
'import { EventLog, BigInt, Address, Bytes, decodeDynamicString, decodeDynamicBytes } from "@logrix/sdk";',
|
|
61
61
|
"",
|
|
62
62
|
];
|
|
63
63
|
// Contract address constants for multi-contract indexers
|
|
@@ -138,23 +138,31 @@ function generateEventsTs(dataSources, projectDir) {
|
|
|
138
138
|
topicIdx++;
|
|
139
139
|
}
|
|
140
140
|
else {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
lines.push(` const offset = ${nonIndexedIdx * 64};`);
|
|
144
|
-
lines.push(` const rawWord = dataHex.length >= offset + 64 ? "0x" + dataHex.slice(offset, offset + 64) : (offset == 0 ? raw.data : "");`);
|
|
145
|
-
if (asType === "BigInt") {
|
|
146
|
-
lines.push(` this.${inp.name} = BigInt.fromString(rawWord);`);
|
|
147
|
-
}
|
|
148
|
-
else if (asType === "Address") {
|
|
149
|
-
lines.push(` this.${inp.name} = Address.fromString(rawWord);`);
|
|
141
|
+
if (inp.type === "string") {
|
|
142
|
+
lines.push(` this.${inp.name} = decodeDynamicString(raw.data, ${nonIndexedIdx});`);
|
|
150
143
|
}
|
|
151
|
-
else if (
|
|
152
|
-
lines.push(`
|
|
144
|
+
else if (inp.type === "bytes") {
|
|
145
|
+
lines.push(` this.${inp.name} = decodeDynamicBytes(raw.data, ${nonIndexedIdx});`);
|
|
153
146
|
}
|
|
154
147
|
else {
|
|
155
|
-
lines.push(`
|
|
148
|
+
lines.push(` {`);
|
|
149
|
+
lines.push(` const dataHex = raw.data.startsWith("0x") ? raw.data.slice(2) : raw.data;`);
|
|
150
|
+
lines.push(` const offset = ${nonIndexedIdx * 64};`);
|
|
151
|
+
lines.push(` const rawWord = dataHex.length >= offset + 64 ? "0x" + dataHex.slice(offset, offset + 64) : (offset == 0 ? raw.data : "");`);
|
|
152
|
+
if (asType === "BigInt") {
|
|
153
|
+
lines.push(` this.${inp.name} = BigInt.fromString(rawWord);`);
|
|
154
|
+
}
|
|
155
|
+
else if (asType === "Address") {
|
|
156
|
+
lines.push(` this.${inp.name} = Address.fromString(rawWord);`);
|
|
157
|
+
}
|
|
158
|
+
else if (asType === "Bytes") {
|
|
159
|
+
lines.push(` this.${inp.name} = Bytes.fromHexString(rawWord);`);
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
lines.push(` this.${inp.name} = rawWord;`);
|
|
163
|
+
}
|
|
164
|
+
lines.push(` }`);
|
|
156
165
|
}
|
|
157
|
-
lines.push(` }`);
|
|
158
166
|
nonIndexedIdx++;
|
|
159
167
|
}
|
|
160
168
|
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.statusCommand = statusCommand;
|
|
37
|
+
const commander_1 = require("commander");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const yaml_1 = require("yaml");
|
|
41
|
+
async function jsonRpcCall(url, method, params = []) {
|
|
42
|
+
const start = Date.now();
|
|
43
|
+
try {
|
|
44
|
+
const res = await fetch(url, {
|
|
45
|
+
method: "POST",
|
|
46
|
+
headers: { "Content-Type": "application/json" },
|
|
47
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method, params }),
|
|
48
|
+
});
|
|
49
|
+
const latencyMs = Date.now() - start;
|
|
50
|
+
if (!res.ok) {
|
|
51
|
+
return { error: `HTTP ${res.status}: ${res.statusText}`, latencyMs };
|
|
52
|
+
}
|
|
53
|
+
const json = await res.json();
|
|
54
|
+
return { result: json.result, error: json.error, latencyMs };
|
|
55
|
+
}
|
|
56
|
+
catch (e) {
|
|
57
|
+
return { error: e.message, latencyMs: Date.now() - start };
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function statusCommand() {
|
|
61
|
+
const cmd = new commander_1.Command("status");
|
|
62
|
+
cmd
|
|
63
|
+
.description("Check RPC connectivity, network block height, and contract deployment status")
|
|
64
|
+
.action(async () => {
|
|
65
|
+
const projectDir = process.cwd();
|
|
66
|
+
const configPath = path.join(projectDir, "logrix.yaml");
|
|
67
|
+
if (!fs.existsSync(configPath)) {
|
|
68
|
+
console.error("Error: logrix.yaml not found. Run this command from your project root.");
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
let config;
|
|
72
|
+
try {
|
|
73
|
+
config = (0, yaml_1.parse)(fs.readFileSync(configPath, "utf-8"));
|
|
74
|
+
}
|
|
75
|
+
catch (e) {
|
|
76
|
+
console.error(`Error parsing logrix.yaml: ${e.message}`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
const networkName = config.network?.name || "unspecified";
|
|
80
|
+
const configuredChainId = config.network?.chainId;
|
|
81
|
+
const rpcUrl = config.network?.rpcUrl;
|
|
82
|
+
console.log("\nLogrix Indexer Status");
|
|
83
|
+
console.log("============================================================");
|
|
84
|
+
console.log(`Network Name: ${networkName}`);
|
|
85
|
+
console.log(`Configured Chain: ${configuredChainId ?? "Not set"}`);
|
|
86
|
+
console.log(`RPC Endpoint: ${rpcUrl || "None"}`);
|
|
87
|
+
if (!rpcUrl) {
|
|
88
|
+
console.error("\nError: No RPC URL configured in logrix.yaml.");
|
|
89
|
+
console.log("============================================================\n");
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
// 1. Query block height
|
|
93
|
+
process.stdout.write("Connecting to RPC endpoint ... ");
|
|
94
|
+
const blockRes = await jsonRpcCall(rpcUrl, "eth_blockNumber");
|
|
95
|
+
if (blockRes.error || !blockRes.result) {
|
|
96
|
+
console.log("FAILED");
|
|
97
|
+
console.error(`RPC Error: ${JSON.stringify(blockRes.error)} (${blockRes.latencyMs} ms)`);
|
|
98
|
+
console.log("============================================================\n");
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
const latestBlock = parseInt(blockRes.result, 16);
|
|
102
|
+
console.log(`ONLINE (${blockRes.latencyMs} ms)`);
|
|
103
|
+
console.log(`Latest Block: #${latestBlock.toLocaleString()}`);
|
|
104
|
+
// 2. Query chain ID
|
|
105
|
+
const chainRes = await jsonRpcCall(rpcUrl, "eth_chainId");
|
|
106
|
+
if (chainRes.result) {
|
|
107
|
+
const reportedChainId = parseInt(chainRes.result, 16);
|
|
108
|
+
const match = configuredChainId === reportedChainId;
|
|
109
|
+
console.log(`Chain ID Check: Reported ${reportedChainId} (${match ? "VERIFIED MATCH" : "MISMATCH WARNING"})`);
|
|
110
|
+
}
|
|
111
|
+
// 3. Inspect contracts
|
|
112
|
+
const dataSources = config.dataSources || [];
|
|
113
|
+
console.log(`\nContracts Monitored (${dataSources.length}):`);
|
|
114
|
+
console.log("------------------------------------------------------------");
|
|
115
|
+
for (const ds of dataSources) {
|
|
116
|
+
const addr = ds.source?.address || "";
|
|
117
|
+
const name = ds.name || "Unnamed";
|
|
118
|
+
const startBlock = ds.source?.startBlock ?? 0;
|
|
119
|
+
if (!addr) {
|
|
120
|
+
console.log(` - ${name}: [NO ADDRESS SPECIFIED]`);
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
const codeRes = await jsonRpcCall(rpcUrl, "eth_getCode", [addr, "latest"]);
|
|
124
|
+
const code = codeRes.result || "";
|
|
125
|
+
const isDeployed = code.length > 2 && code !== "0x" && code !== "0x0";
|
|
126
|
+
const byteSize = isDeployed ? (code.length - 2) / 2 : 0;
|
|
127
|
+
console.log(` - Name: ${name}`);
|
|
128
|
+
console.log(` Address: ${addr}`);
|
|
129
|
+
console.log(` Deployed: ${isDeployed ? `YES (${byteSize.toLocaleString()} bytes bytecode)` : "NO (Empty bytecode / Not found)"}`);
|
|
130
|
+
console.log(` Start Block: #${startBlock.toLocaleString()}`);
|
|
131
|
+
if (latestBlock > startBlock) {
|
|
132
|
+
const lag = latestBlock - startBlock;
|
|
133
|
+
console.log(` Sync Scope: #${startBlock} -> #${latestBlock} (${lag.toLocaleString()} blocks to process)`);
|
|
134
|
+
}
|
|
135
|
+
console.log("");
|
|
136
|
+
}
|
|
137
|
+
console.log("============================================================\n");
|
|
138
|
+
});
|
|
139
|
+
return cmd;
|
|
140
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -9,11 +9,12 @@ const add_1 = require("./commands/add");
|
|
|
9
9
|
const validate_1 = require("./commands/validate");
|
|
10
10
|
const export_values_1 = require("./commands/export_values");
|
|
11
11
|
const test_1 = require("./commands/test");
|
|
12
|
+
const status_1 = require("./commands/status");
|
|
12
13
|
const program = new commander_1.Command();
|
|
13
14
|
program
|
|
14
15
|
.name("logrix")
|
|
15
16
|
.description("Developer CLI for Logrix blockchain indexers")
|
|
16
|
-
.version("0.3.
|
|
17
|
+
.version("0.3.2");
|
|
17
18
|
program.addCommand((0, init_1.initCommand)());
|
|
18
19
|
program.addCommand((0, add_1.addCommand)());
|
|
19
20
|
program.addCommand((0, codegen_1.codegenCommand)());
|
|
@@ -21,4 +22,5 @@ program.addCommand((0, validate_1.validateCommand)());
|
|
|
21
22
|
program.addCommand((0, build_1.buildCommand)());
|
|
22
23
|
program.addCommand((0, export_values_1.exportValuesCommand)());
|
|
23
24
|
program.addCommand((0, test_1.testCommand)());
|
|
25
|
+
program.addCommand((0, status_1.statusCommand)());
|
|
24
26
|
program.parse(process.argv);
|
package/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
-
// Root
|
|
1
|
+
// Root entry point -- re-exports runtime and testing utilities.
|
|
2
2
|
// asc resolves @logrix/sdk to this file when building user handler projects.
|
|
3
3
|
export * from "./src/runtime/index";
|
|
4
|
+
export * from "./src/testing/index";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@logrix/sdk",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "Official CLI and AssemblyScript runtime library for Logrix blockchain indexers",
|
|
5
5
|
"main": "index.ts",
|
|
6
6
|
"bin": {
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
"dist",
|
|
15
15
|
"src/runtime",
|
|
16
|
+
"src/testing",
|
|
16
17
|
"index.ts",
|
|
17
18
|
"tsconfig.as.json",
|
|
18
19
|
"README.md"
|
package/src/runtime/index.ts
CHANGED
|
@@ -258,6 +258,58 @@ export class Bytes {
|
|
|
258
258
|
}
|
|
259
259
|
}
|
|
260
260
|
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
// Dynamic ABI Offset Decoding Helpers (Solidity Head/Tail)
|
|
263
|
+
// ---------------------------------------------------------------------------
|
|
264
|
+
|
|
265
|
+
function parseHexI32(hex: string): i32 {
|
|
266
|
+
let val: i32 = 0;
|
|
267
|
+
for (let i = 0; i < hex.length; i++) {
|
|
268
|
+
val = (val << 4) | parseHexChar(hex.charCodeAt(i));
|
|
269
|
+
}
|
|
270
|
+
return val;
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export function decodeDynamicString(dataHex: string, paramIndex: i32): string {
|
|
274
|
+
let clean = dataHex.startsWith("0x") ? dataHex.slice(2) : dataHex;
|
|
275
|
+
const headOffset = paramIndex * 64;
|
|
276
|
+
if (clean.length < headOffset + 64) return "";
|
|
277
|
+
const ptrWord = clean.slice(headOffset, headOffset + 64);
|
|
278
|
+
const offsetBytes = parseHexI32(ptrWord);
|
|
279
|
+
const offsetHex = offsetBytes * 2;
|
|
280
|
+
if (clean.length < offsetHex + 64) return "";
|
|
281
|
+
const lenWord = clean.slice(offsetHex, offsetHex + 64);
|
|
282
|
+
const byteLen = parseHexI32(lenWord);
|
|
283
|
+
const contentHexStart = offsetHex + 64;
|
|
284
|
+
const contentHexEnd = contentHexStart + byteLen * 2;
|
|
285
|
+
if (clean.length < contentHexEnd) return "";
|
|
286
|
+
const contentHex = clean.slice(contentHexStart, contentHexEnd);
|
|
287
|
+
const bytes = new Uint8Array(byteLen);
|
|
288
|
+
for (let i = 0; i < byteLen; i++) {
|
|
289
|
+
const high = parseHexChar(contentHex.charCodeAt(i * 2));
|
|
290
|
+
const low = parseHexChar(contentHex.charCodeAt(i * 2 + 1));
|
|
291
|
+
bytes[i] = ((high << 4) | low) as u8;
|
|
292
|
+
}
|
|
293
|
+
return String.UTF8.decode(bytes.buffer);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function decodeDynamicBytes(dataHex: string, paramIndex: i32): Bytes {
|
|
297
|
+
let clean = dataHex.startsWith("0x") ? dataHex.slice(2) : dataHex;
|
|
298
|
+
const headOffset = paramIndex * 64;
|
|
299
|
+
if (clean.length < headOffset + 64) return Bytes.fromHexString("0x");
|
|
300
|
+
const ptrWord = clean.slice(headOffset, headOffset + 64);
|
|
301
|
+
const offsetBytes = parseHexI32(ptrWord);
|
|
302
|
+
const offsetHex = offsetBytes * 2;
|
|
303
|
+
if (clean.length < offsetHex + 64) return Bytes.fromHexString("0x");
|
|
304
|
+
const lenWord = clean.slice(offsetHex, offsetHex + 64);
|
|
305
|
+
const byteLen = parseHexI32(lenWord);
|
|
306
|
+
const contentHexStart = offsetHex + 64;
|
|
307
|
+
const contentHexEnd = contentHexStart + byteLen * 2;
|
|
308
|
+
if (clean.length < contentHexEnd) return Bytes.fromHexString("0x");
|
|
309
|
+
const contentHex = clean.slice(contentHexStart, contentHexEnd);
|
|
310
|
+
return Bytes.fromHexString("0x" + contentHex);
|
|
311
|
+
}
|
|
312
|
+
|
|
261
313
|
// ---------------------------------------------------------------------------
|
|
262
314
|
// Keccak-256 Implementation & Crypto namespace
|
|
263
315
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { EventLog } from "../runtime";
|
|
2
|
+
|
|
3
|
+
export interface MockEventLogConfig {
|
|
4
|
+
address?: string;
|
|
5
|
+
blockNumber?: number;
|
|
6
|
+
blockHash?: string;
|
|
7
|
+
transactionHash?: string;
|
|
8
|
+
logIndex?: number;
|
|
9
|
+
blockTimestamp?: number;
|
|
10
|
+
topics?: string[];
|
|
11
|
+
data?: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Construct an EventLog instance pre-populated with test values.
|
|
16
|
+
* Useful for writing local unit tests for AssemblyScript/TypeScript mapping handlers.
|
|
17
|
+
*/
|
|
18
|
+
export function createMockEventLog(config: MockEventLogConfig = {}): EventLog {
|
|
19
|
+
const log = new EventLog();
|
|
20
|
+
log.address = config.address || "0x0000000000000000000000000000000000000000";
|
|
21
|
+
log.block_number = BigInt(config.blockNumber ?? 1) as any;
|
|
22
|
+
log.block_hash = config.blockHash || "0x0000000000000000000000000000000000000000000000000000000000000000";
|
|
23
|
+
log.transaction_hash = config.transactionHash || "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef";
|
|
24
|
+
log.log_index = (config.logIndex ?? 0) as any;
|
|
25
|
+
log.block_timestamp = BigInt(config.blockTimestamp ?? 1700000000) as any;
|
|
26
|
+
log.topics = config.topics || [];
|
|
27
|
+
log.data = config.data || "0x";
|
|
28
|
+
return log;
|
|
29
|
+
}
|