Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/curvy-melons-sip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@cartesi/cli": patch
---

option to send abi packed encoded inputs
2 changes: 1 addition & 1 deletion apps/cli/biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.11/schema.json",
"$schema": "https://biomejs.dev/schemas/2.3.14/schema.json",
"root": false,
"extends": "//",
"linter": {
Expand Down
2 changes: 1 addition & 1 deletion apps/cli/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ await Bun.build({
});

// build bun binaries for all supported platforms
const targets: Bun.Build.Target[] = [
const targets: Bun.Build.CompileTarget[] = [
"bun-darwin-arm64",
"bun-darwin-x64",
"bun-linux-arm64",
Expand Down
10 changes: 5 additions & 5 deletions apps/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@
"bytes": "^3.1.2",
"chalk": "^5.6.2",
"cli-table3": "^0.6.5",
"commander": "^14.0.1",
"commander": "^14.0.3",
"execa": "^9.6.0",
"fs-extra": "^11.3.2",
"get-port": "^7.1.0",
"listr2": "^9.0.4",
"listr2": "^10.1.0",
"lookpath": "^1.2.3",
"modern-tar": "^0.7.3",
"ora": "^9.0.0",
Expand All @@ -37,7 +37,7 @@
"semver": "^7.7.3",
"smol-toml": "^1.4.2",
"tmp": "^0.2.5",
"viem": "^2.44.1",
"viem": "^2.45.1",
"yaml": "^2.8.2"
},
"devDependencies": {
Expand All @@ -47,13 +47,13 @@
"@types/bytes": "^3.1.5",
"@types/fs-extra": "^11.0.4",
"@types/inquirer": "^9.0.9",
"@types/node": "^25.0.7",
"@types/node": "^25.2.1",
"@types/node-fetch": "^2.6.13",
"@types/progress-stream": "^2.0.5",
"@types/prompts": "^2.4.9",
"@types/semver": "^7.7.1",
"@types/tmp": "^0.2.6",
"@wagmi/cli": "^2.5.1",
"@wagmi/cli": "^2.9.0",
"npm-run-all": "^4.1.5",
"rimraf": "^6.0.1",
"ts-node": "^10.9.2",
Expand Down
21 changes: 18 additions & 3 deletions apps/cli/src/commands/send.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Command, Option } from "@commander-js/extra-typings";
import ora from "ora";
import {
encodeAbiParameters,
encodePacked,
getAddress,
isAddress,
isHex,
Expand All @@ -16,7 +17,7 @@ import { connect } from "../wallet.js";
const getInput = async (
input: string | undefined,
options: {
encoding?: "abi" | "hex" | "string";
encoding?: "abi" | "abi-packed" | "hex" | "string";
abiParams?: string;
},
): Promise<`0x${string}` | undefined> => {
Expand All @@ -33,7 +34,7 @@ const getInput = async (
// encode UTF-8 string as hex
return stringToHex(input);
}
if (encoding === "abi") {
if (encoding === "abi" || encoding === "abi-packed") {
const abiParams = options.abiParams;
if (!abiParams) {
throw new Error("Undefined input-abi-params");
Expand Down Expand Up @@ -61,6 +62,13 @@ const getInput = async (
case "uint64":
case "uint128":
case "uint256":
case "int":
case "int8":
case "int16":
case "int32":
case "int64":
case "int128":
case "int256":
try {
return BigInt(v);
} catch {
Expand All @@ -85,7 +93,12 @@ const getInput = async (
`Not enough values, expected ${abiParameters.length} values based on --input-abi-params '${abiParams}', parsed ${values.length} values from input '${input}'`,
);
}
return encodeAbiParameters(abiParameters, values);
if (encoding === "abi") {
return encodeAbiParameters(abiParameters, values);
} else if (encoding === "abi-packed") {
const types = abiParameters.map((p) => p.type);
return encodePacked(types, values);
}
}
if (isHex(input)) {
// encoding not specified, if starts with 0x, assume hex
Expand All @@ -108,6 +121,7 @@ export const createSendCommand = () => {
"hex",
"string",
"abi",
"abi-packed",
]),
)
.option("--abi-params <abi-params>", "input abi params")
Expand Down Expand Up @@ -139,6 +153,7 @@ export const createSendCommand = () => {
const payload =
(await getInput(input, options)) ||
(await bytesInput({
abiParams: options.abiParams,
encoding: options.encoding,
message: "Input",
}));
Expand Down
111 changes: 59 additions & 52 deletions apps/cli/src/prompts.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import confirm from "@inquirer/confirm";
import { Separator, createPrompt, useKeypress } from "@inquirer/core";
import { type Separator, createPrompt, useKeypress } from "@inquirer/core";
import input from "@inquirer/input";
import select from "@inquirer/select";
import type { Context } from "@inquirer/type";
import chalk from "chalk";
import {
type Address,
type Hex,
encodeAbiParameters,
encodePacked,
formatUnits,
getAddress,
isAddress,
Expand Down Expand Up @@ -75,9 +75,12 @@ export const bigintInput = async (
* @returns bytes as hex string
*/
export const bytesInput = async (
config: InputConfig & {
encoding?: "string" | "hex" | "abi";
message: string;
config: Omit<
SelectConfig<"string" | "hex" | "abi" | "abi-packed">,
"choices"
> & {
abiParams?: string;
encoding?: "string" | "hex" | "abi" | "abi-packed";
},
): Promise<Hex> => {
const encoding =
Expand All @@ -102,6 +105,12 @@ export const bytesInput = async (
description:
"Input as ABI encoding parameters https://abitype.dev/api/human#parseabiparameters",
},
{
value: "abi-packed",
name: "ABI packed encoding",
description:
"Input as ABI encoding parameters https://abitype.dev/api/human#parseabiparameters",
},
] as const,
}));

Expand All @@ -123,8 +132,13 @@ export const bytesInput = async (
return stringToHex(valueString);
}

case "abi":
return await abiParamsInput(config);
case "abi": {
return abiParamsInput(config);
}

case "abi-packed": {
return abiParamsInput(config, true);
}

default:
throw new Error(`Unsupported encoding ${encoding}`);
Expand All @@ -137,39 +151,51 @@ export const bytesInput = async (
* @returns ABI encoded parameters as hex string
*/
export const abiParamsInput = async (
config: InputConfig & { message: string },
config: InputConfig & { abiParams?: string },
packed?: boolean,
): Promise<`0x${string}`> => {
const encoding = await input({
message: `${config.message} (as ABI encoded https://abitype.dev/api/human#parseabiparameters )`,
validate: (value) => {
try {
parseAbiParameters(value);
return true;
} catch {
return "Invalid ABI parameters";
}
},
});
const encoding =
config.abiParams ??
(await input({
message: `${config.message} (as ABI encoded https://abitype.dev/api/human#parseabiparameters )`,
validate: (value) => {
try {
parseAbiParameters(value);
return true;
} catch {
return "Invalid ABI parameters";
}
},
}));
const abiParameters = parseAbiParameters(encoding);
const values = [];
const values: (string | boolean | Hex)[] = [];
for (const param of abiParameters) {
const message = `${config.message} -> ${param.type} ${
param.name ?? ""
}`;
switch (param.type) {
case "string":
case "string": {
values.push(await input({ message }));
break;
case "bool":
}
case "bool": {
values.push(await confirm({ message }));
break;
}
case "uint":
case "uint8":
case "uint16":
case "uint32":
case "uint64":
case "uint128":
case "uint256":
case "int":
case "int8":
case "int16":
case "int32":
case "int64":
case "int128":
case "int256": {
values.push(
await input({
message,
Expand All @@ -184,10 +210,12 @@ export const abiParamsInput = async (
}),
);
break;
case "bytes":
}
case "bytes": {
values.push(await bytesInput({ message }));
break;
case "address":
}
case "address": {
values.push(
await input({
message,
Expand All @@ -196,11 +224,17 @@ export const abiParamsInput = async (
}),
);
break;
}
default:
throw new Error(`Unsupported type ${param.type}`);
}
}
return encodeAbiParameters(abiParameters, values);
return packed
? encodePacked(
abiParameters.map((p) => p.type),
values,
)
: encodeAbiParameters(abiParameters, values);
};

// types below should be exported by @inquirer/select
Expand All @@ -217,33 +251,6 @@ export type SelectAutoConfig<ValueType> = SelectConfig<ValueType> & {
pageSize?: number;
};

export const selectAuto = <ValueType>(
config: SelectAutoConfig<ValueType> & { discardDisabled?: boolean },
context?: Context | undefined,
): Promise<ValueType> => {
const choices = config.choices;

const list = config.discardDisabled
? choices.filter((c) => !(c instanceof Separator) && !c.disabled)
: choices;

if (list.length === 1) {
const choice = list[0];
if (!(choice instanceof Separator)) {
const output = context?.output || process.stdout;
const prefix = chalk.green("?");
const message: string = chalk.bold(config.message);
output.write(
`${prefix} ${message} ${chalk.cyan(
choice.name || choice.value,
)}\n`,
);
return new Promise<ValueType>((resolve) => resolve(choice.value));
}
}
return select(config, context);
};

type KeySelectConfig<Value> = {
choices: ReadonlyArray<Choice<Value>>;
separator?: string;
Expand Down
2 changes: 1 addition & 1 deletion biome.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.11/schema.json",
"$schema": "https://biomejs.dev/schemas/2.3.14/schema.json",
"assist": {
"actions": {
"source": {
Expand Down
Loading
Loading