genlayer 0.39.0 → 0.39.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/.claude/skills/release/SKILL.md +89 -0
- package/.github/e2e-track +1 -0
- package/.github/workflows/e2e-housekeeper.yml +84 -0
- package/.github/workflows/e2e.yml +740 -0
- package/.github/workflows/publish.yml +43 -29
- package/.github/workflows/{cli-docs.yml → sync-docs.yml} +3 -20
- package/.github/workflows/validate-code.yml +1 -1
- package/CHANGELOG.md +19 -0
- package/CONTRIBUTING.md +24 -0
- package/README.md +50 -1
- package/dist/index.js +418 -48
- package/package.json +3 -4
- package/scripts/release.sh +157 -0
- package/src/commands/contracts/deploy.ts +6 -1
- package/src/commands/contracts/estimateFees.ts +133 -0
- package/src/commands/contracts/fees.ts +236 -0
- package/src/commands/contracts/index.ts +43 -0
- package/src/commands/contracts/write.ts +16 -3
- package/src/commands/localnet/validators.ts +4 -5
- package/src/lib/clients/jsonRpcClient.ts +9 -4
- package/src/lib/clients/system.ts +19 -17
- package/tests/actions/deploy.test.ts +49 -0
- package/tests/actions/estimateFees.test.ts +271 -0
- package/tests/actions/validators.test.ts +5 -5
- package/tests/actions/write.test.ts +47 -0
- package/tests/commands/deploy.test.ts +25 -0
- package/tests/commands/estimateFees.test.ts +98 -0
- package/tests/commands/write.test.ts +26 -0
- package/tests/libs/jsonRpcClient.test.ts +18 -0
- package/tests/libs/system.test.ts +36 -3
- package/tsconfig.json +2 -3
|
@@ -89,6 +89,7 @@ export class ValidatorsAction extends BaseAction {
|
|
|
89
89
|
method: "sim_countValidators",
|
|
90
90
|
params: [],
|
|
91
91
|
});
|
|
92
|
+
console.log(`Total Validators: ${result.result}`);
|
|
92
93
|
this.succeedSpinner(`Total Validators: ${result.result}`);
|
|
93
94
|
} catch (error) {
|
|
94
95
|
this.failSpinner("Error counting validators", error);
|
|
@@ -107,15 +108,15 @@ export class ValidatorsAction extends BaseAction {
|
|
|
107
108
|
|
|
108
109
|
const parsedStake = options.stake
|
|
109
110
|
? parseInt(options.stake, 10)
|
|
110
|
-
: currentValidator.result.stake;
|
|
111
|
+
: Number(currentValidator.result.stake);
|
|
111
112
|
|
|
112
|
-
if (
|
|
113
|
+
if (!Number.isInteger(parsedStake) || parsedStake < 0) {
|
|
113
114
|
return this.failSpinner("Invalid stake value. Stake must be a positive integer.");
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
const updatedValidator = {
|
|
117
118
|
address: options.address,
|
|
118
|
-
stake:
|
|
119
|
+
stake: parsedStake,
|
|
119
120
|
provider: options.provider || currentValidator.result.provider,
|
|
120
121
|
model: options.model || currentValidator.result.model,
|
|
121
122
|
config: options.config ? JSON.parse(options.config) : currentValidator.result.config,
|
|
@@ -265,5 +266,3 @@ export class ValidatorsAction extends BaseAction {
|
|
|
265
266
|
}
|
|
266
267
|
}
|
|
267
268
|
}
|
|
268
|
-
|
|
269
|
-
|
|
@@ -29,12 +29,17 @@ export class JsonRpcClient {
|
|
|
29
29
|
}),
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
-
if (response.ok) {
|
|
33
|
-
return response.json();
|
|
34
|
-
}
|
|
35
32
|
const result = await response.json();
|
|
36
33
|
|
|
37
|
-
|
|
34
|
+
if (!response.ok) {
|
|
35
|
+
throw new Error(result?.error?.message || result?.error || response.statusText);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (result?.error) {
|
|
39
|
+
throw new Error(result.error.message || String(result.error));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return result;
|
|
38
43
|
|
|
39
44
|
}
|
|
40
45
|
}
|
|
@@ -9,9 +9,7 @@ export async function checkCommand(command: string, toolName: string): Promise<v
|
|
|
9
9
|
try {
|
|
10
10
|
await util.promisify(exec)(command);
|
|
11
11
|
}catch (error:any) {
|
|
12
|
-
|
|
13
|
-
throw new MissingRequirementError(toolName);
|
|
14
|
-
}
|
|
12
|
+
throw new MissingRequirementError(toolName);
|
|
15
13
|
}
|
|
16
14
|
}
|
|
17
15
|
|
|
@@ -50,24 +48,28 @@ export function openUrl(url: string): Promise<ChildProcess> {
|
|
|
50
48
|
}
|
|
51
49
|
|
|
52
50
|
export async function getVersion(toolName: string): Promise<string> {
|
|
51
|
+
let toolResponse: {stdout?: string; stderr?: string};
|
|
52
|
+
|
|
53
53
|
try {
|
|
54
|
-
|
|
54
|
+
toolResponse = await util.promisify(exec)(`${toolName} --version`);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
throw new Error(`Error getting ${toolName} version.`);
|
|
57
|
+
}
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
+
if (toolResponse.stderr) {
|
|
60
|
+
throw new Error(`Error getting ${toolName} version.`);
|
|
61
|
+
}
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
const versionMatch = toolResponse.stdout.match(/(\d+\.\d+\.\d+)/);
|
|
62
|
-
if (versionMatch) {
|
|
63
|
-
return versionMatch[1];
|
|
64
|
-
}
|
|
65
|
-
} catch (err) {
|
|
66
|
-
throw new Error(`Could not parse ${toolName} version.`);
|
|
67
|
-
}
|
|
68
|
-
} catch (error) {
|
|
63
|
+
if (toolResponse.stdout == null) {
|
|
69
64
|
throw new Error(`Error getting ${toolName} version.`);
|
|
70
65
|
}
|
|
71
66
|
|
|
72
|
-
|
|
67
|
+
const versionMatch = toolResponse.stdout.match(/(\d+\.\d+\.\d+)/);
|
|
68
|
+
if (versionMatch) {
|
|
69
|
+
return versionMatch[1];
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
throw new Error(
|
|
73
|
+
`Could not parse ${toolName} version from output: "${toolResponse.stdout}". Expected format: X.Y.Z`
|
|
74
|
+
);
|
|
73
75
|
}
|
|
@@ -95,6 +95,55 @@ describe("DeployAction", () => {
|
|
|
95
95
|
expect(mockClient.deployContract).toHaveReturnedWith(Promise.resolve("mocked_tx_hash"));
|
|
96
96
|
});
|
|
97
97
|
|
|
98
|
+
test("deploys contract with fee options", async () => {
|
|
99
|
+
const options: DeployOptions = {
|
|
100
|
+
contract: "/mocked/contract/path",
|
|
101
|
+
args: [1],
|
|
102
|
+
fees: JSON.stringify({
|
|
103
|
+
distribution: {
|
|
104
|
+
leaderTimeunitsAllocation: "10",
|
|
105
|
+
rotations: ["0"],
|
|
106
|
+
},
|
|
107
|
+
messageAllocations: [{
|
|
108
|
+
messageType: "internal",
|
|
109
|
+
recipient: "0x0000000000000000000000000000000000000001",
|
|
110
|
+
budget: "5",
|
|
111
|
+
}],
|
|
112
|
+
}),
|
|
113
|
+
feeValue: "123",
|
|
114
|
+
validUntil: "999",
|
|
115
|
+
};
|
|
116
|
+
const contractContent = "contract code";
|
|
117
|
+
|
|
118
|
+
vi.mocked(fs.existsSync).mockReturnValue(true);
|
|
119
|
+
vi.mocked(fs.readFileSync).mockReturnValue(contractContent);
|
|
120
|
+
vi.mocked(mockClient.deployContract).mockResolvedValue("mocked_tx_hash");
|
|
121
|
+
vi.mocked(mockClient.waitForTransactionReceipt).mockResolvedValue({
|
|
122
|
+
data: {contract_address: "0xdasdsadasdasdada"},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
await deployer.deploy(options);
|
|
126
|
+
|
|
127
|
+
expect(mockClient.deployContract).toHaveBeenCalledWith({
|
|
128
|
+
code: contractContent,
|
|
129
|
+
args: [1],
|
|
130
|
+
leaderOnly: false,
|
|
131
|
+
fees: {
|
|
132
|
+
distribution: {
|
|
133
|
+
leaderTimeunitsAllocation: "10",
|
|
134
|
+
rotations: ["0"],
|
|
135
|
+
},
|
|
136
|
+
messageAllocations: [{
|
|
137
|
+
messageType: 1,
|
|
138
|
+
recipient: "0x0000000000000000000000000000000000000001",
|
|
139
|
+
budget: "5",
|
|
140
|
+
}],
|
|
141
|
+
feeValue: "123",
|
|
142
|
+
},
|
|
143
|
+
validUntil: "999",
|
|
144
|
+
});
|
|
145
|
+
});
|
|
146
|
+
|
|
98
147
|
test("throws error for missing contract", async () => {
|
|
99
148
|
const options: DeployOptions = {};
|
|
100
149
|
|
|
@@ -0,0 +1,271 @@
|
|
|
1
|
+
import {describe, test, vi, beforeEach, afterEach, expect} from "vitest";
|
|
2
|
+
import {createClient, createAccount} from "genlayer-js";
|
|
3
|
+
import {EstimateFeesAction} from "../../src/commands/contracts/estimateFees";
|
|
4
|
+
|
|
5
|
+
vi.mock("genlayer-js");
|
|
6
|
+
|
|
7
|
+
describe("EstimateFeesAction", () => {
|
|
8
|
+
let action: EstimateFeesAction;
|
|
9
|
+
const mockClient = {
|
|
10
|
+
initializeConsensusSmartContract: vi.fn(),
|
|
11
|
+
estimateTransactionFees: vi.fn(),
|
|
12
|
+
estimateTransactionFeesForWrite: vi.fn(),
|
|
13
|
+
simulateWriteContract: vi.fn(),
|
|
14
|
+
estimateTransactionFeesFromSimulation: vi.fn(),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const mockPrivateKey = "mocked_private_key";
|
|
18
|
+
|
|
19
|
+
beforeEach(() => {
|
|
20
|
+
vi.clearAllMocks();
|
|
21
|
+
vi.mocked(createClient).mockReturnValue(mockClient as any);
|
|
22
|
+
vi.mocked(createAccount).mockReturnValue({privateKey: mockPrivateKey} as any);
|
|
23
|
+
action = new EstimateFeesAction();
|
|
24
|
+
vi.spyOn(action as any, "getAccount").mockResolvedValue({privateKey: mockPrivateKey});
|
|
25
|
+
vi.spyOn(action as any, "startSpinner").mockImplementation(() => {});
|
|
26
|
+
vi.spyOn(action as any, "succeedSpinner").mockImplementation(() => {});
|
|
27
|
+
vi.spyOn(action as any, "failSpinner").mockImplementation(() => {});
|
|
28
|
+
vi.spyOn(action as any, "setSpinnerText").mockImplementation(() => {});
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
afterEach(() => {
|
|
32
|
+
vi.restoreAllMocks();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("builds a static fee estimate", async () => {
|
|
36
|
+
const estimate = {
|
|
37
|
+
distribution: {leaderTimeunitsAllocation: 100n, rotations: [0n]},
|
|
38
|
+
feeValue: 1100n,
|
|
39
|
+
policy: {enabled: true},
|
|
40
|
+
};
|
|
41
|
+
vi.mocked(mockClient.estimateTransactionFees).mockResolvedValue(estimate);
|
|
42
|
+
|
|
43
|
+
await action.estimate({
|
|
44
|
+
fees: JSON.stringify({
|
|
45
|
+
distribution: {
|
|
46
|
+
leaderTimeunitsAllocation: "100",
|
|
47
|
+
rotations: ["0"],
|
|
48
|
+
},
|
|
49
|
+
}),
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
expect(mockClient.estimateTransactionFees).toHaveBeenCalledWith({
|
|
53
|
+
leaderTimeunitsAllocation: "100",
|
|
54
|
+
rotations: ["0"],
|
|
55
|
+
});
|
|
56
|
+
expect(mockClient.simulateWriteContract).not.toHaveBeenCalled();
|
|
57
|
+
expect(action["succeedSpinner"]).toHaveBeenCalledWith("Fee estimate generated", {
|
|
58
|
+
distribution: {leaderTimeunitsAllocation: "100", rotations: ["0"]},
|
|
59
|
+
feeValue: "1100",
|
|
60
|
+
policy: {enabled: true},
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("prints a static fee estimate as JSON without spinner output", async () => {
|
|
65
|
+
const estimate = {
|
|
66
|
+
distribution: {leaderTimeunitsAllocation: 100n, rotations: [0n]},
|
|
67
|
+
feeValue: 1100n,
|
|
68
|
+
policy: {enabled: true},
|
|
69
|
+
};
|
|
70
|
+
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
|
71
|
+
vi.mocked(mockClient.estimateTransactionFees).mockResolvedValue(estimate);
|
|
72
|
+
|
|
73
|
+
await action.estimate({json: true});
|
|
74
|
+
|
|
75
|
+
expect(action["startSpinner"]).not.toHaveBeenCalled();
|
|
76
|
+
expect(action["succeedSpinner"]).not.toHaveBeenCalled();
|
|
77
|
+
expect(logSpy).toHaveBeenCalledWith(JSON.stringify({
|
|
78
|
+
distribution: {leaderTimeunitsAllocation: "100", rotations: ["0"]},
|
|
79
|
+
feeValue: "1100",
|
|
80
|
+
policy: {enabled: true},
|
|
81
|
+
}));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("derives a fee estimate for a target write through the SDK one-call helper", async () => {
|
|
85
|
+
const finalEstimate = {
|
|
86
|
+
distribution: {leaderTimeunitsAllocation: 100n, totalMessageFees: 110n, rotations: [0n]},
|
|
87
|
+
messageAllocations: [{messageType: 1, budget: 110n}],
|
|
88
|
+
feeValue: 1310n,
|
|
89
|
+
observed: {messageFeeBudget: 110n, messageFeeConsumed: 55n},
|
|
90
|
+
policy: {enabled: true},
|
|
91
|
+
};
|
|
92
|
+
vi.mocked(mockClient.estimateTransactionFeesForWrite).mockResolvedValue(finalEstimate);
|
|
93
|
+
|
|
94
|
+
await action.estimate({
|
|
95
|
+
contractAddress: "0x0000000000000000000000000000000000000001",
|
|
96
|
+
method: "update",
|
|
97
|
+
args: ["after"],
|
|
98
|
+
fees: JSON.stringify({
|
|
99
|
+
messageAllocations: [{
|
|
100
|
+
messageType: "internal",
|
|
101
|
+
callKeyMethod: "settle_campaign",
|
|
102
|
+
budget: "110",
|
|
103
|
+
}],
|
|
104
|
+
}),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
expect(mockClient.estimateTransactionFeesForWrite).toHaveBeenCalledWith({
|
|
108
|
+
messageAllocations: [{
|
|
109
|
+
messageType: 1,
|
|
110
|
+
callKey: `0x${Buffer.from("settle_campaign", "utf8").toString("hex").padEnd(64, "0")}`,
|
|
111
|
+
budget: "110",
|
|
112
|
+
}],
|
|
113
|
+
address: "0x0000000000000000000000000000000000000001",
|
|
114
|
+
functionName: "update",
|
|
115
|
+
args: ["after"],
|
|
116
|
+
});
|
|
117
|
+
expect(mockClient.estimateTransactionFees).not.toHaveBeenCalled();
|
|
118
|
+
expect(mockClient.simulateWriteContract).not.toHaveBeenCalled();
|
|
119
|
+
expect(mockClient.estimateTransactionFeesFromSimulation).not.toHaveBeenCalled();
|
|
120
|
+
expect(action["succeedSpinner"]).toHaveBeenCalledWith("Fee estimate generated", {
|
|
121
|
+
distribution: {leaderTimeunitsAllocation: "100", totalMessageFees: "110", rotations: ["0"]},
|
|
122
|
+
messageAllocations: [{messageType: 1, budget: "110"}],
|
|
123
|
+
feeValue: "1310",
|
|
124
|
+
observed: {messageFeeBudget: "110", messageFeeConsumed: "55"},
|
|
125
|
+
policy: {enabled: true},
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
test("falls back to explicit simulation when the SDK one-call helper is unavailable", async () => {
|
|
130
|
+
const legacyClient = {
|
|
131
|
+
...mockClient,
|
|
132
|
+
estimateTransactionFeesForWrite: undefined,
|
|
133
|
+
};
|
|
134
|
+
vi.mocked(createClient).mockReturnValue(legacyClient as any);
|
|
135
|
+
const initialEstimate = {
|
|
136
|
+
distribution: {leaderTimeunitsAllocation: 100n, rotations: [0n]},
|
|
137
|
+
messageAllocations: [{messageType: 1, budget: 55n}],
|
|
138
|
+
feeValue: 1200n,
|
|
139
|
+
policy: {enabled: true},
|
|
140
|
+
};
|
|
141
|
+
const simulation = {feeAccounting: {status: "active"}};
|
|
142
|
+
const finalEstimate = {
|
|
143
|
+
distribution: {leaderTimeunitsAllocation: 100n, totalMessageFees: 55n, rotations: [0n]},
|
|
144
|
+
messageAllocations: [{messageType: 1, budget: 55n}],
|
|
145
|
+
feeValue: 1255n,
|
|
146
|
+
observed: {messageFeeConsumed: 55n},
|
|
147
|
+
policy: {enabled: true},
|
|
148
|
+
};
|
|
149
|
+
vi.mocked(mockClient.estimateTransactionFees).mockResolvedValue(initialEstimate);
|
|
150
|
+
vi.mocked(mockClient.simulateWriteContract).mockResolvedValue(simulation);
|
|
151
|
+
vi.mocked(mockClient.estimateTransactionFeesFromSimulation).mockResolvedValue(finalEstimate);
|
|
152
|
+
|
|
153
|
+
await action.estimate({
|
|
154
|
+
contractAddress: "0x0000000000000000000000000000000000000001",
|
|
155
|
+
method: "update",
|
|
156
|
+
args: ["after"],
|
|
157
|
+
fees: JSON.stringify({
|
|
158
|
+
messageAllocations: [{messageType: "internal", budget: "55"}],
|
|
159
|
+
}),
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
expect(mockClient.estimateTransactionFees).toHaveBeenCalledWith({
|
|
163
|
+
messageAllocations: [{messageType: 1, budget: "55"}],
|
|
164
|
+
});
|
|
165
|
+
expect(mockClient.simulateWriteContract).toHaveBeenCalledWith({
|
|
166
|
+
address: "0x0000000000000000000000000000000000000001",
|
|
167
|
+
functionName: "update",
|
|
168
|
+
args: ["after"],
|
|
169
|
+
includeReceipt: true,
|
|
170
|
+
fees: {
|
|
171
|
+
distribution: initialEstimate.distribution,
|
|
172
|
+
messageAllocations: initialEstimate.messageAllocations,
|
|
173
|
+
feeValue: initialEstimate.feeValue,
|
|
174
|
+
},
|
|
175
|
+
});
|
|
176
|
+
expect(mockClient.estimateTransactionFeesFromSimulation).toHaveBeenCalledWith({
|
|
177
|
+
messageAllocations: [{messageType: 1, budget: "55"}],
|
|
178
|
+
simulation,
|
|
179
|
+
});
|
|
180
|
+
expect(action["succeedSpinner"]).toHaveBeenCalledWith("Fee estimate generated", {
|
|
181
|
+
distribution: {leaderTimeunitsAllocation: "100", totalMessageFees: "55", rotations: ["0"]},
|
|
182
|
+
messageAllocations: [{messageType: 1, budget: "55"}],
|
|
183
|
+
feeValue: "1255",
|
|
184
|
+
observed: {messageFeeConsumed: "55"},
|
|
185
|
+
policy: {enabled: true},
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test("includes simulation fee report when requested", async () => {
|
|
190
|
+
const initialEstimate = {
|
|
191
|
+
distribution: {leaderTimeunitsAllocation: 100n, rotations: [0n]},
|
|
192
|
+
messageAllocations: [{messageType: 1, budget: 55n}],
|
|
193
|
+
feeValue: 1200n,
|
|
194
|
+
policy: {enabled: true},
|
|
195
|
+
};
|
|
196
|
+
const simulation = {
|
|
197
|
+
feeAccounting: {
|
|
198
|
+
status: "active",
|
|
199
|
+
message_fee_budget: 55n,
|
|
200
|
+
message_fee_consumed: 55n,
|
|
201
|
+
execution_fee_report: {
|
|
202
|
+
messageFees: {
|
|
203
|
+
budget: 55n,
|
|
204
|
+
declaredConsumed: 55n,
|
|
205
|
+
remaining: 0n,
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
feeReport: {
|
|
210
|
+
totalEstimatedFee: 501664n,
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
const finalEstimate = {
|
|
214
|
+
distribution: {leaderTimeunitsAllocation: 100n, totalMessageFees: 55n, rotations: [0n]},
|
|
215
|
+
messageAllocations: [{messageType: 1, budget: 55n}],
|
|
216
|
+
feeValue: 1255n,
|
|
217
|
+
observed: {messageFeeConsumed: 55n},
|
|
218
|
+
policy: {enabled: true},
|
|
219
|
+
};
|
|
220
|
+
vi.mocked(mockClient.estimateTransactionFees).mockResolvedValue(initialEstimate);
|
|
221
|
+
vi.mocked(mockClient.simulateWriteContract).mockResolvedValue(simulation);
|
|
222
|
+
vi.mocked(mockClient.estimateTransactionFeesFromSimulation).mockResolvedValue(finalEstimate);
|
|
223
|
+
|
|
224
|
+
await action.estimate({
|
|
225
|
+
contractAddress: "0x0000000000000000000000000000000000000001",
|
|
226
|
+
method: "update",
|
|
227
|
+
args: ["after"],
|
|
228
|
+
includeReport: true,
|
|
229
|
+
fees: JSON.stringify({
|
|
230
|
+
messageAllocations: [{messageType: "internal", budget: "55"}],
|
|
231
|
+
}),
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
expect(mockClient.estimateTransactionFeesForWrite).not.toHaveBeenCalled();
|
|
235
|
+
expect(mockClient.simulateWriteContract).toHaveBeenCalledWith({
|
|
236
|
+
address: "0x0000000000000000000000000000000000000001",
|
|
237
|
+
functionName: "update",
|
|
238
|
+
args: ["after"],
|
|
239
|
+
includeReceipt: true,
|
|
240
|
+
fees: {
|
|
241
|
+
distribution: initialEstimate.distribution,
|
|
242
|
+
messageAllocations: initialEstimate.messageAllocations,
|
|
243
|
+
feeValue: initialEstimate.feeValue,
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
expect(action["succeedSpinner"]).toHaveBeenCalledWith("Fee estimate generated", {
|
|
247
|
+
distribution: {leaderTimeunitsAllocation: "100", totalMessageFees: "55", rotations: ["0"]},
|
|
248
|
+
messageAllocations: [{messageType: 1, budget: "55"}],
|
|
249
|
+
feeValue: "1255",
|
|
250
|
+
observed: {messageFeeConsumed: "55"},
|
|
251
|
+
policy: {enabled: true},
|
|
252
|
+
simulation: {
|
|
253
|
+
feeAccounting: {
|
|
254
|
+
status: "active",
|
|
255
|
+
message_fee_budget: "55",
|
|
256
|
+
message_fee_consumed: "55",
|
|
257
|
+
execution_fee_report: {
|
|
258
|
+
messageFees: {
|
|
259
|
+
budget: "55",
|
|
260
|
+
declaredConsumed: "55",
|
|
261
|
+
remaining: "0",
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
feeReport: {
|
|
266
|
+
totalEstimatedFee: "501664",
|
|
267
|
+
},
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
});
|
|
271
|
+
});
|
|
@@ -581,7 +581,7 @@ describe("ValidatorsAction", () => {
|
|
|
581
581
|
method: "sim_updateValidator",
|
|
582
582
|
params: [
|
|
583
583
|
"mocked_address",
|
|
584
|
-
|
|
584
|
+
200,
|
|
585
585
|
"Provider1",
|
|
586
586
|
"Model1",
|
|
587
587
|
{ max_tokens: 500 },
|
|
@@ -624,7 +624,7 @@ describe("ValidatorsAction", () => {
|
|
|
624
624
|
method: "sim_updateValidator",
|
|
625
625
|
params: [
|
|
626
626
|
"mocked_address",
|
|
627
|
-
|
|
627
|
+
100,
|
|
628
628
|
"Provider2",
|
|
629
629
|
"Model2",
|
|
630
630
|
{ max_tokens: 500 },
|
|
@@ -668,7 +668,7 @@ describe("ValidatorsAction", () => {
|
|
|
668
668
|
method: "sim_updateValidator",
|
|
669
669
|
params: [
|
|
670
670
|
"mocked_address",
|
|
671
|
-
|
|
671
|
+
100,
|
|
672
672
|
"Provider1",
|
|
673
673
|
"Model1",
|
|
674
674
|
{ max_tokens: 1000 },
|
|
@@ -709,7 +709,7 @@ describe("ValidatorsAction", () => {
|
|
|
709
709
|
method: "sim_updateValidator",
|
|
710
710
|
params: [
|
|
711
711
|
"mocked_address",
|
|
712
|
-
|
|
712
|
+
200,
|
|
713
713
|
"Provider1",
|
|
714
714
|
"Model1",
|
|
715
715
|
{ max_tokens: 500 },
|
|
@@ -747,4 +747,4 @@ describe("ValidatorsAction", () => {
|
|
|
747
747
|
expect(rpcClient.request).toHaveBeenCalledTimes(1);
|
|
748
748
|
});
|
|
749
749
|
});
|
|
750
|
-
});
|
|
750
|
+
});
|
|
@@ -58,6 +58,53 @@ describe("WriteAction", () => {
|
|
|
58
58
|
);
|
|
59
59
|
});
|
|
60
60
|
|
|
61
|
+
test("calls writeContract with fee options", async () => {
|
|
62
|
+
const mockHash = "0xMockedTransactionHash";
|
|
63
|
+
const mockReceipt = {status: "success"};
|
|
64
|
+
|
|
65
|
+
vi.mocked(mockClient.writeContract).mockResolvedValue(mockHash);
|
|
66
|
+
vi.mocked(mockClient.waitForTransactionReceipt).mockResolvedValue(mockReceipt);
|
|
67
|
+
|
|
68
|
+
await writeAction.write({
|
|
69
|
+
contractAddress: "0xMockedContract",
|
|
70
|
+
method: "updateData",
|
|
71
|
+
args: [42],
|
|
72
|
+
fees: JSON.stringify({
|
|
73
|
+
distribution: {
|
|
74
|
+
totalMessageFees: "3",
|
|
75
|
+
},
|
|
76
|
+
messageAllocations: [{
|
|
77
|
+
messageType: "external",
|
|
78
|
+
recipient: "0x0000000000000000000000000000000000000001",
|
|
79
|
+
callKeySelector: "0xaabbccdd",
|
|
80
|
+
budget: "3",
|
|
81
|
+
}],
|
|
82
|
+
}),
|
|
83
|
+
feeValue: "4",
|
|
84
|
+
validUntil: "999",
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(mockClient.writeContract).toHaveBeenCalledWith({
|
|
88
|
+
address: "0xMockedContract",
|
|
89
|
+
functionName: "updateData",
|
|
90
|
+
args: [42],
|
|
91
|
+
value: 0n,
|
|
92
|
+
fees: {
|
|
93
|
+
distribution: {
|
|
94
|
+
totalMessageFees: "3",
|
|
95
|
+
},
|
|
96
|
+
messageAllocations: [{
|
|
97
|
+
messageType: 0,
|
|
98
|
+
recipient: "0x0000000000000000000000000000000000000001",
|
|
99
|
+
callKey: `0xaabbccdd${"0".repeat(56)}`,
|
|
100
|
+
budget: "3",
|
|
101
|
+
}],
|
|
102
|
+
feeValue: "4",
|
|
103
|
+
},
|
|
104
|
+
validUntil: "999",
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
61
108
|
test("handles writeContract errors", async () => {
|
|
62
109
|
vi.mocked(mockClient.writeContract).mockRejectedValue(new Error("Mocked write error"));
|
|
63
110
|
|
|
@@ -52,6 +52,31 @@ describe("deploy command", () => {
|
|
|
52
52
|
});
|
|
53
53
|
});
|
|
54
54
|
|
|
55
|
+
test("DeployAction.deploy receives fee options", async () => {
|
|
56
|
+
const fees = '{"distribution":{"totalMessageFees":"3"}}';
|
|
57
|
+
program.parse([
|
|
58
|
+
"node",
|
|
59
|
+
"test",
|
|
60
|
+
"deploy",
|
|
61
|
+
"--contract",
|
|
62
|
+
"./path/to/contract",
|
|
63
|
+
"--fees",
|
|
64
|
+
fees,
|
|
65
|
+
"--fee-value",
|
|
66
|
+
"4",
|
|
67
|
+
"--valid-until",
|
|
68
|
+
"999",
|
|
69
|
+
]);
|
|
70
|
+
|
|
71
|
+
expect(DeployAction.prototype.deploy).toHaveBeenCalledWith({
|
|
72
|
+
contract: "./path/to/contract",
|
|
73
|
+
args: [],
|
|
74
|
+
fees,
|
|
75
|
+
feeValue: "4",
|
|
76
|
+
validUntil: "999",
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
55
80
|
test("DeployAction is instantiated when the deploy command is executed", async () => {
|
|
56
81
|
program.parse(["node", "test", "deploy", "--contract", "./path/to/contract"]);
|
|
57
82
|
expect(DeployAction).toHaveBeenCalledTimes(1);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import {Command} from "commander";
|
|
2
|
+
import {vi, describe, beforeEach, afterEach, test, expect} from "vitest";
|
|
3
|
+
import {initializeContractsCommands} from "../../src/commands/contracts";
|
|
4
|
+
import {EstimateFeesAction} from "../../src/commands/contracts/estimateFees";
|
|
5
|
+
|
|
6
|
+
vi.mock("../../src/commands/contracts/estimateFees");
|
|
7
|
+
vi.mock("esbuild", () => ({
|
|
8
|
+
buildSync: vi.fn(),
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
describe("estimate-fees command", () => {
|
|
12
|
+
let program: Command;
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
program = new Command();
|
|
16
|
+
initializeContractsCommands(program);
|
|
17
|
+
vi.clearAllMocks();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
vi.restoreAllMocks();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("EstimateFeesAction.estimate is called with static estimate options", async () => {
|
|
25
|
+
const fees = '{"distribution":{"totalMessageFees":"3"}}';
|
|
26
|
+
program.parse([
|
|
27
|
+
"node",
|
|
28
|
+
"test",
|
|
29
|
+
"estimate-fees",
|
|
30
|
+
"--fees",
|
|
31
|
+
fees,
|
|
32
|
+
"--rpc",
|
|
33
|
+
"http://127.0.0.1:4000/api",
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
expect(EstimateFeesAction).toHaveBeenCalledTimes(1);
|
|
37
|
+
expect(EstimateFeesAction.prototype.estimate).toHaveBeenCalledWith({
|
|
38
|
+
args: [],
|
|
39
|
+
fees,
|
|
40
|
+
rpc: "http://127.0.0.1:4000/api",
|
|
41
|
+
contractAddress: undefined,
|
|
42
|
+
method: undefined,
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("EstimateFeesAction.estimate is called with simulation target and args", async () => {
|
|
47
|
+
program.parse([
|
|
48
|
+
"node",
|
|
49
|
+
"test",
|
|
50
|
+
"estimate-fees",
|
|
51
|
+
"0x0000000000000000000000000000000000000001",
|
|
52
|
+
"update",
|
|
53
|
+
"--args",
|
|
54
|
+
"after",
|
|
55
|
+
"2",
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
expect(EstimateFeesAction.prototype.estimate).toHaveBeenCalledWith({
|
|
59
|
+
args: ["after", 2],
|
|
60
|
+
contractAddress: "0x0000000000000000000000000000000000000001",
|
|
61
|
+
method: "update",
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("EstimateFeesAction.estimate receives json output flag", async () => {
|
|
66
|
+
program.parse([
|
|
67
|
+
"node",
|
|
68
|
+
"test",
|
|
69
|
+
"estimate-fees",
|
|
70
|
+
"--json",
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
expect(EstimateFeesAction.prototype.estimate).toHaveBeenCalledWith({
|
|
74
|
+
args: [],
|
|
75
|
+
contractAddress: undefined,
|
|
76
|
+
json: true,
|
|
77
|
+
method: undefined,
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("EstimateFeesAction.estimate receives include-report flag", async () => {
|
|
82
|
+
program.parse([
|
|
83
|
+
"node",
|
|
84
|
+
"test",
|
|
85
|
+
"estimate-fees",
|
|
86
|
+
"0x0000000000000000000000000000000000000001",
|
|
87
|
+
"update",
|
|
88
|
+
"--include-report",
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
expect(EstimateFeesAction.prototype.estimate).toHaveBeenCalledWith({
|
|
92
|
+
args: [],
|
|
93
|
+
contractAddress: "0x0000000000000000000000000000000000000001",
|
|
94
|
+
includeReport: true,
|
|
95
|
+
method: "update",
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -54,6 +54,32 @@ describe("write command", () => {
|
|
|
54
54
|
});
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
+
test("WriteAction.write receives fee options", async () => {
|
|
58
|
+
const fees = '{"distribution":{"totalMessageFees":"3"}}';
|
|
59
|
+
program.parse([
|
|
60
|
+
"node",
|
|
61
|
+
"test",
|
|
62
|
+
"write",
|
|
63
|
+
"0xMockedContract",
|
|
64
|
+
"updateCounter",
|
|
65
|
+
"--fees",
|
|
66
|
+
fees,
|
|
67
|
+
"--fee-value",
|
|
68
|
+
"4",
|
|
69
|
+
"--valid-until",
|
|
70
|
+
"999",
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
expect(WriteAction.prototype.write).toHaveBeenCalledWith({
|
|
74
|
+
contractAddress: "0xMockedContract",
|
|
75
|
+
method: "updateCounter",
|
|
76
|
+
args: [],
|
|
77
|
+
fees,
|
|
78
|
+
feeValue: "4",
|
|
79
|
+
validUntil: "999",
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
|
|
57
83
|
test("WriteAction is instantiated when the write command is executed", async () => {
|
|
58
84
|
program.parse(["node", "test", "write", "0xMockedContract", "anotherMethod"]);
|
|
59
85
|
expect(WriteAction).toHaveBeenCalledTimes(1);
|