@everyprotocol/every-cli 0.1.13 → 0.1.14
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/artifact.js +53 -0
- package/dist/cmds/set.js +81 -1
- package/package.json +1 -1
package/dist/artifact.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
/**
|
|
3
|
+
* Load a Foundry artifact path info by ID.
|
|
4
|
+
* ID forms supported:
|
|
5
|
+
* - "File.sol"
|
|
6
|
+
* - "File.sol:Contract"
|
|
7
|
+
* - "path/to/File.sol:Contract"
|
|
8
|
+
* Default artifactDir is "out".
|
|
9
|
+
*
|
|
10
|
+
* Returns: { contract: "Contract", file: "out/File.sol/Contract.json" }
|
|
11
|
+
*/
|
|
12
|
+
export function getArtifactPath(id, artifactDir = "out") {
|
|
13
|
+
// split on LAST ":" so paths containing ":" still work
|
|
14
|
+
const c = id.lastIndexOf(":");
|
|
15
|
+
let contract = c === -1 ? "" : id.slice(c + 1);
|
|
16
|
+
const filePart = c === -1 ? id : id.slice(0, c);
|
|
17
|
+
const fileBase = path.basename(filePart);
|
|
18
|
+
if (!fileBase.toLowerCase().endsWith(".sol")) {
|
|
19
|
+
throw new Error(`"${filePart}" is not a .sol file`);
|
|
20
|
+
}
|
|
21
|
+
// infer contract name if omitted -> File.sol => File
|
|
22
|
+
if (!contract)
|
|
23
|
+
contract = fileBase.slice(0, -".sol".length);
|
|
24
|
+
const file = path.join(artifactDir, fileBase, `${contract}.json`);
|
|
25
|
+
return { contract, file };
|
|
26
|
+
}
|
|
27
|
+
/** Get creation (deploy) bytecode from a Foundry/solc artifact JSON object. */
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
29
|
+
export function getCreationCode(artifact) {
|
|
30
|
+
// Foundry may store as string or { object: string }
|
|
31
|
+
const bc = typeof artifact?.bytecode === "string" ? artifact.bytecode : artifact?.bytecode?.object;
|
|
32
|
+
if (typeof bc !== "string") {
|
|
33
|
+
throw new Error("bytecode missing or not a string");
|
|
34
|
+
}
|
|
35
|
+
// Check for unlinked library placeholders like __$abcd...$__
|
|
36
|
+
if (bc.includes("__$")) {
|
|
37
|
+
const matches = [...bc.matchAll(/__\$([0-9a-fA-F]{34})\$__/g)];
|
|
38
|
+
const labels = [...new Set(matches.map((m) => m[0]))];
|
|
39
|
+
throw new Error(`Unlinked: ${labels.join(", ")}`);
|
|
40
|
+
}
|
|
41
|
+
if (!/^0x[0-9a-fA-F]*$/.test(bc)) {
|
|
42
|
+
throw new Error("bytecode not valid hex (expected 0x-prefixed hex string)");
|
|
43
|
+
}
|
|
44
|
+
return bc;
|
|
45
|
+
}
|
|
46
|
+
/** Get ABI array from a Foundry/solc artifact JSON object. */
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
48
|
+
export function getAbi(artifact) {
|
|
49
|
+
const abi = artifact?.abi;
|
|
50
|
+
if (!Array.isArray(abi))
|
|
51
|
+
throw new Error("abi missing or not an array");
|
|
52
|
+
return abi;
|
|
53
|
+
}
|
package/dist/cmds/set.js
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import { Argument, Command } from "commander";
|
|
1
|
+
import { Argument, Command, Option } from "commander";
|
|
2
2
|
import { abi } from "../abi.js";
|
|
3
3
|
import { CommandGenDefaults, getCommandGen, makeFuncName } from "../cmdgen.js";
|
|
4
|
+
import { outputOptions, writeOptions } from "../commander-patch.js";
|
|
5
|
+
import { FromOpts } from "../from-opts.js";
|
|
6
|
+
import { coerceValue, loadJson } from "../utils.js";
|
|
7
|
+
import { getAbi, getArtifactPath, getCreationCode } from "../artifact.js";
|
|
8
|
+
import { Logger } from "../logger.js";
|
|
4
9
|
const adminCmdConfig = {
|
|
5
10
|
getFuncName: (cmdName) => `${cmdName}Set`,
|
|
6
11
|
getAbiFuncs: (funcName) => abi.funcs.setRegistryAdmin.filter((i) => i.name == funcName),
|
|
@@ -25,7 +30,82 @@ const userCmdConfig = {
|
|
|
25
30
|
};
|
|
26
31
|
const userCmds = "owner,descriptor,snapshot".split(",");
|
|
27
32
|
const adminCmds = "register,update,upgrade,touch".split(",");
|
|
33
|
+
const deployCmd = genDeployCmd();
|
|
28
34
|
export const setCmd = new Command("set")
|
|
29
35
|
.description("manage sets")
|
|
36
|
+
.addCommand(deployCmd)
|
|
30
37
|
.addCommands(adminCmds.map(getCommandGen(adminCmdConfig)))
|
|
31
38
|
.addCommands(userCmds.map(getCommandGen(userCmdConfig)));
|
|
39
|
+
function genDeployCmd() {
|
|
40
|
+
const args = [
|
|
41
|
+
new Argument(`<contract>`, "Artifact ID of the contract"),
|
|
42
|
+
new Argument(`[args...]`, "Constructor arguments"),
|
|
43
|
+
];
|
|
44
|
+
const options = [
|
|
45
|
+
new Option("--artifact-dir <dir>", "Artifact directory").default("./out"),
|
|
46
|
+
...writeOptions,
|
|
47
|
+
...outputOptions,
|
|
48
|
+
];
|
|
49
|
+
function getDeployArgs(cmd) {
|
|
50
|
+
const opts = cmd.opts();
|
|
51
|
+
const artifactInfo = getArtifactPath(cmd.args[0], opts.artifactDir);
|
|
52
|
+
const userArgs = cmd.args.slice(1);
|
|
53
|
+
const artifact = loadJson(artifactInfo.file);
|
|
54
|
+
const abi = getAbi(artifact);
|
|
55
|
+
const bytecode = getCreationCode(artifact);
|
|
56
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
57
|
+
const ctor = abi.find((i) => i.type === "constructor");
|
|
58
|
+
if (!ctor) {
|
|
59
|
+
if (userArgs.length > 0) {
|
|
60
|
+
throw new Error(`No constructor defined, got ${userArgs.length} args`);
|
|
61
|
+
}
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
63
|
+
return { abi, bytecode, args: [] };
|
|
64
|
+
}
|
|
65
|
+
else if (!ctor.inputs || ctor.inputs.length === 0) {
|
|
66
|
+
if (userArgs.length > 0) {
|
|
67
|
+
throw new Error(`Constructor expects 0 args, got ${userArgs.length}`);
|
|
68
|
+
}
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
70
|
+
return { abi, bytecode, args: [] };
|
|
71
|
+
}
|
|
72
|
+
const expected = ctor.inputs.length;
|
|
73
|
+
if (userArgs.length !== expected) {
|
|
74
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
75
|
+
const sig = `(${ctor.inputs.map((p) => p.type).join(", ")})`;
|
|
76
|
+
throw new Error(`Constructor expects ${expected} args ${sig}, got ${userArgs.length}`);
|
|
77
|
+
}
|
|
78
|
+
const args = userArgs.map((arg, i) => coerceValue(arg, ctor.inputs[i]));
|
|
79
|
+
return { abi, bytecode, args };
|
|
80
|
+
}
|
|
81
|
+
async function action() {
|
|
82
|
+
const opts = this.opts();
|
|
83
|
+
const { args, abi, bytecode } = getDeployArgs(this);
|
|
84
|
+
const { walletClient, publicClient } = await FromOpts.toWriteEthereum(opts);
|
|
85
|
+
const console = new Logger(opts);
|
|
86
|
+
console.log("Transaction sending...");
|
|
87
|
+
const hash = await walletClient.deployContract({
|
|
88
|
+
abi,
|
|
89
|
+
account: walletClient.account,
|
|
90
|
+
bytecode,
|
|
91
|
+
args,
|
|
92
|
+
chain: undefined,
|
|
93
|
+
});
|
|
94
|
+
console.log(`Transaction sent: ${hash}`);
|
|
95
|
+
console.log("Waiting for confirmation...");
|
|
96
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash });
|
|
97
|
+
console.log(`Confirmed in: block ${receipt.blockNumber}, hash ${receipt.blockHash}`);
|
|
98
|
+
console.log(`Contract deployed at: ${receipt.contractAddress}`);
|
|
99
|
+
const result = {
|
|
100
|
+
transaction: hash,
|
|
101
|
+
block: { number: receipt.blockNumber, hash: receipt.blockHash },
|
|
102
|
+
deployedTo: receipt.contractAddress,
|
|
103
|
+
};
|
|
104
|
+
console.result(result);
|
|
105
|
+
}
|
|
106
|
+
return new Command("deploy")
|
|
107
|
+
.description("Deploy a set contract")
|
|
108
|
+
.addOptions(options)
|
|
109
|
+
.addArguments(args)
|
|
110
|
+
.action(action);
|
|
111
|
+
}
|