genlayer 0.32.0 → 0.32.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/CHANGELOG.md +4 -0
- package/README.md +1 -1
- package/dist/index.js +1425 -222
- package/docs/delegator-guide.md +6 -6
- package/docs/validator-guide.md +51 -18
- package/package.json +2 -2
- package/src/commands/account/create.ts +10 -4
- package/src/commands/account/export.ts +106 -0
- package/src/commands/account/import.ts +85 -31
- package/src/commands/account/index.ts +77 -18
- package/src/commands/account/list.ts +34 -0
- package/src/commands/account/lock.ts +16 -7
- package/src/commands/account/remove.ts +30 -0
- package/src/commands/account/send.ts +14 -8
- package/src/commands/account/show.ts +22 -8
- package/src/commands/account/unlock.ts +20 -10
- package/src/commands/account/use.ts +21 -0
- package/src/commands/network/index.ts +18 -3
- package/src/commands/network/setNetwork.ts +38 -22
- package/src/commands/staking/StakingAction.ts +51 -19
- package/src/commands/staking/delegatorJoin.ts +2 -0
- package/src/commands/staking/index.ts +29 -2
- package/src/commands/staking/setIdentity.ts +5 -0
- package/src/commands/staking/stakingInfo.ts +29 -21
- package/src/commands/staking/wizard.ts +809 -0
- package/src/lib/actions/BaseAction.ts +71 -45
- package/src/lib/config/ConfigFileManager.ts +143 -0
- package/src/lib/config/KeychainManager.ts +68 -8
- package/tests/actions/create.test.ts +30 -10
- package/tests/actions/deploy.test.ts +7 -0
- package/tests/actions/lock.test.ts +28 -8
- package/tests/actions/unlock.test.ts +44 -26
- package/tests/commands/account.test.ts +43 -18
- package/tests/commands/network.test.ts +10 -10
- package/tests/commands/staking.test.ts +122 -0
- package/tests/libs/baseAction.test.ts +64 -41
- package/tests/libs/configFileManager.test.ts +8 -1
- package/tests/libs/keychainManager.test.ts +62 -18
- package/src/lib/interfaces/KeystoreData.ts +0 -5
package/dist/index.js
CHANGED
|
@@ -18249,7 +18249,7 @@ var require_semver2 = __commonJS({
|
|
|
18249
18249
|
import { program } from "commander";
|
|
18250
18250
|
|
|
18251
18251
|
// package.json
|
|
18252
|
-
var version = "0.32.
|
|
18252
|
+
var version = "0.32.2";
|
|
18253
18253
|
var package_default = {
|
|
18254
18254
|
name: "genlayer",
|
|
18255
18255
|
version,
|
|
@@ -18317,7 +18317,7 @@ var package_default = {
|
|
|
18317
18317
|
dotenv: "^17.0.0",
|
|
18318
18318
|
ethers: "^6.13.4",
|
|
18319
18319
|
"fs-extra": "^11.3.0",
|
|
18320
|
-
"genlayer-js": "^0.18.
|
|
18320
|
+
"genlayer-js": "^0.18.7",
|
|
18321
18321
|
inquirer: "^12.0.0",
|
|
18322
18322
|
keytar: "^7.9.0",
|
|
18323
18323
|
"node-fetch": "^3.0.0",
|
|
@@ -19771,21 +19771,79 @@ var ConfigFileManager = class {
|
|
|
19771
19771
|
constructor(baseFolder = ".genlayer/", configFileName = "genlayer-config.json") {
|
|
19772
19772
|
__publicField(this, "folderPath");
|
|
19773
19773
|
__publicField(this, "configFilePath");
|
|
19774
|
+
__publicField(this, "keystoresPath");
|
|
19774
19775
|
this.folderPath = path.resolve(os.homedir(), baseFolder);
|
|
19775
19776
|
this.configFilePath = path.resolve(this.folderPath, configFileName);
|
|
19777
|
+
this.keystoresPath = path.resolve(this.folderPath, "keystores");
|
|
19776
19778
|
this.ensureFolderExists();
|
|
19779
|
+
this.ensureKeystoresDirExists();
|
|
19777
19780
|
this.ensureConfigFileExists();
|
|
19781
|
+
this.migrateOldConfig();
|
|
19782
|
+
this.migrateKeystoreFormats();
|
|
19778
19783
|
}
|
|
19779
19784
|
ensureFolderExists() {
|
|
19780
19785
|
if (!fs2.existsSync(this.folderPath)) {
|
|
19781
19786
|
fs2.mkdirSync(this.folderPath, { recursive: true });
|
|
19782
19787
|
}
|
|
19783
19788
|
}
|
|
19789
|
+
ensureKeystoresDirExists() {
|
|
19790
|
+
if (!fs2.existsSync(this.keystoresPath)) {
|
|
19791
|
+
fs2.mkdirSync(this.keystoresPath, { recursive: true });
|
|
19792
|
+
}
|
|
19793
|
+
}
|
|
19784
19794
|
ensureConfigFileExists() {
|
|
19785
19795
|
if (!fs2.existsSync(this.configFilePath)) {
|
|
19786
19796
|
fs2.writeFileSync(this.configFilePath, JSON.stringify({}, null, 2));
|
|
19787
19797
|
}
|
|
19788
19798
|
}
|
|
19799
|
+
migrateOldConfig() {
|
|
19800
|
+
const config = this.getConfig();
|
|
19801
|
+
if (config.keyPairPath && !config.activeAccount) {
|
|
19802
|
+
const oldPath = config.keyPairPath;
|
|
19803
|
+
if (fs2.existsSync(oldPath)) {
|
|
19804
|
+
const newPath = this.getKeystorePath("default");
|
|
19805
|
+
const content = fs2.readFileSync(oldPath, "utf-8");
|
|
19806
|
+
const web3Content = this.convertToWeb3Format(content);
|
|
19807
|
+
fs2.writeFileSync(newPath, web3Content);
|
|
19808
|
+
delete config.keyPairPath;
|
|
19809
|
+
config.activeAccount = "default";
|
|
19810
|
+
fs2.writeFileSync(this.configFilePath, JSON.stringify(config, null, 2));
|
|
19811
|
+
}
|
|
19812
|
+
}
|
|
19813
|
+
}
|
|
19814
|
+
migrateKeystoreFormats() {
|
|
19815
|
+
if (!fs2.existsSync(this.keystoresPath)) {
|
|
19816
|
+
return;
|
|
19817
|
+
}
|
|
19818
|
+
const files = fs2.readdirSync(this.keystoresPath);
|
|
19819
|
+
if (!Array.isArray(files)) {
|
|
19820
|
+
return;
|
|
19821
|
+
}
|
|
19822
|
+
for (const file of files) {
|
|
19823
|
+
if (!file.endsWith(".json")) continue;
|
|
19824
|
+
const filePath = path.resolve(this.keystoresPath, file);
|
|
19825
|
+
try {
|
|
19826
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
19827
|
+
const parsed = JSON.parse(content);
|
|
19828
|
+
if (parsed.encrypted && typeof parsed.encrypted === "string") {
|
|
19829
|
+
const web3Content = this.convertToWeb3Format(content);
|
|
19830
|
+
fs2.writeFileSync(filePath, web3Content);
|
|
19831
|
+
}
|
|
19832
|
+
} catch {
|
|
19833
|
+
}
|
|
19834
|
+
}
|
|
19835
|
+
}
|
|
19836
|
+
convertToWeb3Format(content) {
|
|
19837
|
+
try {
|
|
19838
|
+
const parsed = JSON.parse(content);
|
|
19839
|
+
if (parsed.encrypted && typeof parsed.encrypted === "string") {
|
|
19840
|
+
return parsed.encrypted;
|
|
19841
|
+
}
|
|
19842
|
+
return content;
|
|
19843
|
+
} catch {
|
|
19844
|
+
return content;
|
|
19845
|
+
}
|
|
19846
|
+
}
|
|
19789
19847
|
getFolderPath() {
|
|
19790
19848
|
return this.folderPath;
|
|
19791
19849
|
}
|
|
@@ -19805,33 +19863,140 @@ var ConfigFileManager = class {
|
|
|
19805
19863
|
config[key] = value;
|
|
19806
19864
|
fs2.writeFileSync(this.configFilePath, JSON.stringify(config, null, 2));
|
|
19807
19865
|
}
|
|
19866
|
+
removeConfig(key) {
|
|
19867
|
+
const config = this.getConfig();
|
|
19868
|
+
delete config[key];
|
|
19869
|
+
fs2.writeFileSync(this.configFilePath, JSON.stringify(config, null, 2));
|
|
19870
|
+
}
|
|
19871
|
+
getKeystoresPath() {
|
|
19872
|
+
return this.keystoresPath;
|
|
19873
|
+
}
|
|
19874
|
+
getKeystorePath(name) {
|
|
19875
|
+
return path.resolve(this.keystoresPath, `${name}.json`);
|
|
19876
|
+
}
|
|
19877
|
+
accountExists(name) {
|
|
19878
|
+
return fs2.existsSync(this.getKeystorePath(name));
|
|
19879
|
+
}
|
|
19880
|
+
getActiveAccount() {
|
|
19881
|
+
return this.getConfigByKey("activeAccount");
|
|
19882
|
+
}
|
|
19883
|
+
setActiveAccount(name) {
|
|
19884
|
+
if (!this.accountExists(name)) {
|
|
19885
|
+
throw new Error(`Account '${name}' does not exist`);
|
|
19886
|
+
}
|
|
19887
|
+
this.writeConfig("activeAccount", name);
|
|
19888
|
+
}
|
|
19889
|
+
listAccounts() {
|
|
19890
|
+
if (!fs2.existsSync(this.keystoresPath)) {
|
|
19891
|
+
return [];
|
|
19892
|
+
}
|
|
19893
|
+
const files = fs2.readdirSync(this.keystoresPath);
|
|
19894
|
+
const accounts = [];
|
|
19895
|
+
for (const file of files) {
|
|
19896
|
+
if (!file.endsWith(".json")) continue;
|
|
19897
|
+
const name = file.replace(".json", "");
|
|
19898
|
+
const filePath = this.getKeystorePath(name);
|
|
19899
|
+
try {
|
|
19900
|
+
const content = JSON.parse(fs2.readFileSync(filePath, "utf-8"));
|
|
19901
|
+
const addr = content.address || "unknown";
|
|
19902
|
+
accounts.push({
|
|
19903
|
+
name,
|
|
19904
|
+
address: addr.startsWith("0x") ? addr : `0x${addr}`,
|
|
19905
|
+
path: filePath
|
|
19906
|
+
});
|
|
19907
|
+
} catch {
|
|
19908
|
+
}
|
|
19909
|
+
}
|
|
19910
|
+
return accounts;
|
|
19911
|
+
}
|
|
19912
|
+
removeAccount(name) {
|
|
19913
|
+
const keystorePath = this.getKeystorePath(name);
|
|
19914
|
+
if (!fs2.existsSync(keystorePath)) {
|
|
19915
|
+
throw new Error(`Account '${name}' does not exist`);
|
|
19916
|
+
}
|
|
19917
|
+
fs2.unlinkSync(keystorePath);
|
|
19918
|
+
if (this.getActiveAccount() === name) {
|
|
19919
|
+
this.removeConfig("activeAccount");
|
|
19920
|
+
}
|
|
19921
|
+
}
|
|
19808
19922
|
};
|
|
19809
19923
|
|
|
19810
19924
|
// src/lib/config/KeychainManager.ts
|
|
19811
|
-
|
|
19925
|
+
var keytarModule = null;
|
|
19926
|
+
var keytarLoadAttempted = false;
|
|
19927
|
+
async function getKeytar() {
|
|
19928
|
+
if (keytarLoadAttempted) return keytarModule;
|
|
19929
|
+
keytarLoadAttempted = true;
|
|
19930
|
+
try {
|
|
19931
|
+
const mod3 = await import("keytar");
|
|
19932
|
+
keytarModule = mod3.default ?? mod3;
|
|
19933
|
+
return keytarModule;
|
|
19934
|
+
} catch {
|
|
19935
|
+
return null;
|
|
19936
|
+
}
|
|
19937
|
+
}
|
|
19812
19938
|
var _KeychainManager = class _KeychainManager {
|
|
19813
19939
|
constructor() {
|
|
19814
19940
|
}
|
|
19941
|
+
getKeychainAccount(accountName) {
|
|
19942
|
+
return `account:${accountName}`;
|
|
19943
|
+
}
|
|
19815
19944
|
async isKeychainAvailable() {
|
|
19816
19945
|
try {
|
|
19946
|
+
const keytar = await getKeytar();
|
|
19947
|
+
if (!keytar) return false;
|
|
19817
19948
|
await keytar.findCredentials("test-service");
|
|
19818
19949
|
return true;
|
|
19819
19950
|
} catch {
|
|
19820
19951
|
return false;
|
|
19821
19952
|
}
|
|
19822
19953
|
}
|
|
19823
|
-
async storePrivateKey(privateKey) {
|
|
19824
|
-
|
|
19954
|
+
async storePrivateKey(accountName, privateKey) {
|
|
19955
|
+
const keytar = await getKeytar();
|
|
19956
|
+
if (!keytar) throw new Error("Keychain not available. Install libsecret-1-dev on Linux.");
|
|
19957
|
+
try {
|
|
19958
|
+
return await keytar.setPassword(_KeychainManager.SERVICE, this.getKeychainAccount(accountName), privateKey);
|
|
19959
|
+
} catch (error) {
|
|
19960
|
+
if (error?.message?.includes("org.freedesktop.secrets")) {
|
|
19961
|
+
throw new Error("Keychain service not running. Install and start gnome-keyring or another secrets service.");
|
|
19962
|
+
}
|
|
19963
|
+
throw error;
|
|
19964
|
+
}
|
|
19825
19965
|
}
|
|
19826
|
-
async getPrivateKey() {
|
|
19827
|
-
|
|
19966
|
+
async getPrivateKey(accountName) {
|
|
19967
|
+
const keytar = await getKeytar();
|
|
19968
|
+
if (!keytar) return null;
|
|
19969
|
+
try {
|
|
19970
|
+
return await keytar.getPassword(_KeychainManager.SERVICE, this.getKeychainAccount(accountName));
|
|
19971
|
+
} catch {
|
|
19972
|
+
return null;
|
|
19973
|
+
}
|
|
19974
|
+
}
|
|
19975
|
+
async removePrivateKey(accountName) {
|
|
19976
|
+
const keytar = await getKeytar();
|
|
19977
|
+
if (!keytar) return false;
|
|
19978
|
+
try {
|
|
19979
|
+
return await keytar.deletePassword(_KeychainManager.SERVICE, this.getKeychainAccount(accountName));
|
|
19980
|
+
} catch {
|
|
19981
|
+
return false;
|
|
19982
|
+
}
|
|
19828
19983
|
}
|
|
19829
|
-
async
|
|
19830
|
-
|
|
19984
|
+
async listUnlockedAccounts() {
|
|
19985
|
+
const keytar = await getKeytar();
|
|
19986
|
+
if (!keytar) return [];
|
|
19987
|
+
try {
|
|
19988
|
+
const credentials = await keytar.findCredentials(_KeychainManager.SERVICE);
|
|
19989
|
+
return credentials.map((c) => c.account).filter((a) => a.startsWith("account:")).map((a) => a.replace("account:", ""));
|
|
19990
|
+
} catch {
|
|
19991
|
+
return [];
|
|
19992
|
+
}
|
|
19993
|
+
}
|
|
19994
|
+
async isAccountUnlocked(accountName) {
|
|
19995
|
+
const key = await this.getPrivateKey(accountName);
|
|
19996
|
+
return key !== null;
|
|
19831
19997
|
}
|
|
19832
19998
|
};
|
|
19833
19999
|
__publicField(_KeychainManager, "SERVICE", "genlayer-cli");
|
|
19834
|
-
__publicField(_KeychainManager, "ACCOUNT", "default-user");
|
|
19835
20000
|
var KeychainManager = _KeychainManager;
|
|
19836
20001
|
|
|
19837
20002
|
// node_modules/ora/index.js
|
|
@@ -31516,12 +31681,13 @@ function custom(provider, config = {}) {
|
|
|
31516
31681
|
// node_modules/viem/_esm/index.js
|
|
31517
31682
|
init_base();
|
|
31518
31683
|
init_contract();
|
|
31684
|
+
init_decodeErrorResult();
|
|
31519
31685
|
init_encodeFunctionData();
|
|
31520
31686
|
init_fromHex();
|
|
31521
31687
|
init_toHex();
|
|
31522
31688
|
init_formatEther();
|
|
31523
31689
|
|
|
31524
|
-
// node_modules/genlayer-js/dist/chunk-
|
|
31690
|
+
// node_modules/genlayer-js/dist/chunk-RW6PLN5W.js
|
|
31525
31691
|
var chains_exports = {};
|
|
31526
31692
|
__export2(chains_exports, {
|
|
31527
31693
|
localnet: () => localnet,
|
|
@@ -39550,6 +39716,13 @@ var studionet = defineChain({
|
|
|
39550
39716
|
defaultConsensusMaxRotations: 3
|
|
39551
39717
|
});
|
|
39552
39718
|
var VALIDATOR_WALLET_ABI = [
|
|
39719
|
+
// Custom errors
|
|
39720
|
+
{ name: "NotOperator", type: "error", inputs: [] },
|
|
39721
|
+
{ name: "InvalidAddress", type: "error", inputs: [] },
|
|
39722
|
+
{ name: "TransferFailed", type: "error", inputs: [] },
|
|
39723
|
+
{ name: "OperatorTransferNotReady", type: "error", inputs: [] },
|
|
39724
|
+
{ name: "NoPendingOperator", type: "error", inputs: [] },
|
|
39725
|
+
// Functions
|
|
39553
39726
|
{
|
|
39554
39727
|
name: "operator",
|
|
39555
39728
|
type: "function",
|
|
@@ -39613,19 +39786,51 @@ var VALIDATOR_WALLET_ABI = [
|
|
|
39613
39786
|
}
|
|
39614
39787
|
];
|
|
39615
39788
|
var STAKING_ABI = [
|
|
39616
|
-
// Custom errors
|
|
39617
|
-
{ name: "
|
|
39618
|
-
{ name: "
|
|
39619
|
-
{ name: "
|
|
39620
|
-
{ name: "
|
|
39621
|
-
{ name: "
|
|
39622
|
-
{ name: "
|
|
39623
|
-
{ name: "
|
|
39624
|
-
{ name: "
|
|
39625
|
-
{ name: "
|
|
39626
|
-
{ name: "
|
|
39627
|
-
{ name: "
|
|
39628
|
-
{ name: "
|
|
39789
|
+
// Custom errors from IGenLayerStaking
|
|
39790
|
+
{ name: "OnlyGEN", type: "error", inputs: [] },
|
|
39791
|
+
{ name: "OnlyTribunal", type: "error", inputs: [] },
|
|
39792
|
+
{ name: "OnlyIdleness", type: "error", inputs: [] },
|
|
39793
|
+
{ name: "OnlyTransactions", type: "error", inputs: [] },
|
|
39794
|
+
{ name: "OnlyIdlenessOrTribunal", type: "error", inputs: [] },
|
|
39795
|
+
{ name: "OnlyTransactionsOrTribunal", type: "error", inputs: [] },
|
|
39796
|
+
{ name: "InvalidAtEpoch", type: "error", inputs: [] },
|
|
39797
|
+
{ name: "MaxValidatorsCannotBeZero", type: "error", inputs: [] },
|
|
39798
|
+
{ name: "ValidatorExitExceedsShares", type: "error", inputs: [] },
|
|
39799
|
+
{ name: "DelegatorExitExceedsShares", type: "error", inputs: [] },
|
|
39800
|
+
{ name: "DelegatorMayNotJoinWithZeroValue", type: "error", inputs: [] },
|
|
39801
|
+
{ name: "DelegatorMayNotJoinTwoValidatorsSimultaneously", type: "error", inputs: [] },
|
|
39802
|
+
{ name: "DelegatorBelowMinimumStake", type: "error", inputs: [] },
|
|
39803
|
+
{ name: "DelegatorExitWouldBeBelowMinimum", type: "error", inputs: [] },
|
|
39804
|
+
{ name: "ValidatorNotActive", type: "error", inputs: [] },
|
|
39805
|
+
{ name: "ValidatorMayNotBeDelegator", type: "error", inputs: [] },
|
|
39806
|
+
{ name: "ValidatorMustNotBeDelegator", type: "error", inputs: [] },
|
|
39807
|
+
{ name: "ValidatorMayNotJoinWithZeroValue", type: "error", inputs: [] },
|
|
39808
|
+
{ name: "ValidatorMayNotDepositZeroValue", type: "error", inputs: [] },
|
|
39809
|
+
{ name: "ValidatorWithdrawalExceedsStake", type: "error", inputs: [] },
|
|
39810
|
+
{ name: "ValidatorAlreadyJoined", type: "error", inputs: [] },
|
|
39811
|
+
{ name: "ValidatorNotJoined", type: "error", inputs: [] },
|
|
39812
|
+
{ name: "ValidatorBelowMinimumStake", type: "error", inputs: [] },
|
|
39813
|
+
{ name: "OperatorAlreadyAssigned", type: "error", inputs: [] },
|
|
39814
|
+
{ name: "InvalidOperatorAddress", type: "error", inputs: [] },
|
|
39815
|
+
{ name: "MaxNumberOfValidatorsReached", type: "error", inputs: [] },
|
|
39816
|
+
{ name: "ValidatorsConsumed", type: "error", inputs: [] },
|
|
39817
|
+
{ name: "ValidatorsUnavailable", type: "error", inputs: [] },
|
|
39818
|
+
{ name: "EpochNotFinished", type: "error", inputs: [] },
|
|
39819
|
+
{ name: "EpochNotFinalized", type: "error", inputs: [] },
|
|
39820
|
+
{ name: "InflationInvalidAmount", type: "error", inputs: [] },
|
|
39821
|
+
{ name: "InflationAlreadyReceived", type: "error", inputs: [] },
|
|
39822
|
+
{ name: "InflationAlreadyInitialized", type: "error", inputs: [] },
|
|
39823
|
+
{ name: "EpochAlreadyFinalized", type: "error", inputs: [] },
|
|
39824
|
+
{ name: "PendingTribunals", type: "error", inputs: [{ name: "epoch", type: "uint256" }] },
|
|
39825
|
+
{ name: "FailedTransfer", type: "error", inputs: [{ name: "validator", type: "address" }] },
|
|
39826
|
+
{ name: "NFTMinterCallFailed", type: "error", inputs: [] },
|
|
39827
|
+
{ name: "DeepthoughtCallFailed", type: "error", inputs: [] },
|
|
39828
|
+
{ name: "NFTMinterNotConfigured", type: "error", inputs: [] },
|
|
39829
|
+
{ name: "NumberOfValidatorsExceedsAvailable", type: "error", inputs: [] },
|
|
39830
|
+
{ name: "EpochAdvanceNotReady", type: "error", inputs: [] },
|
|
39831
|
+
{ name: "PreviousEpochNotFinalizable", type: "error", inputs: [] },
|
|
39832
|
+
{ name: "NoBurning", type: "error", inputs: [] },
|
|
39833
|
+
{ name: "ReductionFactorCannotBeZero", type: "error", inputs: [] },
|
|
39629
39834
|
// Validator functions
|
|
39630
39835
|
{
|
|
39631
39836
|
name: "validatorJoin",
|
|
@@ -39841,6 +40046,13 @@ var STAKING_ABI = [
|
|
|
39841
40046
|
inputs: [],
|
|
39842
40047
|
outputs: [{ name: "", type: "uint256" }]
|
|
39843
40048
|
},
|
|
40049
|
+
{
|
|
40050
|
+
name: "epochZeroMinDuration",
|
|
40051
|
+
type: "function",
|
|
40052
|
+
stateMutability: "view",
|
|
40053
|
+
inputs: [],
|
|
40054
|
+
outputs: [{ name: "", type: "uint256" }]
|
|
40055
|
+
},
|
|
39844
40056
|
{
|
|
39845
40057
|
name: "getQuarantinedValidators",
|
|
39846
40058
|
type: "function",
|
|
@@ -45214,19 +45426,19 @@ var decodeTransaction = (tx) => {
|
|
|
45214
45426
|
return decodedTx;
|
|
45215
45427
|
};
|
|
45216
45428
|
var simplifyTransactionReceipt = (tx) => {
|
|
45217
|
-
const simplifyObject = (obj,
|
|
45429
|
+
const simplifyObject = (obj, path8 = "") => {
|
|
45218
45430
|
if (obj === null || obj === void 0) return obj;
|
|
45219
45431
|
if (Array.isArray(obj)) {
|
|
45220
|
-
return obj.map((item) => simplifyObject(item,
|
|
45432
|
+
return obj.map((item) => simplifyObject(item, path8)).filter((item) => item !== void 0);
|
|
45221
45433
|
}
|
|
45222
45434
|
if (typeof obj === "object") {
|
|
45223
45435
|
const result = {};
|
|
45224
45436
|
for (const [key, value] of Object.entries(obj)) {
|
|
45225
|
-
const currentPath =
|
|
45437
|
+
const currentPath = path8 ? `${path8}.${key}` : key;
|
|
45226
45438
|
if (FIELDS_TO_REMOVE.includes(key)) {
|
|
45227
45439
|
continue;
|
|
45228
45440
|
}
|
|
45229
|
-
if (key === "node_config" && !
|
|
45441
|
+
if (key === "node_config" && !path8.includes("consensus_data")) {
|
|
45230
45442
|
continue;
|
|
45231
45443
|
}
|
|
45232
45444
|
if (key === "consensus_data" && typeof value === "object" && value !== null) {
|
|
@@ -45514,11 +45726,45 @@ function formatStakingAmount(amount) {
|
|
|
45514
45726
|
}
|
|
45515
45727
|
var FALLBACK_GAS = 1000000n;
|
|
45516
45728
|
var GAS_BUFFER_MULTIPLIER = 2n;
|
|
45729
|
+
var COMBINED_ERROR_ABI = [...STAKING_ABI, ...VALIDATOR_WALLET_ABI];
|
|
45517
45730
|
function extractRevertReason(err) {
|
|
45518
45731
|
if (err instanceof BaseError2) {
|
|
45732
|
+
const rawError = err.walk((e2) => e2 instanceof RawContractError);
|
|
45733
|
+
if (rawError instanceof RawContractError && rawError.data && typeof rawError.data === "string") {
|
|
45734
|
+
try {
|
|
45735
|
+
const decoded = decodeErrorResult({
|
|
45736
|
+
abi: COMBINED_ERROR_ABI,
|
|
45737
|
+
data: rawError.data
|
|
45738
|
+
});
|
|
45739
|
+
return decoded.errorName;
|
|
45740
|
+
} catch {
|
|
45741
|
+
}
|
|
45742
|
+
}
|
|
45743
|
+
let current = err;
|
|
45744
|
+
while (current) {
|
|
45745
|
+
if (current && typeof current === "object") {
|
|
45746
|
+
const obj = current;
|
|
45747
|
+
if (obj.data && typeof obj.data === "string" && obj.data.startsWith("0x")) {
|
|
45748
|
+
try {
|
|
45749
|
+
const decoded = decodeErrorResult({
|
|
45750
|
+
abi: COMBINED_ERROR_ABI,
|
|
45751
|
+
data: obj.data
|
|
45752
|
+
});
|
|
45753
|
+
return decoded.errorName;
|
|
45754
|
+
} catch {
|
|
45755
|
+
}
|
|
45756
|
+
}
|
|
45757
|
+
current = obj.cause;
|
|
45758
|
+
} else {
|
|
45759
|
+
break;
|
|
45760
|
+
}
|
|
45761
|
+
}
|
|
45519
45762
|
const revertError = err.walk((e2) => e2 instanceof ContractFunctionRevertedError);
|
|
45520
45763
|
if (revertError instanceof ContractFunctionRevertedError) {
|
|
45521
|
-
|
|
45764
|
+
if (revertError.data?.errorName) {
|
|
45765
|
+
return revertError.data.errorName;
|
|
45766
|
+
}
|
|
45767
|
+
return revertError.reason || "Unknown reason";
|
|
45522
45768
|
}
|
|
45523
45769
|
if (err.shortMessage) return err.shortMessage;
|
|
45524
45770
|
}
|
|
@@ -45913,12 +46159,22 @@ var stakingActions = (client, publicClient) => {
|
|
|
45913
46159
|
},
|
|
45914
46160
|
getEpochInfo: async () => {
|
|
45915
46161
|
const contract = getReadOnlyStakingContract();
|
|
45916
|
-
const [
|
|
46162
|
+
const [
|
|
46163
|
+
epoch,
|
|
46164
|
+
validatorMinStake,
|
|
46165
|
+
delegatorMinStake,
|
|
46166
|
+
activeCount,
|
|
46167
|
+
epochMinDuration,
|
|
46168
|
+
epochZeroMinDuration,
|
|
46169
|
+
epochOdd,
|
|
46170
|
+
epochEven
|
|
46171
|
+
] = await Promise.all([
|
|
45917
46172
|
contract.read.epoch(),
|
|
45918
46173
|
contract.read.validatorMinStake(),
|
|
45919
46174
|
contract.read.delegatorMinStake(),
|
|
45920
46175
|
contract.read.activeValidatorsCount(),
|
|
45921
46176
|
contract.read.epochMinDuration(),
|
|
46177
|
+
contract.read.epochZeroMinDuration(),
|
|
45922
46178
|
contract.read.epochOdd(),
|
|
45923
46179
|
contract.read.epochEven()
|
|
45924
46180
|
]);
|
|
@@ -45927,7 +46183,8 @@ var stakingActions = (client, publicClient) => {
|
|
|
45927
46183
|
const currentEpochEnd = currentEpochData.end > 0n ? new Date(Number(currentEpochData.end) * 1e3) : null;
|
|
45928
46184
|
let nextEpochEstimate = null;
|
|
45929
46185
|
if (!currentEpochEnd) {
|
|
45930
|
-
const
|
|
46186
|
+
const duration = epoch === 0n ? epochZeroMinDuration : epochMinDuration;
|
|
46187
|
+
const estimatedEndMs = Number(currentEpochData.start + duration) * 1e3;
|
|
45931
46188
|
nextEpochEstimate = new Date(estimatedEndMs);
|
|
45932
46189
|
}
|
|
45933
46190
|
return {
|
|
@@ -46136,25 +46393,26 @@ var _BaseAction = class _BaseAction extends ConfigFileManager {
|
|
|
46136
46393
|
__publicField(this, "spinner");
|
|
46137
46394
|
__publicField(this, "_genlayerClient", null);
|
|
46138
46395
|
__publicField(this, "keychainManager");
|
|
46396
|
+
__publicField(this, "accountOverride", null);
|
|
46139
46397
|
this.spinner = ora({ text: "", spinner: "dots" });
|
|
46140
46398
|
this.keychainManager = new KeychainManager();
|
|
46141
46399
|
}
|
|
46142
|
-
async decryptKeystore(
|
|
46400
|
+
async decryptKeystore(keystoreJson, attempt = 1) {
|
|
46143
46401
|
try {
|
|
46144
46402
|
const message = attempt === 1 ? "Enter password to decrypt keystore:" : `Invalid password. Attempt ${attempt}/${_BaseAction.MAX_PASSWORD_ATTEMPTS} - Enter password to decrypt keystore:`;
|
|
46145
46403
|
const password = await this.promptPassword(message);
|
|
46146
|
-
const wallet = await ethers.Wallet.fromEncryptedJson(
|
|
46404
|
+
const wallet = await ethers.Wallet.fromEncryptedJson(keystoreJson, password);
|
|
46147
46405
|
return wallet.privateKey;
|
|
46148
46406
|
} catch (error) {
|
|
46149
46407
|
if (attempt >= _BaseAction.MAX_PASSWORD_ATTEMPTS) {
|
|
46150
46408
|
this.failSpinner(`Maximum password attempts exceeded (${_BaseAction.MAX_PASSWORD_ATTEMPTS}/${_BaseAction.MAX_PASSWORD_ATTEMPTS}).`);
|
|
46151
46409
|
}
|
|
46152
|
-
return await this.decryptKeystore(
|
|
46410
|
+
return await this.decryptKeystore(keystoreJson, attempt + 1);
|
|
46153
46411
|
}
|
|
46154
46412
|
}
|
|
46155
46413
|
isValidKeystoreFormat(data) {
|
|
46156
46414
|
return Boolean(
|
|
46157
|
-
data && data.
|
|
46415
|
+
data && (data.crypto || data.Crypto) && typeof data.address === "string"
|
|
46158
46416
|
);
|
|
46159
46417
|
}
|
|
46160
46418
|
formatOutput(data) {
|
|
@@ -46175,40 +46433,64 @@ var _BaseAction = class _BaseAction extends ConfigFileManager {
|
|
|
46175
46433
|
}
|
|
46176
46434
|
return this._genlayerClient;
|
|
46177
46435
|
}
|
|
46436
|
+
resolveAccountName() {
|
|
46437
|
+
if (this.accountOverride) {
|
|
46438
|
+
return this.accountOverride;
|
|
46439
|
+
}
|
|
46440
|
+
const activeAccount = this.getActiveAccount();
|
|
46441
|
+
if (activeAccount) {
|
|
46442
|
+
return activeAccount;
|
|
46443
|
+
}
|
|
46444
|
+
return _BaseAction.DEFAULT_ACCOUNT_NAME;
|
|
46445
|
+
}
|
|
46178
46446
|
async getAccount(readOnly = false) {
|
|
46179
|
-
|
|
46447
|
+
const accountName = this.resolveAccountName();
|
|
46448
|
+
const keystorePath = this.getKeystorePath(accountName);
|
|
46180
46449
|
let decryptedPrivateKey;
|
|
46450
|
+
let keystoreJson;
|
|
46181
46451
|
let keystoreData;
|
|
46182
|
-
if (!
|
|
46183
|
-
await this.confirmPrompt(
|
|
46184
|
-
decryptedPrivateKey = await this.
|
|
46185
|
-
keypairPath = this.getConfigByKey("keyPairPath");
|
|
46452
|
+
if (!existsSync(keystorePath)) {
|
|
46453
|
+
await this.confirmPrompt(`Account '${accountName}' not found. Would you like to create it?`);
|
|
46454
|
+
decryptedPrivateKey = await this.createKeypairByName(accountName, false);
|
|
46186
46455
|
}
|
|
46187
|
-
|
|
46456
|
+
keystoreJson = readFileSync(keystorePath, "utf-8");
|
|
46457
|
+
keystoreData = JSON.parse(keystoreJson);
|
|
46188
46458
|
if (!this.isValidKeystoreFormat(keystoreData)) {
|
|
46189
46459
|
this.failSpinner("Invalid keystore format. Expected encrypted keystore file.", void 0, false);
|
|
46190
|
-
await this.confirmPrompt(
|
|
46191
|
-
decryptedPrivateKey = await this.
|
|
46192
|
-
|
|
46193
|
-
keystoreData = JSON.parse(
|
|
46460
|
+
await this.confirmPrompt(`Would you like to recreate account '${accountName}'?`);
|
|
46461
|
+
decryptedPrivateKey = await this.createKeypairByName(accountName, true);
|
|
46462
|
+
keystoreJson = readFileSync(keystorePath, "utf-8");
|
|
46463
|
+
keystoreData = JSON.parse(keystoreJson);
|
|
46194
46464
|
}
|
|
46195
46465
|
if (readOnly) {
|
|
46196
46466
|
return this.getAddress(keystoreData);
|
|
46197
46467
|
}
|
|
46198
46468
|
if (!decryptedPrivateKey) {
|
|
46199
|
-
const cachedKey = await this.keychainManager.getPrivateKey();
|
|
46200
|
-
|
|
46469
|
+
const cachedKey = await this.keychainManager.getPrivateKey(accountName);
|
|
46470
|
+
if (cachedKey) {
|
|
46471
|
+
const tempAccount = createAccount(cachedKey);
|
|
46472
|
+
const cachedAddress = tempAccount.address.toLowerCase();
|
|
46473
|
+
const keystoreAddress = `0x${keystoreData.address.toLowerCase().replace(/^0x/, "")}`;
|
|
46474
|
+
if (cachedAddress === keystoreAddress) {
|
|
46475
|
+
decryptedPrivateKey = cachedKey;
|
|
46476
|
+
} else {
|
|
46477
|
+
await this.keychainManager.removePrivateKey(accountName);
|
|
46478
|
+
decryptedPrivateKey = await this.decryptKeystore(keystoreJson);
|
|
46479
|
+
}
|
|
46480
|
+
} else {
|
|
46481
|
+
decryptedPrivateKey = await this.decryptKeystore(keystoreJson);
|
|
46482
|
+
}
|
|
46201
46483
|
}
|
|
46202
46484
|
return createAccount(decryptedPrivateKey);
|
|
46203
46485
|
}
|
|
46204
46486
|
getAddress(keystoreData) {
|
|
46205
46487
|
return keystoreData.address;
|
|
46206
46488
|
}
|
|
46207
|
-
async
|
|
46208
|
-
const
|
|
46489
|
+
async createKeypairByName(accountName, overwrite) {
|
|
46490
|
+
const keystorePath = this.getKeystorePath(accountName);
|
|
46209
46491
|
this.stopSpinner();
|
|
46210
|
-
if (existsSync(
|
|
46211
|
-
this.failSpinner(`
|
|
46492
|
+
if (existsSync(keystorePath) && !overwrite) {
|
|
46493
|
+
this.failSpinner(`Account '${accountName}' already exists. Use '--overwrite' to replace it.`);
|
|
46212
46494
|
}
|
|
46213
46495
|
const wallet = ethers.Wallet.createRandom();
|
|
46214
46496
|
const password = await this.promptPassword("Enter a password to encrypt your keystore (minimum 8 characters):");
|
|
@@ -46220,14 +46502,11 @@ var _BaseAction = class _BaseAction extends ConfigFileManager {
|
|
|
46220
46502
|
this.failSpinner(`Password must be at least ${_BaseAction.MIN_PASSWORD_LENGTH} characters long`);
|
|
46221
46503
|
}
|
|
46222
46504
|
const encryptedJson = await wallet.encrypt(password);
|
|
46223
|
-
|
|
46224
|
-
|
|
46225
|
-
|
|
46226
|
-
|
|
46227
|
-
|
|
46228
|
-
writeFileSync(finalOutputPath, JSON.stringify(keystoreData, null, 2));
|
|
46229
|
-
this.writeConfig("keyPairPath", finalOutputPath);
|
|
46230
|
-
await this.keychainManager.removePrivateKey();
|
|
46505
|
+
writeFileSync(keystorePath, encryptedJson);
|
|
46506
|
+
if (!this.getActiveAccount()) {
|
|
46507
|
+
this.setActiveAccount(accountName);
|
|
46508
|
+
}
|
|
46509
|
+
await this.keychainManager.removePrivateKey(accountName);
|
|
46231
46510
|
return wallet.privateKey;
|
|
46232
46511
|
}
|
|
46233
46512
|
async promptPassword(message) {
|
|
@@ -46310,7 +46589,7 @@ ${message}`));
|
|
|
46310
46589
|
this.spinner.text = source_default.blue(message);
|
|
46311
46590
|
}
|
|
46312
46591
|
};
|
|
46313
|
-
__publicField(_BaseAction, "
|
|
46592
|
+
__publicField(_BaseAction, "DEFAULT_ACCOUNT_NAME", "default");
|
|
46314
46593
|
__publicField(_BaseAction, "MAX_PASSWORD_ATTEMPTS", 3);
|
|
46315
46594
|
__publicField(_BaseAction, "MIN_PASSWORD_LENGTH", 8);
|
|
46316
46595
|
var BaseAction = _BaseAction;
|
|
@@ -47492,33 +47771,42 @@ var ShowAccountAction = class extends BaseAction {
|
|
|
47492
47771
|
getNetwork() {
|
|
47493
47772
|
return resolveNetwork(this.getConfig().network);
|
|
47494
47773
|
}
|
|
47495
|
-
async execute() {
|
|
47774
|
+
async execute(options) {
|
|
47496
47775
|
this.startSpinner("Fetching account info...");
|
|
47497
47776
|
try {
|
|
47498
|
-
|
|
47499
|
-
|
|
47500
|
-
|
|
47777
|
+
if (options?.account) {
|
|
47778
|
+
this.accountOverride = options.account;
|
|
47779
|
+
}
|
|
47780
|
+
const accountName = this.resolveAccountName();
|
|
47781
|
+
const keystorePath = this.getKeystorePath(accountName);
|
|
47782
|
+
if (!existsSync2(keystorePath)) {
|
|
47783
|
+
this.failSpinner(`Account '${accountName}' not found. Run 'genlayer account create --name ${accountName}' first.`);
|
|
47501
47784
|
return;
|
|
47502
47785
|
}
|
|
47503
|
-
const keystoreData = JSON.parse(readFileSync3(
|
|
47786
|
+
const keystoreData = JSON.parse(readFileSync3(keystorePath, "utf-8"));
|
|
47504
47787
|
if (!this.isValidKeystoreFormat(keystoreData)) {
|
|
47505
47788
|
this.failSpinner("Invalid keystore format.");
|
|
47506
47789
|
return;
|
|
47507
47790
|
}
|
|
47508
|
-
const
|
|
47791
|
+
const rawAddr = keystoreData.address;
|
|
47792
|
+
const address = rawAddr.startsWith("0x") ? rawAddr : `0x${rawAddr}`;
|
|
47509
47793
|
const network = this.getNetwork();
|
|
47510
47794
|
const client = createClient2({
|
|
47511
47795
|
chain: network,
|
|
47512
|
-
account: address
|
|
47796
|
+
account: address,
|
|
47797
|
+
endpoint: options?.rpc
|
|
47513
47798
|
});
|
|
47514
47799
|
const balance = await client.getBalance({ address });
|
|
47515
47800
|
const formattedBalance = formatEther(balance);
|
|
47516
|
-
const isUnlocked = await this.keychainManager.
|
|
47801
|
+
const isUnlocked = await this.keychainManager.isAccountUnlocked(accountName);
|
|
47802
|
+
const isActive = this.getActiveAccount() === accountName;
|
|
47517
47803
|
const result = {
|
|
47804
|
+
name: accountName,
|
|
47518
47805
|
address,
|
|
47519
47806
|
balance: `${formattedBalance} GEN`,
|
|
47520
47807
|
network: network.name || "localnet",
|
|
47521
|
-
status: isUnlocked ? "unlocked" : "locked"
|
|
47808
|
+
status: isUnlocked ? "unlocked" : "locked",
|
|
47809
|
+
active: isActive
|
|
47522
47810
|
};
|
|
47523
47811
|
this.succeedSpinner("Account info", result);
|
|
47524
47812
|
} catch (error) {
|
|
@@ -47534,9 +47822,13 @@ var CreateAccountAction = class extends BaseAction {
|
|
|
47534
47822
|
}
|
|
47535
47823
|
async execute(options) {
|
|
47536
47824
|
try {
|
|
47537
|
-
this.startSpinner(
|
|
47538
|
-
await this.
|
|
47539
|
-
|
|
47825
|
+
this.startSpinner(`Creating account '${options.name}'...`);
|
|
47826
|
+
await this.createKeypairByName(options.name, options.overwrite);
|
|
47827
|
+
if (options.setActive !== false) {
|
|
47828
|
+
this.setActiveAccount(options.name);
|
|
47829
|
+
}
|
|
47830
|
+
const keystorePath = this.getKeystorePath(options.name);
|
|
47831
|
+
this.succeedSpinner(`Account '${options.name}' created at: ${keystorePath}`);
|
|
47540
47832
|
} catch (error) {
|
|
47541
47833
|
this.failSpinner("Failed to create account", error);
|
|
47542
47834
|
}
|
|
@@ -47545,45 +47837,86 @@ var CreateAccountAction = class extends BaseAction {
|
|
|
47545
47837
|
|
|
47546
47838
|
// src/commands/account/import.ts
|
|
47547
47839
|
import { ethers as ethers2 } from "ethers";
|
|
47548
|
-
import { writeFileSync as writeFileSync3, existsSync as existsSync3 } from "fs";
|
|
47549
|
-
var
|
|
47840
|
+
import { writeFileSync as writeFileSync3, existsSync as existsSync3, readFileSync as readFileSync4 } from "fs";
|
|
47841
|
+
var ImportAccountAction = class extends BaseAction {
|
|
47550
47842
|
constructor() {
|
|
47551
47843
|
super();
|
|
47552
47844
|
}
|
|
47553
47845
|
async execute(options) {
|
|
47554
47846
|
try {
|
|
47555
|
-
const
|
|
47556
|
-
|
|
47557
|
-
|
|
47558
|
-
const finalOutputPath = this.getFilePath(options.output);
|
|
47559
|
-
if (existsSync3(finalOutputPath) && !options.overwrite) {
|
|
47560
|
-
this.failSpinner(`File at ${finalOutputPath} already exists. Use '--overwrite' to replace.`);
|
|
47561
|
-
}
|
|
47562
|
-
const wallet = new ethers2.Wallet(normalizedKey);
|
|
47563
|
-
const password = await this.promptPassword("Enter a password to encrypt your keystore (minimum 8 characters):");
|
|
47564
|
-
const confirmPassword = await this.promptPassword("Confirm password:");
|
|
47565
|
-
if (password !== confirmPassword) {
|
|
47566
|
-
this.failSpinner("Passwords do not match");
|
|
47847
|
+
const keystorePath = this.getKeystorePath(options.name);
|
|
47848
|
+
if (existsSync3(keystorePath) && !options.overwrite) {
|
|
47849
|
+
this.failSpinner(`Account '${options.name}' already exists. Use '--overwrite' to replace.`);
|
|
47567
47850
|
}
|
|
47568
|
-
|
|
47569
|
-
|
|
47851
|
+
let privateKey;
|
|
47852
|
+
if (options.keystore) {
|
|
47853
|
+
privateKey = await this.importFromKeystore(options.keystore, options.sourcePassword);
|
|
47854
|
+
} else if (options.privateKey) {
|
|
47855
|
+
const normalizedKey = this.normalizePrivateKey(options.privateKey);
|
|
47856
|
+
this.validatePrivateKey(normalizedKey);
|
|
47857
|
+
privateKey = normalizedKey;
|
|
47858
|
+
} else {
|
|
47859
|
+
const inputKey = await this.promptPrivateKey();
|
|
47860
|
+
const normalizedKey = this.normalizePrivateKey(inputKey);
|
|
47861
|
+
this.validatePrivateKey(normalizedKey);
|
|
47862
|
+
privateKey = normalizedKey;
|
|
47863
|
+
}
|
|
47864
|
+
const wallet = new ethers2.Wallet(privateKey);
|
|
47865
|
+
let password;
|
|
47866
|
+
if (options.password) {
|
|
47867
|
+
password = options.password;
|
|
47868
|
+
} else {
|
|
47869
|
+
password = await this.promptPassword("Enter a password to encrypt your keystore (minimum 8 characters):");
|
|
47870
|
+
const confirmPassword = await this.promptPassword("Confirm password:");
|
|
47871
|
+
if (password !== confirmPassword) {
|
|
47872
|
+
this.failSpinner("Passwords do not match");
|
|
47873
|
+
}
|
|
47570
47874
|
}
|
|
47571
|
-
|
|
47875
|
+
if (password.length < BaseAction.MIN_PASSWORD_LENGTH) {
|
|
47876
|
+
this.failSpinner(`Password must be at least ${BaseAction.MIN_PASSWORD_LENGTH} characters long`);
|
|
47877
|
+
}
|
|
47878
|
+
this.startSpinner(`Importing account '${options.name}'...`);
|
|
47572
47879
|
const encryptedJson = await wallet.encrypt(password);
|
|
47573
|
-
|
|
47574
|
-
|
|
47575
|
-
|
|
47576
|
-
|
|
47577
|
-
|
|
47578
|
-
|
|
47579
|
-
this.writeConfig("keyPairPath", finalOutputPath);
|
|
47580
|
-
await this.keychainManager.removePrivateKey();
|
|
47581
|
-
this.succeedSpinner(`Account imported and saved to: ${finalOutputPath}`);
|
|
47880
|
+
writeFileSync3(keystorePath, encryptedJson);
|
|
47881
|
+
if (options.setActive !== false) {
|
|
47882
|
+
this.setActiveAccount(options.name);
|
|
47883
|
+
}
|
|
47884
|
+
await this.keychainManager.removePrivateKey(options.name);
|
|
47885
|
+
this.succeedSpinner(`Account '${options.name}' imported to: ${keystorePath}`);
|
|
47582
47886
|
this.logInfo(`Address: ${wallet.address}`);
|
|
47583
47887
|
} catch (error) {
|
|
47584
47888
|
this.failSpinner("Failed to import account", error);
|
|
47585
47889
|
}
|
|
47586
47890
|
}
|
|
47891
|
+
async importFromKeystore(keystorePath, sourcePassword) {
|
|
47892
|
+
if (!existsSync3(keystorePath)) {
|
|
47893
|
+
this.failSpinner(`Keystore file not found: ${keystorePath}`);
|
|
47894
|
+
}
|
|
47895
|
+
const fileContent = readFileSync4(keystorePath, "utf-8");
|
|
47896
|
+
let encryptedJson;
|
|
47897
|
+
try {
|
|
47898
|
+
const parsed = JSON.parse(fileContent);
|
|
47899
|
+
if (parsed.encrypted) {
|
|
47900
|
+
encryptedJson = parsed.encrypted;
|
|
47901
|
+
} else if (parsed.crypto || parsed.Crypto) {
|
|
47902
|
+
encryptedJson = fileContent;
|
|
47903
|
+
} else {
|
|
47904
|
+
this.failSpinner("Invalid keystore format. Expected encrypted keystore file.");
|
|
47905
|
+
}
|
|
47906
|
+
} catch {
|
|
47907
|
+
this.failSpinner("Invalid keystore file. Could not parse JSON.");
|
|
47908
|
+
}
|
|
47909
|
+
const password = sourcePassword || await this.promptPassword("Enter password to decrypt keystore:");
|
|
47910
|
+
this.startSpinner("Decrypting keystore...");
|
|
47911
|
+
try {
|
|
47912
|
+
const wallet = await ethers2.Wallet.fromEncryptedJson(encryptedJson, password);
|
|
47913
|
+
this.stopSpinner();
|
|
47914
|
+
return wallet.privateKey;
|
|
47915
|
+
} catch {
|
|
47916
|
+
this.failSpinner("Failed to decrypt keystore. Wrong password?");
|
|
47917
|
+
}
|
|
47918
|
+
throw new Error("Unreachable");
|
|
47919
|
+
}
|
|
47587
47920
|
async promptPrivateKey() {
|
|
47588
47921
|
return this.promptPassword("Enter private key to import:");
|
|
47589
47922
|
}
|
|
@@ -47597,37 +47930,110 @@ var _ImportAccountAction = class _ImportAccountAction extends BaseAction {
|
|
|
47597
47930
|
}
|
|
47598
47931
|
}
|
|
47599
47932
|
};
|
|
47600
|
-
__publicField(_ImportAccountAction, "MIN_PASSWORD_LENGTH", 8);
|
|
47601
|
-
var ImportAccountAction = _ImportAccountAction;
|
|
47602
47933
|
|
|
47603
|
-
// src/commands/account/
|
|
47604
|
-
import { readFileSync as readFileSync4, existsSync as existsSync4 } from "fs";
|
|
47934
|
+
// src/commands/account/export.ts
|
|
47605
47935
|
import { ethers as ethers3 } from "ethers";
|
|
47936
|
+
import { writeFileSync as writeFileSync4, existsSync as existsSync4, readFileSync as readFileSync5 } from "fs";
|
|
47937
|
+
import path4 from "path";
|
|
47938
|
+
var ExportAccountAction = class extends BaseAction {
|
|
47939
|
+
constructor() {
|
|
47940
|
+
super();
|
|
47941
|
+
}
|
|
47942
|
+
async execute(options) {
|
|
47943
|
+
try {
|
|
47944
|
+
if (options.account) {
|
|
47945
|
+
this.accountOverride = options.account;
|
|
47946
|
+
}
|
|
47947
|
+
const accountName = this.resolveAccountName();
|
|
47948
|
+
const keystorePath = this.getKeystorePath(accountName);
|
|
47949
|
+
if (!existsSync4(keystorePath)) {
|
|
47950
|
+
this.failSpinner(`Account '${accountName}' not found.`);
|
|
47951
|
+
}
|
|
47952
|
+
const outputPath = path4.resolve(options.output);
|
|
47953
|
+
if (existsSync4(outputPath) && !options.overwrite) {
|
|
47954
|
+
this.failSpinner(`Output file already exists: ${outputPath}`);
|
|
47955
|
+
}
|
|
47956
|
+
const privateKey = await this.getPrivateKeyForExport(accountName, keystorePath, options.sourcePassword);
|
|
47957
|
+
let password;
|
|
47958
|
+
if (options.password) {
|
|
47959
|
+
password = options.password;
|
|
47960
|
+
} else {
|
|
47961
|
+
password = await this.promptPassword("Enter password for exported keystore (minimum 8 characters):");
|
|
47962
|
+
const confirmPassword = await this.promptPassword("Confirm password:");
|
|
47963
|
+
if (password !== confirmPassword) {
|
|
47964
|
+
this.failSpinner("Passwords do not match");
|
|
47965
|
+
}
|
|
47966
|
+
}
|
|
47967
|
+
if (password.length < BaseAction.MIN_PASSWORD_LENGTH) {
|
|
47968
|
+
this.failSpinner(`Password must be at least ${BaseAction.MIN_PASSWORD_LENGTH} characters long`);
|
|
47969
|
+
}
|
|
47970
|
+
this.startSpinner(`Exporting account '${accountName}'...`);
|
|
47971
|
+
const wallet = new ethers3.Wallet(privateKey);
|
|
47972
|
+
const encryptedJson = await wallet.encrypt(password);
|
|
47973
|
+
writeFileSync4(outputPath, encryptedJson);
|
|
47974
|
+
this.succeedSpinner(`Account '${accountName}' exported to: ${outputPath}`);
|
|
47975
|
+
this.logInfo(`Address: ${wallet.address}`);
|
|
47976
|
+
} catch (error) {
|
|
47977
|
+
this.failSpinner("Failed to export account", error);
|
|
47978
|
+
}
|
|
47979
|
+
}
|
|
47980
|
+
async getPrivateKeyForExport(accountName, keystorePath, sourcePassword) {
|
|
47981
|
+
const isAvailable = await this.keychainManager.isKeychainAvailable();
|
|
47982
|
+
if (isAvailable) {
|
|
47983
|
+
const cachedKey = await this.keychainManager.getPrivateKey(accountName);
|
|
47984
|
+
if (cachedKey) {
|
|
47985
|
+
return cachedKey;
|
|
47986
|
+
}
|
|
47987
|
+
}
|
|
47988
|
+
const fileContent = readFileSync5(keystorePath, "utf-8");
|
|
47989
|
+
const parsed = JSON.parse(fileContent);
|
|
47990
|
+
const encryptedJson = parsed.encrypted || fileContent;
|
|
47991
|
+
const password = sourcePassword || await this.promptPassword(`Enter password to unlock '${accountName}':`);
|
|
47992
|
+
this.startSpinner("Decrypting keystore...");
|
|
47993
|
+
try {
|
|
47994
|
+
const wallet = await ethers3.Wallet.fromEncryptedJson(encryptedJson, password);
|
|
47995
|
+
this.stopSpinner();
|
|
47996
|
+
return wallet.privateKey;
|
|
47997
|
+
} catch {
|
|
47998
|
+
this.failSpinner("Failed to decrypt keystore. Wrong password?");
|
|
47999
|
+
}
|
|
48000
|
+
throw new Error("Unreachable");
|
|
48001
|
+
}
|
|
48002
|
+
};
|
|
48003
|
+
|
|
48004
|
+
// src/commands/account/unlock.ts
|
|
48005
|
+
import { readFileSync as readFileSync6, existsSync as existsSync5 } from "fs";
|
|
48006
|
+
import { ethers as ethers4 } from "ethers";
|
|
47606
48007
|
var UnlockAccountAction = class extends BaseAction {
|
|
47607
|
-
async execute() {
|
|
48008
|
+
async execute(options) {
|
|
47608
48009
|
this.startSpinner("Checking keychain availability...");
|
|
47609
48010
|
const keychainAvailable = await this.keychainManager.isKeychainAvailable();
|
|
47610
48011
|
if (!keychainAvailable) {
|
|
47611
48012
|
this.failSpinner("OS keychain is not available. This command requires a supported keychain (e.g. macOS Keychain, Windows Credential Manager, or GNOME Keyring).");
|
|
47612
48013
|
return;
|
|
47613
48014
|
}
|
|
47614
|
-
|
|
47615
|
-
|
|
47616
|
-
|
|
47617
|
-
|
|
48015
|
+
if (options?.account) {
|
|
48016
|
+
this.accountOverride = options.account;
|
|
48017
|
+
}
|
|
48018
|
+
const accountName = this.resolveAccountName();
|
|
48019
|
+
this.setSpinnerText(`Checking for account '${accountName}'...`);
|
|
48020
|
+
const keystorePath = this.getKeystorePath(accountName);
|
|
48021
|
+
if (!existsSync5(keystorePath)) {
|
|
48022
|
+
this.failSpinner(`Account '${accountName}' not found. Run 'genlayer account create --name ${accountName}' first.`);
|
|
47618
48023
|
return;
|
|
47619
48024
|
}
|
|
47620
|
-
const
|
|
48025
|
+
const keystoreJson = readFileSync6(keystorePath, "utf-8");
|
|
48026
|
+
const keystoreData = JSON.parse(keystoreJson);
|
|
47621
48027
|
if (!this.isValidKeystoreFormat(keystoreData)) {
|
|
47622
48028
|
this.failSpinner("Invalid keystore format.");
|
|
47623
48029
|
return;
|
|
47624
48030
|
}
|
|
47625
48031
|
this.stopSpinner();
|
|
47626
48032
|
try {
|
|
47627
|
-
const password = await this.promptPassword(
|
|
47628
|
-
const wallet = await
|
|
47629
|
-
await this.keychainManager.storePrivateKey(wallet.privateKey);
|
|
47630
|
-
this.succeedSpinner(
|
|
48033
|
+
const password = await this.promptPassword(`Enter password to unlock '${accountName}':`);
|
|
48034
|
+
const wallet = await ethers4.Wallet.fromEncryptedJson(keystoreJson, password);
|
|
48035
|
+
await this.keychainManager.storePrivateKey(accountName, wallet.privateKey);
|
|
48036
|
+
this.succeedSpinner(`Account '${accountName}' unlocked! Private key cached in OS keychain.`);
|
|
47631
48037
|
} catch (error) {
|
|
47632
48038
|
this.failSpinner("Failed to unlock account.", error);
|
|
47633
48039
|
}
|
|
@@ -47636,23 +48042,27 @@ var UnlockAccountAction = class extends BaseAction {
|
|
|
47636
48042
|
|
|
47637
48043
|
// src/commands/account/lock.ts
|
|
47638
48044
|
var LockAccountAction = class extends BaseAction {
|
|
47639
|
-
async execute() {
|
|
48045
|
+
async execute(options) {
|
|
47640
48046
|
this.startSpinner("Checking keychain availability...");
|
|
47641
48047
|
const keychainAvailable = await this.keychainManager.isKeychainAvailable();
|
|
47642
48048
|
if (!keychainAvailable) {
|
|
47643
48049
|
this.failSpinner("OS keychain is not available. This command requires a supported keychain (e.g. macOS Keychain, Windows Credential Manager, or GNOME Keyring).");
|
|
47644
48050
|
return;
|
|
47645
48051
|
}
|
|
47646
|
-
|
|
47647
|
-
|
|
48052
|
+
if (options?.account) {
|
|
48053
|
+
this.accountOverride = options.account;
|
|
48054
|
+
}
|
|
48055
|
+
const accountName = this.resolveAccountName();
|
|
48056
|
+
this.setSpinnerText(`Checking for cached private key for '${accountName}'...`);
|
|
48057
|
+
const hasCachedKey = await this.keychainManager.getPrivateKey(accountName);
|
|
47648
48058
|
if (!hasCachedKey) {
|
|
47649
|
-
this.succeedSpinner(
|
|
48059
|
+
this.succeedSpinner(`Account '${accountName}' is already locked.`);
|
|
47650
48060
|
return;
|
|
47651
48061
|
}
|
|
47652
|
-
this.setSpinnerText(
|
|
48062
|
+
this.setSpinnerText(`Removing private key for '${accountName}' from OS keychain...`);
|
|
47653
48063
|
try {
|
|
47654
|
-
await this.keychainManager.removePrivateKey();
|
|
47655
|
-
this.succeedSpinner(
|
|
48064
|
+
await this.keychainManager.removePrivateKey(accountName);
|
|
48065
|
+
this.succeedSpinner(`Account '${accountName}' locked! Private key removed from OS keychain.`);
|
|
47656
48066
|
} catch (error) {
|
|
47657
48067
|
this.failSpinner("Failed to lock account.", error);
|
|
47658
48068
|
}
|
|
@@ -47660,8 +48070,8 @@ var LockAccountAction = class extends BaseAction {
|
|
|
47660
48070
|
};
|
|
47661
48071
|
|
|
47662
48072
|
// src/commands/account/send.ts
|
|
47663
|
-
import { readFileSync as
|
|
47664
|
-
import { ethers as
|
|
48073
|
+
import { readFileSync as readFileSync7, existsSync as existsSync6 } from "fs";
|
|
48074
|
+
import { ethers as ethers5 } from "ethers";
|
|
47665
48075
|
var SendAction = class extends BaseAction {
|
|
47666
48076
|
constructor() {
|
|
47667
48077
|
super();
|
|
@@ -47690,25 +48100,30 @@ var SendAction = class extends BaseAction {
|
|
|
47690
48100
|
async execute(options) {
|
|
47691
48101
|
this.startSpinner("Preparing transfer...");
|
|
47692
48102
|
try {
|
|
47693
|
-
|
|
47694
|
-
|
|
47695
|
-
|
|
48103
|
+
if (options.account) {
|
|
48104
|
+
this.accountOverride = options.account;
|
|
48105
|
+
}
|
|
48106
|
+
const accountName = this.resolveAccountName();
|
|
48107
|
+
const keystorePath = this.getKeystorePath(accountName);
|
|
48108
|
+
if (!existsSync6(keystorePath)) {
|
|
48109
|
+
this.failSpinner(`Account '${accountName}' not found. Run 'genlayer account create --name ${accountName}' first.`);
|
|
47696
48110
|
return;
|
|
47697
48111
|
}
|
|
47698
|
-
const
|
|
48112
|
+
const keystoreJson = readFileSync7(keystorePath, "utf-8");
|
|
48113
|
+
const keystoreData = JSON.parse(keystoreJson);
|
|
47699
48114
|
if (!this.isValidKeystoreFormat(keystoreData)) {
|
|
47700
48115
|
this.failSpinner("Invalid keystore format.");
|
|
47701
48116
|
return;
|
|
47702
48117
|
}
|
|
47703
|
-
const cachedKey = await this.keychainManager.getPrivateKey();
|
|
48118
|
+
const cachedKey = await this.keychainManager.getPrivateKey(accountName);
|
|
47704
48119
|
let privateKey;
|
|
47705
48120
|
if (cachedKey) {
|
|
47706
48121
|
privateKey = cachedKey;
|
|
47707
48122
|
} else {
|
|
47708
48123
|
this.stopSpinner();
|
|
47709
|
-
const password = await this.promptPassword(
|
|
48124
|
+
const password = await this.promptPassword(`Enter password to unlock account '${accountName}':`);
|
|
47710
48125
|
this.startSpinner("Preparing transfer...");
|
|
47711
|
-
const wallet = await
|
|
48126
|
+
const wallet = await ethers5.Wallet.fromEncryptedJson(keystoreJson, password);
|
|
47712
48127
|
privateKey = wallet.privateKey;
|
|
47713
48128
|
}
|
|
47714
48129
|
const network = this.getNetwork(options.network);
|
|
@@ -47769,44 +48184,136 @@ Waiting for confirmation...`);
|
|
|
47769
48184
|
}
|
|
47770
48185
|
};
|
|
47771
48186
|
|
|
48187
|
+
// src/commands/account/list.ts
|
|
48188
|
+
var ListAccountsAction = class extends BaseAction {
|
|
48189
|
+
constructor() {
|
|
48190
|
+
super();
|
|
48191
|
+
}
|
|
48192
|
+
async execute() {
|
|
48193
|
+
try {
|
|
48194
|
+
const accounts = this.listAccounts();
|
|
48195
|
+
const activeAccount = this.getActiveAccount();
|
|
48196
|
+
const unlockedAccounts = await this.keychainManager.listUnlockedAccounts();
|
|
48197
|
+
if (accounts.length === 0) {
|
|
48198
|
+
this.logInfo("No accounts found. Run 'genlayer account create --name <name>' to create one.");
|
|
48199
|
+
return;
|
|
48200
|
+
}
|
|
48201
|
+
console.log("");
|
|
48202
|
+
for (const account of accounts) {
|
|
48203
|
+
const isActive = account.name === activeAccount;
|
|
48204
|
+
const isUnlocked = unlockedAccounts.includes(account.name);
|
|
48205
|
+
const marker = isActive ? "*" : " ";
|
|
48206
|
+
const status = isUnlocked ? "(unlocked)" : "";
|
|
48207
|
+
const activeLabel = isActive ? "(active)" : "";
|
|
48208
|
+
console.log(`${marker} ${account.name.padEnd(16)} ${account.address} ${activeLabel} ${status}`.trim());
|
|
48209
|
+
}
|
|
48210
|
+
console.log("");
|
|
48211
|
+
} catch (error) {
|
|
48212
|
+
this.failSpinner("Failed to list accounts", error);
|
|
48213
|
+
}
|
|
48214
|
+
}
|
|
48215
|
+
};
|
|
48216
|
+
|
|
48217
|
+
// src/commands/account/use.ts
|
|
48218
|
+
var UseAccountAction = class extends BaseAction {
|
|
48219
|
+
constructor() {
|
|
48220
|
+
super();
|
|
48221
|
+
}
|
|
48222
|
+
async execute(name) {
|
|
48223
|
+
try {
|
|
48224
|
+
if (!this.accountExists(name)) {
|
|
48225
|
+
this.failSpinner(`Account '${name}' does not exist. Run 'genlayer account list' to see available accounts.`);
|
|
48226
|
+
return;
|
|
48227
|
+
}
|
|
48228
|
+
this.setActiveAccount(name);
|
|
48229
|
+
this.logSuccess(`Active account set to '${name}'`);
|
|
48230
|
+
} catch (error) {
|
|
48231
|
+
this.failSpinner("Failed to set active account", error);
|
|
48232
|
+
}
|
|
48233
|
+
}
|
|
48234
|
+
};
|
|
48235
|
+
|
|
48236
|
+
// src/commands/account/remove.ts
|
|
48237
|
+
var RemoveAccountAction = class extends BaseAction {
|
|
48238
|
+
constructor() {
|
|
48239
|
+
super();
|
|
48240
|
+
}
|
|
48241
|
+
async execute(name, options) {
|
|
48242
|
+
try {
|
|
48243
|
+
if (!this.accountExists(name)) {
|
|
48244
|
+
this.failSpinner(`Account '${name}' does not exist.`);
|
|
48245
|
+
return;
|
|
48246
|
+
}
|
|
48247
|
+
if (!options.force) {
|
|
48248
|
+
await this.confirmPrompt(`Are you sure you want to remove account '${name}'? This cannot be undone.`);
|
|
48249
|
+
}
|
|
48250
|
+
await this.keychainManager.removePrivateKey(name);
|
|
48251
|
+
this.removeAccount(name);
|
|
48252
|
+
this.logSuccess(`Account '${name}' removed`);
|
|
48253
|
+
} catch (error) {
|
|
48254
|
+
this.failSpinner("Failed to remove account", error);
|
|
48255
|
+
}
|
|
48256
|
+
}
|
|
48257
|
+
};
|
|
48258
|
+
|
|
47772
48259
|
// src/commands/account/index.ts
|
|
47773
48260
|
function initializeAccountCommands(program2) {
|
|
47774
|
-
const accountCommand = program2.command("account").description("Manage your
|
|
48261
|
+
const accountCommand = program2.command("account").description("Manage your accounts (address, balance, keys)").action(async () => {
|
|
48262
|
+
const showAction = new ShowAccountAction();
|
|
48263
|
+
await showAction.execute({});
|
|
48264
|
+
});
|
|
48265
|
+
accountCommand.command("list").description("List all accounts").action(async () => {
|
|
48266
|
+
const listAction = new ListAccountsAction();
|
|
48267
|
+
await listAction.execute();
|
|
48268
|
+
});
|
|
48269
|
+
accountCommand.command("show").description("Show account details (address, balance)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--account <name>", "Account to show").action(async (options) => {
|
|
47775
48270
|
const showAction = new ShowAccountAction();
|
|
47776
|
-
await showAction.execute();
|
|
48271
|
+
await showAction.execute(options);
|
|
47777
48272
|
});
|
|
47778
|
-
accountCommand.command("create").description("Create a new account with encrypted keystore").
|
|
48273
|
+
accountCommand.command("create").description("Create a new account with encrypted keystore").requiredOption("--name <name>", "Name for the account").option("--overwrite", "Overwrite existing account", false).option("--no-set-active", "Do not set as active account").action(async (options) => {
|
|
47779
48274
|
const createAction = new CreateAccountAction();
|
|
47780
48275
|
await createAction.execute(options);
|
|
47781
48276
|
});
|
|
47782
|
-
accountCommand.command("import").description("Import an account from a private key").option("--private-key <key>", "Private key to import
|
|
48277
|
+
accountCommand.command("import").description("Import an account from a private key or keystore file").requiredOption("--name <name>", "Name for the account").option("--private-key <key>", "Private key to import").option("--keystore <path>", "Path to keystore file to import (geth, foundry, etc.)").option("--password <password>", "Password for the new keystore (skips confirmation prompt)").option("--source-password <password>", "Password to decrypt source keystore (with --keystore)").option("--overwrite", "Overwrite existing account", false).option("--no-set-active", "Do not set as active account").action(async (options) => {
|
|
47783
48278
|
const importAction = new ImportAccountAction();
|
|
47784
48279
|
await importAction.execute(options);
|
|
47785
48280
|
});
|
|
47786
|
-
accountCommand.command("
|
|
48281
|
+
accountCommand.command("export").description("Export an account to a keystore file (web3/geth/foundry compatible)").requiredOption("--output <path>", "Output path for the keystore file").option("--account <name>", "Account to export (defaults to active account)").option("--password <password>", "Password for exported keystore (skips confirmation)").option("--source-password <password>", "Password to decrypt account (if not unlocked)").action(async (options) => {
|
|
48282
|
+
const exportAction = new ExportAccountAction();
|
|
48283
|
+
await exportAction.execute(options);
|
|
48284
|
+
});
|
|
48285
|
+
accountCommand.command("use <name>").description("Set the active account").action(async (name) => {
|
|
48286
|
+
const useAction = new UseAccountAction();
|
|
48287
|
+
await useAction.execute(name);
|
|
48288
|
+
});
|
|
48289
|
+
accountCommand.command("remove <name>").description("Remove an account").option("--force", "Skip confirmation prompt", false).action(async (name, options) => {
|
|
48290
|
+
const removeAction = new RemoveAccountAction();
|
|
48291
|
+
await removeAction.execute(name, options);
|
|
48292
|
+
});
|
|
48293
|
+
accountCommand.command("send <to> <amount>").description("Send GEN to an address").option("--rpc <rpcUrl>", "RPC URL for the network").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--account <name>", "Account to send from").action(async (to, amount, options) => {
|
|
47787
48294
|
const sendAction = new SendAction();
|
|
47788
|
-
await sendAction.execute({ to, amount, rpc: options.rpc, network: options.network });
|
|
48295
|
+
await sendAction.execute({ to, amount, rpc: options.rpc, network: options.network, account: options.account });
|
|
47789
48296
|
});
|
|
47790
|
-
accountCommand.command("unlock").description("Unlock account by caching private key in OS keychain").action(async () => {
|
|
48297
|
+
accountCommand.command("unlock").description("Unlock account by caching private key in OS keychain").option("--account <name>", "Account to unlock").action(async (options) => {
|
|
47791
48298
|
const unlockAction = new UnlockAccountAction();
|
|
47792
|
-
await unlockAction.execute();
|
|
48299
|
+
await unlockAction.execute(options);
|
|
47793
48300
|
});
|
|
47794
|
-
accountCommand.command("lock").description("Lock account by removing private key from OS keychain").action(async () => {
|
|
48301
|
+
accountCommand.command("lock").description("Lock account by removing private key from OS keychain").option("--account <name>", "Account to lock").action(async (options) => {
|
|
47795
48302
|
const lockAction = new LockAccountAction();
|
|
47796
|
-
await lockAction.execute();
|
|
48303
|
+
await lockAction.execute(options);
|
|
47797
48304
|
});
|
|
47798
48305
|
return program2;
|
|
47799
48306
|
}
|
|
47800
48307
|
|
|
47801
48308
|
// src/commands/contracts/deploy.ts
|
|
47802
48309
|
import fs9 from "fs";
|
|
47803
|
-
import
|
|
48310
|
+
import path5 from "path";
|
|
47804
48311
|
import { pathToFileURL } from "url";
|
|
47805
48312
|
import { buildSync } from "esbuild";
|
|
47806
48313
|
var DeployAction = class extends BaseAction {
|
|
47807
48314
|
constructor() {
|
|
47808
48315
|
super();
|
|
47809
|
-
__publicField(this, "deployDir",
|
|
48316
|
+
__publicField(this, "deployDir", path5.resolve(process.cwd(), "deploy"));
|
|
47810
48317
|
}
|
|
47811
48318
|
readContractCode(contractPath) {
|
|
47812
48319
|
if (!fs9.existsSync(contractPath)) {
|
|
@@ -47869,7 +48376,7 @@ var DeployAction = class extends BaseAction {
|
|
|
47869
48376
|
}
|
|
47870
48377
|
this.setSpinnerText(`Found ${files.length} deploy scripts. Executing...`);
|
|
47871
48378
|
for (const file of files) {
|
|
47872
|
-
const filePath =
|
|
48379
|
+
const filePath = path5.resolve(this.deployDir, file);
|
|
47873
48380
|
this.setSpinnerText(`Executing script: ${filePath}`);
|
|
47874
48381
|
try {
|
|
47875
48382
|
if (file.endsWith(".ts")) {
|
|
@@ -48388,18 +48895,18 @@ function initializeUpdateCommands(program2) {
|
|
|
48388
48895
|
|
|
48389
48896
|
// src/commands/scaffold/new.ts
|
|
48390
48897
|
import fs10 from "fs-extra";
|
|
48391
|
-
import
|
|
48898
|
+
import path6 from "path";
|
|
48392
48899
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
48393
48900
|
var NewAction = class extends BaseAction {
|
|
48394
48901
|
constructor() {
|
|
48395
48902
|
super();
|
|
48396
48903
|
__publicField(this, "templatePath");
|
|
48397
48904
|
const __filename = fileURLToPath3(import.meta.url);
|
|
48398
|
-
const basePath =
|
|
48399
|
-
this.templatePath =
|
|
48905
|
+
const basePath = path6.resolve(path6.dirname(__filename), "..");
|
|
48906
|
+
this.templatePath = path6.join(basePath, "templates", "default");
|
|
48400
48907
|
}
|
|
48401
48908
|
async createProject(projectName, options) {
|
|
48402
|
-
const targetPath =
|
|
48909
|
+
const targetPath = path6.resolve(options.path, projectName);
|
|
48403
48910
|
if (fs10.existsSync(targetPath) && !options.overwrite) {
|
|
48404
48911
|
return this.failSpinner(
|
|
48405
48912
|
`Project directory "${targetPath}" already exists. Use --overwrite to replace it.`
|
|
@@ -48426,27 +48933,40 @@ function initializeScaffoldCommands(program2) {
|
|
|
48426
48933
|
|
|
48427
48934
|
// src/commands/network/setNetwork.ts
|
|
48428
48935
|
import inquirer5 from "inquirer";
|
|
48429
|
-
var networks2 = [
|
|
48430
|
-
|
|
48431
|
-
|
|
48432
|
-
|
|
48433
|
-
|
|
48434
|
-
},
|
|
48435
|
-
{
|
|
48436
|
-
name: studionet.name,
|
|
48437
|
-
alias: "studionet",
|
|
48438
|
-
value: studionet
|
|
48439
|
-
},
|
|
48440
|
-
{
|
|
48441
|
-
name: testnetAsimov.name,
|
|
48442
|
-
alias: "testnet-asimov",
|
|
48443
|
-
value: testnetAsimov
|
|
48444
|
-
}
|
|
48445
|
-
];
|
|
48936
|
+
var networks2 = Object.entries(BUILT_IN_NETWORKS).map(([alias, network]) => ({
|
|
48937
|
+
name: network.name,
|
|
48938
|
+
alias,
|
|
48939
|
+
value: network
|
|
48940
|
+
}));
|
|
48446
48941
|
var NetworkActions = class extends BaseAction {
|
|
48447
48942
|
constructor() {
|
|
48448
48943
|
super();
|
|
48449
48944
|
}
|
|
48945
|
+
async showInfo() {
|
|
48946
|
+
const storedNetwork = this.getConfigByKey("network") || "localnet";
|
|
48947
|
+
const network = resolveNetwork(storedNetwork);
|
|
48948
|
+
const info = {
|
|
48949
|
+
alias: storedNetwork,
|
|
48950
|
+
name: network.name,
|
|
48951
|
+
chainId: network.id?.toString() || "unknown",
|
|
48952
|
+
rpc: network.rpcUrls?.default?.http?.[0] || "unknown",
|
|
48953
|
+
mainContract: network.consensusMainContract?.address || "not set",
|
|
48954
|
+
stakingContract: network.stakingContract?.address || "not set"
|
|
48955
|
+
};
|
|
48956
|
+
if (network.blockExplorers?.default?.url) {
|
|
48957
|
+
info.explorer = network.blockExplorers.default.url;
|
|
48958
|
+
}
|
|
48959
|
+
this.succeedSpinner("Current network", info);
|
|
48960
|
+
}
|
|
48961
|
+
async listNetworks() {
|
|
48962
|
+
const currentNetwork = this.getConfigByKey("network") || "localnet";
|
|
48963
|
+
console.log("");
|
|
48964
|
+
for (const net of networks2) {
|
|
48965
|
+
const marker = net.alias === currentNetwork ? "*" : " ";
|
|
48966
|
+
console.log(`${marker} ${net.alias.padEnd(16)} ${net.name}`);
|
|
48967
|
+
}
|
|
48968
|
+
console.log("");
|
|
48969
|
+
}
|
|
48450
48970
|
async setNetwork(networkName) {
|
|
48451
48971
|
if (networkName || networkName === "") {
|
|
48452
48972
|
if (!networks2.some((n) => n.name === networkName || n.alias === networkName)) {
|
|
@@ -48481,7 +49001,10 @@ var NetworkActions = class extends BaseAction {
|
|
|
48481
49001
|
// src/commands/network/index.ts
|
|
48482
49002
|
function initializeNetworkCommands(program2) {
|
|
48483
49003
|
const networkActions = new NetworkActions();
|
|
48484
|
-
program2.command("network").description("
|
|
49004
|
+
const network = program2.command("network").description("Network configuration");
|
|
49005
|
+
network.command("set").description("Set the network to use").argument("[network]", "The network to set").action((networkName) => networkActions.setNetwork(networkName));
|
|
49006
|
+
network.command("info").description("Show current network configuration and contract addresses").action(() => networkActions.showInfo());
|
|
49007
|
+
network.command("list").description("List available networks").action(() => networkActions.listNetworks());
|
|
48485
49008
|
return program2;
|
|
48486
49009
|
}
|
|
48487
49010
|
|
|
@@ -48595,8 +49118,8 @@ function initializeTransactionsCommands(program2) {
|
|
|
48595
49118
|
}
|
|
48596
49119
|
|
|
48597
49120
|
// src/commands/staking/StakingAction.ts
|
|
48598
|
-
import { readFileSync as
|
|
48599
|
-
import { ethers as
|
|
49121
|
+
import { readFileSync as readFileSync8, existsSync as existsSync7 } from "fs";
|
|
49122
|
+
import { ethers as ethers6 } from "ethers";
|
|
48600
49123
|
var StakingAction = class extends BaseAction {
|
|
48601
49124
|
constructor() {
|
|
48602
49125
|
super();
|
|
@@ -48614,6 +49137,9 @@ var StakingAction = class extends BaseAction {
|
|
|
48614
49137
|
}
|
|
48615
49138
|
async getStakingClient(config) {
|
|
48616
49139
|
if (!this._stakingClient) {
|
|
49140
|
+
if (config.account) {
|
|
49141
|
+
this.accountOverride = config.account;
|
|
49142
|
+
}
|
|
48617
49143
|
const network = this.getNetwork(config);
|
|
48618
49144
|
if (config.stakingAddress) {
|
|
48619
49145
|
network.stakingContract = {
|
|
@@ -48632,6 +49158,9 @@ var StakingAction = class extends BaseAction {
|
|
|
48632
49158
|
return this._stakingClient;
|
|
48633
49159
|
}
|
|
48634
49160
|
async getReadOnlyStakingClient(config) {
|
|
49161
|
+
if (config.account) {
|
|
49162
|
+
this.accountOverride = config.account;
|
|
49163
|
+
}
|
|
48635
49164
|
const network = this.getNetwork(config);
|
|
48636
49165
|
if (config.stakingAddress) {
|
|
48637
49166
|
network.stakingContract = {
|
|
@@ -48639,34 +49168,46 @@ var StakingAction = class extends BaseAction {
|
|
|
48639
49168
|
abi: abi_exports.STAKING_ABI
|
|
48640
49169
|
};
|
|
48641
49170
|
}
|
|
48642
|
-
const
|
|
48643
|
-
|
|
48644
|
-
|
|
49171
|
+
const accountName = this.resolveAccountName();
|
|
49172
|
+
const keystorePath = this.getKeystorePath(accountName);
|
|
49173
|
+
if (!existsSync7(keystorePath)) {
|
|
49174
|
+
throw new Error(`Account '${accountName}' not found. Run 'genlayer account create --name ${accountName}' first.`);
|
|
48645
49175
|
}
|
|
48646
|
-
const keystoreData = JSON.parse(
|
|
49176
|
+
const keystoreData = JSON.parse(readFileSync8(keystorePath, "utf-8"));
|
|
49177
|
+
const addr = keystoreData.address;
|
|
49178
|
+
const normalizedAddress = addr.startsWith("0x") ? addr : `0x${addr}`;
|
|
48647
49179
|
return createClient2({
|
|
48648
49180
|
chain: network,
|
|
48649
49181
|
endpoint: config.rpc,
|
|
48650
|
-
account:
|
|
49182
|
+
account: normalizedAddress
|
|
48651
49183
|
});
|
|
48652
49184
|
}
|
|
48653
49185
|
async getPrivateKeyForStaking() {
|
|
48654
|
-
const
|
|
48655
|
-
|
|
48656
|
-
|
|
49186
|
+
const accountName = this.resolveAccountName();
|
|
49187
|
+
const keystorePath = this.getKeystorePath(accountName);
|
|
49188
|
+
if (!existsSync7(keystorePath)) {
|
|
49189
|
+
throw new Error(`Account '${accountName}' not found. Run 'genlayer account create --name ${accountName}' first.`);
|
|
48657
49190
|
}
|
|
48658
|
-
const
|
|
49191
|
+
const keystoreJson = readFileSync8(keystorePath, "utf-8");
|
|
49192
|
+
const keystoreData = JSON.parse(keystoreJson);
|
|
48659
49193
|
if (!this.isValidKeystoreFormat(keystoreData)) {
|
|
48660
49194
|
throw new Error("Invalid keystore format.");
|
|
48661
49195
|
}
|
|
48662
|
-
const cachedKey = await this.keychainManager.getPrivateKey();
|
|
49196
|
+
const cachedKey = await this.keychainManager.getPrivateKey(accountName);
|
|
48663
49197
|
if (cachedKey) {
|
|
48664
|
-
|
|
49198
|
+
const tempAccount = createAccount(cachedKey);
|
|
49199
|
+
const cachedAddress = tempAccount.address.toLowerCase();
|
|
49200
|
+
const keystoreAddress = `0x${keystoreData.address.toLowerCase().replace(/^0x/, "")}`;
|
|
49201
|
+
if (cachedAddress !== keystoreAddress) {
|
|
49202
|
+
await this.keychainManager.removePrivateKey(accountName);
|
|
49203
|
+
} else {
|
|
49204
|
+
return cachedKey;
|
|
49205
|
+
}
|
|
48665
49206
|
}
|
|
48666
49207
|
this.stopSpinner();
|
|
48667
|
-
const password = await this.promptPassword(
|
|
49208
|
+
const password = await this.promptPassword(`Enter password to unlock account '${accountName}':`);
|
|
48668
49209
|
this.startSpinner("Continuing...");
|
|
48669
|
-
const wallet = await
|
|
49210
|
+
const wallet = await ethers6.Wallet.fromEncryptedJson(keystoreJson, password);
|
|
48670
49211
|
return wallet.privateKey;
|
|
48671
49212
|
}
|
|
48672
49213
|
parseAmount(amount) {
|
|
@@ -48676,12 +49217,14 @@ var StakingAction = class extends BaseAction {
|
|
|
48676
49217
|
return formatStakingAmount(amount);
|
|
48677
49218
|
}
|
|
48678
49219
|
async getSignerAddress() {
|
|
48679
|
-
const
|
|
48680
|
-
|
|
48681
|
-
|
|
49220
|
+
const accountName = this.resolveAccountName();
|
|
49221
|
+
const keystorePath = this.getKeystorePath(accountName);
|
|
49222
|
+
if (!existsSync7(keystorePath)) {
|
|
49223
|
+
throw new Error(`Account '${accountName}' not found.`);
|
|
48682
49224
|
}
|
|
48683
|
-
const keystoreData = JSON.parse(
|
|
48684
|
-
|
|
49225
|
+
const keystoreData = JSON.parse(readFileSync8(keystorePath, "utf-8"));
|
|
49226
|
+
const addr = keystoreData.address;
|
|
49227
|
+
return addr.startsWith("0x") ? addr : `0x${addr}`;
|
|
48685
49228
|
}
|
|
48686
49229
|
};
|
|
48687
49230
|
|
|
@@ -48886,9 +49429,14 @@ var SetIdentityAction = class extends StakingAction {
|
|
|
48886
49429
|
blockNumber: result.blockNumber.toString(),
|
|
48887
49430
|
gasUsed: result.gasUsed.toString()
|
|
48888
49431
|
};
|
|
49432
|
+
if (options.logoUri) output.logoUri = options.logoUri;
|
|
48889
49433
|
if (options.website) output.website = options.website;
|
|
49434
|
+
if (options.description) output.description = options.description;
|
|
49435
|
+
if (options.email) output.email = options.email;
|
|
48890
49436
|
if (options.twitter) output.twitter = options.twitter;
|
|
49437
|
+
if (options.telegram) output.telegram = options.telegram;
|
|
48891
49438
|
if (options.github) output.github = options.github;
|
|
49439
|
+
if (options.extraCid) output.extraCid = options.extraCid;
|
|
48892
49440
|
this.succeedSpinner("Validator identity set!", output);
|
|
48893
49441
|
} catch (error) {
|
|
48894
49442
|
this.failSpinner("Failed to set identity", error.message || error);
|
|
@@ -48920,6 +49468,8 @@ var DelegatorJoinAction = class extends StakingAction {
|
|
|
48920
49468
|
gasUsed: result.gasUsed.toString()
|
|
48921
49469
|
};
|
|
48922
49470
|
this.succeedSpinner("Successfully joined as delegator!", output);
|
|
49471
|
+
console.log(source_default.dim(`
|
|
49472
|
+
To view your delegation: genlayer staking delegation-info --validator ${options.validator}`));
|
|
48923
49473
|
} catch (error) {
|
|
48924
49474
|
this.failSpinner("Failed to join as delegator", error.message || error);
|
|
48925
49475
|
}
|
|
@@ -48993,6 +49543,8 @@ var DelegatorClaimAction = class extends StakingAction {
|
|
|
48993
49543
|
};
|
|
48994
49544
|
|
|
48995
49545
|
// src/commands/staking/stakingInfo.ts
|
|
49546
|
+
var ACTIVATION_DELAY_EPOCHS = 2n;
|
|
49547
|
+
var UNBONDING_PERIOD_EPOCHS = 7n;
|
|
48996
49548
|
var StakingInfoAction = class extends StakingAction {
|
|
48997
49549
|
constructor() {
|
|
48998
49550
|
super();
|
|
@@ -49026,21 +49578,24 @@ var StakingInfoAction = class extends StakingAction {
|
|
|
49026
49578
|
needsPriming: info.needsPriming,
|
|
49027
49579
|
live: info.live,
|
|
49028
49580
|
banned: info.banned ? info.bannedEpoch?.toString() : "Not banned",
|
|
49029
|
-
selfStakePendingDeposits:
|
|
49030
|
-
const
|
|
49031
|
-
|
|
49032
|
-
|
|
49033
|
-
|
|
49034
|
-
|
|
49035
|
-
|
|
49036
|
-
|
|
49037
|
-
|
|
49038
|
-
|
|
49039
|
-
|
|
49040
|
-
|
|
49581
|
+
selfStakePendingDeposits: (() => {
|
|
49582
|
+
const pending = info.pendingDeposits.filter((d) => d.epoch + ACTIVATION_DELAY_EPOCHS > currentEpoch);
|
|
49583
|
+
return pending.length > 0 ? pending.map((d) => {
|
|
49584
|
+
const depositEpoch = d.epoch;
|
|
49585
|
+
const activationEpoch = depositEpoch + ACTIVATION_DELAY_EPOCHS;
|
|
49586
|
+
const epochsUntilActive = activationEpoch - currentEpoch;
|
|
49587
|
+
return {
|
|
49588
|
+
epoch: depositEpoch.toString(),
|
|
49589
|
+
stake: d.stake,
|
|
49590
|
+
shares: d.shares.toString(),
|
|
49591
|
+
activatesAtEpoch: activationEpoch.toString(),
|
|
49592
|
+
epochsRemaining: epochsUntilActive.toString()
|
|
49593
|
+
};
|
|
49594
|
+
}) : "None";
|
|
49595
|
+
})(),
|
|
49041
49596
|
selfStakePendingWithdrawals: info.pendingWithdrawals.length > 0 ? info.pendingWithdrawals.map((w) => {
|
|
49042
49597
|
const exitEpoch = w.epoch;
|
|
49043
|
-
const claimableEpoch = exitEpoch +
|
|
49598
|
+
const claimableEpoch = exitEpoch + UNBONDING_PERIOD_EPOCHS;
|
|
49044
49599
|
const epochsUntilClaimable = claimableEpoch - currentEpoch;
|
|
49045
49600
|
return {
|
|
49046
49601
|
epoch: exitEpoch.toString(),
|
|
@@ -49073,6 +49628,8 @@ var StakingInfoAction = class extends StakingAction {
|
|
|
49073
49628
|
try {
|
|
49074
49629
|
const client = await this.getReadOnlyStakingClient(options);
|
|
49075
49630
|
const delegatorAddress = options.delegator || await this.getSignerAddress();
|
|
49631
|
+
const isOwnDelegation = !options.delegator;
|
|
49632
|
+
this.setSpinnerText(`Fetching delegation info for ${delegatorAddress}...`);
|
|
49076
49633
|
if (!options.validator) {
|
|
49077
49634
|
this.failSpinner("Validator address is required");
|
|
49078
49635
|
return;
|
|
@@ -49095,21 +49652,24 @@ var StakingInfoAction = class extends StakingAction {
|
|
|
49095
49652
|
shares: info.shares.toString(),
|
|
49096
49653
|
stake: info.stake,
|
|
49097
49654
|
projectedReward,
|
|
49098
|
-
pendingDeposits:
|
|
49099
|
-
const
|
|
49100
|
-
|
|
49101
|
-
|
|
49102
|
-
|
|
49103
|
-
|
|
49104
|
-
|
|
49105
|
-
|
|
49106
|
-
|
|
49107
|
-
|
|
49108
|
-
|
|
49109
|
-
|
|
49655
|
+
pendingDeposits: (() => {
|
|
49656
|
+
const pending = info.pendingDeposits.filter((d) => d.epoch + ACTIVATION_DELAY_EPOCHS > currentEpoch);
|
|
49657
|
+
return pending.length > 0 ? pending.map((d) => {
|
|
49658
|
+
const depositEpoch = d.epoch;
|
|
49659
|
+
const activationEpoch = depositEpoch + ACTIVATION_DELAY_EPOCHS;
|
|
49660
|
+
const epochsUntilActive = activationEpoch - currentEpoch;
|
|
49661
|
+
return {
|
|
49662
|
+
epoch: depositEpoch.toString(),
|
|
49663
|
+
stake: d.stake,
|
|
49664
|
+
shares: d.shares.toString(),
|
|
49665
|
+
activatesAtEpoch: activationEpoch.toString(),
|
|
49666
|
+
epochsRemaining: epochsUntilActive.toString()
|
|
49667
|
+
};
|
|
49668
|
+
}) : "None";
|
|
49669
|
+
})(),
|
|
49110
49670
|
pendingWithdrawals: info.pendingWithdrawals.length > 0 ? info.pendingWithdrawals.map((w) => {
|
|
49111
49671
|
const exitEpoch = w.epoch;
|
|
49112
|
-
const claimableEpoch = exitEpoch +
|
|
49672
|
+
const claimableEpoch = exitEpoch + UNBONDING_PERIOD_EPOCHS;
|
|
49113
49673
|
const epochsUntilClaimable = claimableEpoch - currentEpoch;
|
|
49114
49674
|
return {
|
|
49115
49675
|
epoch: exitEpoch.toString(),
|
|
@@ -49120,7 +49680,8 @@ var StakingInfoAction = class extends StakingAction {
|
|
|
49120
49680
|
};
|
|
49121
49681
|
}) : "None"
|
|
49122
49682
|
};
|
|
49123
|
-
|
|
49683
|
+
const msg = isOwnDelegation ? "Your delegation info" : `Delegation info for ${delegatorAddress}`;
|
|
49684
|
+
this.succeedSpinner(msg, result);
|
|
49124
49685
|
} catch (error) {
|
|
49125
49686
|
this.failSpinner("Failed to get stake info", error.message || error);
|
|
49126
49687
|
}
|
|
@@ -49214,54 +49775,696 @@ var StakingInfoAction = class extends StakingAction {
|
|
|
49214
49775
|
}
|
|
49215
49776
|
};
|
|
49216
49777
|
|
|
49778
|
+
// src/commands/staking/wizard.ts
|
|
49779
|
+
import inquirer6 from "inquirer";
|
|
49780
|
+
import { readFileSync as readFileSync9, existsSync as existsSync8 } from "fs";
|
|
49781
|
+
import path7 from "path";
|
|
49782
|
+
function ensureHexPrefix(address) {
|
|
49783
|
+
if (!address) return address;
|
|
49784
|
+
return address.startsWith("0x") ? address : `0x${address}`;
|
|
49785
|
+
}
|
|
49786
|
+
var ValidatorWizardAction = class extends StakingAction {
|
|
49787
|
+
constructor() {
|
|
49788
|
+
super();
|
|
49789
|
+
}
|
|
49790
|
+
async execute(options) {
|
|
49791
|
+
console.log("\n========================================");
|
|
49792
|
+
console.log(" GenLayer Validator Setup Wizard");
|
|
49793
|
+
console.log("========================================\n");
|
|
49794
|
+
const state = {};
|
|
49795
|
+
try {
|
|
49796
|
+
await this.stepAccountSetup(state, options);
|
|
49797
|
+
await this.stepNetworkSelection(state, options);
|
|
49798
|
+
await this.stepBalanceCheck(state, options);
|
|
49799
|
+
await this.stepOperatorSetup(state);
|
|
49800
|
+
await this.stepStakeAmount(state);
|
|
49801
|
+
await this.stepJoinValidator(state, options);
|
|
49802
|
+
if (!options.skipIdentity) {
|
|
49803
|
+
await this.stepIdentitySetup(state, options);
|
|
49804
|
+
}
|
|
49805
|
+
this.showSummary(state);
|
|
49806
|
+
} catch (error) {
|
|
49807
|
+
if (error.message === "WIZARD_ABORTED") {
|
|
49808
|
+
this.logError("Wizard aborted.");
|
|
49809
|
+
return;
|
|
49810
|
+
}
|
|
49811
|
+
this.failSpinner("Wizard failed", error.message || error);
|
|
49812
|
+
}
|
|
49813
|
+
}
|
|
49814
|
+
async stepAccountSetup(state, options) {
|
|
49815
|
+
console.log("Step 1: Account Setup");
|
|
49816
|
+
console.log("---------------------\n");
|
|
49817
|
+
if (options.account) {
|
|
49818
|
+
const keystorePath = this.getKeystorePath(options.account);
|
|
49819
|
+
if (!this.accountExists(options.account)) {
|
|
49820
|
+
this.failSpinner(`Account '${options.account}' not found.`);
|
|
49821
|
+
}
|
|
49822
|
+
state.accountName = options.account;
|
|
49823
|
+
this.accountOverride = options.account;
|
|
49824
|
+
const address = await this.getSignerAddress();
|
|
49825
|
+
state.accountAddress = ensureHexPrefix(address);
|
|
49826
|
+
console.log(`Using account: ${options.account} (${state.accountAddress})
|
|
49827
|
+
`);
|
|
49828
|
+
return;
|
|
49829
|
+
}
|
|
49830
|
+
const accounts = this.listAccounts();
|
|
49831
|
+
if (accounts.length === 0) {
|
|
49832
|
+
console.log("No accounts found. Let's create one.\n");
|
|
49833
|
+
const { accountName } = await inquirer6.prompt([
|
|
49834
|
+
{
|
|
49835
|
+
type: "input",
|
|
49836
|
+
name: "accountName",
|
|
49837
|
+
message: "Enter a name for your validator account:",
|
|
49838
|
+
default: "validator",
|
|
49839
|
+
validate: (input) => input.length > 0 || "Name cannot be empty"
|
|
49840
|
+
}
|
|
49841
|
+
]);
|
|
49842
|
+
const createAction = new CreateAccountAction();
|
|
49843
|
+
await createAction.execute({ name: accountName, overwrite: false, setActive: true });
|
|
49844
|
+
state.accountName = accountName;
|
|
49845
|
+
this.accountOverride = accountName;
|
|
49846
|
+
const address = await this.getSignerAddress();
|
|
49847
|
+
state.accountAddress = ensureHexPrefix(address);
|
|
49848
|
+
} else {
|
|
49849
|
+
const choices = [
|
|
49850
|
+
...accounts.map((a) => ({
|
|
49851
|
+
name: `${a.name} (${a.address})`,
|
|
49852
|
+
value: a.name
|
|
49853
|
+
})),
|
|
49854
|
+
{ name: "Create new account", value: "__create_new__" }
|
|
49855
|
+
];
|
|
49856
|
+
const { selectedAccount } = await inquirer6.prompt([
|
|
49857
|
+
{
|
|
49858
|
+
type: "list",
|
|
49859
|
+
name: "selectedAccount",
|
|
49860
|
+
message: "Select an account that will be the owner of the validator:",
|
|
49861
|
+
choices
|
|
49862
|
+
}
|
|
49863
|
+
]);
|
|
49864
|
+
if (selectedAccount === "__create_new__") {
|
|
49865
|
+
const { accountName } = await inquirer6.prompt([
|
|
49866
|
+
{
|
|
49867
|
+
type: "input",
|
|
49868
|
+
name: "accountName",
|
|
49869
|
+
message: "Enter a name for your validator account:",
|
|
49870
|
+
default: "validator",
|
|
49871
|
+
validate: (input) => {
|
|
49872
|
+
if (input.length === 0) return "Name cannot be empty";
|
|
49873
|
+
if (accounts.find((a) => a.name === input)) return "Account with this name already exists";
|
|
49874
|
+
return true;
|
|
49875
|
+
}
|
|
49876
|
+
}
|
|
49877
|
+
]);
|
|
49878
|
+
const createAction = new CreateAccountAction();
|
|
49879
|
+
await createAction.execute({ name: accountName, overwrite: false, setActive: true });
|
|
49880
|
+
state.accountName = accountName;
|
|
49881
|
+
this.accountOverride = accountName;
|
|
49882
|
+
const address = await this.getSignerAddress();
|
|
49883
|
+
state.accountAddress = ensureHexPrefix(address);
|
|
49884
|
+
} else {
|
|
49885
|
+
state.accountName = selectedAccount;
|
|
49886
|
+
this.accountOverride = selectedAccount;
|
|
49887
|
+
this.setActiveAccount(selectedAccount);
|
|
49888
|
+
const address = await this.getSignerAddress();
|
|
49889
|
+
state.accountAddress = ensureHexPrefix(address);
|
|
49890
|
+
console.log(`
|
|
49891
|
+
Using account: ${selectedAccount} (${state.accountAddress})`);
|
|
49892
|
+
}
|
|
49893
|
+
}
|
|
49894
|
+
console.log("");
|
|
49895
|
+
}
|
|
49896
|
+
async stepNetworkSelection(state, options) {
|
|
49897
|
+
console.log("Step 2: Network Selection");
|
|
49898
|
+
console.log("-------------------------\n");
|
|
49899
|
+
if (options.network) {
|
|
49900
|
+
const network = BUILT_IN_NETWORKS[options.network];
|
|
49901
|
+
if (!network) {
|
|
49902
|
+
this.failSpinner(`Unknown network: ${options.network}`);
|
|
49903
|
+
}
|
|
49904
|
+
state.networkAlias = options.network;
|
|
49905
|
+
this.writeConfig("network", options.network);
|
|
49906
|
+
console.log(`Using network: ${network.name}
|
|
49907
|
+
`);
|
|
49908
|
+
return;
|
|
49909
|
+
}
|
|
49910
|
+
const currentNetwork = this.getConfigByKey("network");
|
|
49911
|
+
const excludedNetworks = ["studionet"];
|
|
49912
|
+
const networks3 = Object.entries(BUILT_IN_NETWORKS).filter(([alias]) => !excludedNetworks.includes(alias)).map(([alias, chain]) => ({
|
|
49913
|
+
name: chain.name,
|
|
49914
|
+
value: alias
|
|
49915
|
+
}));
|
|
49916
|
+
const { selectedNetwork } = await inquirer6.prompt([
|
|
49917
|
+
{
|
|
49918
|
+
type: "list",
|
|
49919
|
+
name: "selectedNetwork",
|
|
49920
|
+
message: "Select network:",
|
|
49921
|
+
choices: networks3,
|
|
49922
|
+
default: currentNetwork || "testnet-asimov"
|
|
49923
|
+
}
|
|
49924
|
+
]);
|
|
49925
|
+
state.networkAlias = selectedNetwork;
|
|
49926
|
+
this.writeConfig("network", selectedNetwork);
|
|
49927
|
+
console.log(`
|
|
49928
|
+
Network set to: ${BUILT_IN_NETWORKS[selectedNetwork].name}
|
|
49929
|
+
`);
|
|
49930
|
+
}
|
|
49931
|
+
async stepBalanceCheck(state, options) {
|
|
49932
|
+
console.log("Step 3: Balance Check");
|
|
49933
|
+
console.log("---------------------\n");
|
|
49934
|
+
this.startSpinner("Checking balance and staking requirements...");
|
|
49935
|
+
const network = BUILT_IN_NETWORKS[state.networkAlias];
|
|
49936
|
+
const client = createClient2({
|
|
49937
|
+
chain: network,
|
|
49938
|
+
account: state.accountAddress,
|
|
49939
|
+
endpoint: options.rpc
|
|
49940
|
+
});
|
|
49941
|
+
const [balance, epochInfo] = await Promise.all([
|
|
49942
|
+
client.getBalance({ address: state.accountAddress }),
|
|
49943
|
+
client.getEpochInfo()
|
|
49944
|
+
]);
|
|
49945
|
+
this.stopSpinner();
|
|
49946
|
+
const balanceFormatted = formatEther(balance);
|
|
49947
|
+
const minStakeRaw = epochInfo.validatorMinStakeRaw;
|
|
49948
|
+
const minStakeFormatted = epochInfo.validatorMinStake;
|
|
49949
|
+
const currentEpoch = epochInfo.currentEpoch;
|
|
49950
|
+
const MIN_GAS_BUFFER = parseEther("0.01");
|
|
49951
|
+
console.log(`Balance: ${balanceFormatted} GEN`);
|
|
49952
|
+
console.log(`Minimum stake required: ${minStakeFormatted}`);
|
|
49953
|
+
if (currentEpoch === 0n) {
|
|
49954
|
+
console.log("(Epoch 0: minimum stake not enforced, but gas fees still required)");
|
|
49955
|
+
console.log(`Note: Validator won't become active until self-stake reaches ${minStakeFormatted}`);
|
|
49956
|
+
}
|
|
49957
|
+
const minRequired = currentEpoch === 0n ? MIN_GAS_BUFFER : minStakeRaw + MIN_GAS_BUFFER;
|
|
49958
|
+
if (balance < minRequired) {
|
|
49959
|
+
console.log("");
|
|
49960
|
+
const minFormatted = currentEpoch === 0n ? "0.01 GEN (for gas)" : `${minStakeFormatted} + gas`;
|
|
49961
|
+
this.failSpinner(
|
|
49962
|
+
`Insufficient balance. You need at least ${minFormatted} to become a validator.
|
|
49963
|
+
Fund your account (${state.accountAddress}) and run the wizard again.`
|
|
49964
|
+
);
|
|
49965
|
+
}
|
|
49966
|
+
state.balance = balance;
|
|
49967
|
+
state.minStake = currentEpoch === 0n ? 0n : minStakeRaw;
|
|
49968
|
+
console.log("Balance sufficient!\n");
|
|
49969
|
+
}
|
|
49970
|
+
async stepOperatorSetup(state) {
|
|
49971
|
+
console.log("Step 4: Operator Setup");
|
|
49972
|
+
console.log("----------------------\n");
|
|
49973
|
+
console.log("Using a separate operator address is recommended for security:");
|
|
49974
|
+
console.log("- Owner account: holds staked funds (keep secure)");
|
|
49975
|
+
console.log("- Operator account: signs blocks (hot wallet on validator server)\n");
|
|
49976
|
+
const { useOperator } = await inquirer6.prompt([
|
|
49977
|
+
{
|
|
49978
|
+
type: "confirm",
|
|
49979
|
+
name: "useOperator",
|
|
49980
|
+
message: "Do you want to use a separate operator address?",
|
|
49981
|
+
default: true
|
|
49982
|
+
}
|
|
49983
|
+
]);
|
|
49984
|
+
if (!useOperator) {
|
|
49985
|
+
state.operatorAddress = ensureHexPrefix(state.accountAddress);
|
|
49986
|
+
state.operatorAccountName = state.accountName;
|
|
49987
|
+
console.log("\nOperator will be the same as owner address.\n");
|
|
49988
|
+
return;
|
|
49989
|
+
}
|
|
49990
|
+
const accounts = this.listAccounts();
|
|
49991
|
+
const otherAccounts = accounts.filter((a) => a.name !== state.accountName);
|
|
49992
|
+
const choices = [
|
|
49993
|
+
{ name: "Create new operator account", value: "create" },
|
|
49994
|
+
...otherAccounts.length > 0 ? [{ name: "Select from my accounts", value: "select" }] : [],
|
|
49995
|
+
{ name: "Enter existing operator address", value: "existing" }
|
|
49996
|
+
];
|
|
49997
|
+
const { operatorChoice } = await inquirer6.prompt([
|
|
49998
|
+
{
|
|
49999
|
+
type: "list",
|
|
50000
|
+
name: "operatorChoice",
|
|
50001
|
+
message: "How would you like to set up the operator?",
|
|
50002
|
+
choices
|
|
50003
|
+
}
|
|
50004
|
+
]);
|
|
50005
|
+
if (operatorChoice === "existing") {
|
|
50006
|
+
const { operatorAddress } = await inquirer6.prompt([
|
|
50007
|
+
{
|
|
50008
|
+
type: "input",
|
|
50009
|
+
name: "operatorAddress",
|
|
50010
|
+
message: "Enter operator address (0x...):",
|
|
50011
|
+
validate: (input) => {
|
|
50012
|
+
if (!input.match(/^0x[a-fA-F0-9]{40}$/)) {
|
|
50013
|
+
return "Invalid address format. Expected 0x followed by 40 hex characters.";
|
|
50014
|
+
}
|
|
50015
|
+
return true;
|
|
50016
|
+
}
|
|
50017
|
+
}
|
|
50018
|
+
]);
|
|
50019
|
+
state.operatorAddress = ensureHexPrefix(operatorAddress);
|
|
50020
|
+
console.log("");
|
|
50021
|
+
return;
|
|
50022
|
+
}
|
|
50023
|
+
if (operatorChoice === "select") {
|
|
50024
|
+
const { selectedOperator } = await inquirer6.prompt([
|
|
50025
|
+
{
|
|
50026
|
+
type: "list",
|
|
50027
|
+
name: "selectedOperator",
|
|
50028
|
+
message: "Select an account to use as operator:",
|
|
50029
|
+
choices: otherAccounts.map((a) => ({
|
|
50030
|
+
name: `${a.name} (${a.address})`,
|
|
50031
|
+
value: a.name
|
|
50032
|
+
}))
|
|
50033
|
+
}
|
|
50034
|
+
]);
|
|
50035
|
+
const operatorKeystorePath2 = this.getKeystorePath(selectedOperator);
|
|
50036
|
+
const operatorData2 = JSON.parse(readFileSync9(operatorKeystorePath2, "utf-8"));
|
|
50037
|
+
state.operatorAddress = ensureHexPrefix(operatorData2.address);
|
|
50038
|
+
state.operatorAccountName = selectedOperator;
|
|
50039
|
+
const defaultFilename2 = `${selectedOperator}-keystore.json`;
|
|
50040
|
+
const { outputFilename: outputFilename2 } = await inquirer6.prompt([
|
|
50041
|
+
{
|
|
50042
|
+
type: "input",
|
|
50043
|
+
name: "outputFilename",
|
|
50044
|
+
message: "Export keystore filename:",
|
|
50045
|
+
default: defaultFilename2
|
|
50046
|
+
}
|
|
50047
|
+
]);
|
|
50048
|
+
let outputPath2 = path7.resolve(`./${outputFilename2}`);
|
|
50049
|
+
if (existsSync8(outputPath2)) {
|
|
50050
|
+
const { overwrite } = await inquirer6.prompt([
|
|
50051
|
+
{
|
|
50052
|
+
type: "confirm",
|
|
50053
|
+
name: "overwrite",
|
|
50054
|
+
message: `File ${outputFilename2} already exists. Overwrite?`,
|
|
50055
|
+
default: false
|
|
50056
|
+
}
|
|
50057
|
+
]);
|
|
50058
|
+
if (!overwrite) {
|
|
50059
|
+
const { newFilename } = await inquirer6.prompt([
|
|
50060
|
+
{
|
|
50061
|
+
type: "input",
|
|
50062
|
+
name: "newFilename",
|
|
50063
|
+
message: "Enter new filename:"
|
|
50064
|
+
}
|
|
50065
|
+
]);
|
|
50066
|
+
outputPath2 = path7.resolve(`./${newFilename}`);
|
|
50067
|
+
}
|
|
50068
|
+
}
|
|
50069
|
+
const { exportPassword: exportPassword2 } = await inquirer6.prompt([
|
|
50070
|
+
{
|
|
50071
|
+
type: "password",
|
|
50072
|
+
name: "exportPassword",
|
|
50073
|
+
message: "Enter password for exported keystore (needed to import in node):",
|
|
50074
|
+
mask: "*",
|
|
50075
|
+
validate: (input) => input.length >= 8 || "Password must be at least 8 characters"
|
|
50076
|
+
}
|
|
50077
|
+
]);
|
|
50078
|
+
const { confirmPassword: confirmPassword2 } = await inquirer6.prompt([
|
|
50079
|
+
{
|
|
50080
|
+
type: "password",
|
|
50081
|
+
name: "confirmPassword",
|
|
50082
|
+
message: "Confirm password:",
|
|
50083
|
+
mask: "*"
|
|
50084
|
+
}
|
|
50085
|
+
]);
|
|
50086
|
+
if (exportPassword2 !== confirmPassword2) {
|
|
50087
|
+
this.failSpinner("Passwords do not match");
|
|
50088
|
+
}
|
|
50089
|
+
const exportAction2 = new ExportAccountAction();
|
|
50090
|
+
await exportAction2.execute({
|
|
50091
|
+
account: selectedOperator,
|
|
50092
|
+
output: outputPath2,
|
|
50093
|
+
password: exportPassword2,
|
|
50094
|
+
overwrite: true
|
|
50095
|
+
});
|
|
50096
|
+
state.operatorKeystorePath = outputPath2;
|
|
50097
|
+
console.log("\n========================================");
|
|
50098
|
+
console.log(" IMPORTANT: Transfer operator keystore");
|
|
50099
|
+
console.log("========================================");
|
|
50100
|
+
console.log(`File: ${outputPath2}`);
|
|
50101
|
+
console.log("Transfer this file to your validator server and import it");
|
|
50102
|
+
console.log("into your validator node software.");
|
|
50103
|
+
console.log("========================================\n");
|
|
50104
|
+
return;
|
|
50105
|
+
}
|
|
50106
|
+
const { operatorName } = await inquirer6.prompt([
|
|
50107
|
+
{
|
|
50108
|
+
type: "input",
|
|
50109
|
+
name: "operatorName",
|
|
50110
|
+
message: "Enter a name for the operator account:",
|
|
50111
|
+
default: "operator",
|
|
50112
|
+
validate: (input) => {
|
|
50113
|
+
if (input.length === 0) return "Name cannot be empty";
|
|
50114
|
+
if (accounts.find((a) => a.name === input)) return "Account with this name already exists";
|
|
50115
|
+
return true;
|
|
50116
|
+
}
|
|
50117
|
+
}
|
|
50118
|
+
]);
|
|
50119
|
+
console.log("");
|
|
50120
|
+
const createAction = new CreateAccountAction();
|
|
50121
|
+
await createAction.execute({ name: operatorName, overwrite: false, setActive: false });
|
|
50122
|
+
const operatorKeystorePath = this.getKeystorePath(operatorName);
|
|
50123
|
+
const operatorData = JSON.parse(readFileSync9(operatorKeystorePath, "utf-8"));
|
|
50124
|
+
state.operatorAddress = ensureHexPrefix(operatorData.address);
|
|
50125
|
+
state.operatorAccountName = operatorName;
|
|
50126
|
+
const defaultFilename = `${operatorName}-keystore.json`;
|
|
50127
|
+
const { outputFilename } = await inquirer6.prompt([
|
|
50128
|
+
{
|
|
50129
|
+
type: "input",
|
|
50130
|
+
name: "outputFilename",
|
|
50131
|
+
message: "Export keystore filename:",
|
|
50132
|
+
default: defaultFilename
|
|
50133
|
+
}
|
|
50134
|
+
]);
|
|
50135
|
+
let outputPath = path7.resolve(`./${outputFilename}`);
|
|
50136
|
+
if (existsSync8(outputPath)) {
|
|
50137
|
+
const { overwrite } = await inquirer6.prompt([
|
|
50138
|
+
{
|
|
50139
|
+
type: "confirm",
|
|
50140
|
+
name: "overwrite",
|
|
50141
|
+
message: `File ${outputFilename} already exists. Overwrite?`,
|
|
50142
|
+
default: false
|
|
50143
|
+
}
|
|
50144
|
+
]);
|
|
50145
|
+
if (!overwrite) {
|
|
50146
|
+
const { newFilename } = await inquirer6.prompt([
|
|
50147
|
+
{
|
|
50148
|
+
type: "input",
|
|
50149
|
+
name: "newFilename",
|
|
50150
|
+
message: "Enter new filename:"
|
|
50151
|
+
}
|
|
50152
|
+
]);
|
|
50153
|
+
outputPath = path7.resolve(`./${newFilename}`);
|
|
50154
|
+
}
|
|
50155
|
+
}
|
|
50156
|
+
const { exportPassword } = await inquirer6.prompt([
|
|
50157
|
+
{
|
|
50158
|
+
type: "password",
|
|
50159
|
+
name: "exportPassword",
|
|
50160
|
+
message: "Enter password for exported keystore (needed to import in node):",
|
|
50161
|
+
mask: "*",
|
|
50162
|
+
validate: (input) => input.length >= 8 || "Password must be at least 8 characters"
|
|
50163
|
+
}
|
|
50164
|
+
]);
|
|
50165
|
+
const { confirmPassword } = await inquirer6.prompt([
|
|
50166
|
+
{
|
|
50167
|
+
type: "password",
|
|
50168
|
+
name: "confirmPassword",
|
|
50169
|
+
message: "Confirm password:",
|
|
50170
|
+
mask: "*"
|
|
50171
|
+
}
|
|
50172
|
+
]);
|
|
50173
|
+
if (exportPassword !== confirmPassword) {
|
|
50174
|
+
this.failSpinner("Passwords do not match");
|
|
50175
|
+
}
|
|
50176
|
+
const exportAction = new ExportAccountAction();
|
|
50177
|
+
await exportAction.execute({
|
|
50178
|
+
account: operatorName,
|
|
50179
|
+
output: outputPath,
|
|
50180
|
+
password: exportPassword,
|
|
50181
|
+
overwrite: true
|
|
50182
|
+
});
|
|
50183
|
+
state.operatorKeystorePath = outputPath;
|
|
50184
|
+
console.log("\n========================================");
|
|
50185
|
+
console.log(" IMPORTANT: Transfer operator keystore");
|
|
50186
|
+
console.log("========================================");
|
|
50187
|
+
console.log(`File: ${outputPath}`);
|
|
50188
|
+
console.log("Transfer this file to your validator server and import it");
|
|
50189
|
+
console.log("into your validator node software.");
|
|
50190
|
+
console.log("========================================\n");
|
|
50191
|
+
}
|
|
50192
|
+
async stepStakeAmount(state) {
|
|
50193
|
+
console.log("Step 5: Stake Amount");
|
|
50194
|
+
console.log("--------------------\n");
|
|
50195
|
+
const balanceGEN = formatEther(state.balance);
|
|
50196
|
+
const minStakeGEN = formatEther(state.minStake);
|
|
50197
|
+
const hasMinStake = state.minStake > 0n;
|
|
50198
|
+
const { stakeAmount } = await inquirer6.prompt([
|
|
50199
|
+
{
|
|
50200
|
+
type: "input",
|
|
50201
|
+
name: "stakeAmount",
|
|
50202
|
+
message: hasMinStake ? `Enter stake amount (min: ${minStakeGEN}, max: ${balanceGEN} GEN):` : `Enter stake amount (max: ${balanceGEN} GEN):`,
|
|
50203
|
+
default: hasMinStake ? minStakeGEN : "1",
|
|
50204
|
+
validate: (input) => {
|
|
50205
|
+
const cleaned = input.toLowerCase().replace("gen", "").trim();
|
|
50206
|
+
const num2 = parseFloat(cleaned);
|
|
50207
|
+
if (isNaN(num2) || num2 <= 0) {
|
|
50208
|
+
return "Please enter a valid positive number";
|
|
50209
|
+
}
|
|
50210
|
+
const amountWei = BigInt(Math.floor(num2 * 1e18));
|
|
50211
|
+
if (hasMinStake && amountWei < state.minStake) {
|
|
50212
|
+
return `Amount must be at least ${minStakeGEN} GEN`;
|
|
50213
|
+
}
|
|
50214
|
+
if (amountWei > state.balance) {
|
|
50215
|
+
return `Amount exceeds balance (${balanceGEN} GEN)`;
|
|
50216
|
+
}
|
|
50217
|
+
return true;
|
|
50218
|
+
}
|
|
50219
|
+
}
|
|
50220
|
+
]);
|
|
50221
|
+
const normalizedAmount = stakeAmount.toLowerCase().endsWith("gen") ? stakeAmount : `${stakeAmount}gen`;
|
|
50222
|
+
state.stakeAmount = normalizedAmount;
|
|
50223
|
+
const { confirm } = await inquirer6.prompt([
|
|
50224
|
+
{
|
|
50225
|
+
type: "confirm",
|
|
50226
|
+
name: "confirm",
|
|
50227
|
+
message: `You will stake ${stakeAmount}. Continue?`,
|
|
50228
|
+
default: true
|
|
50229
|
+
}
|
|
50230
|
+
]);
|
|
50231
|
+
if (!confirm) {
|
|
50232
|
+
throw new Error("WIZARD_ABORTED");
|
|
50233
|
+
}
|
|
50234
|
+
console.log("");
|
|
50235
|
+
}
|
|
50236
|
+
async stepJoinValidator(state, options) {
|
|
50237
|
+
console.log("Step 6: Join as Validator");
|
|
50238
|
+
console.log("-------------------------\n");
|
|
50239
|
+
this.startSpinner("Creating validator...");
|
|
50240
|
+
try {
|
|
50241
|
+
const client = await this.getStakingClient({
|
|
50242
|
+
...options,
|
|
50243
|
+
account: state.accountName,
|
|
50244
|
+
network: state.networkAlias
|
|
50245
|
+
});
|
|
50246
|
+
const amount = this.parseAmount(state.stakeAmount);
|
|
50247
|
+
this.setSpinnerText(`Creating validator with ${this.formatAmount(amount)} stake...`);
|
|
50248
|
+
const result = await client.validatorJoin({
|
|
50249
|
+
amount,
|
|
50250
|
+
operator: state.operatorAddress
|
|
50251
|
+
});
|
|
50252
|
+
state.validatorWalletAddress = ensureHexPrefix(result.validatorWallet);
|
|
50253
|
+
this.succeedSpinner("Validator created successfully!", {
|
|
50254
|
+
transactionHash: result.transactionHash,
|
|
50255
|
+
validatorWallet: state.validatorWalletAddress,
|
|
50256
|
+
amount: result.amount,
|
|
50257
|
+
operator: result.operator,
|
|
50258
|
+
blockNumber: result.blockNumber.toString()
|
|
50259
|
+
});
|
|
50260
|
+
console.log("");
|
|
50261
|
+
} catch (error) {
|
|
50262
|
+
this.failSpinner("Failed to create validator", error.message || error);
|
|
50263
|
+
}
|
|
50264
|
+
}
|
|
50265
|
+
async stepIdentitySetup(state, options) {
|
|
50266
|
+
console.log("Step 7: Identity Setup");
|
|
50267
|
+
console.log("----------------------\n");
|
|
50268
|
+
const { setupIdentity } = await inquirer6.prompt([
|
|
50269
|
+
{
|
|
50270
|
+
type: "confirm",
|
|
50271
|
+
name: "setupIdentity",
|
|
50272
|
+
message: "Would you like to set up your validator identity now?",
|
|
50273
|
+
default: true
|
|
50274
|
+
}
|
|
50275
|
+
]);
|
|
50276
|
+
if (!setupIdentity) {
|
|
50277
|
+
console.log("\nYou can set up identity later with: genlayer staking set-identity\n");
|
|
50278
|
+
return;
|
|
50279
|
+
}
|
|
50280
|
+
const { moniker } = await inquirer6.prompt([
|
|
50281
|
+
{
|
|
50282
|
+
type: "input",
|
|
50283
|
+
name: "moniker",
|
|
50284
|
+
message: "Enter validator display name (moniker):",
|
|
50285
|
+
validate: (input) => input.length > 0 || "Moniker is required"
|
|
50286
|
+
}
|
|
50287
|
+
]);
|
|
50288
|
+
const { logoUri } = await inquirer6.prompt([
|
|
50289
|
+
{
|
|
50290
|
+
type: "input",
|
|
50291
|
+
name: "logoUri",
|
|
50292
|
+
message: "Enter logo URL (optional):"
|
|
50293
|
+
}
|
|
50294
|
+
]);
|
|
50295
|
+
const { website } = await inquirer6.prompt([
|
|
50296
|
+
{
|
|
50297
|
+
type: "input",
|
|
50298
|
+
name: "website",
|
|
50299
|
+
message: "Enter website URL (optional):"
|
|
50300
|
+
}
|
|
50301
|
+
]);
|
|
50302
|
+
const { description } = await inquirer6.prompt([
|
|
50303
|
+
{
|
|
50304
|
+
type: "input",
|
|
50305
|
+
name: "description",
|
|
50306
|
+
message: "Enter description (optional):"
|
|
50307
|
+
}
|
|
50308
|
+
]);
|
|
50309
|
+
const { email } = await inquirer6.prompt([
|
|
50310
|
+
{
|
|
50311
|
+
type: "input",
|
|
50312
|
+
name: "email",
|
|
50313
|
+
message: "Enter contact email (optional):"
|
|
50314
|
+
}
|
|
50315
|
+
]);
|
|
50316
|
+
const { twitter } = await inquirer6.prompt([
|
|
50317
|
+
{
|
|
50318
|
+
type: "input",
|
|
50319
|
+
name: "twitter",
|
|
50320
|
+
message: "Enter Twitter handle (optional):"
|
|
50321
|
+
}
|
|
50322
|
+
]);
|
|
50323
|
+
const { telegram } = await inquirer6.prompt([
|
|
50324
|
+
{
|
|
50325
|
+
type: "input",
|
|
50326
|
+
name: "telegram",
|
|
50327
|
+
message: "Enter Telegram handle (optional):"
|
|
50328
|
+
}
|
|
50329
|
+
]);
|
|
50330
|
+
const { github } = await inquirer6.prompt([
|
|
50331
|
+
{
|
|
50332
|
+
type: "input",
|
|
50333
|
+
name: "github",
|
|
50334
|
+
message: "Enter GitHub handle (optional):"
|
|
50335
|
+
}
|
|
50336
|
+
]);
|
|
50337
|
+
state.identity = {
|
|
50338
|
+
moniker,
|
|
50339
|
+
logoUri: logoUri || void 0,
|
|
50340
|
+
website: website || void 0,
|
|
50341
|
+
description: description || void 0,
|
|
50342
|
+
email: email || void 0,
|
|
50343
|
+
twitter: twitter || void 0,
|
|
50344
|
+
telegram: telegram || void 0,
|
|
50345
|
+
github: github || void 0
|
|
50346
|
+
};
|
|
50347
|
+
this.startSpinner("Setting validator identity...");
|
|
50348
|
+
try {
|
|
50349
|
+
const client = await this.getStakingClient({
|
|
50350
|
+
...options,
|
|
50351
|
+
account: state.accountName,
|
|
50352
|
+
network: state.networkAlias
|
|
50353
|
+
});
|
|
50354
|
+
const validatorAddress = state.validatorWalletAddress || state.accountAddress;
|
|
50355
|
+
await client.setIdentity({
|
|
50356
|
+
validator: ensureHexPrefix(validatorAddress),
|
|
50357
|
+
moniker,
|
|
50358
|
+
logoUri: logoUri || void 0,
|
|
50359
|
+
website: website || void 0,
|
|
50360
|
+
description: description || void 0,
|
|
50361
|
+
email: email || void 0,
|
|
50362
|
+
twitter: twitter || void 0,
|
|
50363
|
+
telegram: telegram || void 0,
|
|
50364
|
+
github: github || void 0
|
|
50365
|
+
});
|
|
50366
|
+
this.succeedSpinner("Validator identity set!");
|
|
50367
|
+
console.log("");
|
|
50368
|
+
} catch (error) {
|
|
50369
|
+
this.stopSpinner();
|
|
50370
|
+
this.logWarning(`Failed to set identity: ${error.message || error}`);
|
|
50371
|
+
console.log("You can try again later with: genlayer staking set-identity\n");
|
|
50372
|
+
}
|
|
50373
|
+
}
|
|
50374
|
+
showSummary(state) {
|
|
50375
|
+
console.log("\n========================================");
|
|
50376
|
+
console.log(" Validator Setup Complete!");
|
|
50377
|
+
console.log("========================================\n");
|
|
50378
|
+
const validatorWallet = ensureHexPrefix(state.validatorWalletAddress || state.accountAddress);
|
|
50379
|
+
const ownerAddress = ensureHexPrefix(state.accountAddress);
|
|
50380
|
+
const operatorAddress = ensureHexPrefix(state.operatorAddress || "");
|
|
50381
|
+
console.log("Summary:");
|
|
50382
|
+
console.log(` Validator Wallet: ${validatorWallet}`);
|
|
50383
|
+
console.log(` Owner: ${ownerAddress} (${state.accountName})`);
|
|
50384
|
+
if (state.operatorAccountName) {
|
|
50385
|
+
console.log(` Operator: ${operatorAddress} (${state.operatorAccountName})`);
|
|
50386
|
+
} else {
|
|
50387
|
+
console.log(` Operator: ${operatorAddress}`);
|
|
50388
|
+
}
|
|
50389
|
+
console.log(` Staked Amount: ${state.stakeAmount}`);
|
|
50390
|
+
console.log(` Network: ${BUILT_IN_NETWORKS[state.networkAlias].name}`);
|
|
50391
|
+
if (state.identity) {
|
|
50392
|
+
console.log(` Identity:`);
|
|
50393
|
+
console.log(` Moniker: ${state.identity.moniker}`);
|
|
50394
|
+
if (state.identity.logoUri) console.log(` Logo: ${state.identity.logoUri}`);
|
|
50395
|
+
if (state.identity.website) console.log(` Website: ${state.identity.website}`);
|
|
50396
|
+
if (state.identity.description) console.log(` Description: ${state.identity.description}`);
|
|
50397
|
+
if (state.identity.email) console.log(` Email: ${state.identity.email}`);
|
|
50398
|
+
if (state.identity.twitter) console.log(` Twitter: ${state.identity.twitter}`);
|
|
50399
|
+
if (state.identity.telegram) console.log(` Telegram: ${state.identity.telegram}`);
|
|
50400
|
+
if (state.identity.github) console.log(` GitHub: ${state.identity.github}`);
|
|
50401
|
+
}
|
|
50402
|
+
console.log("\nNext Steps:");
|
|
50403
|
+
let step = 1;
|
|
50404
|
+
if (state.operatorKeystorePath) {
|
|
50405
|
+
console.log(` ${step++}. Transfer operator keystore to your validator server:`);
|
|
50406
|
+
console.log(` ${state.operatorKeystorePath}`);
|
|
50407
|
+
console.log(` ${step++}. Import it into your validator node software`);
|
|
50408
|
+
}
|
|
50409
|
+
console.log(` ${step++}. Monitor your validator:`);
|
|
50410
|
+
console.log(` genlayer staking validator-info --validator ${validatorWallet}`);
|
|
50411
|
+
console.log(` ${step++}. Lock your account when done: genlayer account lock`);
|
|
50412
|
+
console.log("\n========================================\n");
|
|
50413
|
+
}
|
|
50414
|
+
};
|
|
50415
|
+
|
|
49217
50416
|
// src/commands/staking/index.ts
|
|
49218
50417
|
function initializeStakingCommands(program2) {
|
|
49219
50418
|
const staking = program2.command("staking").description("Staking operations for validators and delegators");
|
|
49220
|
-
staking.command("
|
|
50419
|
+
staking.command("wizard").description("Interactive wizard to become a validator").option("--account <name>", "Account to use (skip selection)").option("--network <network>", "Network to use (skip selection)").option("--skip-identity", "Skip identity setup step").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
50420
|
+
const wizard = new ValidatorWizardAction();
|
|
50421
|
+
await wizard.execute(options);
|
|
50422
|
+
});
|
|
50423
|
+
staking.command("validator-join").description("Join as a validator by staking tokens").requiredOption("--amount <amount>", "Amount to stake (in wei or with 'eth'/'gen' suffix, e.g., '42000gen')").option("--operator <address>", "Operator address (defaults to signer)").option("--account <name>", "Account to use").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
49221
50424
|
const action = new ValidatorJoinAction();
|
|
49222
50425
|
await action.execute(options);
|
|
49223
50426
|
});
|
|
49224
|
-
staking.command("validator-deposit").description("Make an additional deposit as a validator").requiredOption("--amount <amount>", "Amount to deposit (in wei or with 'eth'/'gen' suffix)").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
50427
|
+
staking.command("validator-deposit").description("Make an additional deposit as a validator").requiredOption("--amount <amount>", "Amount to deposit (in wei or with 'eth'/'gen' suffix)").option("--account <name>", "Account to use").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
49225
50428
|
const action = new ValidatorDepositAction();
|
|
49226
50429
|
await action.execute(options);
|
|
49227
50430
|
});
|
|
49228
|
-
staking.command("validator-exit").description("Exit as a validator by withdrawing shares").requiredOption("--shares <shares>", "Number of shares to withdraw").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
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").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
49229
50432
|
const action = new ValidatorExitAction();
|
|
49230
50433
|
await action.execute(options);
|
|
49231
50434
|
});
|
|
49232
|
-
staking.command("validator-claim").description("Claim validator withdrawals after unbonding period").option("--validator <address>", "Validator address (defaults to signer)").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
50435
|
+
staking.command("validator-claim").description("Claim validator withdrawals after unbonding period").option("--validator <address>", "Validator address (defaults to signer)").option("--account <name>", "Account to use").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
49233
50436
|
const action = new ValidatorClaimAction();
|
|
49234
50437
|
await action.execute(options);
|
|
49235
50438
|
});
|
|
49236
|
-
staking.command("validator-prime").description("Prime a validator to prepare their stake record for the next epoch").requiredOption("--validator <address>", "Validator address to prime").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
50439
|
+
staking.command("validator-prime").description("Prime a validator to prepare their stake record for the next epoch").requiredOption("--validator <address>", "Validator address to prime").option("--account <name>", "Account to use").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
49237
50440
|
const action = new ValidatorPrimeAction();
|
|
49238
50441
|
await action.execute(options);
|
|
49239
50442
|
});
|
|
49240
|
-
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("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
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").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
49241
50444
|
const action = new SetOperatorAction();
|
|
49242
50445
|
await action.execute(options);
|
|
49243
50446
|
});
|
|
49244
|
-
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("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
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").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
49245
50448
|
const action = new SetIdentityAction();
|
|
49246
50449
|
await action.execute(options);
|
|
49247
50450
|
});
|
|
49248
|
-
staking.command("delegator-join").description("Join as a delegator by staking with a validator").requiredOption("--validator <address>", "Validator address to delegate to").requiredOption("--amount <amount>", "Amount to stake (in wei or with 'eth'/'gen' suffix)").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
50451
|
+
staking.command("delegator-join").description("Join as a delegator by staking with a validator").requiredOption("--validator <address>", "Validator address to delegate to").requiredOption("--amount <amount>", "Amount to stake (in wei or with 'eth'/'gen' suffix)").option("--account <name>", "Account to use").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
49249
50452
|
const action = new DelegatorJoinAction();
|
|
49250
50453
|
await action.execute(options);
|
|
49251
50454
|
});
|
|
49252
|
-
staking.command("delegator-exit").description("Exit as a delegator by withdrawing shares from a validator").requiredOption("--validator <address>", "Validator address to exit from").requiredOption("--shares <shares>", "Number of shares to withdraw").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
50455
|
+
staking.command("delegator-exit").description("Exit as a delegator by withdrawing shares from a validator").requiredOption("--validator <address>", "Validator address to exit from").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").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
49253
50456
|
const action = new DelegatorExitAction();
|
|
49254
50457
|
await action.execute(options);
|
|
49255
50458
|
});
|
|
49256
|
-
staking.command("delegator-claim").description("Claim delegator withdrawals after unbonding period").requiredOption("--validator <address>", "Validator address").option("--delegator <address>", "Delegator address (defaults to signer)").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
50459
|
+
staking.command("delegator-claim").description("Claim delegator withdrawals after unbonding period").requiredOption("--validator <address>", "Validator address").option("--delegator <address>", "Delegator address (defaults to signer)").option("--account <name>", "Account to use").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
49257
50460
|
const action = new DelegatorClaimAction();
|
|
49258
50461
|
await action.execute(options);
|
|
49259
50462
|
});
|
|
49260
|
-
staking.command("validator-info").description("Get information about a validator").option("--validator <address>", "Validator address (defaults to signer)").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
50463
|
+
staking.command("validator-info").description("Get information about a validator").option("--validator <address>", "Validator address (defaults to signer)").option("--account <name>", "Account to use (for default validator address)").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
49261
50464
|
const action = new StakingInfoAction();
|
|
49262
50465
|
await action.getValidatorInfo(options);
|
|
49263
50466
|
});
|
|
49264
|
-
staking.command("
|
|
50467
|
+
staking.command("delegation-info").description("Get delegation info for a delegator with a validator").requiredOption("--validator <address>", "Validator address").option("--delegator <address>", "Delegator address (defaults to signer)").option("--account <name>", "Account to use (for default delegator address)").option("--network <network>", "Network to use (localnet, testnet-asimov)").option("--rpc <rpcUrl>", "RPC URL for the network").option("--staking-address <address>", "Staking contract address (overrides chain config)").action(async (options) => {
|
|
49265
50468
|
const action = new StakingInfoAction();
|
|
49266
50469
|
await action.getStakeInfo(options);
|
|
49267
50470
|
});
|