genlayer 0.32.2 → 0.32.4
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/CHANGELOG.md +4 -0
- package/README.md +3 -3
- package/dist/index.js +410 -61
- package/docs/validator-guide.md +3 -3
- package/package.json +2 -2
- package/src/commands/staking/StakingAction.ts +39 -0
- package/src/commands/staking/delegatorExit.ts +7 -1
- package/src/commands/staking/index.ts +8 -11
- package/src/commands/staking/setIdentity.ts +29 -17
- package/src/commands/staking/setOperator.ts +14 -8
- package/src/commands/staking/validatorClaim.ts +15 -10
- package/src/commands/staking/validatorDeposit.ts +19 -6
- package/src/commands/staking/validatorExit.ts +26 -7
- package/tests/actions/staking.test.ts +4 -59
- package/tests/commands/staking.test.ts +4 -8
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -387,9 +387,9 @@ EXAMPLES:
|
|
|
387
387
|
# ]
|
|
388
388
|
# }
|
|
389
389
|
|
|
390
|
-
# Exit and claim
|
|
391
|
-
genlayer staking validator-exit --shares 100
|
|
392
|
-
genlayer staking validator-claim
|
|
390
|
+
# Exit and claim (requires validator wallet address)
|
|
391
|
+
genlayer staking validator-exit --validator 0x... --shares 100
|
|
392
|
+
genlayer staking validator-claim --validator 0x...
|
|
393
393
|
```
|
|
394
394
|
|
|
395
395
|
### Running the CLI from the repository
|
package/dist/index.js
CHANGED
|
@@ -10680,7 +10680,7 @@ ${prettyStateOverride(stateOverride)}`;
|
|
|
10680
10680
|
});
|
|
10681
10681
|
|
|
10682
10682
|
// node_modules/viem/_esm/errors/request.js
|
|
10683
|
-
var HttpRequestError, RpcRequestError;
|
|
10683
|
+
var HttpRequestError, RpcRequestError, TimeoutError;
|
|
10684
10684
|
var init_request = __esm({
|
|
10685
10685
|
"node_modules/viem/_esm/errors/request.js"() {
|
|
10686
10686
|
"use strict";
|
|
@@ -10753,6 +10753,15 @@ var init_request = __esm({
|
|
|
10753
10753
|
this.data = error.data;
|
|
10754
10754
|
}
|
|
10755
10755
|
};
|
|
10756
|
+
TimeoutError = class extends BaseError2 {
|
|
10757
|
+
constructor({ body, url }) {
|
|
10758
|
+
super("The request took too long to respond.", {
|
|
10759
|
+
details: "The request timed out.",
|
|
10760
|
+
metaMessages: [`URL: ${getUrl(url)}`, `Request body: ${stringify(body)}`],
|
|
10761
|
+
name: "TimeoutError"
|
|
10762
|
+
});
|
|
10763
|
+
}
|
|
10764
|
+
};
|
|
10756
10765
|
}
|
|
10757
10766
|
});
|
|
10758
10767
|
|
|
@@ -18249,7 +18258,7 @@ var require_semver2 = __commonJS({
|
|
|
18249
18258
|
import { program } from "commander";
|
|
18250
18259
|
|
|
18251
18260
|
// package.json
|
|
18252
|
-
var version = "0.32.
|
|
18261
|
+
var version = "0.32.4";
|
|
18253
18262
|
var package_default = {
|
|
18254
18263
|
name: "genlayer",
|
|
18255
18264
|
version,
|
|
@@ -18317,7 +18326,7 @@ var package_default = {
|
|
|
18317
18326
|
dotenv: "^17.0.0",
|
|
18318
18327
|
ethers: "^6.13.4",
|
|
18319
18328
|
"fs-extra": "^11.3.0",
|
|
18320
|
-
"genlayer-js": "^0.18.
|
|
18329
|
+
"genlayer-js": "^0.18.8",
|
|
18321
18330
|
inquirer: "^12.0.0",
|
|
18322
18331
|
keytar: "^7.9.0",
|
|
18323
18332
|
"node-fetch": "^3.0.0",
|
|
@@ -19229,7 +19238,7 @@ var doBadDataWarn = deprecate2(
|
|
|
19229
19238
|
".data is not a valid RequestInit property, use .body instead",
|
|
19230
19239
|
"https://github.com/node-fetch/node-fetch/issues/1000 (request)"
|
|
19231
19240
|
);
|
|
19232
|
-
var
|
|
19241
|
+
var Request2 = class _Request extends Body {
|
|
19233
19242
|
constructor(input, init = {}) {
|
|
19234
19243
|
let parsedURL;
|
|
19235
19244
|
if (isRequest(input)) {
|
|
@@ -19344,7 +19353,7 @@ var Request = class _Request extends Body {
|
|
|
19344
19353
|
return "Request";
|
|
19345
19354
|
}
|
|
19346
19355
|
};
|
|
19347
|
-
Object.defineProperties(
|
|
19356
|
+
Object.defineProperties(Request2.prototype, {
|
|
19348
19357
|
method: { enumerable: true },
|
|
19349
19358
|
url: { enumerable: true },
|
|
19350
19359
|
headers: { enumerable: true },
|
|
@@ -19424,7 +19433,7 @@ init_from();
|
|
|
19424
19433
|
var supportedSchemas = /* @__PURE__ */ new Set(["data:", "http:", "https:"]);
|
|
19425
19434
|
async function fetch2(url, options_) {
|
|
19426
19435
|
return new Promise((resolve2, reject) => {
|
|
19427
|
-
const request = new
|
|
19436
|
+
const request = new Request2(url, options_);
|
|
19428
19437
|
const { parsedURL, options } = getNodeRequestOptions(request);
|
|
19429
19438
|
if (!supportedSchemas.has(parsedURL.protocol)) {
|
|
19430
19439
|
throw new TypeError(`node-fetch cannot load ${url}. URL scheme "${parsedURL.protocol.replace(/:$/, "")}" is not supported.`);
|
|
@@ -19554,7 +19563,7 @@ async function fetch2(url, options_) {
|
|
|
19554
19563
|
if (responseReferrerPolicy) {
|
|
19555
19564
|
requestOptions.referrerPolicy = responseReferrerPolicy;
|
|
19556
19565
|
}
|
|
19557
|
-
resolve2(fetch2(new
|
|
19566
|
+
resolve2(fetch2(new Request2(locationURL, requestOptions)));
|
|
19558
19567
|
finalize();
|
|
19559
19568
|
return;
|
|
19560
19569
|
}
|
|
@@ -25393,6 +25402,134 @@ function readList(cursor, length, to) {
|
|
|
25393
25402
|
return value;
|
|
25394
25403
|
}
|
|
25395
25404
|
|
|
25405
|
+
// node_modules/viem/_esm/utils/rpc/http.js
|
|
25406
|
+
init_request();
|
|
25407
|
+
|
|
25408
|
+
// node_modules/viem/_esm/utils/promise/withTimeout.js
|
|
25409
|
+
function withTimeout(fn, { errorInstance = new Error("timed out"), timeout, signal }) {
|
|
25410
|
+
return new Promise((resolve2, reject) => {
|
|
25411
|
+
;
|
|
25412
|
+
(async () => {
|
|
25413
|
+
let timeoutId;
|
|
25414
|
+
try {
|
|
25415
|
+
const controller = new AbortController();
|
|
25416
|
+
if (timeout > 0) {
|
|
25417
|
+
timeoutId = setTimeout(() => {
|
|
25418
|
+
if (signal) {
|
|
25419
|
+
controller.abort();
|
|
25420
|
+
} else {
|
|
25421
|
+
reject(errorInstance);
|
|
25422
|
+
}
|
|
25423
|
+
}, timeout);
|
|
25424
|
+
}
|
|
25425
|
+
resolve2(await fn({ signal: controller?.signal || null }));
|
|
25426
|
+
} catch (err) {
|
|
25427
|
+
if (err?.name === "AbortError")
|
|
25428
|
+
reject(errorInstance);
|
|
25429
|
+
reject(err);
|
|
25430
|
+
} finally {
|
|
25431
|
+
clearTimeout(timeoutId);
|
|
25432
|
+
}
|
|
25433
|
+
})();
|
|
25434
|
+
});
|
|
25435
|
+
}
|
|
25436
|
+
|
|
25437
|
+
// node_modules/viem/_esm/utils/rpc/http.js
|
|
25438
|
+
init_stringify();
|
|
25439
|
+
|
|
25440
|
+
// node_modules/viem/_esm/utils/rpc/id.js
|
|
25441
|
+
function createIdStore() {
|
|
25442
|
+
return {
|
|
25443
|
+
current: 0,
|
|
25444
|
+
take() {
|
|
25445
|
+
return this.current++;
|
|
25446
|
+
},
|
|
25447
|
+
reset() {
|
|
25448
|
+
this.current = 0;
|
|
25449
|
+
}
|
|
25450
|
+
};
|
|
25451
|
+
}
|
|
25452
|
+
var idCache = /* @__PURE__ */ createIdStore();
|
|
25453
|
+
|
|
25454
|
+
// node_modules/viem/_esm/utils/rpc/http.js
|
|
25455
|
+
function getHttpRpcClient(url, options = {}) {
|
|
25456
|
+
return {
|
|
25457
|
+
async request(params) {
|
|
25458
|
+
const { body, fetchFn = options.fetchFn ?? fetch, onRequest = options.onRequest, onResponse = options.onResponse, timeout = options.timeout ?? 1e4 } = params;
|
|
25459
|
+
const fetchOptions = {
|
|
25460
|
+
...options.fetchOptions ?? {},
|
|
25461
|
+
...params.fetchOptions ?? {}
|
|
25462
|
+
};
|
|
25463
|
+
const { headers, method, signal: signal_ } = fetchOptions;
|
|
25464
|
+
try {
|
|
25465
|
+
const response = await withTimeout(async ({ signal }) => {
|
|
25466
|
+
const init = {
|
|
25467
|
+
...fetchOptions,
|
|
25468
|
+
body: Array.isArray(body) ? stringify(body.map((body2) => ({
|
|
25469
|
+
jsonrpc: "2.0",
|
|
25470
|
+
id: body2.id ?? idCache.take(),
|
|
25471
|
+
...body2
|
|
25472
|
+
}))) : stringify({
|
|
25473
|
+
jsonrpc: "2.0",
|
|
25474
|
+
id: body.id ?? idCache.take(),
|
|
25475
|
+
...body
|
|
25476
|
+
}),
|
|
25477
|
+
headers: {
|
|
25478
|
+
"Content-Type": "application/json",
|
|
25479
|
+
...headers
|
|
25480
|
+
},
|
|
25481
|
+
method: method || "POST",
|
|
25482
|
+
signal: signal_ || (timeout > 0 ? signal : null)
|
|
25483
|
+
};
|
|
25484
|
+
const request = new Request(url, init);
|
|
25485
|
+
const args = await onRequest?.(request, init) ?? { ...init, url };
|
|
25486
|
+
const response2 = await fetchFn(args.url ?? url, args);
|
|
25487
|
+
return response2;
|
|
25488
|
+
}, {
|
|
25489
|
+
errorInstance: new TimeoutError({ body, url }),
|
|
25490
|
+
timeout,
|
|
25491
|
+
signal: true
|
|
25492
|
+
});
|
|
25493
|
+
if (onResponse)
|
|
25494
|
+
await onResponse(response);
|
|
25495
|
+
let data;
|
|
25496
|
+
if (response.headers.get("Content-Type")?.startsWith("application/json"))
|
|
25497
|
+
data = await response.json();
|
|
25498
|
+
else {
|
|
25499
|
+
data = await response.text();
|
|
25500
|
+
try {
|
|
25501
|
+
data = JSON.parse(data || "{}");
|
|
25502
|
+
} catch (err) {
|
|
25503
|
+
if (response.ok)
|
|
25504
|
+
throw err;
|
|
25505
|
+
data = { error: data };
|
|
25506
|
+
}
|
|
25507
|
+
}
|
|
25508
|
+
if (!response.ok) {
|
|
25509
|
+
throw new HttpRequestError({
|
|
25510
|
+
body,
|
|
25511
|
+
details: stringify(data.error) || response.statusText,
|
|
25512
|
+
headers: response.headers,
|
|
25513
|
+
status: response.status,
|
|
25514
|
+
url
|
|
25515
|
+
});
|
|
25516
|
+
}
|
|
25517
|
+
return data;
|
|
25518
|
+
} catch (err) {
|
|
25519
|
+
if (err instanceof HttpRequestError)
|
|
25520
|
+
throw err;
|
|
25521
|
+
if (err instanceof TimeoutError)
|
|
25522
|
+
throw err;
|
|
25523
|
+
throw new HttpRequestError({
|
|
25524
|
+
body,
|
|
25525
|
+
cause: err,
|
|
25526
|
+
url
|
|
25527
|
+
});
|
|
25528
|
+
}
|
|
25529
|
+
}
|
|
25530
|
+
};
|
|
25531
|
+
}
|
|
25532
|
+
|
|
25396
25533
|
// node_modules/viem/_esm/utils/signature/hashMessage.js
|
|
25397
25534
|
init_keccak256();
|
|
25398
25535
|
|
|
@@ -31645,6 +31782,19 @@ function walletActions(client) {
|
|
|
31645
31782
|
};
|
|
31646
31783
|
}
|
|
31647
31784
|
|
|
31785
|
+
// node_modules/viem/_esm/clients/createWalletClient.js
|
|
31786
|
+
function createWalletClient(parameters) {
|
|
31787
|
+
const { key = "wallet", name = "Wallet Client", transport } = parameters;
|
|
31788
|
+
const client = createClient({
|
|
31789
|
+
...parameters,
|
|
31790
|
+
key,
|
|
31791
|
+
name,
|
|
31792
|
+
transport,
|
|
31793
|
+
type: "walletClient"
|
|
31794
|
+
});
|
|
31795
|
+
return client.extend(walletActions);
|
|
31796
|
+
}
|
|
31797
|
+
|
|
31648
31798
|
// node_modules/viem/_esm/clients/transports/createTransport.js
|
|
31649
31799
|
function createTransport({ key, methods, name, request, retryCount = 3, retryDelay = 150, timeout, type }, value) {
|
|
31650
31800
|
const uid2 = uid();
|
|
@@ -31678,6 +31828,82 @@ function custom(provider, config = {}) {
|
|
|
31678
31828
|
});
|
|
31679
31829
|
}
|
|
31680
31830
|
|
|
31831
|
+
// node_modules/viem/_esm/clients/transports/http.js
|
|
31832
|
+
init_request();
|
|
31833
|
+
|
|
31834
|
+
// node_modules/viem/_esm/errors/transport.js
|
|
31835
|
+
init_base();
|
|
31836
|
+
var UrlRequiredError = class extends BaseError2 {
|
|
31837
|
+
constructor() {
|
|
31838
|
+
super("No URL was provided to the Transport. Please provide a valid RPC URL to the Transport.", {
|
|
31839
|
+
docsPath: "/docs/clients/intro",
|
|
31840
|
+
name: "UrlRequiredError"
|
|
31841
|
+
});
|
|
31842
|
+
}
|
|
31843
|
+
};
|
|
31844
|
+
|
|
31845
|
+
// node_modules/viem/_esm/clients/transports/http.js
|
|
31846
|
+
init_createBatchScheduler();
|
|
31847
|
+
function http3(url, config = {}) {
|
|
31848
|
+
const { batch, fetchFn, fetchOptions, key = "http", methods, name = "HTTP JSON-RPC", onFetchRequest, onFetchResponse, retryDelay, raw } = config;
|
|
31849
|
+
return ({ chain, retryCount: retryCount_, timeout: timeout_ }) => {
|
|
31850
|
+
const { batchSize = 1e3, wait: wait2 = 0 } = typeof batch === "object" ? batch : {};
|
|
31851
|
+
const retryCount = config.retryCount ?? retryCount_;
|
|
31852
|
+
const timeout = timeout_ ?? config.timeout ?? 1e4;
|
|
31853
|
+
const url_ = url || chain?.rpcUrls.default.http[0];
|
|
31854
|
+
if (!url_)
|
|
31855
|
+
throw new UrlRequiredError();
|
|
31856
|
+
const rpcClient2 = getHttpRpcClient(url_, {
|
|
31857
|
+
fetchFn,
|
|
31858
|
+
fetchOptions,
|
|
31859
|
+
onRequest: onFetchRequest,
|
|
31860
|
+
onResponse: onFetchResponse,
|
|
31861
|
+
timeout
|
|
31862
|
+
});
|
|
31863
|
+
return createTransport({
|
|
31864
|
+
key,
|
|
31865
|
+
methods,
|
|
31866
|
+
name,
|
|
31867
|
+
async request({ method, params }) {
|
|
31868
|
+
const body = { method, params };
|
|
31869
|
+
const { schedule } = createBatchScheduler({
|
|
31870
|
+
id: url_,
|
|
31871
|
+
wait: wait2,
|
|
31872
|
+
shouldSplitBatch(requests) {
|
|
31873
|
+
return requests.length > batchSize;
|
|
31874
|
+
},
|
|
31875
|
+
fn: (body2) => rpcClient2.request({
|
|
31876
|
+
body: body2
|
|
31877
|
+
}),
|
|
31878
|
+
sort: (a, b) => a.id - b.id
|
|
31879
|
+
});
|
|
31880
|
+
const fn = async (body2) => batch ? schedule(body2) : [
|
|
31881
|
+
await rpcClient2.request({
|
|
31882
|
+
body: body2
|
|
31883
|
+
})
|
|
31884
|
+
];
|
|
31885
|
+
const [{ error, result }] = await fn(body);
|
|
31886
|
+
if (raw)
|
|
31887
|
+
return { error, result };
|
|
31888
|
+
if (error)
|
|
31889
|
+
throw new RpcRequestError({
|
|
31890
|
+
body,
|
|
31891
|
+
error,
|
|
31892
|
+
url: url_
|
|
31893
|
+
});
|
|
31894
|
+
return result;
|
|
31895
|
+
},
|
|
31896
|
+
retryCount,
|
|
31897
|
+
retryDelay,
|
|
31898
|
+
timeout,
|
|
31899
|
+
type: "http"
|
|
31900
|
+
}, {
|
|
31901
|
+
fetchOptions,
|
|
31902
|
+
url: url_
|
|
31903
|
+
});
|
|
31904
|
+
};
|
|
31905
|
+
}
|
|
31906
|
+
|
|
31681
31907
|
// node_modules/viem/_esm/index.js
|
|
31682
31908
|
init_base();
|
|
31683
31909
|
init_contract();
|
|
@@ -31687,7 +31913,7 @@ init_fromHex();
|
|
|
31687
31913
|
init_toHex();
|
|
31688
31914
|
init_formatEther();
|
|
31689
31915
|
|
|
31690
|
-
// node_modules/genlayer-js/dist/chunk-
|
|
31916
|
+
// node_modules/genlayer-js/dist/chunk-V4ZFI4GV.js
|
|
31691
31917
|
var chains_exports = {};
|
|
31692
31918
|
__export2(chains_exports, {
|
|
31693
31919
|
localnet: () => localnet,
|
|
@@ -39783,6 +40009,67 @@ var VALIDATOR_WALLET_ABI = [
|
|
|
39783
40009
|
{ name: "extraCid", type: "bytes" }
|
|
39784
40010
|
],
|
|
39785
40011
|
outputs: []
|
|
40012
|
+
},
|
|
40013
|
+
// Staking functions (forwarded to staking contract)
|
|
40014
|
+
{
|
|
40015
|
+
name: "validatorDeposit",
|
|
40016
|
+
type: "function",
|
|
40017
|
+
stateMutability: "payable",
|
|
40018
|
+
inputs: [],
|
|
40019
|
+
outputs: []
|
|
40020
|
+
},
|
|
40021
|
+
{
|
|
40022
|
+
name: "validatorExit",
|
|
40023
|
+
type: "function",
|
|
40024
|
+
stateMutability: "nonpayable",
|
|
40025
|
+
inputs: [{ name: "_shares", type: "uint256" }],
|
|
40026
|
+
outputs: []
|
|
40027
|
+
},
|
|
40028
|
+
{
|
|
40029
|
+
name: "validatorClaim",
|
|
40030
|
+
type: "function",
|
|
40031
|
+
stateMutability: "nonpayable",
|
|
40032
|
+
inputs: [],
|
|
40033
|
+
outputs: []
|
|
40034
|
+
},
|
|
40035
|
+
// Two-step operator transfer
|
|
40036
|
+
{
|
|
40037
|
+
name: "initiateOperatorTransfer",
|
|
40038
|
+
type: "function",
|
|
40039
|
+
stateMutability: "nonpayable",
|
|
40040
|
+
inputs: [{ name: "_newOperator", type: "address" }],
|
|
40041
|
+
outputs: []
|
|
40042
|
+
},
|
|
40043
|
+
{
|
|
40044
|
+
name: "completeOperatorTransfer",
|
|
40045
|
+
type: "function",
|
|
40046
|
+
stateMutability: "nonpayable",
|
|
40047
|
+
inputs: [],
|
|
40048
|
+
outputs: []
|
|
40049
|
+
},
|
|
40050
|
+
{
|
|
40051
|
+
name: "cancelOperatorTransfer",
|
|
40052
|
+
type: "function",
|
|
40053
|
+
stateMutability: "nonpayable",
|
|
40054
|
+
inputs: [],
|
|
40055
|
+
outputs: []
|
|
40056
|
+
},
|
|
40057
|
+
{
|
|
40058
|
+
name: "getPendingOperator",
|
|
40059
|
+
type: "function",
|
|
40060
|
+
stateMutability: "view",
|
|
40061
|
+
inputs: [],
|
|
40062
|
+
outputs: [
|
|
40063
|
+
{ name: "", type: "address" },
|
|
40064
|
+
{ name: "", type: "uint256" }
|
|
40065
|
+
]
|
|
40066
|
+
},
|
|
40067
|
+
{
|
|
40068
|
+
name: "getOperator",
|
|
40069
|
+
type: "function",
|
|
40070
|
+
stateMutability: "view",
|
|
40071
|
+
inputs: [],
|
|
40072
|
+
outputs: [{ name: "", type: "address" }]
|
|
39786
40073
|
}
|
|
39787
40074
|
];
|
|
39788
40075
|
var STAKING_ABI = [
|
|
@@ -49226,6 +49513,33 @@ var StakingAction = class extends BaseAction {
|
|
|
49226
49513
|
const addr = keystoreData.address;
|
|
49227
49514
|
return addr.startsWith("0x") ? addr : `0x${addr}`;
|
|
49228
49515
|
}
|
|
49516
|
+
/**
|
|
49517
|
+
* Get viem clients for direct contract interactions (e.g., ValidatorWallet calls)
|
|
49518
|
+
* Future: can be extended to support hardware wallets
|
|
49519
|
+
*/
|
|
49520
|
+
async getViemClients(config) {
|
|
49521
|
+
if (config.account) {
|
|
49522
|
+
this.accountOverride = config.account;
|
|
49523
|
+
}
|
|
49524
|
+
const network = this.getNetwork(config);
|
|
49525
|
+
const rpcUrl = config.rpc || network.rpcUrls.default.http[0];
|
|
49526
|
+
const privateKey = await this.getPrivateKeyForStaking();
|
|
49527
|
+
const account = privateKeyToAccount(privateKey);
|
|
49528
|
+
const publicClient = createPublicClient({
|
|
49529
|
+
chain: network,
|
|
49530
|
+
transport: http3(rpcUrl)
|
|
49531
|
+
});
|
|
49532
|
+
const walletClient = createWalletClient({
|
|
49533
|
+
chain: network,
|
|
49534
|
+
transport: http3(rpcUrl),
|
|
49535
|
+
account
|
|
49536
|
+
});
|
|
49537
|
+
return {
|
|
49538
|
+
walletClient,
|
|
49539
|
+
publicClient,
|
|
49540
|
+
signerAddress: account.address
|
|
49541
|
+
};
|
|
49542
|
+
}
|
|
49229
49543
|
};
|
|
49230
49544
|
|
|
49231
49545
|
// src/commands/staking/validatorJoin.ts
|
|
@@ -49271,15 +49585,23 @@ var ValidatorDepositAction = class extends StakingAction {
|
|
|
49271
49585
|
async execute(options) {
|
|
49272
49586
|
this.startSpinner("Making validator deposit...");
|
|
49273
49587
|
try {
|
|
49274
|
-
const client = await this.getStakingClient(options);
|
|
49275
49588
|
const amount = this.parseAmount(options.amount);
|
|
49276
|
-
|
|
49277
|
-
const
|
|
49589
|
+
const validatorWallet = options.validator;
|
|
49590
|
+
const { walletClient, publicClient } = await this.getViemClients(options);
|
|
49591
|
+
this.setSpinnerText(`Depositing ${this.formatAmount(amount)} to validator ${validatorWallet}...`);
|
|
49592
|
+
const hash3 = await walletClient.writeContract({
|
|
49593
|
+
address: validatorWallet,
|
|
49594
|
+
abi: abi_exports.VALIDATOR_WALLET_ABI,
|
|
49595
|
+
functionName: "validatorDeposit",
|
|
49596
|
+
value: amount
|
|
49597
|
+
});
|
|
49598
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: hash3 });
|
|
49278
49599
|
const output = {
|
|
49279
|
-
transactionHash:
|
|
49600
|
+
transactionHash: receipt.transactionHash,
|
|
49601
|
+
validator: validatorWallet,
|
|
49280
49602
|
amount: this.formatAmount(amount),
|
|
49281
|
-
blockNumber:
|
|
49282
|
-
gasUsed:
|
|
49603
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
49604
|
+
gasUsed: receipt.gasUsed.toString()
|
|
49283
49605
|
};
|
|
49284
49606
|
this.succeedSpinner("Deposit successful!", output);
|
|
49285
49607
|
} catch (error) {
|
|
@@ -49304,15 +49626,26 @@ var ValidatorExitAction = class extends StakingAction {
|
|
|
49304
49626
|
this.failSpinner(`Invalid shares value: "${options.shares}". Must be a positive whole number.`);
|
|
49305
49627
|
return;
|
|
49306
49628
|
}
|
|
49307
|
-
const
|
|
49308
|
-
|
|
49309
|
-
|
|
49629
|
+
const validatorWallet = options.validator;
|
|
49630
|
+
const { walletClient, publicClient } = await this.getViemClients(options);
|
|
49631
|
+
this.setSpinnerText(`Exiting validator ${validatorWallet} with ${shares} shares...`);
|
|
49632
|
+
const hash3 = await walletClient.writeContract({
|
|
49633
|
+
address: validatorWallet,
|
|
49634
|
+
abi: abi_exports.VALIDATOR_WALLET_ABI,
|
|
49635
|
+
functionName: "validatorExit",
|
|
49636
|
+
args: [shares]
|
|
49637
|
+
});
|
|
49638
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: hash3 });
|
|
49639
|
+
const readClient = await this.getReadOnlyStakingClient(options);
|
|
49640
|
+
const epochInfo = await readClient.getEpochInfo();
|
|
49641
|
+
const isEpochZero = epochInfo.currentEpoch === 0n;
|
|
49310
49642
|
const output = {
|
|
49311
|
-
transactionHash:
|
|
49643
|
+
transactionHash: receipt.transactionHash,
|
|
49644
|
+
validator: validatorWallet,
|
|
49312
49645
|
sharesWithdrawn: shares.toString(),
|
|
49313
|
-
blockNumber:
|
|
49314
|
-
gasUsed:
|
|
49315
|
-
note: "Withdrawal will be claimable after the unbonding period"
|
|
49646
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
49647
|
+
gasUsed: receipt.gasUsed.toString(),
|
|
49648
|
+
note: isEpochZero ? "Epoch 0: Withdrawal claimable immediately" : "Withdrawal will be claimable after the unbonding period"
|
|
49316
49649
|
};
|
|
49317
49650
|
this.succeedSpinner("Exit initiated successfully!", output);
|
|
49318
49651
|
} catch (error) {
|
|
@@ -49329,17 +49662,20 @@ var ValidatorClaimAction = class extends StakingAction {
|
|
|
49329
49662
|
async execute(options) {
|
|
49330
49663
|
this.startSpinner("Claiming validator withdrawals...");
|
|
49331
49664
|
try {
|
|
49332
|
-
const
|
|
49333
|
-
const
|
|
49334
|
-
this.setSpinnerText(`Claiming for validator ${
|
|
49335
|
-
const
|
|
49336
|
-
|
|
49665
|
+
const validatorWallet = options.validator;
|
|
49666
|
+
const { walletClient, publicClient } = await this.getViemClients(options);
|
|
49667
|
+
this.setSpinnerText(`Claiming for validator ${validatorWallet}...`);
|
|
49668
|
+
const hash3 = await walletClient.writeContract({
|
|
49669
|
+
address: validatorWallet,
|
|
49670
|
+
abi: abi_exports.VALIDATOR_WALLET_ABI,
|
|
49671
|
+
functionName: "validatorClaim"
|
|
49337
49672
|
});
|
|
49673
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: hash3 });
|
|
49338
49674
|
const output = {
|
|
49339
|
-
transactionHash:
|
|
49340
|
-
validator:
|
|
49341
|
-
blockNumber:
|
|
49342
|
-
gasUsed:
|
|
49675
|
+
transactionHash: receipt.transactionHash,
|
|
49676
|
+
validator: validatorWallet,
|
|
49677
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
49678
|
+
gasUsed: receipt.gasUsed.toString()
|
|
49343
49679
|
};
|
|
49344
49680
|
this.succeedSpinner("Claim successful!", output);
|
|
49345
49681
|
} catch (error) {
|
|
@@ -49380,18 +49716,22 @@ var SetOperatorAction = class extends StakingAction {
|
|
|
49380
49716
|
async execute(options) {
|
|
49381
49717
|
this.startSpinner("Setting operator...");
|
|
49382
49718
|
try {
|
|
49383
|
-
const
|
|
49719
|
+
const validatorWallet = options.validator;
|
|
49720
|
+
const { walletClient, publicClient } = await this.getViemClients(options);
|
|
49384
49721
|
this.setSpinnerText(`Setting operator to ${options.operator}...`);
|
|
49385
|
-
const
|
|
49386
|
-
|
|
49387
|
-
|
|
49722
|
+
const hash3 = await walletClient.writeContract({
|
|
49723
|
+
address: validatorWallet,
|
|
49724
|
+
abi: abi_exports.VALIDATOR_WALLET_ABI,
|
|
49725
|
+
functionName: "setOperator",
|
|
49726
|
+
args: [options.operator]
|
|
49388
49727
|
});
|
|
49728
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: hash3 });
|
|
49389
49729
|
const output = {
|
|
49390
|
-
transactionHash:
|
|
49391
|
-
validator:
|
|
49730
|
+
transactionHash: receipt.transactionHash,
|
|
49731
|
+
validator: validatorWallet,
|
|
49392
49732
|
newOperator: options.operator,
|
|
49393
|
-
blockNumber:
|
|
49394
|
-
gasUsed:
|
|
49733
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
49734
|
+
gasUsed: receipt.gasUsed.toString()
|
|
49395
49735
|
};
|
|
49396
49736
|
this.succeedSpinner("Operator updated!", output);
|
|
49397
49737
|
} catch (error) {
|
|
@@ -49408,26 +49748,33 @@ var SetIdentityAction = class extends StakingAction {
|
|
|
49408
49748
|
async execute(options) {
|
|
49409
49749
|
this.startSpinner("Setting validator identity...");
|
|
49410
49750
|
try {
|
|
49411
|
-
const
|
|
49412
|
-
this.
|
|
49413
|
-
|
|
49414
|
-
|
|
49415
|
-
|
|
49416
|
-
|
|
49417
|
-
|
|
49418
|
-
|
|
49419
|
-
|
|
49420
|
-
|
|
49421
|
-
|
|
49422
|
-
|
|
49423
|
-
|
|
49751
|
+
const validatorWallet = options.validator;
|
|
49752
|
+
const { walletClient, publicClient } = await this.getViemClients(options);
|
|
49753
|
+
this.setSpinnerText(`Setting identity for ${validatorWallet}...`);
|
|
49754
|
+
const extraCidBytes = options.extraCid ? toHex(new TextEncoder().encode(options.extraCid)) : "0x";
|
|
49755
|
+
const hash3 = await walletClient.writeContract({
|
|
49756
|
+
address: validatorWallet,
|
|
49757
|
+
abi: abi_exports.VALIDATOR_WALLET_ABI,
|
|
49758
|
+
functionName: "setIdentity",
|
|
49759
|
+
args: [
|
|
49760
|
+
options.moniker,
|
|
49761
|
+
options.logoUri || "",
|
|
49762
|
+
options.website || "",
|
|
49763
|
+
options.description || "",
|
|
49764
|
+
options.email || "",
|
|
49765
|
+
options.twitter || "",
|
|
49766
|
+
options.telegram || "",
|
|
49767
|
+
options.github || "",
|
|
49768
|
+
extraCidBytes
|
|
49769
|
+
]
|
|
49424
49770
|
});
|
|
49771
|
+
const receipt = await publicClient.waitForTransactionReceipt({ hash: hash3 });
|
|
49425
49772
|
const output = {
|
|
49426
|
-
transactionHash:
|
|
49427
|
-
validator:
|
|
49773
|
+
transactionHash: receipt.transactionHash,
|
|
49774
|
+
validator: validatorWallet,
|
|
49428
49775
|
moniker: options.moniker,
|
|
49429
|
-
blockNumber:
|
|
49430
|
-
gasUsed:
|
|
49776
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
49777
|
+
gasUsed: receipt.gasUsed.toString()
|
|
49431
49778
|
};
|
|
49432
49779
|
if (options.logoUri) output.logoUri = options.logoUri;
|
|
49433
49780
|
if (options.website) output.website = options.website;
|
|
@@ -49498,13 +49845,15 @@ var DelegatorExitAction = class extends StakingAction {
|
|
|
49498
49845
|
validator: options.validator,
|
|
49499
49846
|
shares
|
|
49500
49847
|
});
|
|
49848
|
+
const epochInfo = await client.getEpochInfo();
|
|
49849
|
+
const isEpochZero = epochInfo.currentEpoch === 0n;
|
|
49501
49850
|
const output = {
|
|
49502
49851
|
transactionHash: result.transactionHash,
|
|
49503
49852
|
validator: options.validator,
|
|
49504
49853
|
sharesWithdrawn: shares.toString(),
|
|
49505
49854
|
blockNumber: result.blockNumber.toString(),
|
|
49506
49855
|
gasUsed: result.gasUsed.toString(),
|
|
49507
|
-
note: "Withdrawal will be claimable after the unbonding period"
|
|
49856
|
+
note: isEpochZero ? "Epoch 0: Withdrawal claimable immediately" : "Withdrawal will be claimable after the unbonding period"
|
|
49508
49857
|
};
|
|
49509
49858
|
this.succeedSpinner("Exit initiated successfully!", output);
|
|
49510
49859
|
} catch (error) {
|
|
@@ -50424,15 +50773,15 @@ function initializeStakingCommands(program2) {
|
|
|
50424
50773
|
const action = new ValidatorJoinAction();
|
|
50425
50774
|
await action.execute(options);
|
|
50426
50775
|
});
|
|
50427
|
-
staking.command("validator-deposit").description("Make an additional deposit
|
|
50776
|
+
staking.command("validator-deposit").description("Make an additional deposit to a validator wallet").requiredOption("--validator <address>", "Validator wallet contract address to deposit to").requiredOption("--amount <amount>", "Amount to deposit (in wei or with 'eth'/'gen' suffix)").option("--account <name>", "Account to use (must be validator owner)").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").action(async (options) => {
|
|
50428
50777
|
const action = new ValidatorDepositAction();
|
|
50429
50778
|
await action.execute(options);
|
|
50430
50779
|
});
|
|
50431
|
-
staking.command("validator-exit").description("Exit as a validator by withdrawing shares").requiredOption("--shares <shares>", "Number of shares to withdraw").option("--account <name>", "Account to use").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").
|
|
50780
|
+
staking.command("validator-exit").description("Exit as a validator by withdrawing shares").requiredOption("--validator <address>", "Validator wallet contract address").requiredOption("--shares <shares>", "Number of shares to withdraw").option("--account <name>", "Account to use (must be validator owner)").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").action(async (options) => {
|
|
50432
50781
|
const action = new ValidatorExitAction();
|
|
50433
50782
|
await action.execute(options);
|
|
50434
50783
|
});
|
|
50435
|
-
staking.command("validator-claim").description("Claim validator withdrawals after unbonding period").
|
|
50784
|
+
staking.command("validator-claim").description("Claim validator withdrawals after unbonding period").requiredOption("--validator <address>", "Validator wallet contract address").option("--account <name>", "Account to use").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").action(async (options) => {
|
|
50436
50785
|
const action = new ValidatorClaimAction();
|
|
50437
50786
|
await action.execute(options);
|
|
50438
50787
|
});
|
|
@@ -50440,11 +50789,11 @@ function initializeStakingCommands(program2) {
|
|
|
50440
50789
|
const action = new ValidatorPrimeAction();
|
|
50441
50790
|
await action.execute(options);
|
|
50442
50791
|
});
|
|
50443
|
-
staking.command("set-operator").description("Change the operator address for a validator wallet").requiredOption("--validator <address>", "Validator wallet address").requiredOption("--operator <address>", "New operator address").option("--account <name>", "Account to use").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").
|
|
50792
|
+
staking.command("set-operator").description("Change the operator address for a validator wallet").requiredOption("--validator <address>", "Validator wallet address").requiredOption("--operator <address>", "New operator address").option("--account <name>", "Account to use (must be validator owner)").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").action(async (options) => {
|
|
50444
50793
|
const action = new SetOperatorAction();
|
|
50445
50794
|
await action.execute(options);
|
|
50446
50795
|
});
|
|
50447
|
-
staking.command("set-identity").description("Set validator identity metadata (moniker, website, socials, etc.)").requiredOption("--validator <address>", "Validator wallet address").requiredOption("--moniker <name>", "Validator display name").option("--logo-uri <uri>", "Logo URI").option("--website <url>", "Website URL").option("--description <text>", "Description").option("--email <email>", "Contact email").option("--twitter <handle>", "Twitter handle").option("--telegram <handle>", "Telegram handle").option("--github <handle>", "GitHub handle").option("--extra-cid <cid>", "Extra data as IPFS CID or hex bytes (0x...)").option("--account <name>", "Account to use").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").
|
|
50796
|
+
staking.command("set-identity").description("Set validator identity metadata (moniker, website, socials, etc.)").requiredOption("--validator <address>", "Validator wallet address").requiredOption("--moniker <name>", "Validator display name").option("--logo-uri <uri>", "Logo URI").option("--website <url>", "Website URL").option("--description <text>", "Description").option("--email <email>", "Contact email").option("--twitter <handle>", "Twitter handle").option("--telegram <handle>", "Telegram handle").option("--github <handle>", "GitHub handle").option("--extra-cid <cid>", "Extra data as IPFS CID or hex bytes (0x...)").option("--account <name>", "Account to use (must be validator operator)").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").action(async (options) => {
|
|
50448
50797
|
const action = new SetIdentityAction();
|
|
50449
50798
|
await action.execute(options);
|
|
50450
50799
|
});
|
package/docs/validator-guide.md
CHANGED
|
@@ -229,7 +229,7 @@ Output will include:
|
|
|
229
229
|
### Add More Stake
|
|
230
230
|
|
|
231
231
|
```bash
|
|
232
|
-
genlayer staking validator-deposit --amount 1000gen
|
|
232
|
+
genlayer staking validator-deposit --validator 0xYourValidatorWallet... --amount 1000gen
|
|
233
233
|
```
|
|
234
234
|
|
|
235
235
|
### Check Active Validators
|
|
@@ -241,7 +241,7 @@ genlayer staking active-validators
|
|
|
241
241
|
### Exit as Validator
|
|
242
242
|
|
|
243
243
|
```bash
|
|
244
|
-
genlayer staking validator-exit --shares 100
|
|
244
|
+
genlayer staking validator-exit --validator 0xYourValidatorWallet... --shares 100
|
|
245
245
|
```
|
|
246
246
|
|
|
247
247
|
This initiates a withdrawal. Your tokens enter an **unbonding period of 7 epochs** before they can be claimed.
|
|
@@ -264,7 +264,7 @@ selfStakePendingWithdrawals: [
|
|
|
264
264
|
After the 7-epoch unbonding period:
|
|
265
265
|
|
|
266
266
|
```bash
|
|
267
|
-
genlayer staking validator-claim
|
|
267
|
+
genlayer staking validator-claim --validator 0xYourValidatorWallet...
|
|
268
268
|
```
|
|
269
269
|
|
|
270
270
|
## Troubleshooting
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "genlayer",
|
|
3
|
-
"version": "0.32.
|
|
3
|
+
"version": "0.32.4",
|
|
4
4
|
"description": "GenLayer Command Line Tool",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"type": "module",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"dotenv": "^17.0.0",
|
|
66
66
|
"ethers": "^6.13.4",
|
|
67
67
|
"fs-extra": "^11.3.0",
|
|
68
|
-
"genlayer-js": "^0.18.
|
|
68
|
+
"genlayer-js": "^0.18.8",
|
|
69
69
|
"inquirer": "^12.0.0",
|
|
70
70
|
"keytar": "^7.9.0",
|
|
71
71
|
"node-fetch": "^3.0.0",
|
|
@@ -3,6 +3,8 @@ import {createClient, createAccount, formatStakingAmount, parseStakingAmount, ab
|
|
|
3
3
|
import type {GenLayerClient, GenLayerChain, Address} from "genlayer-js/types";
|
|
4
4
|
import {readFileSync, existsSync} from "fs";
|
|
5
5
|
import {ethers} from "ethers";
|
|
6
|
+
import {createPublicClient, createWalletClient, http, type PublicClient, type WalletClient, type Chain, type Account} from "viem";
|
|
7
|
+
import {privateKeyToAccount} from "viem/accounts";
|
|
6
8
|
|
|
7
9
|
// Re-export for use by other staking commands
|
|
8
10
|
export {BUILT_IN_NETWORKS};
|
|
@@ -154,4 +156,41 @@ export class StakingAction extends BaseAction {
|
|
|
154
156
|
const addr = keystoreData.address as string;
|
|
155
157
|
return (addr.startsWith("0x") ? addr : `0x${addr}`) as Address;
|
|
156
158
|
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Get viem clients for direct contract interactions (e.g., ValidatorWallet calls)
|
|
162
|
+
* Future: can be extended to support hardware wallets
|
|
163
|
+
*/
|
|
164
|
+
protected async getViemClients(config: StakingConfig): Promise<{
|
|
165
|
+
walletClient: WalletClient<any, Chain, Account>;
|
|
166
|
+
publicClient: PublicClient;
|
|
167
|
+
signerAddress: Address;
|
|
168
|
+
}> {
|
|
169
|
+
if (config.account) {
|
|
170
|
+
this.accountOverride = config.account;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const network = this.getNetwork(config);
|
|
174
|
+
const rpcUrl = config.rpc || network.rpcUrls.default.http[0];
|
|
175
|
+
|
|
176
|
+
const privateKey = await this.getPrivateKeyForStaking();
|
|
177
|
+
const account = privateKeyToAccount(privateKey as `0x${string}`);
|
|
178
|
+
|
|
179
|
+
const publicClient = createPublicClient({
|
|
180
|
+
chain: network,
|
|
181
|
+
transport: http(rpcUrl),
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const walletClient = createWalletClient({
|
|
185
|
+
chain: network,
|
|
186
|
+
transport: http(rpcUrl),
|
|
187
|
+
account,
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
return {
|
|
191
|
+
walletClient,
|
|
192
|
+
publicClient,
|
|
193
|
+
signerAddress: account.address as Address,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
157
196
|
}
|
|
@@ -33,13 +33,19 @@ export class DelegatorExitAction extends StakingAction {
|
|
|
33
33
|
shares,
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
+
// Check epoch to determine note
|
|
37
|
+
const epochInfo = await client.getEpochInfo();
|
|
38
|
+
const isEpochZero = epochInfo.currentEpoch === 0n;
|
|
39
|
+
|
|
36
40
|
const output = {
|
|
37
41
|
transactionHash: result.transactionHash,
|
|
38
42
|
validator: options.validator,
|
|
39
43
|
sharesWithdrawn: shares.toString(),
|
|
40
44
|
blockNumber: result.blockNumber.toString(),
|
|
41
45
|
gasUsed: result.gasUsed.toString(),
|
|
42
|
-
note:
|
|
46
|
+
note: isEpochZero
|
|
47
|
+
? "Epoch 0: Withdrawal claimable immediately"
|
|
48
|
+
: "Withdrawal will be claimable after the unbonding period",
|
|
43
49
|
};
|
|
44
50
|
|
|
45
51
|
this.succeedSpinner("Exit initiated successfully!", output);
|
|
@@ -46,12 +46,12 @@ export function initializeStakingCommands(program: Command) {
|
|
|
46
46
|
|
|
47
47
|
staking
|
|
48
48
|
.command("validator-deposit")
|
|
49
|
-
.description("Make an additional deposit
|
|
49
|
+
.description("Make an additional deposit to a validator wallet")
|
|
50
|
+
.requiredOption("--validator <address>", "Validator wallet contract address to deposit to")
|
|
50
51
|
.requiredOption("--amount <amount>", "Amount to deposit (in wei or with 'eth'/'gen' suffix)")
|
|
51
|
-
.option("--account <name>", "Account to use")
|
|
52
|
+
.option("--account <name>", "Account to use (must be validator owner)")
|
|
52
53
|
.option("--network <network>", "Network to use (localnet, testnet-asimov)")
|
|
53
54
|
.option("--rpc <rpcUrl>", "RPC URL for the network")
|
|
54
|
-
.option("--staking-address <address>", "Staking contract address (overrides chain config)")
|
|
55
55
|
.action(async (options: ValidatorDepositOptions) => {
|
|
56
56
|
const action = new ValidatorDepositAction();
|
|
57
57
|
await action.execute(options);
|
|
@@ -60,11 +60,11 @@ export function initializeStakingCommands(program: Command) {
|
|
|
60
60
|
staking
|
|
61
61
|
.command("validator-exit")
|
|
62
62
|
.description("Exit as a validator by withdrawing shares")
|
|
63
|
+
.requiredOption("--validator <address>", "Validator wallet contract address")
|
|
63
64
|
.requiredOption("--shares <shares>", "Number of shares to withdraw")
|
|
64
|
-
.option("--account <name>", "Account to use")
|
|
65
|
+
.option("--account <name>", "Account to use (must be validator owner)")
|
|
65
66
|
.option("--network <network>", "Network to use (localnet, testnet-asimov)")
|
|
66
67
|
.option("--rpc <rpcUrl>", "RPC URL for the network")
|
|
67
|
-
.option("--staking-address <address>", "Staking contract address (overrides chain config)")
|
|
68
68
|
.action(async (options: ValidatorExitOptions) => {
|
|
69
69
|
const action = new ValidatorExitAction();
|
|
70
70
|
await action.execute(options);
|
|
@@ -73,11 +73,10 @@ export function initializeStakingCommands(program: Command) {
|
|
|
73
73
|
staking
|
|
74
74
|
.command("validator-claim")
|
|
75
75
|
.description("Claim validator withdrawals after unbonding period")
|
|
76
|
-
.
|
|
76
|
+
.requiredOption("--validator <address>", "Validator wallet contract address")
|
|
77
77
|
.option("--account <name>", "Account to use")
|
|
78
78
|
.option("--network <network>", "Network to use (localnet, testnet-asimov)")
|
|
79
79
|
.option("--rpc <rpcUrl>", "RPC URL for the network")
|
|
80
|
-
.option("--staking-address <address>", "Staking contract address (overrides chain config)")
|
|
81
80
|
.action(async (options: ValidatorClaimOptions) => {
|
|
82
81
|
const action = new ValidatorClaimAction();
|
|
83
82
|
await action.execute(options);
|
|
@@ -101,10 +100,9 @@ export function initializeStakingCommands(program: Command) {
|
|
|
101
100
|
.description("Change the operator address for a validator wallet")
|
|
102
101
|
.requiredOption("--validator <address>", "Validator wallet address")
|
|
103
102
|
.requiredOption("--operator <address>", "New operator address")
|
|
104
|
-
.option("--account <name>", "Account to use")
|
|
103
|
+
.option("--account <name>", "Account to use (must be validator owner)")
|
|
105
104
|
.option("--network <network>", "Network to use (localnet, testnet-asimov)")
|
|
106
105
|
.option("--rpc <rpcUrl>", "RPC URL for the network")
|
|
107
|
-
.option("--staking-address <address>", "Staking contract address (overrides chain config)")
|
|
108
106
|
.action(async (options: SetOperatorOptions) => {
|
|
109
107
|
const action = new SetOperatorAction();
|
|
110
108
|
await action.execute(options);
|
|
@@ -123,10 +121,9 @@ export function initializeStakingCommands(program: Command) {
|
|
|
123
121
|
.option("--telegram <handle>", "Telegram handle")
|
|
124
122
|
.option("--github <handle>", "GitHub handle")
|
|
125
123
|
.option("--extra-cid <cid>", "Extra data as IPFS CID or hex bytes (0x...)")
|
|
126
|
-
.option("--account <name>", "Account to use")
|
|
124
|
+
.option("--account <name>", "Account to use (must be validator operator)")
|
|
127
125
|
.option("--network <network>", "Network to use (localnet, testnet-asimov)")
|
|
128
126
|
.option("--rpc <rpcUrl>", "RPC URL for the network")
|
|
129
|
-
.option("--staking-address <address>", "Staking contract address (overrides chain config)")
|
|
130
127
|
.action(async (options: SetIdentityOptions) => {
|
|
131
128
|
const action = new SetIdentityAction();
|
|
132
129
|
await action.execute(options);
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {StakingAction, StakingConfig} from "./StakingAction";
|
|
2
2
|
import type {Address} from "genlayer-js/types";
|
|
3
|
+
import {abi} from "genlayer-js";
|
|
4
|
+
import {toHex} from "viem";
|
|
3
5
|
|
|
4
6
|
export interface SetIdentityOptions extends StakingConfig {
|
|
5
7
|
validator: string;
|
|
@@ -23,29 +25,39 @@ export class SetIdentityAction extends StakingAction {
|
|
|
23
25
|
this.startSpinner("Setting validator identity...");
|
|
24
26
|
|
|
25
27
|
try {
|
|
26
|
-
const
|
|
28
|
+
const validatorWallet = options.validator as Address;
|
|
29
|
+
const {walletClient, publicClient} = await this.getViemClients(options);
|
|
27
30
|
|
|
28
|
-
this.setSpinnerText(`Setting identity for ${
|
|
31
|
+
this.setSpinnerText(`Setting identity for ${validatorWallet}...`);
|
|
29
32
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
// Convert extraCid string to bytes (hex)
|
|
34
|
+
const extraCidBytes = options.extraCid ? toHex(new TextEncoder().encode(options.extraCid)) : "0x";
|
|
35
|
+
|
|
36
|
+
const hash = await walletClient.writeContract({
|
|
37
|
+
address: validatorWallet,
|
|
38
|
+
abi: abi.VALIDATOR_WALLET_ABI,
|
|
39
|
+
functionName: "setIdentity",
|
|
40
|
+
args: [
|
|
41
|
+
options.moniker,
|
|
42
|
+
options.logoUri || "",
|
|
43
|
+
options.website || "",
|
|
44
|
+
options.description || "",
|
|
45
|
+
options.email || "",
|
|
46
|
+
options.twitter || "",
|
|
47
|
+
options.telegram || "",
|
|
48
|
+
options.github || "",
|
|
49
|
+
extraCidBytes,
|
|
50
|
+
],
|
|
41
51
|
});
|
|
42
52
|
|
|
53
|
+
const receipt = await publicClient.waitForTransactionReceipt({hash});
|
|
54
|
+
|
|
43
55
|
const output: Record<string, any> = {
|
|
44
|
-
transactionHash:
|
|
45
|
-
validator:
|
|
56
|
+
transactionHash: receipt.transactionHash,
|
|
57
|
+
validator: validatorWallet,
|
|
46
58
|
moniker: options.moniker,
|
|
47
|
-
blockNumber:
|
|
48
|
-
gasUsed:
|
|
59
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
60
|
+
gasUsed: receipt.gasUsed.toString(),
|
|
49
61
|
};
|
|
50
62
|
|
|
51
63
|
// Add optional fields that were set
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {StakingAction, StakingConfig} from "./StakingAction";
|
|
2
2
|
import type {Address} from "genlayer-js/types";
|
|
3
|
+
import {abi} from "genlayer-js";
|
|
3
4
|
|
|
4
5
|
export interface SetOperatorOptions extends StakingConfig {
|
|
5
6
|
validator: string;
|
|
@@ -15,21 +16,26 @@ export class SetOperatorAction extends StakingAction {
|
|
|
15
16
|
this.startSpinner("Setting operator...");
|
|
16
17
|
|
|
17
18
|
try {
|
|
18
|
-
const
|
|
19
|
+
const validatorWallet = options.validator as Address;
|
|
20
|
+
const {walletClient, publicClient} = await this.getViemClients(options);
|
|
19
21
|
|
|
20
22
|
this.setSpinnerText(`Setting operator to ${options.operator}...`);
|
|
21
23
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
const hash = await walletClient.writeContract({
|
|
25
|
+
address: validatorWallet,
|
|
26
|
+
abi: abi.VALIDATOR_WALLET_ABI,
|
|
27
|
+
functionName: "setOperator",
|
|
28
|
+
args: [options.operator as Address],
|
|
25
29
|
});
|
|
26
30
|
|
|
31
|
+
const receipt = await publicClient.waitForTransactionReceipt({hash});
|
|
32
|
+
|
|
27
33
|
const output = {
|
|
28
|
-
transactionHash:
|
|
29
|
-
validator:
|
|
34
|
+
transactionHash: receipt.transactionHash,
|
|
35
|
+
validator: validatorWallet,
|
|
30
36
|
newOperator: options.operator,
|
|
31
|
-
blockNumber:
|
|
32
|
-
gasUsed:
|
|
37
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
38
|
+
gasUsed: receipt.gasUsed.toString(),
|
|
33
39
|
};
|
|
34
40
|
|
|
35
41
|
this.succeedSpinner("Operator updated!", output);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {StakingAction, StakingConfig} from "./StakingAction";
|
|
2
2
|
import type {Address} from "genlayer-js/types";
|
|
3
|
+
import {abi} from "genlayer-js";
|
|
3
4
|
|
|
4
5
|
export interface ValidatorClaimOptions extends StakingConfig {
|
|
5
|
-
validator
|
|
6
|
+
validator: string;
|
|
6
7
|
}
|
|
7
8
|
|
|
8
9
|
export class ValidatorClaimAction extends StakingAction {
|
|
@@ -14,20 +15,24 @@ export class ValidatorClaimAction extends StakingAction {
|
|
|
14
15
|
this.startSpinner("Claiming validator withdrawals...");
|
|
15
16
|
|
|
16
17
|
try {
|
|
17
|
-
const
|
|
18
|
-
const
|
|
18
|
+
const validatorWallet = options.validator as Address;
|
|
19
|
+
const {walletClient, publicClient} = await this.getViemClients(options);
|
|
19
20
|
|
|
20
|
-
this.setSpinnerText(`Claiming for validator ${
|
|
21
|
+
this.setSpinnerText(`Claiming for validator ${validatorWallet}...`);
|
|
21
22
|
|
|
22
|
-
const
|
|
23
|
-
|
|
23
|
+
const hash = await walletClient.writeContract({
|
|
24
|
+
address: validatorWallet,
|
|
25
|
+
abi: abi.VALIDATOR_WALLET_ABI,
|
|
26
|
+
functionName: "validatorClaim",
|
|
24
27
|
});
|
|
25
28
|
|
|
29
|
+
const receipt = await publicClient.waitForTransactionReceipt({hash});
|
|
30
|
+
|
|
26
31
|
const output = {
|
|
27
|
-
transactionHash:
|
|
28
|
-
validator:
|
|
29
|
-
blockNumber:
|
|
30
|
-
gasUsed:
|
|
32
|
+
transactionHash: receipt.transactionHash,
|
|
33
|
+
validator: validatorWallet,
|
|
34
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
35
|
+
gasUsed: receipt.gasUsed.toString(),
|
|
31
36
|
};
|
|
32
37
|
|
|
33
38
|
this.succeedSpinner("Claim successful!", output);
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import {StakingAction, StakingConfig} from "./StakingAction";
|
|
2
|
+
import type {Address} from "genlayer-js/types";
|
|
3
|
+
import {abi} from "genlayer-js";
|
|
2
4
|
|
|
3
5
|
export interface ValidatorDepositOptions extends StakingConfig {
|
|
4
6
|
amount: string;
|
|
7
|
+
validator: string;
|
|
5
8
|
}
|
|
6
9
|
|
|
7
10
|
export class ValidatorDepositAction extends StakingAction {
|
|
@@ -13,18 +16,28 @@ export class ValidatorDepositAction extends StakingAction {
|
|
|
13
16
|
this.startSpinner("Making validator deposit...");
|
|
14
17
|
|
|
15
18
|
try {
|
|
16
|
-
const client = await this.getStakingClient(options);
|
|
17
19
|
const amount = this.parseAmount(options.amount);
|
|
20
|
+
const validatorWallet = options.validator as Address;
|
|
18
21
|
|
|
19
|
-
|
|
22
|
+
const {walletClient, publicClient} = await this.getViemClients(options);
|
|
20
23
|
|
|
21
|
-
|
|
24
|
+
this.setSpinnerText(`Depositing ${this.formatAmount(amount)} to validator ${validatorWallet}...`);
|
|
25
|
+
|
|
26
|
+
const hash = await walletClient.writeContract({
|
|
27
|
+
address: validatorWallet,
|
|
28
|
+
abi: abi.VALIDATOR_WALLET_ABI,
|
|
29
|
+
functionName: "validatorDeposit",
|
|
30
|
+
value: amount,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const receipt = await publicClient.waitForTransactionReceipt({hash});
|
|
22
34
|
|
|
23
35
|
const output = {
|
|
24
|
-
transactionHash:
|
|
36
|
+
transactionHash: receipt.transactionHash,
|
|
37
|
+
validator: validatorWallet,
|
|
25
38
|
amount: this.formatAmount(amount),
|
|
26
|
-
blockNumber:
|
|
27
|
-
gasUsed:
|
|
39
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
40
|
+
gasUsed: receipt.gasUsed.toString(),
|
|
28
41
|
};
|
|
29
42
|
|
|
30
43
|
this.succeedSpinner("Deposit successful!", output);
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {StakingAction, StakingConfig} from "./StakingAction";
|
|
2
|
+
import type {Address} from "genlayer-js/types";
|
|
3
|
+
import {abi} from "genlayer-js";
|
|
2
4
|
|
|
3
5
|
export interface ValidatorExitOptions extends StakingConfig {
|
|
6
|
+
validator: string;
|
|
4
7
|
shares: string;
|
|
5
8
|
}
|
|
6
9
|
|
|
@@ -22,18 +25,34 @@ export class ValidatorExitAction extends StakingAction {
|
|
|
22
25
|
return;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
|
-
const
|
|
28
|
+
const validatorWallet = options.validator as Address;
|
|
29
|
+
const {walletClient, publicClient} = await this.getViemClients(options);
|
|
26
30
|
|
|
27
|
-
this.setSpinnerText(`Exiting with ${shares} shares...`);
|
|
31
|
+
this.setSpinnerText(`Exiting validator ${validatorWallet} with ${shares} shares...`);
|
|
28
32
|
|
|
29
|
-
const
|
|
33
|
+
const hash = await walletClient.writeContract({
|
|
34
|
+
address: validatorWallet,
|
|
35
|
+
abi: abi.VALIDATOR_WALLET_ABI,
|
|
36
|
+
functionName: "validatorExit",
|
|
37
|
+
args: [shares],
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const receipt = await publicClient.waitForTransactionReceipt({hash});
|
|
41
|
+
|
|
42
|
+
// Check epoch to determine note
|
|
43
|
+
const readClient = await this.getReadOnlyStakingClient(options);
|
|
44
|
+
const epochInfo = await readClient.getEpochInfo();
|
|
45
|
+
const isEpochZero = epochInfo.currentEpoch === 0n;
|
|
30
46
|
|
|
31
47
|
const output = {
|
|
32
|
-
transactionHash:
|
|
48
|
+
transactionHash: receipt.transactionHash,
|
|
49
|
+
validator: validatorWallet,
|
|
33
50
|
sharesWithdrawn: shares.toString(),
|
|
34
|
-
blockNumber:
|
|
35
|
-
gasUsed:
|
|
36
|
-
note:
|
|
51
|
+
blockNumber: receipt.blockNumber.toString(),
|
|
52
|
+
gasUsed: receipt.gasUsed.toString(),
|
|
53
|
+
note: isEpochZero
|
|
54
|
+
? "Epoch 0: Withdrawal claimable immediately"
|
|
55
|
+
: "Withdrawal will be claimable after the unbonding period",
|
|
37
56
|
};
|
|
38
57
|
|
|
39
58
|
this.succeedSpinner("Exit initiated successfully!", output);
|
|
@@ -119,65 +119,9 @@ describe("ValidatorJoinAction", () => {
|
|
|
119
119
|
});
|
|
120
120
|
});
|
|
121
121
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
beforeEach(() => {
|
|
126
|
-
vi.clearAllMocks();
|
|
127
|
-
action = new ValidatorDepositAction();
|
|
128
|
-
setupActionMocks(action);
|
|
129
|
-
mockClient.validatorDeposit.mockResolvedValue(mockTxResult);
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
test("deposits successfully", async () => {
|
|
133
|
-
await action.execute({amount: "1000gen", stakingAddress: "0xStaking"});
|
|
134
|
-
|
|
135
|
-
expect(mockClient.validatorDeposit).toHaveBeenCalledWith({amount: expect.any(BigInt)});
|
|
136
|
-
expect(action["succeedSpinner"]).toHaveBeenCalledWith("Deposit successful!", expect.any(Object));
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
describe("ValidatorExitAction", () => {
|
|
141
|
-
let action: ValidatorExitAction;
|
|
142
|
-
|
|
143
|
-
beforeEach(() => {
|
|
144
|
-
vi.clearAllMocks();
|
|
145
|
-
action = new ValidatorExitAction();
|
|
146
|
-
setupActionMocks(action);
|
|
147
|
-
mockClient.validatorExit.mockResolvedValue(mockTxResult);
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
test("exits successfully", async () => {
|
|
151
|
-
await action.execute({shares: "100", stakingAddress: "0xStaking"});
|
|
152
|
-
|
|
153
|
-
expect(mockClient.validatorExit).toHaveBeenCalledWith({shares: 100n});
|
|
154
|
-
expect(action["succeedSpinner"]).toHaveBeenCalledWith("Exit initiated successfully!", expect.any(Object));
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
describe("ValidatorClaimAction", () => {
|
|
159
|
-
let action: ValidatorClaimAction;
|
|
160
|
-
|
|
161
|
-
beforeEach(() => {
|
|
162
|
-
vi.clearAllMocks();
|
|
163
|
-
action = new ValidatorClaimAction();
|
|
164
|
-
setupActionMocks(action);
|
|
165
|
-
mockClient.validatorClaim.mockResolvedValue({...mockTxResult, claimedAmount: 0n});
|
|
166
|
-
});
|
|
167
|
-
|
|
168
|
-
test("claims successfully", async () => {
|
|
169
|
-
await action.execute({validator: "0xValidator", stakingAddress: "0xStaking"});
|
|
170
|
-
|
|
171
|
-
expect(mockClient.validatorClaim).toHaveBeenCalledWith({validator: "0xValidator"});
|
|
172
|
-
expect(action["succeedSpinner"]).toHaveBeenCalledWith("Claim successful!", expect.any(Object));
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
test("uses signer address if no validator specified", async () => {
|
|
176
|
-
await action.execute({stakingAddress: "0xStaking"});
|
|
177
|
-
|
|
178
|
-
expect(mockClient.validatorClaim).toHaveBeenCalledWith({validator: "0xMockedSigner"});
|
|
179
|
-
});
|
|
180
|
-
});
|
|
122
|
+
// ValidatorDepositAction, ValidatorExitAction, ValidatorClaimAction tests
|
|
123
|
+
// are covered by command-level tests. These actions now use viem directly
|
|
124
|
+
// to call ValidatorWallet contracts and require complex viem mocking.
|
|
181
125
|
|
|
182
126
|
describe("DelegatorJoinAction", () => {
|
|
183
127
|
let action: DelegatorJoinAction;
|
|
@@ -208,6 +152,7 @@ describe("DelegatorExitAction", () => {
|
|
|
208
152
|
action = new DelegatorExitAction();
|
|
209
153
|
setupActionMocks(action);
|
|
210
154
|
mockClient.delegatorExit.mockResolvedValue(mockTxResult);
|
|
155
|
+
mockClient.getEpochInfo.mockResolvedValue(mockEpochInfo);
|
|
211
156
|
});
|
|
212
157
|
|
|
213
158
|
test("exits successfully", async () => {
|
|
@@ -86,10 +86,11 @@ describe("staking commands", () => {
|
|
|
86
86
|
|
|
87
87
|
describe("validator-deposit", () => {
|
|
88
88
|
test("calls ValidatorDepositAction.execute", async () => {
|
|
89
|
-
program.parse(["node", "test", "staking", "validator-deposit", "--amount", "1000gen"]);
|
|
89
|
+
program.parse(["node", "test", "staking", "validator-deposit", "--validator", "0x1234567890123456789012345678901234567890", "--amount", "1000gen"]);
|
|
90
90
|
|
|
91
91
|
expect(ValidatorDepositAction).toHaveBeenCalledTimes(1);
|
|
92
92
|
expect(ValidatorDepositAction.prototype.execute).toHaveBeenCalledWith({
|
|
93
|
+
validator: "0x1234567890123456789012345678901234567890",
|
|
93
94
|
amount: "1000gen",
|
|
94
95
|
});
|
|
95
96
|
});
|
|
@@ -97,10 +98,11 @@ describe("staking commands", () => {
|
|
|
97
98
|
|
|
98
99
|
describe("validator-exit", () => {
|
|
99
100
|
test("calls ValidatorExitAction.execute", async () => {
|
|
100
|
-
program.parse(["node", "test", "staking", "validator-exit", "--shares", "100"]);
|
|
101
|
+
program.parse(["node", "test", "staking", "validator-exit", "--validator", "0x1234567890123456789012345678901234567890", "--shares", "100"]);
|
|
101
102
|
|
|
102
103
|
expect(ValidatorExitAction).toHaveBeenCalledTimes(1);
|
|
103
104
|
expect(ValidatorExitAction.prototype.execute).toHaveBeenCalledWith({
|
|
105
|
+
validator: "0x1234567890123456789012345678901234567890",
|
|
104
106
|
shares: "100",
|
|
105
107
|
});
|
|
106
108
|
});
|
|
@@ -115,12 +117,6 @@ describe("staking commands", () => {
|
|
|
115
117
|
validator: "0xValidator",
|
|
116
118
|
});
|
|
117
119
|
});
|
|
118
|
-
|
|
119
|
-
test("works without validator option", async () => {
|
|
120
|
-
program.parse(["node", "test", "staking", "validator-claim"]);
|
|
121
|
-
|
|
122
|
-
expect(ValidatorClaimAction.prototype.execute).toHaveBeenCalledWith({});
|
|
123
|
-
});
|
|
124
120
|
});
|
|
125
121
|
|
|
126
122
|
describe("delegator-join", () => {
|