create-ponder 0.2.0-next.1 → 0.2.0-next.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/dist/index.js +361 -211
- package/package.json +5 -4
- package/templates/empty/package.json +1 -4
- package/templates/empty/ponder-env.d.ts +25 -3
- package/templates/etherscan/package.json +1 -4
- package/templates/etherscan/ponder-env.d.ts +25 -3
- package/templates/feature-factory/package.json +1 -4
- package/templates/feature-factory/ponder-env.d.ts +25 -3
- package/templates/feature-filter/package.json +1 -4
- package/templates/feature-filter/ponder-env.d.ts +25 -3
- package/templates/feature-multichain/package.json +1 -4
- package/templates/feature-multichain/ponder-env.d.ts +25 -3
- package/templates/feature-proxy/package.json +1 -4
- package/templates/feature-proxy/ponder-env.d.ts +25 -3
- package/templates/feature-read-contract/package.json +1 -4
- package/templates/feature-read-contract/ponder-env.d.ts +25 -3
- package/templates/project-friendtech/package.json +1 -4
- package/templates/project-friendtech/ponder-env.d.ts +25 -3
- package/templates/project-uniswap-v3-flash/package.json +1 -4
- package/templates/project-uniswap-v3-flash/ponder-env.d.ts +25 -3
- package/templates/reference-erc20/package.json +1 -4
- package/templates/reference-erc20/ponder-env.d.ts +25 -3
- package/templates/reference-erc721/package.json +1 -4
- package/templates/reference-erc721/ponder-env.d.ts +25 -3
- package/templates/subgraph/_dot_env.local +5 -0
- package/templates/subgraph/_dot_eslintrc.json +3 -0
- package/templates/subgraph/_dot_gitignore +18 -0
- package/templates/subgraph/package.json +26 -0
- package/templates/subgraph/ponder-env.d.ts +32 -0
- package/templates/subgraph/ponder.schema.ts +8 -0
- package/templates/subgraph/tsconfig.json +26 -0
package/dist/index.js
CHANGED
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { writeFileSync as
|
|
5
|
-
import
|
|
4
|
+
import { writeFileSync as writeFileSync3 } from "node:fs";
|
|
5
|
+
import path4 from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { cac } from "cac";
|
|
8
8
|
import cpy from "cpy";
|
|
9
9
|
import { execa } from "execa";
|
|
10
10
|
import fs2 from "fs-extra";
|
|
11
11
|
import { oraPromise } from "ora";
|
|
12
|
-
import
|
|
13
|
-
import
|
|
12
|
+
import pico5 from "picocolors";
|
|
13
|
+
import prettier3 from "prettier";
|
|
14
14
|
import { default as prompts } from "prompts";
|
|
15
15
|
|
|
16
16
|
// package.json
|
|
17
17
|
var package_default = {
|
|
18
18
|
name: "create-ponder",
|
|
19
|
-
version: "0.2.0-next.
|
|
19
|
+
version: "0.2.0-next.2",
|
|
20
20
|
type: "module",
|
|
21
21
|
description: "A CLI tool to create Ponder apps",
|
|
22
22
|
license: "MIT",
|
|
@@ -44,7 +44,9 @@ var package_default = {
|
|
|
44
44
|
prettier: "^3.1.0",
|
|
45
45
|
prompts: "^2.4.2",
|
|
46
46
|
"update-check": "^1.5.4",
|
|
47
|
-
"validate-npm-package-name": "^5.0.0"
|
|
47
|
+
"validate-npm-package-name": "^5.0.0",
|
|
48
|
+
viem: "^2.0.10",
|
|
49
|
+
yaml: "^2.3.4"
|
|
48
50
|
},
|
|
49
51
|
devDependencies: {
|
|
50
52
|
"@types/fs-extra": "^11.0.4",
|
|
@@ -54,129 +56,93 @@ var package_default = {
|
|
|
54
56
|
abitype: "^0.10.2",
|
|
55
57
|
dotenv: "^16.3.1",
|
|
56
58
|
tsup: "^8.0.1",
|
|
57
|
-
typescript: "^5.3.2",
|
|
58
59
|
vitest: "^1.0.2"
|
|
59
60
|
},
|
|
60
61
|
engines: {
|
|
61
|
-
node: ">=18"
|
|
62
|
+
node: ">=18.14"
|
|
62
63
|
}
|
|
63
64
|
};
|
|
64
65
|
|
|
65
66
|
// src/etherscan.ts
|
|
66
67
|
import { mkdirSync, writeFileSync } from "node:fs";
|
|
67
68
|
import path from "node:path";
|
|
68
|
-
import prettier from "prettier";
|
|
69
|
-
|
|
70
|
-
// src/helpers/getEtherscanChainId.ts
|
|
71
|
-
var networkByEtherscanHostname = {
|
|
72
|
-
"etherscan.io": {
|
|
73
|
-
name: "mainnet",
|
|
74
|
-
chainId: 1,
|
|
75
|
-
apiUrl: "https://api.etherscan.io/api"
|
|
76
|
-
},
|
|
77
|
-
"ropsten.etherscan.io": {
|
|
78
|
-
name: "ropsten",
|
|
79
|
-
chainId: 3,
|
|
80
|
-
apiUrl: "https://api-ropsten.etherscan.io/api"
|
|
81
|
-
},
|
|
82
|
-
"rinkeby.etherscan.io": {
|
|
83
|
-
name: "rinkeby",
|
|
84
|
-
chainId: 4,
|
|
85
|
-
apiUrl: "https://api-rinkeby.etherscan.io/api"
|
|
86
|
-
},
|
|
87
|
-
"goerli.etherscan.io": {
|
|
88
|
-
name: "goerli",
|
|
89
|
-
chainId: 5,
|
|
90
|
-
apiUrl: "https://api-goerli.etherscan.io/api"
|
|
91
|
-
},
|
|
92
|
-
"kovan.etherscan.io": {
|
|
93
|
-
name: "kovan",
|
|
94
|
-
chainId: 42,
|
|
95
|
-
apiUrl: "https://api-kovan.etherscan.io/api"
|
|
96
|
-
},
|
|
97
|
-
"sepolia.etherscan.io": {
|
|
98
|
-
name: "sepolia",
|
|
99
|
-
chainId: 11155111,
|
|
100
|
-
apiUrl: "https://api-sepolia.etherscan.io/api"
|
|
101
|
-
},
|
|
102
|
-
"optimistic.etherscan.io": {
|
|
103
|
-
name: "optimism",
|
|
104
|
-
chainId: 10,
|
|
105
|
-
apiUrl: "https://api-optimistic.etherscan.io/api"
|
|
106
|
-
},
|
|
107
|
-
"goerli-optimism.etherscan.io": {
|
|
108
|
-
name: "optimism-goerli",
|
|
109
|
-
chainId: 420,
|
|
110
|
-
apiUrl: "https://api-goerli-optimistic.etherscan.io/api"
|
|
111
|
-
},
|
|
112
|
-
"polygonscan.com": {
|
|
113
|
-
name: "polygon",
|
|
114
|
-
chainId: 137,
|
|
115
|
-
apiUrl: "https://api.polygonscan.com/api"
|
|
116
|
-
},
|
|
117
|
-
"mumbai.polygonscan.com": {
|
|
118
|
-
name: "polygon-mumbai",
|
|
119
|
-
chainId: 80001,
|
|
120
|
-
apiUrl: "https://api-testnet.polygonscan.com/api"
|
|
121
|
-
},
|
|
122
|
-
"arbiscan.io": {
|
|
123
|
-
name: "arbitrum",
|
|
124
|
-
chainId: 42161,
|
|
125
|
-
apiUrl: "https://api.arbiscan.io/api"
|
|
126
|
-
},
|
|
127
|
-
"goerli.arbiscan.io": {
|
|
128
|
-
name: "arbitrum-goerli",
|
|
129
|
-
chainId: 421613,
|
|
130
|
-
apiUrl: "https://api-goerli.arbiscan.io/api"
|
|
131
|
-
},
|
|
132
|
-
"explorer.zora.energy": {
|
|
133
|
-
name: "zora",
|
|
134
|
-
chainId: 7777777,
|
|
135
|
-
apiUrl: "https://explorer.zora.energy/api"
|
|
136
|
-
},
|
|
137
|
-
"basescan.org": {
|
|
138
|
-
name: "base",
|
|
139
|
-
chainId: 8453,
|
|
140
|
-
apiUrl: "https://api.basescan.org/api"
|
|
141
|
-
},
|
|
142
|
-
"goerli.basescan.org": {
|
|
143
|
-
name: "base-goerli",
|
|
144
|
-
chainId: 84531,
|
|
145
|
-
apiUrl: "https://api-goerli.basescan.org/api"
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
var getNetworkByEtherscanHostname = (hostname) => {
|
|
149
|
-
return networkByEtherscanHostname[hostname];
|
|
150
|
-
};
|
|
151
69
|
|
|
152
70
|
// src/helpers/wait.ts
|
|
153
71
|
var wait = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
154
72
|
|
|
155
73
|
// src/etherscan.ts
|
|
74
|
+
import pico from "picocolors";
|
|
75
|
+
import prettier from "prettier";
|
|
76
|
+
import * as chains from "viem/chains";
|
|
77
|
+
var chainExplorerByHostname = {};
|
|
78
|
+
for (const [name, chain] of Object.entries(chains)) {
|
|
79
|
+
for (const explorer of Object.values(chain.blockExplorers ?? {})) {
|
|
80
|
+
const hostname = new URL(explorer.url).hostname;
|
|
81
|
+
chainExplorerByHostname[hostname] = {
|
|
82
|
+
name,
|
|
83
|
+
id: chain.id,
|
|
84
|
+
explorer
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
156
88
|
var fromEtherscan = async ({
|
|
157
89
|
rootDir,
|
|
158
90
|
etherscanLink,
|
|
159
91
|
etherscanApiKey
|
|
160
92
|
}) => {
|
|
93
|
+
const warnings = [];
|
|
161
94
|
const apiKey = etherscanApiKey || process.env.ETHERSCAN_API_KEY;
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
if (!
|
|
165
|
-
throw new Error(
|
|
95
|
+
const explorerUrl = new URL(etherscanLink);
|
|
96
|
+
const chainExplorer = chainExplorerByHostname[explorerUrl.hostname];
|
|
97
|
+
if (!chainExplorer)
|
|
98
|
+
throw new Error(
|
|
99
|
+
`Block explorer (${explorerUrl.hostname}) is not present in viem/chains.`
|
|
100
|
+
);
|
|
101
|
+
const name = chainExplorer.name;
|
|
102
|
+
const chainId = chainExplorer.id;
|
|
103
|
+
const apiUrl = chainExplorer.explorer.apiUrl;
|
|
104
|
+
if (!apiUrl)
|
|
105
|
+
throw new Error(
|
|
106
|
+
`${pico.red("\u2717")} Block explorer (${explorerUrl.hostname}) does not have a API URL registered in viem/chains.`
|
|
107
|
+
);
|
|
108
|
+
const pathComponents = explorerUrl.pathname.slice(1).split("/");
|
|
109
|
+
const contractAddress = pathComponents[1];
|
|
110
|
+
if (pathComponents[0] !== "address" || !(typeof contractAddress === "string") || !contractAddress.startsWith("0x")) {
|
|
111
|
+
throw new Error(
|
|
112
|
+
`${pico.red("\u2717")} Invalid block explorer URL (${explorerUrl.href}). Expected path "/address/<contract-address>".`
|
|
113
|
+
);
|
|
166
114
|
}
|
|
167
|
-
|
|
168
|
-
|
|
115
|
+
let abiResult = void 0;
|
|
116
|
+
try {
|
|
117
|
+
abiResult = await getContractAbiAndName(contractAddress, apiUrl, apiKey);
|
|
118
|
+
} catch (e) {
|
|
119
|
+
const error = e;
|
|
120
|
+
throw new Error(
|
|
121
|
+
`${pico.red("\u2717")} Failed to fetch contract ABI from block explorer API: ${error.message}`
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
if (typeof abiResult.abi !== "string")
|
|
125
|
+
throw new Error(
|
|
126
|
+
`${pico.red(
|
|
127
|
+
"\u2717"
|
|
128
|
+
)} Invalid ABI returned from block explorer API. Is the contract verified?`
|
|
129
|
+
);
|
|
130
|
+
const baseAbi = JSON.parse(abiResult.abi);
|
|
131
|
+
let contractName = abiResult.contractName;
|
|
132
|
+
const abis = [
|
|
133
|
+
{ abi: baseAbi, contractName }
|
|
134
|
+
];
|
|
169
135
|
let blockNumber = void 0;
|
|
170
136
|
try {
|
|
137
|
+
if (!apiKey)
|
|
138
|
+
await wait(5e3);
|
|
171
139
|
const txHash = await getContractCreationTxn(
|
|
172
140
|
contractAddress,
|
|
173
141
|
apiUrl,
|
|
174
142
|
apiKey
|
|
175
143
|
);
|
|
176
|
-
if (!apiKey)
|
|
177
|
-
console.log("\n(1/n) Waiting 5 seconds for Etherscan API rate limit");
|
|
144
|
+
if (!apiKey)
|
|
178
145
|
await wait(5e3);
|
|
179
|
-
}
|
|
180
146
|
const contractCreationBlockNumber = await getTxBlockNumber(
|
|
181
147
|
txHash,
|
|
182
148
|
apiUrl,
|
|
@@ -185,47 +151,30 @@ var fromEtherscan = async ({
|
|
|
185
151
|
blockNumber = contractCreationBlockNumber;
|
|
186
152
|
} catch (error) {
|
|
187
153
|
}
|
|
188
|
-
if (
|
|
189
|
-
console.log("(2/n) Waiting 5 seconds for Etherscan API rate limit");
|
|
190
|
-
await wait(5e3);
|
|
191
|
-
}
|
|
192
|
-
const abis = [];
|
|
193
|
-
const abiAndName = await getContractAbiAndName(
|
|
194
|
-
contractAddress,
|
|
195
|
-
apiUrl,
|
|
196
|
-
apiKey
|
|
197
|
-
);
|
|
198
|
-
const { abi } = abiAndName;
|
|
199
|
-
let contractName = abiAndName.contractName;
|
|
200
|
-
abis.push({ abi: JSON.parse(abi), contractName });
|
|
201
|
-
if (JSON.parse(abi).find(
|
|
154
|
+
if (baseAbi.find(
|
|
202
155
|
(item) => item.type === "event" && item.name === "Upgraded" && item.inputs[0].name === "implementation"
|
|
203
156
|
)) {
|
|
204
|
-
|
|
205
|
-
"Detected EIP-1967 proxy, fetching implementation contract ABIs"
|
|
206
|
-
);
|
|
207
|
-
if (!apiKey) {
|
|
208
|
-
console.log("(3/n) Waiting 5 seconds for Etherscan API rate limit");
|
|
157
|
+
if (!apiKey)
|
|
209
158
|
await wait(5e3);
|
|
210
|
-
}
|
|
211
159
|
const { implAddresses } = await getProxyImplementationAddresses({
|
|
212
160
|
contractAddress,
|
|
213
161
|
apiUrl,
|
|
214
162
|
fromBlock: blockNumber,
|
|
215
163
|
apiKey
|
|
216
164
|
});
|
|
217
|
-
for (const
|
|
218
|
-
|
|
219
|
-
if (!apiKey) {
|
|
220
|
-
console.log(
|
|
221
|
-
`(${4 + index}/${4 + implAddresses.length - 1}) Waiting 5 seconds for Etherscan API rate limit`
|
|
222
|
-
);
|
|
165
|
+
for (const implAddress of implAddresses) {
|
|
166
|
+
if (!apiKey)
|
|
223
167
|
await wait(5e3);
|
|
168
|
+
const { abi, contractName: implContractName } = await getContractAbiAndName(implAddress, apiUrl, apiKey);
|
|
169
|
+
if (typeof abi !== "string") {
|
|
170
|
+
warnings.push(
|
|
171
|
+
`Unable to fetch ABI for implementation contract ${implAddress}. Please see the proxy contract documentation for more details: https://ponder.sh/docs/guides/add-contracts#multiple-abis`
|
|
172
|
+
);
|
|
173
|
+
continue;
|
|
224
174
|
}
|
|
225
|
-
const { abi: abi2, contractName: implContractName } = await getContractAbiAndName(implAddress, apiUrl, apiKey);
|
|
226
175
|
contractName = implContractName;
|
|
227
176
|
abis.push({
|
|
228
|
-
abi: JSON.parse(
|
|
177
|
+
abi: JSON.parse(abi),
|
|
229
178
|
contractName: `${contractName}_${implAddress.slice(0, 6)}`
|
|
230
179
|
});
|
|
231
180
|
}
|
|
@@ -233,7 +182,7 @@ var fromEtherscan = async ({
|
|
|
233
182
|
mkdirSync(path.join(rootDir, "abis"), { recursive: true });
|
|
234
183
|
mkdirSync(path.join(rootDir, "src"), { recursive: true });
|
|
235
184
|
let abiConfig;
|
|
236
|
-
for (const { abi
|
|
185
|
+
for (const { abi, contractName: contractName2 } of abis) {
|
|
237
186
|
const abiRelativePath = `./abis/${contractName2}Abi.ts`;
|
|
238
187
|
const abiAbsolutePath = path.join(
|
|
239
188
|
path.resolve(".", rootDir),
|
|
@@ -242,15 +191,13 @@ var fromEtherscan = async ({
|
|
|
242
191
|
writeFileSync(
|
|
243
192
|
abiAbsolutePath,
|
|
244
193
|
await prettier.format(
|
|
245
|
-
`export const ${contractName2}Abi = ${JSON.stringify(
|
|
246
|
-
{
|
|
247
|
-
parser: "typescript"
|
|
248
|
-
}
|
|
194
|
+
`export const ${contractName2}Abi = ${JSON.stringify(abi)} as const`,
|
|
195
|
+
{ parser: "typescript" }
|
|
249
196
|
)
|
|
250
197
|
);
|
|
251
198
|
if (abis.length === 1) {
|
|
252
199
|
abiConfig = {
|
|
253
|
-
abi
|
|
200
|
+
abi,
|
|
254
201
|
dir: abiRelativePath,
|
|
255
202
|
name: `${contractName2}Abi`
|
|
256
203
|
};
|
|
@@ -259,7 +206,7 @@ var fromEtherscan = async ({
|
|
|
259
206
|
abiConfig = [];
|
|
260
207
|
}
|
|
261
208
|
abiConfig.push({
|
|
262
|
-
abi
|
|
209
|
+
abi,
|
|
263
210
|
name: `${contractName2}Abi`,
|
|
264
211
|
dir: abiRelativePath
|
|
265
212
|
});
|
|
@@ -281,7 +228,7 @@ var fromEtherscan = async ({
|
|
|
281
228
|
}
|
|
282
229
|
}
|
|
283
230
|
};
|
|
284
|
-
return config;
|
|
231
|
+
return { config, warnings };
|
|
285
232
|
};
|
|
286
233
|
var fetchEtherscan = async (url) => {
|
|
287
234
|
const maxRetries = 5;
|
|
@@ -290,9 +237,6 @@ var fetchEtherscan = async (url) => {
|
|
|
290
237
|
try {
|
|
291
238
|
const response = await fetch(url);
|
|
292
239
|
const data = await response.json();
|
|
293
|
-
if (data.status === "0") {
|
|
294
|
-
throw new Error(`Etherscan API error: ${data.result}`);
|
|
295
|
-
}
|
|
296
240
|
return data;
|
|
297
241
|
} catch (error) {
|
|
298
242
|
retryCount++;
|
|
@@ -368,7 +312,7 @@ var getProxyImplementationAddresses = async ({
|
|
|
368
312
|
};
|
|
369
313
|
|
|
370
314
|
// src/helpers/getPackageManager.ts
|
|
371
|
-
import
|
|
315
|
+
import pico2 from "picocolors";
|
|
372
316
|
var getPackageManager = ({
|
|
373
317
|
options
|
|
374
318
|
}) => {
|
|
@@ -393,11 +337,11 @@ var getPackageManager = ({
|
|
|
393
337
|
if (userAgent.includes("yarn"))
|
|
394
338
|
return "yarn";
|
|
395
339
|
}
|
|
396
|
-
throw Error(
|
|
340
|
+
throw Error(pico2.red("Undetectable package manager"));
|
|
397
341
|
};
|
|
398
342
|
|
|
399
343
|
// src/helpers/notifyUpdate.ts
|
|
400
|
-
import
|
|
344
|
+
import pico3 from "picocolors";
|
|
401
345
|
import checkForUpdate from "update-check";
|
|
402
346
|
var log = console.log;
|
|
403
347
|
async function notifyUpdate({ options }) {
|
|
@@ -407,11 +351,11 @@ async function notifyUpdate({ options }) {
|
|
|
407
351
|
const packageManager = getPackageManager({ options });
|
|
408
352
|
const updateMessage = packageManager === "bun" ? "bun install --global create-ponder" : packageManager === "pnpm" ? "pnpm add -g create-ponder" : packageManager === "npm" ? "npm install -g create-ponder" : "yarn global add create-ponder";
|
|
409
353
|
log(
|
|
410
|
-
|
|
411
|
-
`${
|
|
354
|
+
pico3.bold(
|
|
355
|
+
`${pico3.yellow(
|
|
412
356
|
"A new version of `create-ponder` is available!"
|
|
413
357
|
)}
|
|
414
|
-
You can update by running: ${
|
|
358
|
+
You can update by running: ${pico3.cyan(updateMessage)}
|
|
415
359
|
`
|
|
416
360
|
)
|
|
417
361
|
);
|
|
@@ -424,7 +368,7 @@ You can update by running: ${pico2.cyan(updateMessage)}
|
|
|
424
368
|
// src/helpers/validate.ts
|
|
425
369
|
import path2 from "path";
|
|
426
370
|
import fs from "fs-extra";
|
|
427
|
-
import
|
|
371
|
+
import pico4 from "picocolors";
|
|
428
372
|
import validatePackageName from "validate-npm-package-name";
|
|
429
373
|
async function validateProjectName({
|
|
430
374
|
projectName,
|
|
@@ -475,10 +419,147 @@ async function validateTemplateName({
|
|
|
475
419
|
var ValidationError = class extends Error {
|
|
476
420
|
name = "ValidationError";
|
|
477
421
|
constructor(validation) {
|
|
478
|
-
super([
|
|
422
|
+
super([pico4.red(validation.message), validation.problems].join("\n"));
|
|
479
423
|
}
|
|
480
424
|
};
|
|
481
425
|
|
|
426
|
+
// src/subgraph.ts
|
|
427
|
+
import { mkdirSync as mkdirSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
428
|
+
import path3 from "node:path";
|
|
429
|
+
import prettier2 from "prettier";
|
|
430
|
+
import { parse } from "yaml";
|
|
431
|
+
|
|
432
|
+
// src/helpers/getGraphProtocolChainId.ts
|
|
433
|
+
var chainIdByGraphNetwork = {
|
|
434
|
+
mainnet: 1,
|
|
435
|
+
kovan: 42,
|
|
436
|
+
rinkeby: 4,
|
|
437
|
+
ropsten: 3,
|
|
438
|
+
goerli: 5,
|
|
439
|
+
"poa-core": 99,
|
|
440
|
+
"poa-sokol": 77,
|
|
441
|
+
xdai: 100,
|
|
442
|
+
matic: 137,
|
|
443
|
+
mumbai: 80001,
|
|
444
|
+
fantom: 250,
|
|
445
|
+
"fantom-testnet": 4002,
|
|
446
|
+
bsc: 56,
|
|
447
|
+
chapel: -1,
|
|
448
|
+
clover: 0,
|
|
449
|
+
avalanche: 43114,
|
|
450
|
+
fuji: 43113,
|
|
451
|
+
celo: 42220,
|
|
452
|
+
"celo-alfajores": 44787,
|
|
453
|
+
fuse: 122,
|
|
454
|
+
moonbeam: 1284,
|
|
455
|
+
moonriver: 1285,
|
|
456
|
+
mbase: -1,
|
|
457
|
+
"arbitrum-one": 42161,
|
|
458
|
+
"arbitrum-rinkeby": 421611,
|
|
459
|
+
optimism: 10,
|
|
460
|
+
"optimism-kovan": 69,
|
|
461
|
+
aurora: 1313161554,
|
|
462
|
+
"aurora-testnet": 1313161555
|
|
463
|
+
};
|
|
464
|
+
var getGraphProtocolChainId = (networkName) => {
|
|
465
|
+
return chainIdByGraphNetwork[networkName];
|
|
466
|
+
};
|
|
467
|
+
var subgraphYamlFileNames = ["subgraph.yaml"].concat(
|
|
468
|
+
Object.keys(chainIdByGraphNetwork).map((n) => `subgraph-${n}.yaml`)
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
// src/helpers/validateGraphProtocolSource.ts
|
|
472
|
+
var validateGraphProtocolSource = (source) => {
|
|
473
|
+
return source;
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
// src/subgraph.ts
|
|
477
|
+
var fetchIpfsFile = async (cid) => {
|
|
478
|
+
const url = `https://ipfs.network.thegraph.com/api/v0/cat?arg=${cid}`;
|
|
479
|
+
const response = await fetch(url);
|
|
480
|
+
const contentRaw = await response.text();
|
|
481
|
+
return contentRaw;
|
|
482
|
+
};
|
|
483
|
+
var fromSubgraphId = async ({
|
|
484
|
+
rootDir,
|
|
485
|
+
subgraphId
|
|
486
|
+
}) => {
|
|
487
|
+
const manifestRaw = await fetchIpfsFile(subgraphId);
|
|
488
|
+
const manifest = parse(manifestRaw);
|
|
489
|
+
const contracts = {};
|
|
490
|
+
manifest.dataSources.forEach((d) => {
|
|
491
|
+
contracts[d.name] = {
|
|
492
|
+
network: d.network,
|
|
493
|
+
address: d.source.address,
|
|
494
|
+
startBlock: d.source.startBlock
|
|
495
|
+
};
|
|
496
|
+
});
|
|
497
|
+
const dataSources = manifest.dataSources;
|
|
498
|
+
mkdirSync2(path3.join(rootDir, "abis"), { recursive: true });
|
|
499
|
+
mkdirSync2(path3.join(rootDir, "src"), { recursive: true });
|
|
500
|
+
const abiFiles = dataSources.flatMap((source) => validateGraphProtocolSource(source).mapping.abis).filter(
|
|
501
|
+
(source, idx, arr) => arr.findIndex((s) => s.name === source.name) === idx
|
|
502
|
+
);
|
|
503
|
+
const abis = {};
|
|
504
|
+
await Promise.all(
|
|
505
|
+
abiFiles.map(async (abi) => {
|
|
506
|
+
const abiContent = await fetchIpfsFile(abi.file["/"].slice(6));
|
|
507
|
+
const abiPath = path3.join(rootDir, `./abis/${abi.name}Abi.ts`);
|
|
508
|
+
writeFileSync2(
|
|
509
|
+
abiPath,
|
|
510
|
+
await prettier2.format(
|
|
511
|
+
`export const ${abi.name}Abi = ${abiContent} as const`,
|
|
512
|
+
{ parser: "typescript" }
|
|
513
|
+
)
|
|
514
|
+
);
|
|
515
|
+
abis[abi.name] = JSON.parse(abiContent);
|
|
516
|
+
})
|
|
517
|
+
);
|
|
518
|
+
const ponderContracts = dataSources.map((sourceInvalid) => {
|
|
519
|
+
const source = validateGraphProtocolSource(sourceInvalid);
|
|
520
|
+
const network = source.network || "mainnet";
|
|
521
|
+
const chainId = getGraphProtocolChainId(network);
|
|
522
|
+
if (!chainId || chainId === -1) {
|
|
523
|
+
throw new Error(`Unhandled network name: ${network}`);
|
|
524
|
+
}
|
|
525
|
+
const abiRelativePath = `./abis/${source.source.abi}Abi.ts`;
|
|
526
|
+
return {
|
|
527
|
+
name: source.name,
|
|
528
|
+
network,
|
|
529
|
+
address: source.source.address,
|
|
530
|
+
abi: {
|
|
531
|
+
abi: abis[source.source.abi],
|
|
532
|
+
dir: abiRelativePath,
|
|
533
|
+
name: `${source.source.abi}Abi`
|
|
534
|
+
},
|
|
535
|
+
startBlock: source.source.startBlock
|
|
536
|
+
};
|
|
537
|
+
});
|
|
538
|
+
const contractsObject = {};
|
|
539
|
+
const networksObject = {};
|
|
540
|
+
ponderContracts.forEach((pc) => {
|
|
541
|
+
contractsObject[pc.name] = pc;
|
|
542
|
+
networksObject[pc.network] = {
|
|
543
|
+
chainId: getGraphProtocolChainId(pc.network),
|
|
544
|
+
transport: `http(process.env.PONDER_RPC_URL_${getGraphProtocolChainId(
|
|
545
|
+
pc.network
|
|
546
|
+
)})`
|
|
547
|
+
};
|
|
548
|
+
contractsObject[pc.name].name = void 0;
|
|
549
|
+
});
|
|
550
|
+
const config = {
|
|
551
|
+
networks: networksObject,
|
|
552
|
+
contracts: contractsObject
|
|
553
|
+
};
|
|
554
|
+
const warnings = [];
|
|
555
|
+
if (manifest.templates?.length > 0) {
|
|
556
|
+
warnings.push(
|
|
557
|
+
"Factory contract detected. Please see the factory contract documentation for more details: https://ponder.sh/docs/guides/add-contracts#factory-contracts"
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
return { config, warnings };
|
|
561
|
+
};
|
|
562
|
+
|
|
482
563
|
// src/index.ts
|
|
483
564
|
var log2 = console.log;
|
|
484
565
|
var templates = [
|
|
@@ -486,7 +567,12 @@ var templates = [
|
|
|
486
567
|
{
|
|
487
568
|
id: "etherscan",
|
|
488
569
|
title: "Etherscan contract link",
|
|
489
|
-
description: "
|
|
570
|
+
description: "Create from an Etherscan contract link"
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
id: "subgraph",
|
|
574
|
+
title: "Subgraph ID",
|
|
575
|
+
description: "Create from a deployed subgraph"
|
|
490
576
|
},
|
|
491
577
|
{
|
|
492
578
|
id: "feature-factory",
|
|
@@ -540,15 +626,16 @@ async function run({
|
|
|
540
626
|
}) {
|
|
541
627
|
if (options.help)
|
|
542
628
|
return;
|
|
629
|
+
const warnings = [];
|
|
543
630
|
log2();
|
|
544
631
|
log2(
|
|
545
|
-
`Welcome to ${
|
|
546
|
-
|
|
632
|
+
`Welcome to ${pico5.bold(
|
|
633
|
+
pico5.blue("create-ponder")
|
|
547
634
|
)} \u2013 the quickest way to get started with Ponder!`
|
|
548
635
|
);
|
|
549
636
|
log2();
|
|
550
637
|
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
551
|
-
const templatesPath =
|
|
638
|
+
const templatesPath = path4.join(__dirname, "..", "templates");
|
|
552
639
|
let templateId = options.template || options.t;
|
|
553
640
|
let templateValidation = await validateTemplateName({
|
|
554
641
|
isNameRequired: false,
|
|
@@ -563,7 +650,7 @@ async function run({
|
|
|
563
650
|
projectPath = args[0].trim();
|
|
564
651
|
const splitPath = projectPath.split("/");
|
|
565
652
|
projectName = splitPath[splitPath.length - 1]?.trim() || "";
|
|
566
|
-
log2(
|
|
653
|
+
log2(pico5.green("\u2714"), pico5.bold("Using project name:"), projectName);
|
|
567
654
|
} else {
|
|
568
655
|
const res = await prompts({
|
|
569
656
|
initial: "my-app",
|
|
@@ -589,8 +676,10 @@ async function run({
|
|
|
589
676
|
});
|
|
590
677
|
if (!nameValidation.valid)
|
|
591
678
|
throw new ValidationError(nameValidation);
|
|
592
|
-
if (options.
|
|
679
|
+
if (options.etherscan && !templateId)
|
|
593
680
|
templateId = "etherscan";
|
|
681
|
+
if (options.subgraph && !templateId)
|
|
682
|
+
templateId = "subgraph";
|
|
594
683
|
if (!templateId) {
|
|
595
684
|
templateId = (await prompts({
|
|
596
685
|
name: "templateId",
|
|
@@ -612,30 +701,69 @@ async function run({
|
|
|
612
701
|
if (!templateValidation.valid)
|
|
613
702
|
throw new ValidationError(templateValidation);
|
|
614
703
|
let config;
|
|
615
|
-
const targetPath =
|
|
704
|
+
const targetPath = path4.join(process.cwd(), projectPath);
|
|
705
|
+
let url = options.etherscan;
|
|
616
706
|
if (templateMeta.id === "etherscan") {
|
|
617
|
-
|
|
618
|
-
if (!link) {
|
|
707
|
+
if (!url) {
|
|
619
708
|
const result = await prompts({
|
|
620
709
|
type: "text",
|
|
621
|
-
name: "
|
|
622
|
-
message: "Enter
|
|
710
|
+
name: "url",
|
|
711
|
+
message: "Enter a block explorer contract url",
|
|
623
712
|
initial: "https://etherscan.io/address/0x97..."
|
|
624
713
|
});
|
|
625
|
-
|
|
714
|
+
url = result.url;
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
let subgraph = options.subgraph;
|
|
718
|
+
if (templateMeta.id === "subgraph") {
|
|
719
|
+
if (!subgraph) {
|
|
720
|
+
const result = await prompts({
|
|
721
|
+
type: "text",
|
|
722
|
+
name: "id",
|
|
723
|
+
message: "Enter a subgraph ID",
|
|
724
|
+
initial: "Qmb3hd2hYd2nWFgcmRswykF1dUBSrDUrinYCgN1dmE1tNy"
|
|
725
|
+
});
|
|
726
|
+
subgraph = result.id;
|
|
727
|
+
}
|
|
728
|
+
if (!subgraph) {
|
|
729
|
+
log2(pico5.red("No subgraph ID provided."));
|
|
730
|
+
process.exit(0);
|
|
626
731
|
}
|
|
627
|
-
config = await fromEtherscan({
|
|
628
|
-
rootDir: targetPath,
|
|
629
|
-
etherscanLink: link,
|
|
630
|
-
etherscanApiKey: options.etherscanApiKey
|
|
631
|
-
});
|
|
632
732
|
}
|
|
633
|
-
log2(`Creating a repo at: ${pico4.green(targetPath)}`);
|
|
634
|
-
log2();
|
|
635
|
-
log2(`Using template: ${pico4.bold(templateMeta.title)}`);
|
|
636
733
|
log2();
|
|
637
|
-
|
|
638
|
-
|
|
734
|
+
if (templateMeta.id === "etherscan") {
|
|
735
|
+
const host = new URL(url).host;
|
|
736
|
+
const result = await oraPromise(
|
|
737
|
+
fromEtherscan({
|
|
738
|
+
rootDir: targetPath,
|
|
739
|
+
etherscanLink: url,
|
|
740
|
+
etherscanApiKey: options.etherscanApiKey
|
|
741
|
+
}),
|
|
742
|
+
{
|
|
743
|
+
text: `Fetching contract metadata from ${pico5.bold(
|
|
744
|
+
host
|
|
745
|
+
)}. This may take a few seconds.`,
|
|
746
|
+
failText: "Failed to fetch contract metadata.",
|
|
747
|
+
successText: `Fetched contract metadata from ${pico5.bold(host)}.`
|
|
748
|
+
}
|
|
749
|
+
);
|
|
750
|
+
config = result.config;
|
|
751
|
+
warnings.push(...result.warnings);
|
|
752
|
+
}
|
|
753
|
+
if (templateMeta.id === "subgraph") {
|
|
754
|
+
const result = await oraPromise(
|
|
755
|
+
fromSubgraphId({ rootDir: targetPath, subgraphId: subgraph }),
|
|
756
|
+
{
|
|
757
|
+
text: "Fetching subgraph metadata. This may take a few seconds.",
|
|
758
|
+
failText: "Failed to fetch subgraph metadata.",
|
|
759
|
+
successText: `Fetched subgraph metadata for ${pico5.bold(subgraph)}.`
|
|
760
|
+
}
|
|
761
|
+
);
|
|
762
|
+
config = result.config;
|
|
763
|
+
warnings.push(...result.warnings);
|
|
764
|
+
}
|
|
765
|
+
const templatePath = path4.join(templatesPath, templateMeta.id);
|
|
766
|
+
await cpy(path4.join(templatePath, "**", "*"), targetPath, {
|
|
639
767
|
rename: (name) => name.replace(/^_dot_/, ".")
|
|
640
768
|
});
|
|
641
769
|
if (config) {
|
|
@@ -643,7 +771,9 @@ async function run({
|
|
|
643
771
|
import { createConfig${Object.values(config.contracts).some((c) => Array.isArray(c.abi)) ? ", mergeAbis" : ""} } from "@ponder/core";
|
|
644
772
|
import { http } from "viem";
|
|
645
773
|
|
|
646
|
-
${Object.values(config.contracts).flatMap((c) => c.abi).
|
|
774
|
+
${Object.values(config.contracts).flatMap((c) => c.abi).filter(
|
|
775
|
+
(tag, index, array) => array.findIndex((t) => t.dir === tag.dir) === index
|
|
776
|
+
).map(
|
|
647
777
|
(abi) => `import {${abi.name}} from "${abi.dir.slice(
|
|
648
778
|
0,
|
|
649
779
|
abi.dir.length - 3
|
|
@@ -669,14 +799,14 @@ async function run({
|
|
|
669
799
|
).replaceAll(/"abi":"(.*?)"/g, "abi:$1")},
|
|
670
800
|
});
|
|
671
801
|
`;
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
await
|
|
802
|
+
writeFileSync3(
|
|
803
|
+
path4.join(targetPath, "ponder.config.ts"),
|
|
804
|
+
await prettier3.format(configContent, { parser: "typescript" })
|
|
675
805
|
);
|
|
676
806
|
for (const [name, contract] of Object.entries(config.contracts)) {
|
|
677
807
|
const abi = Array.isArray(contract.abi) ? contract.abi[1].abi : contract.abi.abi;
|
|
678
808
|
const abiEvents = abi.filter(
|
|
679
|
-
(item) => item.type === "event"
|
|
809
|
+
(item) => item.type === "event" && !item.anonymous
|
|
680
810
|
);
|
|
681
811
|
const eventNamesToWrite = abiEvents.map((event) => event.name).slice(0, 2);
|
|
682
812
|
const indexingFunctionFileContents = `
|
|
@@ -689,25 +819,23 @@ async function run({
|
|
|
689
819
|
})`
|
|
690
820
|
).join("\n")}
|
|
691
821
|
`;
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
await
|
|
822
|
+
writeFileSync3(
|
|
823
|
+
path4.join(targetPath, `./src/${name}.ts`),
|
|
824
|
+
await prettier3.format(indexingFunctionFileContents, {
|
|
695
825
|
parser: "typescript"
|
|
696
826
|
})
|
|
697
827
|
);
|
|
698
828
|
}
|
|
699
829
|
}
|
|
700
|
-
const packageJson = await fs2.readJSON(
|
|
830
|
+
const packageJson = await fs2.readJSON(path4.join(targetPath, "package.json"));
|
|
701
831
|
packageJson.name = projectName;
|
|
702
832
|
packageJson.dependencies["@ponder/core"] = `^${package_default.version}`;
|
|
703
833
|
packageJson.devDependencies["eslint-config-ponder"] = `^${package_default.version}`;
|
|
704
834
|
await fs2.writeFile(
|
|
705
|
-
|
|
835
|
+
path4.join(targetPath, "package.json"),
|
|
706
836
|
JSON.stringify(packageJson, null, 2)
|
|
707
837
|
);
|
|
708
838
|
const packageManager = getPackageManager({ options });
|
|
709
|
-
log2(`Installing with: ${pico4.bold(packageManager)}`);
|
|
710
|
-
log2();
|
|
711
839
|
const installArgs = [
|
|
712
840
|
"install",
|
|
713
841
|
packageManager === "npm" ? "--quiet" : "--silent"
|
|
@@ -725,58 +853,80 @@ async function run({
|
|
|
725
853
|
}
|
|
726
854
|
}),
|
|
727
855
|
{
|
|
728
|
-
text:
|
|
856
|
+
text: `Installing packages with ${pico5.bold(
|
|
857
|
+
packageManager
|
|
858
|
+
)}. This may take a few seconds.`,
|
|
729
859
|
failText: "Failed to install packages.",
|
|
730
|
-
successText:
|
|
860
|
+
successText: `Installed packages with ${pico5.bold(packageManager)}.`
|
|
731
861
|
}
|
|
732
862
|
);
|
|
733
|
-
log2();
|
|
734
863
|
if (!options.skipGit) {
|
|
735
|
-
await
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
864
|
+
await oraPromise(
|
|
865
|
+
async () => {
|
|
866
|
+
await execa("git", ["init"], { cwd: targetPath });
|
|
867
|
+
await execa("git", ["add", "."], { cwd: targetPath });
|
|
868
|
+
await execa(
|
|
869
|
+
"git",
|
|
870
|
+
[
|
|
871
|
+
"commit",
|
|
872
|
+
"--no-verify",
|
|
873
|
+
"--message",
|
|
874
|
+
"chore: initial commit from create-ponder"
|
|
875
|
+
],
|
|
876
|
+
{ cwd: targetPath }
|
|
877
|
+
);
|
|
878
|
+
},
|
|
879
|
+
{
|
|
880
|
+
text: "Initializing git repository.",
|
|
881
|
+
failText: "Failed to initialize git repository.",
|
|
882
|
+
successText: "Initialized git repository."
|
|
883
|
+
}
|
|
746
884
|
);
|
|
747
|
-
|
|
885
|
+
}
|
|
886
|
+
for (const warning of warnings) {
|
|
748
887
|
log2();
|
|
888
|
+
log2(pico5.yellow(warning));
|
|
749
889
|
}
|
|
890
|
+
log2();
|
|
750
891
|
log2("\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015");
|
|
751
892
|
log2();
|
|
752
893
|
log2(
|
|
753
|
-
`${
|
|
894
|
+
`${pico5.green("Success!")} Created ${pico5.bold(
|
|
754
895
|
projectName
|
|
755
|
-
)} at ${
|
|
896
|
+
)} at ${pico5.green(path4.resolve(projectPath))}`
|
|
756
897
|
);
|
|
757
898
|
log2();
|
|
758
899
|
log2(
|
|
759
|
-
`To start your app, run
|
|
760
|
-
|
|
761
|
-
)}
|
|
762
|
-
|
|
900
|
+
`To start your app, run ${pico5.bold(
|
|
901
|
+
pico5.cyan(`cd ${projectPath}`)
|
|
902
|
+
)} and then ${pico5.bold(
|
|
903
|
+
pico5.cyan(
|
|
763
904
|
`${packageManager}${packageManager === "npm" || packageManager === "bun" ? " run" : ""} dev`
|
|
764
905
|
)
|
|
765
|
-
)}
|
|
906
|
+
)}`
|
|
766
907
|
);
|
|
767
908
|
log2();
|
|
768
909
|
log2("\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015\u2015");
|
|
769
910
|
log2();
|
|
770
911
|
}
|
|
771
912
|
(async () => {
|
|
772
|
-
const cli = cac(package_default.name).version(package_default.version).usage(`${
|
|
773
|
-
"-t, --template [
|
|
774
|
-
`
|
|
775
|
-
).option("--etherscan
|
|
776
|
-
|
|
913
|
+
const cli = cac(package_default.name).version(package_default.version).usage(`${pico5.green("<directory>")} [options]`).option(
|
|
914
|
+
"-t, --template [id]",
|
|
915
|
+
`Use a template. Options: ${templates.map(({ id }) => id).join(", ")}`
|
|
916
|
+
).option("--etherscan [url]", "Use the Etherscan template").option("--subgraph [id]", "Use the subgraph template").option("--npm", "Use npm as your package manager").option("--pnpm", "Use pnpm as your package manager").option("--yarn", "Use yarn as your package manager").option("--skip-git", "Skip initializing a git repository").option(
|
|
917
|
+
"--etherscan-api-key [key]",
|
|
918
|
+
"Etherscan API key for Etherscan template"
|
|
919
|
+
).help();
|
|
920
|
+
const _nodeVersion = process.version.split(".");
|
|
921
|
+
const nodeVersion = [
|
|
922
|
+
Number(_nodeVersion[0].slice(1)),
|
|
923
|
+
Number(_nodeVersion[1]),
|
|
924
|
+
Number(_nodeVersion[2])
|
|
925
|
+
];
|
|
926
|
+
if (nodeVersion[0] < 18 || nodeVersion[0] === 18 && nodeVersion[1] < 14)
|
|
777
927
|
throw Error(
|
|
778
|
-
|
|
779
|
-
`Node version:${process.version} does not meet the >=18 requirement`
|
|
928
|
+
pico5.red(
|
|
929
|
+
`Node version:${process.version} does not meet the >=18.14 requirement`
|
|
780
930
|
)
|
|
781
931
|
);
|
|
782
932
|
const { args, options } = cli.parse(process.argv);
|
|
@@ -786,7 +936,7 @@ async function run({
|
|
|
786
936
|
await notifyUpdate({ options });
|
|
787
937
|
} catch (error) {
|
|
788
938
|
log2(
|
|
789
|
-
error instanceof ValidationError ? error.message :
|
|
939
|
+
error instanceof ValidationError ? error.message : pico5.red(error.message)
|
|
790
940
|
);
|
|
791
941
|
log2();
|
|
792
942
|
await notifyUpdate({ options });
|