@logrix/sdk 0.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/README.md +231 -0
- package/dist/commands/add.js +235 -0
- package/dist/commands/build.js +106 -0
- package/dist/commands/codegen.js +364 -0
- package/dist/commands/export_values.js +127 -0
- package/dist/commands/init.js +200 -0
- package/dist/commands/test.js +124 -0
- package/dist/commands/validate.js +159 -0
- package/dist/index.js +24 -0
- package/dist/templates.js +269 -0
- package/index.ts +3 -0
- package/package.json +51 -0
- package/src/runtime/index.ts +736 -0
- package/tsconfig.as.json +4 -0
package/README.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# @logrix/sdk
|
|
2
|
+
|
|
3
|
+
Official CLI and AssemblyScript runtime library for [Logrix](https://github.com/Vishal-770/Logrix) blockchain indexers.
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@logrix/sdk)
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx @logrix/sdk init my-indexer
|
|
13
|
+
cd my-indexer
|
|
14
|
+
npm install
|
|
15
|
+
npm run codegen
|
|
16
|
+
npm run validate
|
|
17
|
+
npm run build
|
|
18
|
+
npm test
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## CLI Commands
|
|
24
|
+
|
|
25
|
+
### `logrix init [name]`
|
|
26
|
+
|
|
27
|
+
Scaffolds a new indexer project. Run interactively or pass flags for CI/CD automation.
|
|
28
|
+
|
|
29
|
+
Options:
|
|
30
|
+
- `-y, --yes` -- non-interactive mode using sensible defaults
|
|
31
|
+
- `--network <name>` -- network preset (`arbitrum-one`, `ethereum`, `base`, `polygon`, `sepolia`)
|
|
32
|
+
- `--rpc <url>` -- custom RPC endpoint
|
|
33
|
+
- `--contract-name <name>` -- contract name
|
|
34
|
+
- `--address <address>` -- target contract 0x address
|
|
35
|
+
- `--start-block <block>` -- initial indexing block
|
|
36
|
+
|
|
37
|
+
Creates:
|
|
38
|
+
```
|
|
39
|
+
my-indexer/
|
|
40
|
+
logrix.yaml # indexer manifest: network, contracts, ABIs, WASM path
|
|
41
|
+
schema.graphql # entity definitions
|
|
42
|
+
abis/
|
|
43
|
+
TokenContract.json # contract ABI
|
|
44
|
+
src/
|
|
45
|
+
mapping.ts # handler logic
|
|
46
|
+
package.json
|
|
47
|
+
tsconfig.json
|
|
48
|
+
.gitignore
|
|
49
|
+
README.md
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
### `logrix add contract <name>`
|
|
55
|
+
|
|
56
|
+
Adds an additional smart contract to an existing `logrix.yaml` project for multi-contract indexing.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
# Add with manual ABI path
|
|
60
|
+
npx logrix add contract SecondaryToken \
|
|
61
|
+
--address 0x1111111111111111111111111111111111111111 \
|
|
62
|
+
--abi ./my-abis/SecondaryToken.json \
|
|
63
|
+
--start-block 1000000
|
|
64
|
+
|
|
65
|
+
# Add with automatic ABI fetch from Etherscan / Sourcify
|
|
66
|
+
npx logrix add contract DaiToken \
|
|
67
|
+
--address 0x6b175474e89094c44da98b954eedeac495271d0f \
|
|
68
|
+
--fetch-abi
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Options:
|
|
72
|
+
- `--address <address>` -- contract address
|
|
73
|
+
- `--abi <path>` -- path to local ABI JSON
|
|
74
|
+
- `--fetch-abi` -- auto-fetch verified contract ABI from Etherscan / Sourcify
|
|
75
|
+
- `--etherscan-key <key>` -- optional Etherscan API key for rate limits
|
|
76
|
+
- `--start-block <block>` -- initial indexing block
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
### `logrix validate`
|
|
81
|
+
|
|
82
|
+
Validates your `logrix.yaml`, contract addresses, ABI files, `schema.graphql` entities, and handler mappings before compilation.
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npx logrix validate
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
### `logrix codegen`
|
|
91
|
+
|
|
92
|
+
Parses `logrix.yaml` dataSources, ABI files, and `schema.graphql` to generate typed AssemblyScript bindings.
|
|
93
|
+
|
|
94
|
+
```bash
|
|
95
|
+
npx logrix codegen
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Outputs:
|
|
99
|
+
- `src/generated/events.ts` -- typed event and parameter classes (namespaced per contract), multi-contract address constants, and router helpers
|
|
100
|
+
- `src/generated/schema.ts` -- entity classes with static `.load(id)`, `.remove(id)`, and `.save()`, handling `@derivedFrom` relationships
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
### `logrix build`
|
|
105
|
+
|
|
106
|
+
Compiles your AssemblyScript handler into an optimized WebAssembly module (`build/mapping.wasm`).
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npx logrix build
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Options:
|
|
113
|
+
- `--entry <file>` -- handler entry point (default: `src/mapping.ts`)
|
|
114
|
+
- `--out <file>` -- output WASM binary path (default: `build/mapping.wasm`)
|
|
115
|
+
- `--debug` -- compile without optimizations for debugging
|
|
116
|
+
|
|
117
|
+
---
|
|
118
|
+
|
|
119
|
+
### `logrix export-values`
|
|
120
|
+
|
|
121
|
+
Reads `logrix.yaml`, `schema.graphql`, and `build/mapping.wasm` to produce a ready-to-deploy `indexer-values.yaml` for Helm.
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
npx logrix export-values
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Options:
|
|
128
|
+
- `--out <file>` -- output values file (default: `indexer-values.yaml`)
|
|
129
|
+
- `--chain-id <id>` -- override chain ID
|
|
130
|
+
- `--rpc <url>` -- override RPC URL
|
|
131
|
+
|
|
132
|
+
Deploy directly to Kubernetes:
|
|
133
|
+
```bash
|
|
134
|
+
helm install logrix oci://ghcr.io/vishal-770/charts/logrix \
|
|
135
|
+
-f cluster-infra.yaml \
|
|
136
|
+
-f indexer-values.yaml
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
### `logrix test`
|
|
142
|
+
|
|
143
|
+
Runs local test validation across configuration, code generation, and WASM compilation, and executes any unit tests in `tests/`.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
npx logrix test
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
---
|
|
150
|
+
|
|
151
|
+
## Runtime API
|
|
152
|
+
|
|
153
|
+
Imported via `import { ... } from "@logrix/sdk"` in AssemblyScript handlers:
|
|
154
|
+
|
|
155
|
+
| Export | Description |
|
|
156
|
+
|--------|-------------|
|
|
157
|
+
| `store.get(entity, id)` | Load raw entity JSON string from persistent store |
|
|
158
|
+
| `store.set(entity, id, data)` | Write entity to persistent store and stage for DB |
|
|
159
|
+
| `store.remove(entity, id)` | Mark entity as deleted and stage soft-delete |
|
|
160
|
+
| `Entity.load(id)` | Auto-generated on each `@entity` class; loads existing entity or returns `null` |
|
|
161
|
+
| `Entity.remove(id)` | Auto-generated on each `@entity` class; removes entity by ID |
|
|
162
|
+
| `entity.save()` | Saves and persists entity state |
|
|
163
|
+
| `BigInt` | EVM 256-bit safe arithmetic: `.plus()`, `.minus()`, `.times()`, `.div()`, `.equals()`, `.gt()`, `.lt()` |
|
|
164
|
+
| `Address` | Address wrapper: `.fromString()`, `.toHexString()`, `.equals()` |
|
|
165
|
+
| `Bytes` | Hex byte wrapper: `.fromHexString()`, `.fromUTF8()`, `.toHexString()`, `.toByteArray()`, `.length` |
|
|
166
|
+
| `crypto.keccak256(data)` | Computes standard EVM Keccak-256 hash |
|
|
167
|
+
| `formatUnits(val, decimals)` | Formats raw BigInt value into human-readable decimal string (default: 18) |
|
|
168
|
+
| `log` | Structured logging: `log.info()`, `log.warning()`, `log.error()`, `log.debug()` |
|
|
169
|
+
| `allocate(size)` | Guest memory allocator (must be re-exported by `mapping.ts`) |
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## Writing Handlers (`src/mapping.ts`)
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
import { EventLog, log, BigInt, Address, crypto, formatUnits, allocate } from "@logrix/sdk";
|
|
177
|
+
import { TransferEvent } from "./generated/events";
|
|
178
|
+
import { Transfer } from "./generated/schema";
|
|
179
|
+
|
|
180
|
+
export { allocate } from "@logrix/sdk";
|
|
181
|
+
|
|
182
|
+
export function handle_event(ptr: i32, len: i32): i32 {
|
|
183
|
+
const bytes = new Uint8Array(len);
|
|
184
|
+
for (let i = 0; i < len; i++) {
|
|
185
|
+
bytes[i] = load<u8>(ptr + i);
|
|
186
|
+
}
|
|
187
|
+
const raw = String.UTF8.decode(bytes.buffer);
|
|
188
|
+
|
|
189
|
+
if (raw.includes("Transfer")) {
|
|
190
|
+
return handleTransfer(raw);
|
|
191
|
+
}
|
|
192
|
+
return 0;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function handleTransfer(raw: string): i32 {
|
|
196
|
+
const event = new TransferEvent(new EventLog());
|
|
197
|
+
|
|
198
|
+
const entityId = event.transactionHash + "-" + event.logIndex.toString();
|
|
199
|
+
|
|
200
|
+
// Load existing entity or instantiate a new one
|
|
201
|
+
let entity = Transfer.load(entityId);
|
|
202
|
+
if (!entity) {
|
|
203
|
+
entity = new Transfer(entityId);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
entity.fromAddress = event.params.from.toHexString();
|
|
207
|
+
entity.toAddress = event.params.to.toHexString();
|
|
208
|
+
entity.amount = event.params.value; // Typed BigInt
|
|
209
|
+
entity.save();
|
|
210
|
+
|
|
211
|
+
log.info("Indexed transfer: " + entityId);
|
|
212
|
+
return 0;
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
---
|
|
217
|
+
|
|
218
|
+
## Deployment
|
|
219
|
+
|
|
220
|
+
1. Use `@logrix/sdk` to build and export your indexer:
|
|
221
|
+
```bash
|
|
222
|
+
logrix codegen && logrix build && logrix export-values
|
|
223
|
+
```
|
|
224
|
+
2. Deploy via Helm to your Kubernetes cluster:
|
|
225
|
+
```bash
|
|
226
|
+
helm install logrix oci://ghcr.io/vishal-770/charts/logrix \
|
|
227
|
+
-f cluster-infra.yaml \
|
|
228
|
+
-f indexer-values.yaml
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
Refer to the [Logrix Helm Chart](https://github.com/Vishal-770/Logrix) for full Helm deployment options.
|
|
@@ -0,0 +1,235 @@
|
|
|
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.addCommand = addCommand;
|
|
37
|
+
const commander_1 = require("commander");
|
|
38
|
+
const prompts_1 = require("@inquirer/prompts");
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const yaml_1 = require("yaml");
|
|
42
|
+
const templates_1 = require("../templates");
|
|
43
|
+
/**
|
|
44
|
+
* Fetch ABI from Etherscan (mainnet) or Sourcify as fallback.
|
|
45
|
+
* Returns the parsed ABI array, or null if both fail.
|
|
46
|
+
*/
|
|
47
|
+
async function fetchAbi(address, etherscanKey) {
|
|
48
|
+
const addr = address.toLowerCase();
|
|
49
|
+
// Try Etherscan first if API key provided
|
|
50
|
+
if (etherscanKey) {
|
|
51
|
+
try {
|
|
52
|
+
const url = `https://api.etherscan.io/api?module=contract&action=getabi&address=${addr}&apikey=${etherscanKey}`;
|
|
53
|
+
const resp = await fetch(url);
|
|
54
|
+
const json = await resp.json();
|
|
55
|
+
if (json.status === "1" && json.result) {
|
|
56
|
+
const parsed = JSON.parse(json.result);
|
|
57
|
+
if (Array.isArray(parsed) && parsed.length > 0) {
|
|
58
|
+
console.log(` Fetched ABI from Etherscan for ${address}`);
|
|
59
|
+
return parsed;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// fall through
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// Try Sourcify (no key required)
|
|
68
|
+
try {
|
|
69
|
+
// Sourcify returns verified contracts at chainId 1 (mainnet)
|
|
70
|
+
const url = `https://repo.sourcify.dev/contracts/full_match/1/${addr}/metadata.json`;
|
|
71
|
+
const resp = await fetch(url);
|
|
72
|
+
if (resp.ok) {
|
|
73
|
+
const json = await resp.json();
|
|
74
|
+
const abi = json?.output?.abi;
|
|
75
|
+
if (Array.isArray(abi) && abi.length > 0) {
|
|
76
|
+
console.log(` Fetched ABI from Sourcify for ${address}`);
|
|
77
|
+
return abi;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
// fall through
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
function addCommand() {
|
|
87
|
+
const cmd = new commander_1.Command("add");
|
|
88
|
+
cmd
|
|
89
|
+
.description("Add components to an existing Logrix indexer project")
|
|
90
|
+
.argument("<type>", "Type of component to add (e.g. contract)")
|
|
91
|
+
.argument("[name]", "Name of the component")
|
|
92
|
+
.option("--address <address>", "Contract address")
|
|
93
|
+
.option("--abi <path>", "Path to ABI JSON file")
|
|
94
|
+
.option("--fetch-abi", "Auto-fetch ABI from Etherscan / Sourcify")
|
|
95
|
+
.option("--etherscan-key <key>", "Etherscan API key for ABI fetch (optional)")
|
|
96
|
+
.option("--start-block <block>", "Starting block number", "0")
|
|
97
|
+
.action(async (type, nameArg, opts) => {
|
|
98
|
+
const projectDir = process.cwd();
|
|
99
|
+
const configPath = path.join(projectDir, "logrix.yaml");
|
|
100
|
+
if (!fs.existsSync(configPath)) {
|
|
101
|
+
console.error("Error: logrix.yaml not found. Please run this command from the project root.");
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
if (type !== "contract") {
|
|
105
|
+
console.error(`Error: Unknown component type '${type}'. Supported types: contract`);
|
|
106
|
+
process.exit(1);
|
|
107
|
+
}
|
|
108
|
+
let config;
|
|
109
|
+
try {
|
|
110
|
+
config = (0, yaml_1.parse)(fs.readFileSync(configPath, "utf-8"));
|
|
111
|
+
}
|
|
112
|
+
catch (e) {
|
|
113
|
+
console.error(`Error parsing logrix.yaml: ${e.message}`);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
if (!config.dataSources) {
|
|
117
|
+
config.dataSources = [];
|
|
118
|
+
}
|
|
119
|
+
// 1. Resolve contract name
|
|
120
|
+
let contractName = nameArg;
|
|
121
|
+
if (!contractName) {
|
|
122
|
+
if (process.stdin.isTTY) {
|
|
123
|
+
contractName = await (0, prompts_1.input)({
|
|
124
|
+
message: "Smart Contract Name:",
|
|
125
|
+
default: "SecondaryContract",
|
|
126
|
+
validate: (v) => /^[A-Za-z][A-Za-z0-9]*$/.test(v.trim()) ||
|
|
127
|
+
"Must be alphanumeric, starting with a letter",
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
contractName = "SecondaryContract";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
contractName = contractName.trim();
|
|
135
|
+
// Check for collision
|
|
136
|
+
const exists = config.dataSources.some((ds) => ds.name === contractName);
|
|
137
|
+
if (exists) {
|
|
138
|
+
console.error(`Error: A contract named '${contractName}' is already defined in logrix.yaml.`);
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
// 2. Resolve contract address
|
|
142
|
+
let contractAddress = opts.address;
|
|
143
|
+
if (!contractAddress) {
|
|
144
|
+
if (process.stdin.isTTY) {
|
|
145
|
+
contractAddress = await (0, prompts_1.input)({
|
|
146
|
+
message: "Target Contract Address:",
|
|
147
|
+
default: "0x0000000000000000000000000000000000000000",
|
|
148
|
+
validate: (v) => /^0x[0-9a-fA-F]{40}$/.test(v.trim()) || "Must be a valid 0x hex address",
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
contractAddress = "0x0000000000000000000000000000000000000000";
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
contractAddress = contractAddress.trim();
|
|
156
|
+
// 3. Resolve start block
|
|
157
|
+
let startBlockStr = opts.startBlock;
|
|
158
|
+
if (!opts.address && process.stdin.isTTY) {
|
|
159
|
+
startBlockStr = await (0, prompts_1.input)({
|
|
160
|
+
message: "Start Block:",
|
|
161
|
+
default: "0",
|
|
162
|
+
validate: (v) => /^\d+$/.test(v.trim()) || "Must be a non-negative integer",
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
const startBlock = parseInt(startBlockStr.trim(), 10);
|
|
166
|
+
// 4. Resolve ABI
|
|
167
|
+
const abisDir = path.join(projectDir, "abis");
|
|
168
|
+
fs.mkdirSync(abisDir, { recursive: true });
|
|
169
|
+
const targetAbiFile = path.join(abisDir, `${contractName}.json`);
|
|
170
|
+
if (opts.abi) {
|
|
171
|
+
// Explicit local file
|
|
172
|
+
const sourceAbi = path.resolve(projectDir, opts.abi);
|
|
173
|
+
if (!fs.existsSync(sourceAbi)) {
|
|
174
|
+
console.error(`Error: Specified ABI file not found: ${opts.abi}`);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
}
|
|
177
|
+
fs.copyFileSync(sourceAbi, targetAbiFile);
|
|
178
|
+
console.log(` Copied ABI from ${opts.abi} to abis/${contractName}.json`);
|
|
179
|
+
}
|
|
180
|
+
else if (opts.fetchAbi) {
|
|
181
|
+
// Auto-fetch from Etherscan / Sourcify
|
|
182
|
+
console.log(` Fetching ABI for ${contractAddress} ...`);
|
|
183
|
+
const abi = await fetchAbi(contractAddress, opts.etherscanKey);
|
|
184
|
+
if (abi) {
|
|
185
|
+
fs.writeFileSync(targetAbiFile, JSON.stringify(abi, null, 2));
|
|
186
|
+
console.log(` Saved fetched ABI to abis/${contractName}.json`);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
console.warn(` Warning: Could not fetch ABI for ${contractAddress}. Using ERC-20 placeholder.`);
|
|
190
|
+
fs.writeFileSync(targetAbiFile, JSON.stringify(templates_1.ERC20_ABI, null, 2));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
else if (!fs.existsSync(targetAbiFile)) {
|
|
194
|
+
// Default placeholder
|
|
195
|
+
fs.writeFileSync(targetAbiFile, JSON.stringify(templates_1.ERC20_ABI, null, 2));
|
|
196
|
+
console.log(` Created default ERC-20 placeholder ABI at abis/${contractName}.json`);
|
|
197
|
+
}
|
|
198
|
+
// 5. Append new dataSource to logrix.yaml
|
|
199
|
+
const networkName = config.network?.name || "mainnet";
|
|
200
|
+
const newDataSource = {
|
|
201
|
+
kind: "ethereum/contract",
|
|
202
|
+
name: contractName,
|
|
203
|
+
network: networkName,
|
|
204
|
+
source: {
|
|
205
|
+
address: contractAddress,
|
|
206
|
+
abi: contractName,
|
|
207
|
+
startBlock: startBlock,
|
|
208
|
+
},
|
|
209
|
+
mapping: {
|
|
210
|
+
kind: "wasm/assemblyscript",
|
|
211
|
+
file: "./build/mapping.wasm",
|
|
212
|
+
abis: [
|
|
213
|
+
{
|
|
214
|
+
name: contractName,
|
|
215
|
+
file: `./abis/${contractName}.json`,
|
|
216
|
+
},
|
|
217
|
+
],
|
|
218
|
+
eventHandlers: [
|
|
219
|
+
{
|
|
220
|
+
event: "Transfer(address indexed,address indexed,uint256)",
|
|
221
|
+
handler: `handle${contractName}Transfer`,
|
|
222
|
+
},
|
|
223
|
+
],
|
|
224
|
+
},
|
|
225
|
+
};
|
|
226
|
+
config.dataSources.push(newDataSource);
|
|
227
|
+
fs.writeFileSync(configPath, (0, yaml_1.stringify)(config, { indent: 2 }));
|
|
228
|
+
console.log(`\nSuccessfully added contract '${contractName}' to logrix.yaml!`);
|
|
229
|
+
console.log("Next steps:");
|
|
230
|
+
console.log(` 1. Review / replace ABI at: abis/${contractName}.json`);
|
|
231
|
+
console.log(" 2. Run: logrix codegen");
|
|
232
|
+
console.log(" 3. Run: logrix build\n");
|
|
233
|
+
});
|
|
234
|
+
return cmd;
|
|
235
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
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.buildCommand = buildCommand;
|
|
37
|
+
const commander_1 = require("commander");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const child_process_1 = require("child_process");
|
|
41
|
+
const yaml_1 = require("yaml");
|
|
42
|
+
function buildCommand() {
|
|
43
|
+
const cmd = new commander_1.Command("build");
|
|
44
|
+
cmd
|
|
45
|
+
.description("Compile AssemblyScript handler to WASM using asc")
|
|
46
|
+
.option("--entry <file>", "Handler entry point", "src/mapping.ts")
|
|
47
|
+
.option("--out <file>", "Output WASM path", "build/mapping.wasm")
|
|
48
|
+
.option("--debug", "Build without optimization (for debugging)")
|
|
49
|
+
.action((opts) => {
|
|
50
|
+
const projectDir = process.cwd();
|
|
51
|
+
// Resolve entry and output paths
|
|
52
|
+
const entryFile = path.resolve(projectDir, opts.entry);
|
|
53
|
+
const outFile = path.resolve(projectDir, opts.out);
|
|
54
|
+
if (!fs.existsSync(entryFile)) {
|
|
55
|
+
console.error(`Error: entry file not found: ${opts.entry}`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
// Ensure output directory exists
|
|
59
|
+
fs.mkdirSync(path.dirname(outFile), { recursive: true });
|
|
60
|
+
// Prefer logrix.yaml mapping.file if present
|
|
61
|
+
const configPath = path.join(projectDir, "logrix.yaml");
|
|
62
|
+
let resolvedEntry = entryFile;
|
|
63
|
+
if (fs.existsSync(configPath)) {
|
|
64
|
+
try {
|
|
65
|
+
const config = (0, yaml_1.parse)(fs.readFileSync(configPath, "utf-8"));
|
|
66
|
+
const mappingFile = config?.dataSources?.[0]?.mapping?.file;
|
|
67
|
+
if (mappingFile) {
|
|
68
|
+
const fromConfig = path.resolve(projectDir, mappingFile);
|
|
69
|
+
if (fs.existsSync(fromConfig)) {
|
|
70
|
+
resolvedEntry = fromConfig;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// fallback to opts.entry
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Locate asc: prefer local node_modules, fall back to global
|
|
79
|
+
let ascBin = path.join(projectDir, "node_modules", ".bin", "asc");
|
|
80
|
+
if (!fs.existsSync(ascBin)) {
|
|
81
|
+
ascBin = "asc"; // global
|
|
82
|
+
}
|
|
83
|
+
const optimizeFlag = opts.debug ? "" : "--optimize";
|
|
84
|
+
const cmd = [
|
|
85
|
+
ascBin,
|
|
86
|
+
resolvedEntry,
|
|
87
|
+
"--config", "tsconfig.json",
|
|
88
|
+
"-o", outFile,
|
|
89
|
+
optimizeFlag,
|
|
90
|
+
"--exportRuntime",
|
|
91
|
+
]
|
|
92
|
+
.filter(Boolean)
|
|
93
|
+
.join(" ");
|
|
94
|
+
console.log(`Running: ${cmd}\n`);
|
|
95
|
+
try {
|
|
96
|
+
(0, child_process_1.execSync)(cmd, { cwd: projectDir, stdio: "inherit" });
|
|
97
|
+
const stat = fs.statSync(outFile);
|
|
98
|
+
console.log(`\nBuild complete: ${path.relative(projectDir, outFile)} (${stat.size} bytes)`);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
console.error("\nBuild failed.");
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
return cmd;
|
|
106
|
+
}
|