From 83c9e797b752a72d4a7dba4ccbdbb0fb8ae37398 Mon Sep 17 00:00:00 2001 From: Leeren Chang Date: Fri, 18 Oct 2024 12:54:55 -0700 Subject: [PATCH] feat(cli): update cli for odyssey changes --- client/cmd/abi/IPTokenSlashing.abi.json | 369 ---------- client/cmd/abi/IPTokenStaking.abi.json | 898 +++++++++--------------- client/cmd/flags.go | 204 ++++-- client/cmd/keys.go | 2 +- client/cmd/transaction.go | 53 +- client/cmd/validator.go | 395 +++++++---- 6 files changed, 764 insertions(+), 1157 deletions(-) delete mode 100644 client/cmd/abi/IPTokenSlashing.abi.json diff --git a/client/cmd/abi/IPTokenSlashing.abi.json b/client/cmd/abi/IPTokenSlashing.abi.json deleted file mode 100644 index 53b19a05..00000000 --- a/client/cmd/abi/IPTokenSlashing.abi.json +++ /dev/null @@ -1,369 +0,0 @@ -[ - { - "type": "constructor", - "inputs": [ - { - "name": "ipTokenStaking", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "IP_TOKEN_STAKING", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "contract IPTokenStaking" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "UPGRADE_INTERFACE_VERSION", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "string", - "internalType": "string" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "acceptOwnership", - "inputs": [], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "initialize", - "inputs": [ - { - "name": "accessManager", - "type": "address", - "internalType": "address" - }, - { - "name": "newUnjailFee", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "owner", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "pendingOwner", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "proxiableUUID", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "bytes32", - "internalType": "bytes32" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "renounceOwnership", - "inputs": [], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "setUnjailFee", - "inputs": [ - { - "name": "newUnjailFee", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "transferOwnership", - "inputs": [ - { - "name": "newOwner", - "type": "address", - "internalType": "address" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "unjail", - "inputs": [ - { - "name": "validatorUncmpPubkey", - "type": "bytes", - "internalType": "bytes" - } - ], - "outputs": [], - "stateMutability": "payable" - }, - { - "type": "function", - "name": "unjailFee", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "uint256", - "internalType": "uint256" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "unjailOnBehalf", - "inputs": [ - { - "name": "validatorCmpPubkey", - "type": "bytes", - "internalType": "bytes" - } - ], - "outputs": [], - "stateMutability": "payable" - }, - { - "type": "function", - "name": "upgradeToAndCall", - "inputs": [ - { - "name": "newImplementation", - "type": "address", - "internalType": "address" - }, - { - "name": "data", - "type": "bytes", - "internalType": "bytes" - } - ], - "outputs": [], - "stateMutability": "payable" - }, - { - "type": "event", - "name": "Initialized", - "inputs": [ - { - "name": "version", - "type": "uint64", - "indexed": false, - "internalType": "uint64" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "OwnershipTransferStarted", - "inputs": [ - { - "name": "previousOwner", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "newOwner", - "type": "address", - "indexed": true, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "OwnershipTransferred", - "inputs": [ - { - "name": "previousOwner", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "newOwner", - "type": "address", - "indexed": true, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Unjail", - "inputs": [ - { - "name": "sender", - "type": "address", - "indexed": true, - "internalType": "address" - }, - { - "name": "validatorCmpPubkey", - "type": "bytes", - "indexed": false, - "internalType": "bytes" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "UnjailFeeSet", - "inputs": [ - { - "name": "newUnjailFee", - "type": "uint256", - "indexed": false, - "internalType": "uint256" - } - ], - "anonymous": false - }, - { - "type": "event", - "name": "Upgraded", - "inputs": [ - { - "name": "implementation", - "type": "address", - "indexed": true, - "internalType": "address" - } - ], - "anonymous": false - }, - { - "type": "error", - "name": "AddressEmptyCode", - "inputs": [ - { - "name": "target", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "ERC1967InvalidImplementation", - "inputs": [ - { - "name": "implementation", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "ERC1967NonPayable", - "inputs": [] - }, - { - "type": "error", - "name": "FailedInnerCall", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidInitialization", - "inputs": [] - }, - { - "type": "error", - "name": "NotInitializing", - "inputs": [] - }, - { - "type": "error", - "name": "OwnableInvalidOwner", - "inputs": [ - { - "name": "owner", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "OwnableUnauthorizedAccount", - "inputs": [ - { - "name": "account", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "UUPSUnauthorizedCallContext", - "inputs": [] - }, - { - "type": "error", - "name": "UUPSUnsupportedProxiableUUID", - "inputs": [ - { - "name": "slot", - "type": "bytes32", - "internalType": "bytes32" - } - ] - } -] diff --git a/client/cmd/abi/IPTokenStaking.abi.json b/client/cmd/abi/IPTokenStaking.abi.json index d6c21b70..2768b295 100644 --- a/client/cmd/abi/IPTokenStaking.abi.json +++ b/client/cmd/abi/IPTokenStaking.abi.json @@ -1,102 +1,4 @@ [ - { - "type": "constructor", - "inputs": [ - { - "name": "stakingRounding", - "type": "uint256", - "internalType": "uint256" - }, - { - "name": "defaultCommissionRate", - "type": "uint32", - "internalType": "uint32" - }, - { - "name": "defaultMaxCommissionRate", - "type": "uint32", - "internalType": "uint32" - }, - { - "name": "defaultMaxCommissionChangeRate", - "type": "uint32", - "internalType": "uint32" - } - ], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "DEFAULT_COMMISSION_RATE", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "uint32", - "internalType": "uint32" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "DEFAULT_MAX_COMMISSION_CHANGE_RATE", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "uint32", - "internalType": "uint32" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "DEFAULT_MAX_COMMISSION_RATE", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "uint32", - "internalType": "uint32" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "STAKE_ROUNDING", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "uint256", - "internalType": "uint256" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "UPGRADE_INTERFACE_VERSION", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "string", - "internalType": "string" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "acceptOwnership", - "inputs": [], - "outputs": [], - "stateMutability": "nonpayable" - }, { "type": "function", "name": "addOperator", @@ -113,7 +15,7 @@ } ], "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "payable" }, { "type": "function", @@ -143,6 +45,16 @@ "name": "maxCommissionChangeRate", "type": "uint32", "internalType": "uint32" + }, + { + "name": "supportsUnlocked", + "type": "bool", + "internalType": "bool" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" } ], "outputs": [], @@ -156,6 +68,36 @@ "name": "validatorUncmpPubkey", "type": "bytes", "internalType": "bytes" + }, + { + "name": "moniker", + "type": "string", + "internalType": "string" + }, + { + "name": "commissionRate", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "maxCommissionRate", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "maxCommissionChangeRate", + "type": "uint32", + "internalType": "uint32" + }, + { + "name": "supportsUnlocked", + "type": "bool", + "internalType": "bool" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" } ], "outputs": [], @@ -163,246 +105,69 @@ }, { "type": "function", - "name": "delegatorTotalStakes", + "name": "redelegate", "inputs": [ { - "name": "delegatorCmpPubkey", + "name": "delegatorUncmpPubkey", "type": "bytes", "internalType": "bytes" - } - ], - "outputs": [ - { - "name": "stakedAmount", - "type": "uint256", - "internalType": "uint256" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "delegatorValidatorStakes", - "inputs": [ + }, { - "name": "delegatorCmpPubkey", + "name": "validatorUncmpSrcPubkey", "type": "bytes", "internalType": "bytes" }, { - "name": "validatorCmpPubkey", + "name": "validatorUncmpDstPubkey", "type": "bytes", "internalType": "bytes" - } - ], - "outputs": [ + }, + { + "name": "delegationId", + "type": "uint256", + "internalType": "uint256" + }, { - "name": "stakedAmount", + "name": "amount", "type": "uint256", "internalType": "uint256" } ], - "stateMutability": "view" + "outputs": [], + "stateMutability": "payable" }, { "type": "function", - "name": "getOperators", + "name": "redelegateOnBehalf", "inputs": [ { - "name": "pubkey", + "name": "delegatorUncmpPubkey", "type": "bytes", "internalType": "bytes" - } - ], - "outputs": [ - { - "name": "", - "type": "address[]", - "internalType": "address[]" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "initialize", - "inputs": [ - { - "name": "accessManager", - "type": "address", - "internalType": "address" }, { - "name": "_minStakeAmount", - "type": "uint256", - "internalType": "uint256" + "name": "validatorUncmpSrcPubkey", + "type": "bytes", + "internalType": "bytes" }, { - "name": "_minUnstakeAmount", - "type": "uint256", - "internalType": "uint256" + "name": "validatorUncmpDstPubkey", + "type": "bytes", + "internalType": "bytes" }, { - "name": "_minRedelegateAmount", + "name": "delegationId", "type": "uint256", "internalType": "uint256" }, { - "name": "_withdrawalAddressChangeInterval", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "minRedelegateAmount", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "uint256", - "internalType": "uint256" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "minStakeAmount", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "uint256", - "internalType": "uint256" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "minUnstakeAmount", - "inputs": [], - "outputs": [ - { - "name": "", + "name": "amount", "type": "uint256", "internalType": "uint256" } ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "owner", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "pendingOwner", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "address", - "internalType": "address" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "proxiableUUID", - "inputs": [], - "outputs": [ - { - "name": "", - "type": "bytes32", - "internalType": "bytes32" - } - ], - "stateMutability": "view" - }, - { - "type": "function", - "name": "redelegate", - "inputs": [ - { - "name": "p", - "type": "tuple", - "internalType": "struct IIPTokenStaking.RedelegateParams", - "components": [ - { - "name": "delegatorUncmpPubkey", - "type": "bytes", - "internalType": "bytes" - }, - { - "name": "validatorCmpSrcPubkey", - "type": "bytes", - "internalType": "bytes" - }, - { - "name": "validatorCmpDstPubkey", - "type": "bytes", - "internalType": "bytes" - }, - { - "name": "amount", - "type": "uint256", - "internalType": "uint256" - } - ] - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "redelegateOnBehalf", - "inputs": [ - { - "name": "p", - "type": "tuple", - "internalType": "struct IIPTokenStaking.RedelegateParams", - "components": [ - { - "name": "delegatorUncmpPubkey", - "type": "bytes", - "internalType": "bytes" - }, - { - "name": "validatorCmpSrcPubkey", - "type": "bytes", - "internalType": "bytes" - }, - { - "name": "validatorCmpDstPubkey", - "type": "bytes", - "internalType": "bytes" - }, - { - "name": "amount", - "type": "uint256", - "internalType": "uint256" - } - ] - } - ], "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "payable" }, { "type": "function", @@ -422,13 +187,6 @@ "outputs": [], "stateMutability": "nonpayable" }, - { - "type": "function", - "name": "renounceOwnership", - "inputs": [], - "outputs": [], - "stateMutability": "nonpayable" - }, { "type": "function", "name": "roundedStakeAmount", @@ -455,42 +213,21 @@ }, { "type": "function", - "name": "setMinRedelegateAmount", - "inputs": [ - { - "name": "newMinRedelegateAmount", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "setMinStakeAmount", + "name": "setRewardsAddress", "inputs": [ { - "name": "newMinStakeAmount", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "setMinUnstakeAmount", - "inputs": [ + "name": "delegatorUncmpPubkey", + "type": "bytes", + "internalType": "bytes" + }, { - "name": "newMinUnstakeAmount", - "type": "uint256", - "internalType": "uint256" + "name": "newRewardsAddress", + "type": "address", + "internalType": "address" } ], "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "payable" }, { "type": "function", @@ -508,20 +245,7 @@ } ], "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "setWithdrawalAddressChangeInterval", - "inputs": [ - { - "name": "newWithdrawalAddressChangeInterval", - "type": "uint256", - "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "payable" }, { "type": "function", @@ -533,12 +257,28 @@ "internalType": "bytes" }, { - "name": "validatorCmpPubkey", + "name": "validatorUncmpPubkey", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "stakingPeriod", + "type": "uint8", + "internalType": "enum IIPTokenStaking.StakingPeriod" + }, + { + "name": "data", "type": "bytes", "internalType": "bytes" } ], - "outputs": [], + "outputs": [ + { + "name": "delegationId", + "type": "uint256", + "internalType": "uint256" + } + ], "stateMutability": "payable" }, { @@ -551,81 +291,89 @@ "internalType": "bytes" }, { - "name": "validatorCmpPubkey", + "name": "validatorUncmpPubkey", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "stakingPeriod", + "type": "uint8", + "internalType": "enum IIPTokenStaking.StakingPeriod" + }, + { + "name": "data", "type": "bytes", "internalType": "bytes" } ], - "outputs": [], + "outputs": [ + { + "name": "delegationId", + "type": "uint256", + "internalType": "uint256" + } + ], "stateMutability": "payable" }, { "type": "function", - "name": "transferOwnership", + "name": "unjail", "inputs": [ { - "name": "newOwner", - "type": "address", - "internalType": "address" + "name": "validatorUncmpPubkey", + "type": "bytes", + "internalType": "bytes" + }, + { + "name": "data", + "type": "bytes", + "internalType": "bytes" } ], "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "payable" }, { "type": "function", - "name": "unstake", + "name": "unjailOnBehalf", "inputs": [ { - "name": "delegatorUncmpPubkey", + "name": "validatorUncmpPubkey", "type": "bytes", "internalType": "bytes" }, { - "name": "validatorCmpPubkey", + "name": "data", "type": "bytes", "internalType": "bytes" - }, - { - "name": "amount", - "type": "uint256", - "internalType": "uint256" } ], "outputs": [], - "stateMutability": "nonpayable" + "stateMutability": "payable" }, { "type": "function", - "name": "unstakeOnBehalf", + "name": "unstake", "inputs": [ { - "name": "delegatorCmpPubkey", + "name": "delegatorUncmpPubkey", "type": "bytes", "internalType": "bytes" }, { - "name": "validatorCmpPubkey", + "name": "validatorUncmpPubkey", "type": "bytes", "internalType": "bytes" }, { - "name": "amount", + "name": "delegationId", "type": "uint256", "internalType": "uint256" - } - ], - "outputs": [], - "stateMutability": "nonpayable" - }, - { - "type": "function", - "name": "upgradeToAndCall", - "inputs": [ + }, { - "name": "newImplementation", - "type": "address", - "internalType": "address" + "name": "amount", + "type": "uint256", + "internalType": "uint256" }, { "name": "data", @@ -634,83 +382,77 @@ } ], "outputs": [], - "stateMutability": "payable" + "stateMutability": "nonpayable" }, { "type": "function", - "name": "validatorMetadata", + "name": "unstakeOnBehalf", "inputs": [ { - "name": "validatorCmpPubkey", + "name": "delegatorUncmpPubkey", "type": "bytes", "internalType": "bytes" - } - ], - "outputs": [ - { - "name": "exists", - "type": "bool", - "internalType": "bool" }, { - "name": "moniker", - "type": "string", - "internalType": "string" + "name": "validatorUncmpPubkey", + "type": "bytes", + "internalType": "bytes" }, { - "name": "totalStake", + "name": "delegationId", "type": "uint256", "internalType": "uint256" }, { - "name": "commissionRate", - "type": "uint32", - "internalType": "uint32" - }, - { - "name": "maxCommissionRate", - "type": "uint32", - "internalType": "uint32" + "name": "amount", + "type": "uint256", + "internalType": "uint256" }, { - "name": "maxCommissionChangeRate", - "type": "uint32", - "internalType": "uint32" + "name": "data", + "type": "bytes", + "internalType": "bytes" } ], - "stateMutability": "view" + "outputs": [], + "stateMutability": "nonpayable" }, { "type": "function", - "name": "withdrawalAddressChange", + "name": "updateValidatorCommission", "inputs": [ { - "name": "delegatorCmpPubkey", + "name": "validatorUncmpPubkey", "type": "bytes", "internalType": "bytes" - } - ], - "outputs": [ + }, { - "name": "lastChange", - "type": "uint256", - "internalType": "uint256" + "name": "commissionRate", + "type": "uint32", + "internalType": "uint32" } ], - "stateMutability": "view" + "outputs": [], + "stateMutability": "payable" }, { - "type": "function", - "name": "withdrawalAddressChangeInterval", - "inputs": [], - "outputs": [ + "type": "event", + "name": "AddOperator", + "inputs": [ { - "name": "", - "type": "uint256", - "internalType": "uint256" + "name": "uncmpPubkey", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + }, + { + "name": "operator", + "type": "address", + "indexed": false, + "internalType": "address" } ], - "stateMutability": "view" + "anonymous": false }, { "type": "event", @@ -722,12 +464,6 @@ "indexed": false, "internalType": "bytes" }, - { - "name": "validatorCmpPubkey", - "type": "bytes", - "indexed": false, - "internalType": "bytes" - }, { "name": "moniker", "type": "string", @@ -757,6 +493,24 @@ "type": "uint32", "indexed": false, "internalType": "uint32" + }, + { + "name": "supportsUnlocked", + "type": "uint8", + "indexed": false, + "internalType": "uint8" + }, + { + "name": "operatorAddress", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "data", + "type": "bytes", + "indexed": false, + "internalType": "bytes" } ], "anonymous": false @@ -772,45 +526,63 @@ "internalType": "bytes" }, { - "name": "delegatorCmpPubkey", + "name": "validatorUnCmpPubkey", "type": "bytes", "indexed": false, "internalType": "bytes" }, { - "name": "validatorCmpPubkey", - "type": "bytes", + "name": "stakeAmount", + "type": "uint256", "indexed": false, - "internalType": "bytes" + "internalType": "uint256" }, { - "name": "amount", + "name": "stakingPeriod", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "delegationId", "type": "uint256", "indexed": false, "internalType": "uint256" + }, + { + "name": "operatorAddress", + "type": "address", + "indexed": false, + "internalType": "address" + }, + { + "name": "data", + "type": "bytes", + "indexed": false, + "internalType": "bytes" } ], "anonymous": false }, { "type": "event", - "name": "Initialized", + "name": "FeeSet", "inputs": [ { - "name": "version", - "type": "uint64", + "name": "newFee", + "type": "uint256", "indexed": false, - "internalType": "uint64" + "internalType": "uint256" } ], "anonymous": false }, { "type": "event", - "name": "MinRedelegateAmountSet", + "name": "MinCommissionRateChanged", "inputs": [ { - "name": "minRedelegateAmount", + "name": "minCommissionRate", "type": "uint256", "indexed": false, "internalType": "uint256" @@ -846,37 +618,61 @@ }, { "type": "event", - "name": "OwnershipTransferStarted", + "name": "Redelegate", "inputs": [ { - "name": "previousOwner", - "type": "address", - "indexed": true, - "internalType": "address" + "name": "delegatorUncmpPubkey", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + }, + { + "name": "validatorUncmpSrcPubkey", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + }, + { + "name": "validatorUncmpDstPubkey", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + }, + { + "name": "delegationId", + "type": "uint256", + "indexed": false, + "internalType": "uint256" }, { - "name": "newOwner", + "name": "operatorAddress", "type": "address", - "indexed": true, + "indexed": false, "internalType": "address" + }, + { + "name": "amount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" } ], "anonymous": false }, { "type": "event", - "name": "OwnershipTransferred", + "name": "RemoveOperator", "inputs": [ { - "name": "previousOwner", - "type": "address", - "indexed": true, - "internalType": "address" + "name": "uncmpPubkey", + "type": "bytes", + "indexed": false, + "internalType": "bytes" }, { - "name": "newOwner", + "name": "operator", "type": "address", - "indexed": true, + "indexed": false, "internalType": "address" } ], @@ -884,31 +680,19 @@ }, { "type": "event", - "name": "Redelegate", + "name": "SetRewardAddress", "inputs": [ { - "name": "delegatorCmpPubkey", - "type": "bytes", - "indexed": false, - "internalType": "bytes" - }, - { - "name": "validatorSrcPubkey", - "type": "bytes", - "indexed": false, - "internalType": "bytes" - }, - { - "name": "validatorDstPubkey", + "name": "delegatorUncmpPubkey", "type": "bytes", "indexed": false, "internalType": "bytes" }, { - "name": "amount", - "type": "uint256", + "name": "executionAddress", + "type": "bytes32", "indexed": false, - "internalType": "uint256" + "internalType": "bytes32" } ], "anonymous": false @@ -918,7 +702,7 @@ "name": "SetWithdrawalAddress", "inputs": [ { - "name": "delegatorCmpPubkey", + "name": "delegatorUncmpPubkey", "type": "bytes", "indexed": false, "internalType": "bytes" @@ -934,138 +718,114 @@ }, { "type": "event", - "name": "Upgraded", + "name": "StakingPeriodsChanged", "inputs": [ { - "name": "implementation", - "type": "address", - "indexed": true, - "internalType": "address" + "name": "short", + "type": "uint32", + "indexed": false, + "internalType": "uint32" + }, + { + "name": "medium", + "type": "uint32", + "indexed": false, + "internalType": "uint32" + }, + { + "name": "long", + "type": "uint32", + "indexed": false, + "internalType": "uint32" } ], "anonymous": false }, { "type": "event", - "name": "Withdraw", + "name": "Unjail", "inputs": [ { - "name": "delegatorCmpPubkey", - "type": "bytes", + "name": "unjailer", + "type": "address", "indexed": false, - "internalType": "bytes" + "internalType": "address" }, { - "name": "validatorCmpPubkey", + "name": "validatorUncmpPubkey", "type": "bytes", "indexed": false, "internalType": "bytes" }, { - "name": "amount", - "type": "uint256", + "name": "data", + "type": "bytes", "indexed": false, - "internalType": "uint256" + "internalType": "bytes" } ], "anonymous": false }, { "type": "event", - "name": "WithdrawalAddressChangeIntervalSet", + "name": "UpdateValidatorCommssion", "inputs": [ { - "name": "newInterval", - "type": "uint256", + "name": "validatorUncmpPubkey", + "type": "bytes", "indexed": false, - "internalType": "uint256" + "internalType": "bytes" + }, + { + "name": "commissionRate", + "type": "uint32", + "indexed": false, + "internalType": "uint32" } ], "anonymous": false }, { - "type": "error", - "name": "AddressEmptyCode", + "type": "event", + "name": "Withdraw", "inputs": [ { - "name": "target", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "ERC1967InvalidImplementation", - "inputs": [ + "name": "delegatorUncmpPubkey", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + }, { - "name": "implementation", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "ERC1967NonPayable", - "inputs": [] - }, - { - "type": "error", - "name": "FailedInnerCall", - "inputs": [] - }, - { - "type": "error", - "name": "InvalidInitialization", - "inputs": [] - }, - { - "type": "error", - "name": "NotInitializing", - "inputs": [] - }, - { - "type": "error", - "name": "OwnableInvalidOwner", - "inputs": [ + "name": "validatorUnCmpPubkey", + "type": "bytes", + "indexed": false, + "internalType": "bytes" + }, { - "name": "owner", - "type": "address", - "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "OwnableUnauthorizedAccount", - "inputs": [ + "name": "stakeAmount", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, + { + "name": "delegationId", + "type": "uint256", + "indexed": false, + "internalType": "uint256" + }, { - "name": "account", + "name": "operatorAddress", "type": "address", + "indexed": false, "internalType": "address" - } - ] - }, - { - "type": "error", - "name": "ReentrancyGuardReentrantCall", - "inputs": [] - }, - { - "type": "error", - "name": "UUPSUnauthorizedCallContext", - "inputs": [] - }, - { - "type": "error", - "name": "UUPSUnsupportedProxiableUUID", - "inputs": [ + }, { - "name": "slot", - "type": "bytes32", - "internalType": "bytes32" + "name": "data", + "type": "bytes", + "indexed": false, + "internalType": "bytes" } - ] + ], + "anonymous": false } ] diff --git a/client/cmd/flags.go b/client/cmd/flags.go index ed814ddb..a23ea2dc 100644 --- a/client/cmd/flags.go +++ b/client/cmd/flags.go @@ -1,7 +1,9 @@ package cmd import ( + "context" "fmt" + "math/big" "path/filepath" "strings" @@ -10,6 +12,7 @@ import ( "github.com/piplabs/story/client/config" libcmd "github.com/piplabs/story/lib/cmd" + "github.com/piplabs/story/lib/errors" "github.com/piplabs/story/lib/netconf" "github.com/piplabs/story/lib/tracer" @@ -47,20 +50,24 @@ func bindInitFlags(flags *pflag.FlagSet, cfg *InitConfig) { flags.StringVar(&cfg.ExternalAddress, "external-address", "", "Override the P2P external address") flags.StringVar(&cfg.Seeds, "seeds", "", "Override the P2P seeds (comma-separated)") flags.BoolVar(&cfg.SeedMode, "seed-mode", false, "Enable seed mode") - flags.StringVar(&cfg.Moniker, "moniker", cfg.Moniker, "Custom moniker name for this node") } func bindValidatorBaseFlags(cmd *cobra.Command, cfg *baseConfig) { - cmd.Flags().StringVar(&cfg.RPC, "rpc", "https://testnet.storyrpc.io", "RPC URL to connect to the testnet") + cmd.Flags().StringVar(&cfg.RPC, "rpc", "https://odyssey.storyrpc.io", "RPC URL to connect to the testnet") cmd.Flags().StringVar(&cfg.PrivateKey, "private-key", "", "Private key used for the transaction") - cmd.Flags().StringVar(&cfg.Explorer, "explorer", "https://testnet.storyscan.xyz", "URL of the blockchain explorer") - cmd.Flags().Int64Var(&cfg.ChainID, "chain-id", 1513, "Chain ID to use for the transaction (default 1513)") + cmd.Flags().StringVar(&cfg.Explorer, "explorer", "https://odyssey.storyscan.xyz", "URL of the blockchain explorer") + cmd.Flags().Int64Var(&cfg.ChainID, "chain-id", 1516, "Chain ID to use for the transaction") } func bindValidatorCreateFlags(cmd *cobra.Command, cfg *createValidatorConfig) { bindValidatorBaseFlags(cmd, &cfg.baseConfig) bindValidatorKeyFlags(cmd, &cfg.ValidatorKeyFile) - cmd.Flags().StringVar(&cfg.StakeAmount, "stake", "", "Amount for the validator to self-delegate in wei") + cmd.Flags().StringVar(&cfg.StakeAmount, "stake", "1024000000000000000000", "Amount for the validator to self-delegate in wei") + cmd.Flags().Uint32Var(&cfg.CommissionRate, "commission-rate", 1000, "The validator commission rate in bips (e.g. 1000 for 10%)") + cmd.Flags().Uint32Var(&cfg.MaxCommissionRate, "max-commission-rate", 5000, "The maximum validator commission rate in bips, e.g. 5000 for 50%") + cmd.Flags().Uint32Var(&cfg.MaxCommissionChangeRate, "max-commission-change-rate", 1000, "The maximum validator commission change rate in bips, e.g. 100 for 1%") + cmd.Flags().BoolVar(&cfg.Unlocked, "unlocked", true, "Whether to support unlocked token staking (true for unlocked staking, false for locked staking)") + cmd.Flags().StringVar(&cfg.Moniker, "moniker", "", "Custom moniker name for this node") } func bindAddOperatorFlags(cmd *cobra.Command, cfg *operatorConfig) { @@ -80,28 +87,32 @@ func bindSetWithdrawalAddressFlags(cmd *cobra.Command, cfg *withdrawalConfig) { func bindValidatorStakeFlags(cmd *cobra.Command, cfg *stakeConfig) { bindValidatorBaseFlags(cmd, &cfg.baseConfig) - cmd.Flags().StringVar(&cfg.ValidatorPubKey, "validator-pubkey", "", "Validator's base64-encoded compressed 33-byte secp256k1 public key") - cmd.Flags().StringVar(&cfg.StakeAmount, "stake", "", "Amount to stake on behalf of the delegator in wei") + cmd.Flags().StringVar(&cfg.ValidatorPubKey, "validator-pubkey", "", "Validator's hex-encoded compressed 33-byte secp256k1 public key") + cmd.Flags().StringVar(&cfg.StakeAmount, "stake", "", "Amount for the validator to self-delegate in wei") + cmd.Flags().Var(&cfg.StakePeriod, "staking-period", `Staking period (options: "flexible", "short", "medium", "long")`) } func bindValidatorStakeOnBehalfFlags(cmd *cobra.Command, cfg *stakeConfig) { bindValidatorBaseFlags(cmd, &cfg.baseConfig) - cmd.Flags().StringVar(&cfg.ValidatorPubKey, "validator-pubkey", "", "Validator's base64-encoded compressed 33-byte secp256k1 public key") - cmd.Flags().StringVar(&cfg.DelegatorPubKey, "delegator-pubkey", "", "Delegator's base64-encoded compressed 33-byte secp256k1 public key") - cmd.Flags().StringVar(&cfg.StakeAmount, "stake", "", "Amount to stake on behalf of the delegator in wei") + cmd.Flags().StringVar(&cfg.ValidatorPubKey, "validator-pubkey", "", "Validator's hex-encoded compressed 33-byte secp256k1 public key") + cmd.Flags().StringVar(&cfg.DelegatorPubKey, "delegator-pubkey", "", "Delegator's hex-encoded compressed 33-byte secp256k1 public key") + cmd.Flags().StringVar(&cfg.StakeAmount, "stake", "", "Amount for the validator to self-delegate in wei") + cmd.Flags().Var(&cfg.StakePeriod, "staking-period", `Staking period (options: "flexible", "short", "medium", "long")`) } -func bindValidatorUnstakeFlags(cmd *cobra.Command, cfg *stakeConfig) { +func bindValidatorUnstakeFlags(cmd *cobra.Command, cfg *unstakeConfig) { bindValidatorBaseFlags(cmd, &cfg.baseConfig) - cmd.Flags().StringVar(&cfg.ValidatorPubKey, "validator-pubkey", "", "Validator's base64-encoded compressed 33-byte secp256k1 public key") - cmd.Flags().StringVar(&cfg.StakeAmount, "unstake", "", "Amount to unstake on behalf of the delegator in wei") + cmd.Flags().StringVar(&cfg.ValidatorPubKey, "validator-pubkey", "", "Validator's hex-encoded compressed 33-byte secp256k1 public key") + cmd.Flags().StringVar(&cfg.StakeAmount, "stake", "", "Amount for the validator to self-delegate in wei") + cmd.Flags().Uint32Var(&cfg.DelegationID, "delegation-id", 0, "The delegation ID (0 for flexible staking)") } -func bindValidatorUnstakeOnBehalfFlags(cmd *cobra.Command, cfg *stakeConfig) { +func bindValidatorUnstakeOnBehalfFlags(cmd *cobra.Command, cfg *unstakeConfig) { bindValidatorBaseFlags(cmd, &cfg.baseConfig) - cmd.Flags().StringVar(&cfg.ValidatorPubKey, "validator-pubkey", "", "Validator's base64-encoded compressed 33-byte secp256k1 public key") - cmd.Flags().StringVar(&cfg.DelegatorPubKey, "delegator-pubkey", "", "Delegator's base64-encoded compressed 33-byte secp256k1 public key") - cmd.Flags().StringVar(&cfg.StakeAmount, "unstake", "", "Amount to unstake on behalf of the delegator in wei") + cmd.Flags().StringVar(&cfg.ValidatorPubKey, "validator-pubkey", "", "Validator's hex-encoded compressed 33-byte secp256k1 public key") + cmd.Flags().StringVar(&cfg.DelegatorPubKey, "delegator-pubkey", "", "Delegator's hex-encoded compressed 33-byte secp256k1 public key") + cmd.Flags().StringVar(&cfg.StakeAmount, "stake", "", "Amount for the validator to self-delegate in wei") + cmd.Flags().Uint32Var(&cfg.DelegationID, "delegation-id", 0, "The delegation ID (0 for flexible staking)") } func bindValidatorKeyExportFlags(cmd *cobra.Command, cfg *exportKeyConfig) { @@ -134,16 +145,16 @@ func bindRollbackFlags(cmd *cobra.Command, cfg *config.Config) { func bindValidatorUnjailFlags(cmd *cobra.Command, cfg *unjailConfig) { bindValidatorBaseFlags(cmd, &cfg.baseConfig) - cmd.Flags().StringVar(&cfg.ValidatorPubKey, "validator-pubkey", "", "Validator's base64-encoded compressed 33-byte secp256k1 public key") + cmd.Flags().StringVar(&cfg.ValidatorPubKey, "validator-pubkey", "", "Validator's hex-encoded compressed 33-byte secp256k1 public key") } // Flag Validation -func validateFlags(flags map[string]string) error { +func validateFlags(cmd *cobra.Command, flags []string) error { var missingFlags []string - for flag, value := range flags { - if value == "" { + for _, flag := range flags { + if !cmd.Flags().Changed(flag) { missingFlags = append(missingFlags, flag) } } @@ -155,80 +166,123 @@ func validateFlags(flags map[string]string) error { return nil } -func validateValidatorCreateFlags(cfg createValidatorConfig) error { - return validateFlags(map[string]string{ - "rpc": cfg.RPC, - "keyfile": cfg.ValidatorKeyFile, - "stake": cfg.StakeAmount, - }) +func validateValidatorCreateFlags(ctx context.Context, cmd *cobra.Command, cfg *createValidatorConfig) error { + if err := validateFlags(cmd, []string{"moniker"}); err != nil { + return errors.Wrap(err, "failed to validate create flags") + } + + if err := validateMinStakeAmount(ctx, &cfg.stakeConfig); err != nil { + return err + } + + return validateCommissionRate(ctx, cfg) } -func validateOperatorFlags(cfg operatorConfig) error { - return validateFlags(map[string]string{ - "rpc": cfg.RPC, - "operator": cfg.Operator, +func validateOperatorFlags(cmd *cobra.Command) error { + return validateFlags(cmd, []string{ + "operator", }) } -func validateWithdrawalFlags(cfg withdrawalConfig) error { - return validateFlags(map[string]string{ - "rpc": cfg.RPC, - "withdrawal-address": cfg.WithdrawalAddress, +func validateWithdrawalFlags(cmd *cobra.Command) error { + return validateFlags(cmd, []string{ + "withdrawal-address", }) } -func validateValidatorStakeFlags(cfg stakeConfig) error { - return validateFlags(map[string]string{ - "rpc": cfg.RPC, - "validator-pubkey": cfg.ValidatorPubKey, - "stake": cfg.StakeAmount, - }) +func validateValidatorStakeFlags(ctx context.Context, cmd *cobra.Command, cfg *stakeConfig) error { + if err := validateFlags(cmd, []string{"validator-pubkey", "stake"}); err != nil { + return errors.Wrap(err, "failed to validate stake flags") + } + + return validateMinStakeAmount(ctx, cfg) } -func validateValidatorStakeOnBehalfFlags(cfg stakeConfig) error { - return validateFlags(map[string]string{ - "rpc": cfg.RPC, - "validator-pubkey": cfg.ValidatorPubKey, - "delegator-pubkey": cfg.DelegatorPubKey, - "stake": cfg.StakeAmount, - }) +func validateValidatorStakeOnBehalfFlags(ctx context.Context, cmd *cobra.Command, cfg *stakeConfig) error { + if err := validateFlags(cmd, []string{"validator-pubkey", "delegator-pubkey", "stake"}); err != nil { + return errors.Wrap(err, "failed to validate stake-on-behalf flags") + } + + return validateMinStakeAmount(ctx, cfg) } -func validateValidatorUnstakeOnBehalfFlags(cfg stakeConfig) error { - return validateFlags(map[string]string{ - "rpc": cfg.RPC, - "validator-pubkey": cfg.ValidatorPubKey, - "delegator-pubkey": cfg.DelegatorPubKey, - "unstake": cfg.StakeAmount, - }) +func validateValidatorUnstakeFlags(ctx context.Context, cmd *cobra.Command, cfg *unstakeConfig) error { + if err := validateFlags(cmd, []string{"validator-pubkey", "stake"}); err != nil { + return errors.Wrap(err, "failed to validate unstake flags") + } + + return validateMinUnstakeAmount(ctx, cfg) } -func validateKeyConvertFlags(cfg keyConfig) error { - flagMap := map[string]string{ - "validator-key-file": cfg.ValidatorKeyFile, - "private-key-file": cfg.PrivateKeyFile, - "pubkey-hex": cfg.PubKeyHex, - "pubkey-base64": cfg.PubKeyBase64, - "pubkey-hex-uncompressed": cfg.PubKeyHexUncompressed, +func validateValidatorUnstakeOnBehalfFlags(ctx context.Context, cmd *cobra.Command, cfg *unstakeConfig) error { + if err := validateFlags(cmd, []string{"validator-pubkey", "delegator-pubkey", "stake"}); err != nil { + return errors.Wrap(err, "failed to validate unstake-on-behalf flags") } - for _, value := range flagMap { - if value != "" { - return nil - } + return validateMinUnstakeAmount(ctx, cfg) +} + +func validateKeyConvertFlags(cmd *cobra.Command) error { + return validateFlags(cmd, []string{}) +} + +func validateValidatorUnjailFlags(cmd *cobra.Command) error { + return validateFlags(cmd, []string{"validator-pubkey"}) +} + +func validateMinStakeAmount(ctx context.Context, cfg *stakeConfig) error { + stakeAmount, ok := new(big.Int).SetString(cfg.StakeAmount, 10) + if !ok { + return fmt.Errorf("invalid stake amount: %s", cfg.StakeAmount) + } + + minStakeAmount, err := getUint256(ctx, &cfg.baseConfig, "minStakeAmount") + if err != nil { + return errors.Wrap(err, "failed to retrieve minimum stake amount") } - flagNames := make([]string, 0, len(flagMap)) - for flag := range flagMap { - flagNames = append(flagNames, "--"+flag) + if stakeAmount.Cmp(minStakeAmount) < 0 { + return fmt.Errorf("stake amount is less than the minimum required: %s", minStakeAmount.String()) } - return fmt.Errorf("at least one of %s must be provided", strings.Join(flagNames, ", ")) + return nil } -func validateValidatorUnjailFlags(cfg unjailConfig) error { - return validateFlags(map[string]string{ - "rpc": cfg.RPC, - "validator-pubkey": cfg.ValidatorPubKey, - }) +func validateMinUnstakeAmount(ctx context.Context, cfg *unstakeConfig) error { + unstakeAmount, ok := new(big.Int).SetString(cfg.StakeAmount, 10) + if !ok { + return fmt.Errorf("invalid unstake amount: %s", cfg.StakeAmount) + } + + minUnstakeAmount, err := getUint256(ctx, &cfg.baseConfig, "minUnstakeAmount") + if err != nil { + return errors.Wrap(err, "failed to retrieve minimum unstake amount") + } + + if unstakeAmount.Cmp(minUnstakeAmount) < 0 { + return fmt.Errorf("unstake amount is less than the minimum required: %s", minUnstakeAmount.String()) + } + + return nil +} + +func validateCommissionRate(ctx context.Context, cfg *createValidatorConfig) error { + commissionRate := new(big.Int).SetUint64(uint64(cfg.CommissionRate)) + + minCommissionRate, err := getUint256(ctx, &cfg.baseConfig, "minCommissionRate") + if err != nil { + return errors.Wrap(err, "failed to retrieve minimum commission rate") + } + + if commissionRate.Cmp(minCommissionRate) < 0 { + return fmt.Errorf("commission rate is less than the minimum required: %s", minCommissionRate.String()) + } + + maxCommissionRate := new(big.Int).SetUint64(uint64(cfg.MaxCommissionRate)) + + if commissionRate.Cmp(maxCommissionRate) > 0 { + return fmt.Errorf("commission rate exceeds the maximum allowed: %s", maxCommissionRate.String()) + } + + return nil } diff --git a/client/cmd/keys.go b/client/cmd/keys.go index ac0d1225..46eaa9c7 100644 --- a/client/cmd/keys.go +++ b/client/cmd/keys.go @@ -40,7 +40,7 @@ func newKeyConvertCmd() *cobra.Command { Short: "Convert between various key formats", Args: cobra.NoArgs, RunE: runValidatorCommand( - func() error { return validateKeyConvertFlags(cfg) }, + validateKeyConvertFlags, func(ctx context.Context) error { return convertKey(ctx, cfg) }, ), } diff --git a/client/cmd/transaction.go b/client/cmd/transaction.go index 7d16f73b..f3411c91 100644 --- a/client/cmd/transaction.go +++ b/client/cmd/transaction.go @@ -16,6 +16,31 @@ import ( "github.com/piplabs/story/lib/errors" ) +func getUint256(ctx context.Context, cfg *baseConfig, getter string) (*big.Int, error) { + result, err := prepareAndReadContract(ctx, cfg, getter) + if err != nil { + return nil, errors.Wrap(err, "failed to fetch uint256") + } + + value := new(big.Int).SetBytes(result) + + return value, nil +} + +func prepareAndReadContract(ctx context.Context, cfg *baseConfig, methodName string) ([]byte, error) { + selector := crypto.Keccak256([]byte(methodName + "()"))[:4] + return readContract(ctx, *cfg, cfg.ContractAddr, selector) +} + +func prepareAndExecuteTransaction(ctx context.Context, cfg *baseConfig, methodName string, value *big.Int, args ...any) (*types.Receipt, error) { + data, err := cfg.ABI.Pack(methodName, args...) + if err != nil { + return nil, errors.Wrap(err, "failed to pack data") + } + + return sendTransaction(ctx, *cfg, cfg.ContractAddr, value, data) +} + func readContract(ctx context.Context, cfg baseConfig, contractAddress common.Address, data []byte) ([]byte, error) { client, err := ethclient.Dial(cfg.RPC) if err != nil { @@ -35,38 +60,38 @@ func readContract(ctx context.Context, cfg baseConfig, contractAddress common.Ad return result, nil } -func prepareAndSendTransaction(ctx context.Context, cfg baseConfig, contractAddress common.Address, value *big.Int, data []byte) error { +func sendTransaction(ctx context.Context, cfg baseConfig, contractAddress common.Address, value *big.Int, data []byte) (*types.Receipt, error) { client, err := ethclient.Dial(cfg.RPC) if err != nil { - return errors.Wrap(err, "failed to connect to Ethereum client") + return nil, errors.Wrap(err, "failed to connect to Ethereum client") } evmPrivKey, err := crypto.HexToECDSA(cfg.PrivateKey) if err != nil { - return errors.Wrap(err, "invalid EVM private key") + return nil, errors.Wrap(err, "invalid EVM private key") } publicKey, ok := evmPrivKey.Public().(*ecdsa.PublicKey) if !ok { - return errors.New("failed to assert type to *ecdsa.PublicKey") + return nil, errors.New("failed to assert type to *ecdsa.PublicKey") } fromAddress := crypto.PubkeyToAddress(*publicKey) nonce, err := client.PendingNonceAt(ctx, fromAddress) if err != nil { - return errors.Wrap(err, "failed to get nonce") + return nil, errors.Wrap(err, "failed to get nonce") } gasPrice, err := client.SuggestGasPrice(ctx) if err != nil { - return errors.Wrap(err, "failed to suggest gas price") + return nil, errors.Wrap(err, "failed to suggest gas price") } gasPrice.Mul(gasPrice, big.NewInt(120)).Div(gasPrice, big.NewInt(100)) gasLimit, err := estimateGas(ctx, client, fromAddress, contractAddress, gasPrice, value, data) if err != nil { - return err + return nil, err } gasTipCap := gasPrice @@ -77,11 +102,11 @@ func prepareAndSendTransaction(ctx context.Context, cfg baseConfig, contractAddr balance, err := client.BalanceAt(ctx, fromAddress, nil) if err != nil { - return errors.Wrap(err, "failed to fetch balance") + return nil, errors.Wrap(err, "failed to fetch balance") } if balance.Cmp(totalTxCost) < 0 { - return errors.New("insufficient funds for gas * price + value", "balance", balance.String(), "totalTxCost", totalTxCost.String()) + return nil, errors.New("insufficient funds for gas * price + value", "balance", balance.String(), "totalTxCost", totalTxCost.String()) } tx := types.NewTx(&types.DynamicFeeTx{ @@ -97,7 +122,7 @@ func prepareAndSendTransaction(ctx context.Context, cfg baseConfig, contractAddr signedTx, err := types.SignTx(tx, types.LatestSignerForChainID(big.NewInt(cfg.ChainID)), evmPrivKey) if err != nil { - return errors.Wrap(err, "failed to sign transaction") + return nil, errors.Wrap(err, "failed to sign transaction") } txHash := signedTx.Hash().Hex() @@ -105,23 +130,23 @@ func prepareAndSendTransaction(ctx context.Context, cfg baseConfig, contractAddr fmt.Printf("Explorer URL: %s/tx/%s\n", cfg.Explorer, txHash) if err = client.SendTransaction(ctx, signedTx); err != nil { - return errors.Wrap(err, "failed to send transaction") + return nil, errors.Wrap(err, "failed to send transaction") } fmt.Println("Transaction sent, waiting for confirmation...") receipt, err := bind.WaitMined(ctx, client, signedTx) if err != nil { - return errors.Wrap(err, "transaction failed") + return nil, errors.Wrap(err, "transaction failed") } if receipt.Status == types.ReceiptStatusFailed { - return errors.New("transaction failed", "status", receipt.Status) + return nil, errors.New("transaction failed", "status", receipt.Status) } fmt.Println("Transaction confirmed successfully!") - return nil + return receipt, nil } func estimateGas(ctx context.Context, client *ethclient.Client, fromAddress, contractAddress common.Address, gasPrice, value *big.Int, data []byte) (uint64, error) { diff --git a/client/cmd/validator.go b/client/cmd/validator.go index 263ed153..319170f7 100644 --- a/client/cmd/validator.go +++ b/client/cmd/validator.go @@ -2,16 +2,15 @@ package cmd import ( "context" - "encoding/base64" "encoding/hex" "fmt" "math/big" "os" "strings" - "github.com/decred/dcrd/dcrec/secp256k1" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/crypto" "github.com/joho/godotenv" "github.com/spf13/cobra" @@ -22,29 +21,71 @@ import ( _ "embed" ) -type ContractType int +type StakingPeriod int const ( - STAKING ContractType = iota - SLASHING + FLEXIBLE StakingPeriod = iota + SHORT + MEDIUM + LONG ) -type ContractInfo struct { - AddressHex string - ABI []byte +func (sp *StakingPeriod) String() string { + switch *sp { + case FLEXIBLE: + return "flexible" + case SHORT: + return "short" + case MEDIUM: + return "medium" + case LONG: + return "long" + default: + return "unknown" + } +} + +func (sp *StakingPeriod) Set(v string) error { + switch strings.ToLower(v) { + case "flexible": + *sp = FLEXIBLE + case "short": + *sp = SHORT + case "medium": + *sp = MEDIUM + case "long": + *sp = LONG + default: + return errors.New(`staking period must be one of "flexible", "short", "medium", or "long"`) + } + + return nil +} + +func (*StakingPeriod) Type() string { + return "stakingPeriod" } //go:embed abi/IPTokenStaking.abi.json var ipTokenStakingABI []byte -//go:embed abi/IPTokenSlashing.abi.json -var ipTokenSlashingABI []byte - type baseConfig struct { - RPC string - PrivateKey string - Explorer string - ChainID int64 + RPC string + PrivateKey string + Explorer string + ChainID int64 + ABI *abi.ABI + ContractAddr common.Address +} + +type createValidatorConfig struct { + stakeConfig + ValidatorKeyFile string + Moniker string + CommissionRate uint32 + MaxCommissionRate uint32 + MaxCommissionChangeRate uint32 + Unlocked bool } type stakeConfig struct { @@ -52,6 +93,12 @@ type stakeConfig struct { DelegatorPubKey string ValidatorPubKey string StakeAmount string + StakePeriod StakingPeriod +} + +type unstakeConfig struct { + stakeConfig + DelegationID uint32 } type unjailConfig struct { @@ -69,29 +116,12 @@ type withdrawalConfig struct { WithdrawalAddress string } -type createValidatorConfig struct { - baseConfig - ValidatorKeyFile string - StakeAmount string -} - type exportKeyConfig struct { ValidatorKeyFile string EvmKeyFile string ExportEVMKey bool } -var contracts = map[ContractType]ContractInfo{ - STAKING: { - AddressHex: predeploys.IPTokenStaking, - ABI: ipTokenStakingABI, - }, - SLASHING: { - AddressHex: predeploys.IPTokenSlashing, - ABI: ipTokenSlashingABI, - }, -} - func loadEnv() { err := godotenv.Load() if err != nil { @@ -130,10 +160,13 @@ func newValidatorCreateCmd() *cobra.Command { Short: "Create a new validator", Args: cobra.NoArgs, PreRunE: func(_ *cobra.Command, _ []string) error { - return loadAndValidatePrivateKey(&cfg.baseConfig) + return initializeBaseConfig(&cfg.baseConfig) }, RunE: runValidatorCommand( - func() error { return validateValidatorCreateFlags(cfg) }, + func(cmd *cobra.Command) error { + ctx := cmd.Context() + return validateValidatorCreateFlags(ctx, cmd, &cfg) + }, func(ctx context.Context) error { return createValidator(ctx, cfg) }, ), } @@ -151,10 +184,10 @@ func newValidatorAddOperatorCmd() *cobra.Command { Short: "Add a new operator to your delegator", Args: cobra.NoArgs, PreRunE: func(_ *cobra.Command, _ []string) error { - return loadAndValidatePrivateKey(&cfg.baseConfig) + return initializeBaseConfig(&cfg.baseConfig) }, RunE: runValidatorCommand( - func() error { return validateOperatorFlags(cfg) }, + validateOperatorFlags, func(ctx context.Context) error { return addOperator(ctx, cfg) }, ), } @@ -172,10 +205,10 @@ func newValidatorRemoveOperatorCmd() *cobra.Command { Short: "Removes an existing operator from your delegator", Args: cobra.NoArgs, PreRunE: func(_ *cobra.Command, _ []string) error { - return loadAndValidatePrivateKey(&cfg.baseConfig) + return initializeBaseConfig(&cfg.baseConfig) }, RunE: runValidatorCommand( - func() error { return validateOperatorFlags(cfg) }, + validateOperatorFlags, func(ctx context.Context) error { return removeOperator(ctx, cfg) }, ), } @@ -193,10 +226,10 @@ func newValidatorSetWithdrawalAddressCmd() *cobra.Command { Short: "Updates the withdrawal address that receives stake and reward withdrawals", Args: cobra.NoArgs, PreRunE: func(_ *cobra.Command, _ []string) error { - return loadAndValidatePrivateKey(&cfg.baseConfig) + return initializeBaseConfig(&cfg.baseConfig) }, RunE: runValidatorCommand( - func() error { return validateWithdrawalFlags(cfg) }, + validateWithdrawalFlags, func(ctx context.Context) error { return setWithdrawalAddress(ctx, cfg) }, ), } @@ -214,11 +247,12 @@ func newValidatorStakeCmd() *cobra.Command { Short: "Stake tokens as the delegator", Args: cobra.NoArgs, PreRunE: func(_ *cobra.Command, _ []string) error { - return loadAndValidatePrivateKey(&cfg.baseConfig) + return initializeBaseConfig(&cfg.baseConfig) }, RunE: runValidatorCommand( - func() error { - return validateValidatorStakeFlags(cfg) + func(cmd *cobra.Command) error { + ctx := cmd.Context() + return validateValidatorStakeFlags(ctx, cmd, &cfg) }, func(ctx context.Context) error { return stake(ctx, cfg) }, ), @@ -237,11 +271,12 @@ func newValidatorStakeOnBehalfCmd() *cobra.Command { Short: "Stake tokens on behalf of a delegator", Args: cobra.NoArgs, PreRunE: func(_ *cobra.Command, _ []string) error { - return loadAndValidatePrivateKey(&cfg.baseConfig) + return initializeBaseConfig(&cfg.baseConfig) }, RunE: runValidatorCommand( - func() error { - return validateValidatorStakeOnBehalfFlags(cfg) + func(cmd *cobra.Command) error { + ctx := cmd.Context() + return validateValidatorStakeOnBehalfFlags(ctx, cmd, &cfg) }, func(ctx context.Context) error { return stakeOnBehalf(ctx, cfg) }, ), @@ -253,17 +288,20 @@ func newValidatorStakeOnBehalfCmd() *cobra.Command { } func newValidatorUnstakeCmd() *cobra.Command { - var cfg stakeConfig + var cfg unstakeConfig cmd := &cobra.Command{ Use: "unstake", Short: "Unstake tokens as the delegator", Args: cobra.NoArgs, PreRunE: func(_ *cobra.Command, _ []string) error { - return loadAndValidatePrivateKey(&cfg.baseConfig) + return initializeBaseConfig(&cfg.baseConfig) }, RunE: runValidatorCommand( - func() error { return validateValidatorStakeFlags(cfg) }, + func(cmd *cobra.Command) error { + ctx := cmd.Context() + return validateValidatorUnstakeFlags(ctx, cmd, &cfg) + }, func(ctx context.Context) error { return unstake(ctx, cfg) }, ), } @@ -274,17 +312,20 @@ func newValidatorUnstakeCmd() *cobra.Command { } func newValidatorUnstakeOnBehalfCmd() *cobra.Command { - var cfg stakeConfig + var cfg unstakeConfig cmd := &cobra.Command{ Use: "unstake-on-behalf", Short: "Unstake tokens on behalf of a delegator", Args: cobra.NoArgs, PreRunE: func(_ *cobra.Command, _ []string) error { - return loadAndValidatePrivateKey(&cfg.baseConfig) + return initializeBaseConfig(&cfg.baseConfig) }, RunE: runValidatorCommand( - func() error { return validateValidatorUnstakeOnBehalfFlags(cfg) }, + func(cmd *cobra.Command) error { + ctx := cmd.Context() + return validateValidatorUnstakeOnBehalfFlags(ctx, cmd, &cfg) + }, func(ctx context.Context) error { return unstakeOnBehalf(ctx, cfg) }, ), } @@ -304,7 +345,7 @@ func newValidatorKeyExportCmd() *cobra.Command { return nil }, RunE: runValidatorCommand( - func() error { return nil }, + func(_ *cobra.Command) error { return nil }, func(ctx context.Context) error { return exportKey(ctx, cfg) }, ), } @@ -322,10 +363,10 @@ func newValidatorUnjailCmd() *cobra.Command { Short: "Unjail the validator", Args: cobra.NoArgs, PreRunE: func(_ *cobra.Command, _ []string) error { - return loadAndValidatePrivateKey(&cfg.baseConfig) + return initializeBaseConfig(&cfg.baseConfig) }, RunE: runValidatorCommand( - func() error { return validateValidatorUnjailFlags(cfg) }, + validateValidatorUnjailFlags, func(ctx context.Context) error { return unjail(ctx, cfg) }, ), } @@ -336,11 +377,11 @@ func newValidatorUnjailCmd() *cobra.Command { } func runValidatorCommand( - validate func() error, + validate func(cmd *cobra.Command) error, execute func(ctx context.Context) error, ) func(cmd *cobra.Command, _ []string) error { return func(cmd *cobra.Command, _ []string) error { - if err := validate(); err != nil { + if err := validate(cmd); err != nil { _ = cmd.Help() return err } @@ -398,7 +439,19 @@ func createValidator(ctx context.Context, cfg createValidatorConfig) error { return errors.New("invalid stake amount", "amount", cfg.StakeAmount) } - err = prepareAndExecuteTransaction(ctx, STAKING, &cfg.baseConfig, "createValidatorOnBehalf", stakeAmount, uncompressedPubKeyBytes) + _, err = prepareAndExecuteTransaction( + ctx, + &cfg.baseConfig, + "createValidatorOnBehalf", + stakeAmount, + uncompressedPubKeyBytes, + cfg.Moniker, + cfg.CommissionRate, + cfg.MaxCommissionRate, + cfg.MaxCommissionChangeRate, + cfg.Unlocked, + []byte{}, + ) if err != nil { return err } @@ -416,7 +469,13 @@ func setWithdrawalAddress(ctx context.Context, cfg withdrawalConfig) error { withdrawalAddress := common.HexToAddress(cfg.WithdrawalAddress) - err = prepareAndExecuteTransaction(ctx, STAKING, &cfg.baseConfig, "setWithdrawalAddress", big.NewInt(0), uncompressedPubKey, withdrawalAddress) + fee, err := getUint256(ctx, &cfg.baseConfig, "fee") + if err != nil { + return err + } + fmt.Printf("Fee for withdrawing: %s wei\n", fee.String()) + + _, err = prepareAndExecuteTransaction(ctx, &cfg.baseConfig, "setWithdrawalAddress", fee, uncompressedPubKey, withdrawalAddress) if err != nil { return err } @@ -434,7 +493,14 @@ func addOperator(ctx context.Context, cfg operatorConfig) error { operatorAddress := common.HexToAddress(cfg.Operator) - err = prepareAndExecuteTransaction(ctx, STAKING, &cfg.baseConfig, "addOperator", big.NewInt(0), uncompressedPubKey, operatorAddress) + fee, err := getUint256(ctx, &cfg.baseConfig, "fee") + if err != nil { + return err + } + + fmt.Printf("Fee for adding operator: %s wei\n", fee.String()) + + _, err = prepareAndExecuteTransaction(ctx, &cfg.baseConfig, "addOperator", fee, uncompressedPubKey, operatorAddress) if err != nil { return err } @@ -452,7 +518,7 @@ func removeOperator(ctx context.Context, cfg operatorConfig) error { operatorAddress := common.HexToAddress(cfg.Operator) - err = prepareAndExecuteTransaction(ctx, STAKING, &cfg.baseConfig, "removeOperator", big.NewInt(0), uncompressedPubKey, operatorAddress) + _, err = prepareAndExecuteTransaction(ctx, &cfg.baseConfig, "removeOperator", big.NewInt(0), uncompressedPubKey, operatorAddress) if err != nil { return err } @@ -463,14 +529,18 @@ func removeOperator(ctx context.Context, cfg operatorConfig) error { } func stake(ctx context.Context, cfg stakeConfig) error { - uncompressedPubKey, err := uncompressPrivateKey(cfg.PrivateKey) + uncompressedDelegatorPubKeyBytes, err := uncompressPrivateKey(cfg.PrivateKey) if err != nil { return err } - validatorPubKeyBytes, err := base64.StdEncoding.DecodeString(cfg.ValidatorPubKey) + validatorPubKeyBytes, err := hex.DecodeString(cfg.ValidatorPubKey) if err != nil { - return errors.Wrap(err, "failed to decode base64 pub key") + return errors.Wrap(err, "failed to decode hex-encoded pub key") + } + uncompressedValidatorPubKeyBytes, err := cmpPubKeyToUncmpPubKey(validatorPubKeyBytes) + if err != nil { + return errors.Wrap(err, "failed to uncompress validator public key") } stakeAmount, ok := new(big.Int).SetString(cfg.StakeAmount, 10) @@ -478,20 +548,36 @@ func stake(ctx context.Context, cfg stakeConfig) error { return errors.New("invalid stake amount", "amount", cfg.StakeAmount) } - err = prepareAndExecuteTransaction(ctx, STAKING, &cfg.baseConfig, "stakeOnBehalf", stakeAmount, uncompressedPubKey, validatorPubKeyBytes) + receipt, err := prepareAndExecuteTransaction( + ctx, + &cfg.baseConfig, + "stakeOnBehalf", + stakeAmount, + uncompressedDelegatorPubKeyBytes, + uncompressedValidatorPubKeyBytes, + uint8(cfg.StakePeriod), + []byte{}, + ) if err != nil { return err } - fmt.Println("Tokens staked successfully!") + fmt.Println("Tokens staked successfully! Extracting delegation ID...") + + delegationID, err := extractDelegationIDFromStake(&cfg, receipt) + if err != nil { + return err + } + + fmt.Printf("Delegation ID: %s\n", delegationID.String()) return nil } func stakeOnBehalf(ctx context.Context, cfg stakeConfig) error { - delegatorPubKeyBytes, err := base64.StdEncoding.DecodeString(cfg.DelegatorPubKey) + delegatorPubKeyBytes, err := hex.DecodeString(cfg.DelegatorPubKey) if err != nil { - return errors.Wrap(err, "failed to decode base64 delegator public key") + return errors.Wrap(err, "failed to decode hex-encoded delegator public key") } uncompressedDelegatorPubKeyBytes, err := cmpPubKeyToUncmpPubKey(delegatorPubKeyBytes) @@ -499,9 +585,13 @@ func stakeOnBehalf(ctx context.Context, cfg stakeConfig) error { return errors.Wrap(err, "failed to uncompress delegator public key") } - validatorPubKeyBytes, err := base64.StdEncoding.DecodeString(cfg.ValidatorPubKey) + validatorPubKeyBytes, err := hex.DecodeString(cfg.ValidatorPubKey) if err != nil { - return errors.Wrap(err, "failed to decode validator public key") + return errors.Wrap(err, "failed to decode hex-encoed validator public key") + } + uncompressedValidatorPubKeyBytes, err := cmpPubKeyToUncmpPubKey(validatorPubKeyBytes) + if err != nil { + return errors.Wrap(err, "failed to uncompress validator public key") } stakeAmount, ok := new(big.Int).SetString(cfg.StakeAmount, 10) @@ -509,33 +599,65 @@ func stakeOnBehalf(ctx context.Context, cfg stakeConfig) error { return errors.New("invalid stake amount", "amount", cfg.StakeAmount) } - err = prepareAndExecuteTransaction(ctx, STAKING, &cfg.baseConfig, "stakeOnBehalf", stakeAmount, uncompressedDelegatorPubKeyBytes, validatorPubKeyBytes) + receipt, err := prepareAndExecuteTransaction( + ctx, + &cfg.baseConfig, + "stakeOnBehalf", + stakeAmount, + uncompressedDelegatorPubKeyBytes, + uncompressedValidatorPubKeyBytes, + uint8(cfg.StakePeriod), + []byte{}, + ) if err != nil { return err } - fmt.Println("Tokens staked on behalf of delegator successfully!") + fmt.Println("Tokens staked on behalf of delegator successfully! Extracting delegation ID...") + + delegationID, err := extractDelegationIDFromStake(&cfg, receipt) + if err != nil { + return err + } + + fmt.Printf("Delegation ID: %s\n", delegationID.String()) return nil } -func unstake(ctx context.Context, cfg stakeConfig) error { - uncompressedPubKey, err := uncompressPrivateKey(cfg.PrivateKey) +func unstake(ctx context.Context, cfg unstakeConfig) error { + uncompressedDelegatorPubKeyBytes, err := uncompressPrivateKey(cfg.PrivateKey) if err != nil { return err } - validatorPubKeyBytes, err := base64.StdEncoding.DecodeString(cfg.ValidatorPubKey) + validatorPubKeyBytes, err := hex.DecodeString(cfg.ValidatorPubKey) if err != nil { - return errors.Wrap(err, "failed to decode base64 pub key") + return errors.Wrap(err, "failed to decode hex-encoded validator pub key") + } + uncompressedValidatorPubKeyBytes, err := cmpPubKeyToUncmpPubKey(validatorPubKeyBytes) + if err != nil { + return errors.Wrap(err, "failed to uncompress validator public key") } unstakeAmount, ok := new(big.Int).SetString(cfg.StakeAmount, 10) if !ok { - return errors.New("invalid unstake amount", "amount", cfg.StakeAmount) + return errors.New("invalid stake amount", "amount", cfg.StakeAmount) } - err = prepareAndExecuteTransaction(ctx, STAKING, &cfg.baseConfig, "unstake", big.NewInt(0), uncompressedPubKey, validatorPubKeyBytes, unstakeAmount) + delegationID := new(big.Int).SetUint64(uint64(cfg.DelegationID)) + + _, err = prepareAndExecuteTransaction( + ctx, + &cfg.baseConfig, + "unstake", + big.NewInt(0), + uncompressedDelegatorPubKeyBytes, + uncompressedValidatorPubKeyBytes, + delegationID, + unstakeAmount, + []byte{}, + ) if err != nil { return err } @@ -545,23 +667,39 @@ func unstake(ctx context.Context, cfg stakeConfig) error { return nil } -func unstakeOnBehalf(ctx context.Context, cfg stakeConfig) error { - delegatorPubKeyBytes, err := base64.StdEncoding.DecodeString(cfg.DelegatorPubKey) +func unstakeOnBehalf(ctx context.Context, cfg unstakeConfig) error { + uncompressedDelegatorPubKeyBytes, err := hex.DecodeString(cfg.DelegatorPubKey) if err != nil { - return errors.Wrap(err, "failed to decode base64 delegator pub key") + return errors.Wrap(err, "failed to decode hex-encoded delegator pub key") } - validatorPubKeyBytes, err := base64.StdEncoding.DecodeString(cfg.ValidatorPubKey) + validatorPubKeyBytes, err := hex.DecodeString(cfg.ValidatorPubKey) if err != nil { - return errors.Wrap(err, "failed to decode base64 validator pub key") + return errors.Wrap(err, "failed to decode hex-encoded validator pub key") + } + uncompressedValidatorPubKeyBytes, err := cmpPubKeyToUncmpPubKey(validatorPubKeyBytes) + if err != nil { + return errors.Wrap(err, "failed to uncompress validator public key") } unstakeAmount, ok := new(big.Int).SetString(cfg.StakeAmount, 10) if !ok { - return errors.New("invalid unstake amount", "amount", cfg.StakeAmount) + return errors.New("invalid stake amount", "amount", cfg.StakeAmount) } - err = prepareAndExecuteTransaction(ctx, STAKING, &cfg.baseConfig, "unstakeOnBehalf", big.NewInt(0), delegatorPubKeyBytes, validatorPubKeyBytes, unstakeAmount) + delegationID := new(big.Int).SetUint64(uint64(cfg.DelegationID)) + + _, err = prepareAndExecuteTransaction( + ctx, + &cfg.baseConfig, + "unstakeOnBehalf", + big.NewInt(0), + uncompressedDelegatorPubKeyBytes, + uncompressedValidatorPubKeyBytes, + delegationID, + unstakeAmount, + []byte{}, + ) if err != nil { return err } @@ -572,34 +710,29 @@ func unstakeOnBehalf(ctx context.Context, cfg stakeConfig) error { } func unjail(ctx context.Context, cfg unjailConfig) error { - validatorPubKeyBytes, err := base64.StdEncoding.DecodeString(cfg.ValidatorPubKey) + validatorPubKeyBytes, err := hex.DecodeString(cfg.ValidatorPubKey) if err != nil { - return errors.Wrap(err, "failed to decode base64 validator public key") + return errors.Wrap(err, "failed to decode hex-encoded validator public key") } - - if len(validatorPubKeyBytes) != secp256k1.PubKeyBytesLenCompressed { - return fmt.Errorf("invalid compressed public key length: %d", len(validatorPubKeyBytes)) - } - - contractABI, err := abi.JSON(strings.NewReader(string(contracts[SLASHING].ABI))) + uncompressedValidatorPubKeyBytes, err := cmpPubKeyToUncmpPubKey(validatorPubKeyBytes) if err != nil { - return err + return errors.Wrap(err, "failed to uncompress validator public key") } - result, err := prepareAndReadContract(ctx, SLASHING, &cfg.baseConfig, "unjailFee") + result, err := prepareAndReadContract(ctx, &cfg.baseConfig, "fee") if err != nil { return err } var unjailFee *big.Int - err = contractABI.UnpackIntoInterface(&unjailFee, "unjailFee", result) + err = cfg.ABI.UnpackIntoInterface(&unjailFee, "fee", result) if err != nil { return errors.Wrap(err, "failed to unpack unjailFee") } fmt.Printf("Unjail fee: %s\n", unjailFee.String()) - err = prepareAndExecuteTransaction(ctx, SLASHING, &cfg.baseConfig, "unjailOnBehalf", unjailFee, validatorPubKeyBytes) + _, err = prepareAndExecuteTransaction(ctx, &cfg.baseConfig, "unjailOnBehalf", unjailFee, uncompressedValidatorPubKeyBytes) if err != nil { return err } @@ -609,38 +742,7 @@ func unjail(ctx context.Context, cfg unjailConfig) error { return nil } -func prepareAndReadContract(ctx context.Context, contractType ContractType, cfg *baseConfig, methodName string, args ...any) ([]byte, error) { - contractInfo := contracts[contractType] - contractAddress := common.HexToAddress(contractInfo.AddressHex) - contractABI, err := abi.JSON(strings.NewReader(string(contractInfo.ABI))) - if err != nil { - return nil, errors.Wrap(err, "failed to parse ABI") - } - - data, err := contractABI.Pack(methodName, args...) - if err != nil { - return nil, errors.Wrap(err, "failed to pack data") - } - - return readContract(ctx, *cfg, contractAddress, data) -} - -func prepareAndExecuteTransaction(ctx context.Context, contractType ContractType, cfg *baseConfig, methodName string, value *big.Int, args ...any) error { - contractInfo := contracts[contractType] - contractAddress := common.HexToAddress(contractInfo.AddressHex) - contractABI, err := abi.JSON(strings.NewReader(string(contractInfo.ABI))) - if err != nil { - return errors.Wrap(err, "failed to parse ABI") - } - data, err := contractABI.Pack(methodName, args...) - if err != nil { - return errors.Wrap(err, "failed to pack data") - } - - return prepareAndSendTransaction(ctx, *cfg, contractAddress, value, data) -} - -func loadAndValidatePrivateKey(cfg *baseConfig) error { +func initializeBaseConfig(cfg *baseConfig) error { if cfg.PrivateKey == "" { loadEnv() cfg.PrivateKey = os.Getenv("PRIVATE_KEY") @@ -654,5 +756,40 @@ func loadAndValidatePrivateKey(cfg *baseConfig) error { return errors.Wrap(err, "invalid EVM private key") } + contractABI, err := abi.JSON(strings.NewReader(string(ipTokenStakingABI))) + if err != nil { + return errors.Wrap(err, "failed to parse contract ABI") + } + + cfg.ABI = &contractABI + cfg.ContractAddr = common.HexToAddress(predeploys.IPTokenStaking) + return nil } + +func extractDelegationIDFromStake(cfg *stakeConfig, receipt *types.Receipt) (*big.Int, error) { + event := cfg.ABI.Events["Deposit"] + eventSignature := event.ID + for _, vLog := range receipt.Logs { + if vLog.Topics[0] == eventSignature { + eventData := struct { + DelegatorUncmpPubkey []byte + ValidatorUnCmpPubkey []byte + StakeAmount *big.Int + StakingPeriod *big.Int + DelegationId *big.Int //nolint:revive,stylecheck // Definition comes from ABI + OperatorAddress common.Address + Data []byte + }{} + + err := cfg.ABI.UnpackIntoInterface(&eventData, "Deposit", vLog.Data) + if err != nil { + return nil, fmt.Errorf("failed to unpack deposit event: %v", err) + } + + return eventData.DelegationId, nil + } + } + + return nil, errors.New("deposit event not found in transaction logs") +}