diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..455808f --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12.4 diff --git a/README.md b/README.md index 5ed1d70..4e2d9e7 100644 --- a/README.md +++ b/README.md @@ -97,12 +97,18 @@ hetucli --help ### Wallet management ```bash hetucli wallet create -hetucli wallet balance
--rpc +hetucli wallet balance +hetucli wallet list +hetucli wallet import --name ``` ### Transfer ```bash -hetucli tx send --private-key --to
--value --rpc +# Using wallet name (recommended) +hetucli tx send --sender --to
--value + +# Direct private key usage (not recommended, private key will be exposed in command history) +hetucli tx send-dk --private-key --to
--value ``` ### Configuration @@ -113,8 +119,9 @@ Set the contract address hetucli c set whetu_address
hetucli c set staking_address
hetucli c set subnet_address
-hetucli c set neuron_address
+hetucli c set dendron_address
hetucli c set amm_address
+hetucli c set weights_address
``` ### Main Process(New,Staking,Swap) @@ -123,21 +130,54 @@ hetucli c set amm_address
```bash hetucli w import --name test0 +hetucli wallet balance test0 hetucli whetu deposit --sender test0 --value 1000 -hetucli whetu balance-of test0 -hetucli subnet get-network-lock-cost -hetucli whetu approve --spender --value 100 --sender test0 -hetucli subnet update-network-params --network-min-lock 100000000000000000000 --network-rate-limit 1 --lock-reduction-interval 10000 --sender
-hetucli subnet regist --sender test0 --name "AI Vision" --description "Computre vision and image processing network" --token-name "VISION" --token-symbol "VIS" +hetucli wallet balance test0 +hetucli subnet regist --sender test0 --name "AI Vision" --description "Computer vision and image processing network" --token-name "VISION" --token-symbol "VIS" +# This will return the created subnet ID (netuid) after successful registration + +# Activate the subnet using the returned netuid +hetucli subnet activate-subnet --netuid --sender test0 + + +```bash +# Check subnet status +hetucli subnet subnet-info --netuid + +# Activate the subnet +hetucli subnet activate-subnet --netuid --sender + +# Verify activation status +hetucli subnet subnet-info --netuid ``` + #### Staking and Participation ```bash -hetucli whetu approve --spender --value 100 --sender test0 hetucli stake add-stake --sender test0 --amount 100 hetucli stake total-staked -hetucli stake allocate-to-subnet --netuid 1 --sender test0 --amount 50 -hetucli neuron regist --sender test0 --netuid 1 --is-validator-role --axon-endpoint "http://my-node.com" --axon-port 8080 --prometheus-endpoint "http://my-metrics.com" --prometheus-port 9090 + +# Register as validator dendron +hetucli dendron regist --sender test0 --netuid 1 --is-validator-role --axon-endpoint "http://my-node.com" --axon-port 8080 --prometheus-endpoint "http://my-metrics.com" --prometheus-port 9090 + +# Register as regular dendron (non-validator) +hetucli dendron regist --sender test0 --netuid 1 --no-validator-role --axon-endpoint "http://my-node.com" --axon-port 8080 --prometheus-endpoint "http://my-metrics.com" --prometheus-port 9090 + +# Check user role in subnet +hetucli dendron get-user-role --netuid 1 --user test0 + +# Query user's created subnets +hetucli subnet user-subnets --sender test0 + +# Update dendron service information (endpoints, ports) +hetucli dendron update-dendron-service --sender test0 --netuid 1 --axon-endpoint "http://new-node.com" --axon-port 8081 --prometheus-endpoint "http://new-metrics.com" --prometheus-port 9091 +``` + +#### Validator Scoring (Weights) + +```bash +# Quick score command for testing (direct input) +hetucli weights quick-score --sender test0 --netuid 1 --targets "0x1234...,0x5678..." --scores "500000,750000" ``` #### Trading Subnet Tokens @@ -153,25 +193,26 @@ hetucli amm swap-hetu-for-alpha --hetu-amount-in 100 --alpha-amount-out-min 0 ### WHETU ```bash -hetucli whetu deposit --contract
--sender
--value -hetucli whetu withdraw --contract
--sender
--amount -hetucli whetu balance-of --contract
--account
-hetucli whetu transfer --contract
--to
--value --sender
-hetucli whetu approve --contract
--spender
--value --sender
-hetucli whetu total-eth --contract
-hetucli whetu total-supply --contract
-hetucli whetu nonces --contract
--owner
+hetucli whetu deposit --sender --value +hetucli whetu withdraw --sender --amount +hetucli wallet balance +hetucli whetu transfer --to
--value --sender +hetucli whetu approve --spender
--value --sender +hetucli whetu total-eth +hetucli whetu total-supply +hetucli whetu nonces --owner
``` ### Staking, Subnet, Swap ```bash -hetucli stake total-staked --contract
-hetucli stake add-stake --contract
--sender
--amount -hetucli subnet next-netuid --contract
-hetucli subnet register-network --contract
--sender
--name ... --description ... --token-name ... --token-symbol ... -hetucli amm alpha-price --contract
-hetucli amm swap-hetu-for-alpha --contract
--sender
--hetu-amount-in --alpha-amount-out-min --to
+hetucli stake total-staked +hetucli stake add-stake --sender --amount +hetucli subnet next-netuid +hetucli subnet regist --sender --name --description --token-name --token-symbol +hetucli subnet get-subnet-hyperparams --netuid +hetucli amm alpha-price +hetucli amm swap-hetu-for-alpha --sender --hetu-amount-in --alpha-amount-out-min --to
``` ### Contract call @@ -192,4 +233,4 @@ poetry run pytest tests ## License -This project is licensed under the MIT License. +This project is licensed under the MIT License. \ No newline at end of file diff --git a/contracts/DendronManager.abi b/contracts/DendronManager.abi new file mode 100644 index 0000000..3554789 --- /dev/null +++ b/contracts/DendronManager.abi @@ -0,0 +1,711 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_subnetManager", + "type": "address" + }, + { + "internalType": "address", + "name": "_globalStaking", + "type": "address" + }, + { + "internalType": "address", + "name": "_initialOwner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "authorized", + "type": "bool" + } + ], + "name": "AuthorizedCallerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "NeuronDeregistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "stake", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "bool", + "name": "isValidator", + "type": "bool" + }, + { + "indexed": false, + "internalType": "bool", + "name": "requestedValidatorRole", + "type": "bool" + }, + { + "indexed": false, + "internalType": "string", + "name": "axonEndpoint", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "axonPort", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "string", + "name": "prometheusEndpoint", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "prometheusPort", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "NeuronRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "string", + "name": "axonEndpoint", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "axonPort", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "string", + "name": "prometheusEndpoint", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint32", + "name": "prometheusPort", + "type": "uint32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "ServiceUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldStake", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newStake", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "StakeAllocationChanged", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "deregisterNeuron", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getNeuronCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getNeuronInfo", + "outputs": [ + { + "components": [ + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "bool", + "name": "isValidator", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "stake", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "registrationBlock", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "lastUpdate", + "type": "uint256" + }, + { + "internalType": "string", + "name": "axonEndpoint", + "type": "string" + }, + { + "internalType": "uint32", + "name": "axonPort", + "type": "uint32" + }, + { + "internalType": "string", + "name": "prometheusEndpoint", + "type": "string" + }, + { + "internalType": "uint32", + "name": "prometheusPort", + "type": "uint32" + } + ], + "internalType": "struct SubnetTypes.NeuronInfo", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getNeuronList", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetNeuronCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetValidatorCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetValidators", + "outputs": [ + { + "internalType": "address[]", + "name": "", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "globalStaking", + "outputs": [ + { + "internalType": "contract IGlobalStaking", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "isNeuron", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "isValidator", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "neuronList", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "stakeAmount", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isValidatorRole", + "type": "bool" + }, + { + "internalType": "string", + "name": "axonEndpoint", + "type": "string" + }, + { + "internalType": "uint32", + "name": "axonPort", + "type": "uint32" + }, + { + "internalType": "string", + "name": "prometheusEndpoint", + "type": "string" + }, + { + "internalType": "uint32", + "name": "prometheusPort", + "type": "uint32" + } + ], + "name": "registerNeuronWithStakeAllocation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "rewardDistributor", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_rewardDistributor", + "type": "address" + } + ], + "name": "setRewardDistributor", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "subnetManager", + "outputs": [ + { + "internalType": "contract ISubnetManager", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "string", + "name": "axonEndpoint", + "type": "string" + }, + { + "internalType": "uint32", + "name": "axonPort", + "type": "uint32" + }, + { + "internalType": "string", + "name": "prometheusEndpoint", + "type": "string" + }, + { + "internalType": "uint32", + "name": "prometheusPort", + "type": "uint32" + } + ], + "name": "updateNeuronService", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "internalType": "uint256", + "name": "newStake", + "type": "uint256" + } + ], + "name": "updateStakeAllocation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] \ No newline at end of file diff --git a/contracts/GlobalStaking.abi b/contracts/GlobalStaking.abi index 2701957..0e815e0 100644 --- a/contracts/GlobalStaking.abi +++ b/contracts/GlobalStaking.abi @@ -1,776 +1,573 @@ [ - { - "inputs": [ - { - "internalType": "address", - "name": "_hetuToken", - "type": "address" - }, - { - "internalType": "address", - "name": "_initialOwner", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "OwnableInvalidOwner", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "OwnableUnauthorizedAccount", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "caller", - "type": "address" - }, - { - "indexed": false, - "internalType": "bool", - "name": "authorized", - "type": "bool" - } - ], - "name": "AuthorizedCallerUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "GlobalStakeAdded", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "GlobalStakeRemoved", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "StakeLocked", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "StakeUnlocked", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "oldAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newAmount", - "type": "uint256" - } - ], - "name": "SubnetAllocationChanged", - "type": "event" - }, - { - "inputs": [], - "name": "MIN_STAKE_TO_PARTICIPATE", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "MIN_SUBNET_ALLOCATION", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "addGlobalStake", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "allocateToSubnet", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "minThreshold", - "type": "uint256" - } - ], - "name": "allocateToSubnetWithThreshold", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "authorizedCallers", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "uint256", - "name": "requiredAmount", - "type": "uint256" - } - ], - "name": "canBecomeNeuron", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "claimRewards", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getAvailableStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getEffectiveStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getLockedStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - } - ], - "name": "getStakeInfo", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "totalStaked", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "totalAllocated", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "availableForAllocation", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastUpdateBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "pendingRewards", - "type": "uint256" - } - ], - "internalType": "struct IGlobalStaking.StakeInfo", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getSubnetAllocation", - "outputs": [ - { - "components": [ - { - "internalType": "uint256", - "name": "allocated", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "locked", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastUpdateBlock", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "isActive", - "type": "bool" - } - ], - "internalType": "struct IGlobalStaking.SubnetAllocation", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getTotalStaked", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - } - ], - "name": "getUserStakeInfo", - "outputs": [ - { - "internalType": "uint256", - "name": "totalStaked_", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "availableForAllocation", - "type": "uint256" - }, - { - "internalType": "uint16[]", - "name": "allocatedSubnets", - "type": "uint16[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - } - ], - "name": "hasParticipationEligibility", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "hetuToken", - "outputs": [ - { - "internalType": "contract IERC20", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "lockSubnetStake", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "name": "lockedStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "removeGlobalStake", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "caller", - "type": "address" - }, - { - "internalType": "bool", - "name": "authorized", - "type": "bool" - } - ], - "name": "setAuthorizedCaller", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "name": "subnetTotalStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - }, - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "subnetUserStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "totalUserStake", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "uint256", - "name": "amount", - "type": "uint256" - } - ], - "name": "unlockSubnetStake", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } + { + "inputs": [ + { + "internalType": "address", + "name": "_hetuToken", + "type": "address" + }, + { + "internalType": "address", + "name": "_treasury", + "type": "address" + }, + { + "internalType": "address", + "name": "_initialOwner", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "indexed": false, + "internalType": "bool", + "name": "authorized", + "type": "bool" + } + ], + "name": "AuthorizedCallerUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "DeallocatedFromSubnet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "GlobalStakeAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "GlobalStakeRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cost", + "type": "uint256" + } + ], + "name": "RegistrationCost", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newAmount", + "type": "uint256" + } + ], + "name": "SubnetAllocationChanged", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "addGlobalStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "allocateToSubnet", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minThreshold", + "type": "uint256" + } + ], + "name": "allocateToSubnetWithMinThreshold", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "authorizedCallers", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "canAllocateToSubnet", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint256", + "name": "cost", + "type": "uint256" + } + ], + "name": "canPayRegistrationCost", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "cost", + "type": "uint256" + } + ], + "name": "chargeRegistrationCost", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "deallocateFromSubnet", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getAvailableStake", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getStakeInfo", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "totalStaked", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalAllocated", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalCost", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastUpdateBlock", + "type": "uint256" + } + ], + "internalType": "struct IGlobalStaking.StakeInfo", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetAllocation", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "allocated", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "cost", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastUpdateBlock", + "type": "uint256" + } + ], + "internalType": "struct IGlobalStaking.SubnetAllocation", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hetuToken", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "removeGlobalStake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "caller", + "type": "address" + }, + { + "internalType": "bool", + "name": "authorized", + "type": "bool" + } + ], + "name": "setAuthorizedCaller", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_treasury", + "type": "address" + } + ], + "name": "setTreasury", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "treasury", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + } ] \ No newline at end of file diff --git a/contracts/NeuronManager.abi b/contracts/NeuronManager.abi deleted file mode 100644 index 2c003ba..0000000 --- a/contracts/NeuronManager.abi +++ /dev/null @@ -1,870 +0,0 @@ -[ - { - "inputs": [ - { - "internalType": "address", - "name": "_subnetManager", - "type": "address" - }, - { - "internalType": "address", - "name": "_globalStaking", - "type": "address" - }, - { - "internalType": "address", - "name": "_initialOwner", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "OwnableInvalidOwner", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "OwnableUnauthorizedAccount", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "blockNumber", - "type": "uint256" - } - ], - "name": "NeuronDeregistered", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "stake", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bool", - "name": "isValidator", - "type": "bool" - }, - { - "indexed": false, - "internalType": "bool", - "name": "requestedValidatorRole", - "type": "bool" - }, - { - "indexed": false, - "internalType": "string", - "name": "axonEndpoint", - "type": "string" - }, - { - "indexed": false, - "internalType": "uint32", - "name": "axonPort", - "type": "uint32" - }, - { - "indexed": false, - "internalType": "string", - "name": "prometheusEndpoint", - "type": "string" - }, - { - "indexed": false, - "internalType": "uint32", - "name": "prometheusPort", - "type": "uint32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "blockNumber", - "type": "uint256" - } - ], - "name": "NeuronRegistered", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "indexed": false, - "internalType": "address[]", - "name": "accounts", - "type": "address[]" - }, - { - "indexed": false, - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "blockNumber", - "type": "uint256" - } - ], - "name": "RewardsDistributed", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": false, - "internalType": "string", - "name": "axonEndpoint", - "type": "string" - }, - { - "indexed": false, - "internalType": "uint32", - "name": "axonPort", - "type": "uint32" - }, - { - "indexed": false, - "internalType": "string", - "name": "prometheusEndpoint", - "type": "string" - }, - { - "indexed": false, - "internalType": "uint32", - "name": "prometheusPort", - "type": "uint32" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "blockNumber", - "type": "uint256" - } - ], - "name": "ServiceUpdated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "indexed": true, - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "oldStake", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "newStake", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "bool", - "name": "wasValidator", - "type": "bool" - }, - { - "indexed": false, - "internalType": "bool", - "name": "isValidator", - "type": "bool" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "blockNumber", - "type": "uint256" - } - ], - "name": "StakeAllocationChanged", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "address[]", - "name": "accounts", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "newStakes", - "type": "uint256[]" - } - ], - "name": "batchUpdateStakeAllocations", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "bool", - "name": "isValidatorRole", - "type": "bool" - } - ], - "name": "canRegisterNeuron", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "deregisterNeuron", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "address[]", - "name": "accounts", - "type": "address[]" - }, - { - "internalType": "uint256[]", - "name": "amounts", - "type": "uint256[]" - } - ], - "name": "distributeRewards", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "getNeuronInfo", - "outputs": [ - { - "components": [ - { - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "internalType": "uint16", - "name": "uid", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "bool", - "name": "isActive", - "type": "bool" - }, - { - "internalType": "bool", - "name": "isValidator", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "stake", - "type": "uint256" - }, - { - "internalType": "uint64", - "name": "registrationBlock", - "type": "uint64" - }, - { - "internalType": "uint256", - "name": "lastUpdate", - "type": "uint256" - }, - { - "internalType": "string", - "name": "axonEndpoint", - "type": "string" - }, - { - "internalType": "uint32", - "name": "axonPort", - "type": "uint32" - }, - { - "internalType": "string", - "name": "prometheusEndpoint", - "type": "string" - }, - { - "internalType": "uint32", - "name": "prometheusPort", - "type": "uint32" - } - ], - "internalType": "struct SubnetTypes.NeuronInfo", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getSubnetNeuronCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getSubnetNeurons", - "outputs": [ - { - "internalType": "address[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getSubnetValidatorCount", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getSubnetValidators", - "outputs": [ - { - "internalType": "address[]", - "name": "", - "type": "address[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "globalStaking", - "outputs": [ - { - "internalType": "contract IGlobalStaking", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "isNeuron", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "isValidator", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "neuronList", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - }, - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "name": "neurons", - "outputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "internalType": "uint16", - "name": "uid", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "bool", - "name": "isActive", - "type": "bool" - }, - { - "internalType": "bool", - "name": "isValidator", - "type": "bool" - }, - { - "internalType": "uint256", - "name": "stake", - "type": "uint256" - }, - { - "internalType": "uint64", - "name": "registrationBlock", - "type": "uint64" - }, - { - "internalType": "uint256", - "name": "lastUpdate", - "type": "uint256" - }, - { - "internalType": "string", - "name": "axonEndpoint", - "type": "string" - }, - { - "internalType": "uint32", - "name": "axonPort", - "type": "uint32" - }, - { - "internalType": "string", - "name": "prometheusEndpoint", - "type": "string" - }, - { - "internalType": "uint32", - "name": "prometheusPort", - "type": "uint32" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "bool", - "name": "isValidatorRole", - "type": "bool" - }, - { - "internalType": "string", - "name": "axonEndpoint", - "type": "string" - }, - { - "internalType": "uint32", - "name": "axonPort", - "type": "uint32" - }, - { - "internalType": "string", - "name": "prometheusEndpoint", - "type": "string" - }, - { - "internalType": "uint32", - "name": "prometheusPort", - "type": "uint32" - } - ], - "name": "registerNeuron", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "rewardDistributor", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "_rewardDistributor", - "type": "address" - } - ], - "name": "setRewardDistributor", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "subnetManager", - "outputs": [ - { - "internalType": "contract ISubnetManager", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "string", - "name": "axonEndpoint", - "type": "string" - }, - { - "internalType": "uint32", - "name": "axonPort", - "type": "uint32" - }, - { - "internalType": "string", - "name": "prometheusEndpoint", - "type": "string" - }, - { - "internalType": "uint32", - "name": "prometheusPort", - "type": "uint32" - } - ], - "name": "updateService", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "address", - "name": "account", - "type": "address" - }, - { - "internalType": "uint256", - "name": "newStake", - "type": "uint256" - } - ], - "name": "updateStakeAllocation", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } -] \ No newline at end of file diff --git a/contracts/SubnetManager.abi b/contracts/SubnetManager.abi index 9496c28..0a3a790 100644 --- a/contracts/SubnetManager.abi +++ b/contracts/SubnetManager.abi @@ -1,1782 +1,1807 @@ [ - { - "inputs": [ - { - "internalType": "address", - "name": "_hetuToken", - "type": "address" - }, - { - "internalType": "address", - "name": "_ammFactory", - "type": "address" - } - ], - "stateMutability": "nonpayable", - "type": "constructor" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "owner", - "type": "address" - } - ], - "name": "OwnableInvalidOwner", - "type": "error" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "account", - "type": "address" - } - ], - "name": "OwnableUnauthorizedAccount", - "type": "error" - }, - { - "inputs": [], - "name": "ReentrancyGuardReentrantCall", - "type": "error" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "alphaToken", - "type": "address" - }, - { - "indexed": false, - "internalType": "address", - "name": "ammPool", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "lockedAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "poolAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "burnedAmount", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "components": [ - { - "internalType": "uint16", - "name": "rho", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "kappa", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "immunityPeriod", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "tempo", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxValidators", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "activityCutoff", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxAllowedUids", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxAllowedValidators", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "minAllowedWeights", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxWeightsLimit", - "type": "uint16" - }, - { - "internalType": "uint256", - "name": "baseBurnCost", - "type": "uint256" - }, - { - "internalType": "uint64", - "name": "currentDifficulty", - "type": "uint64" - }, - { - "internalType": "uint16", - "name": "targetRegsPerInterval", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxRegsPerBlock", - "type": "uint16" - }, - { - "internalType": "uint64", - "name": "weightsRateLimit", - "type": "uint64" - }, - { - "internalType": "bool", - "name": "registrationAllowed", - "type": "bool" - }, - { - "internalType": "bool", - "name": "commitRevealEnabled", - "type": "bool" - }, - { - "internalType": "uint64", - "name": "commitRevealPeriod", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "servingRateLimit", - "type": "uint64" - }, - { - "internalType": "uint16", - "name": "validatorThreshold", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "neuronThreshold", - "type": "uint16" - } - ], - "indexed": false, - "internalType": "struct SubnetTypes.SubnetHyperparams", - "name": "hyperparams", - "type": "tuple" - } - ], - "name": "NetworkRegistered", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "previousOwner", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "OwnershipTransferred", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "indexed": true, - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "timestamp", - "type": "uint256" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "blockNumber", - "type": "uint256" - } - ], - "name": "SubnetActivated", - "type": "event" - }, - { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "indexed": false, - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "indexed": false, - "internalType": "string", - "name": "description", - "type": "string" - } - ], - "name": "SubnetInfoUpdated", - "type": "event" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "activateSubnet", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "ammFactory", - "outputs": [ - { - "internalType": "contract SubnetAMMFactory", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getNetworkLockCost", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getNetworkParams", - "outputs": [ - { - "internalType": "uint256", - "name": "minLock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastLock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "lastLockBlock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "rateLimit", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "reductionInterval", - "type": "uint256" - }, - { - "internalType": "uint16", - "name": "totalNets", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "nextId", - "type": "uint16" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "getNextNetuid", - "outputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getSubnetDetails", - "outputs": [ - { - "components": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "alphaToken", - "type": "address" - }, - { - "internalType": "address", - "name": "ammPool", - "type": "address" - }, - { - "internalType": "uint256", - "name": "lockedAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "poolInitialTao", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "burnedAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "createdAt", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "isActive", - "type": "bool" - }, - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "internalType": "string", - "name": "description", - "type": "string" - } - ], - "internalType": "struct SubnetTypes.SubnetInfo", - "name": "subnetInfo", - "type": "tuple" - }, - { - "internalType": "uint256", - "name": "currentPrice", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "totalVolume", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "hetuReserve", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "alphaReserve", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getSubnetHyperparams", - "outputs": [ - { - "components": [ - { - "internalType": "uint16", - "name": "rho", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "kappa", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "immunityPeriod", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "tempo", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxValidators", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "activityCutoff", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxAllowedUids", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxAllowedValidators", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "minAllowedWeights", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxWeightsLimit", - "type": "uint16" - }, - { - "internalType": "uint256", - "name": "baseBurnCost", - "type": "uint256" - }, - { - "internalType": "uint64", - "name": "currentDifficulty", - "type": "uint64" - }, - { - "internalType": "uint16", - "name": "targetRegsPerInterval", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxRegsPerBlock", - "type": "uint16" - }, - { - "internalType": "uint64", - "name": "weightsRateLimit", - "type": "uint64" - }, - { - "internalType": "bool", - "name": "registrationAllowed", - "type": "bool" - }, - { - "internalType": "bool", - "name": "commitRevealEnabled", - "type": "bool" - }, - { - "internalType": "uint64", - "name": "commitRevealPeriod", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "servingRateLimit", - "type": "uint64" - }, - { - "internalType": "uint16", - "name": "validatorThreshold", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "neuronThreshold", - "type": "uint16" - } - ], - "internalType": "struct SubnetTypes.SubnetHyperparams", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getSubnetInfo", - "outputs": [ - { - "components": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "alphaToken", - "type": "address" - }, - { - "internalType": "address", - "name": "ammPool", - "type": "address" - }, - { - "internalType": "uint256", - "name": "lockedAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "poolInitialTao", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "burnedAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "createdAt", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "isActive", - "type": "bool" - }, - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "internalType": "string", - "name": "description", - "type": "string" - } - ], - "internalType": "struct SubnetTypes.SubnetInfo", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "name": "getSubnetParams", - "outputs": [ - { - "components": [ - { - "internalType": "uint16", - "name": "rho", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "kappa", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "immunityPeriod", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "tempo", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxValidators", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "activityCutoff", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxAllowedUids", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxAllowedValidators", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "minAllowedWeights", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxWeightsLimit", - "type": "uint16" - }, - { - "internalType": "uint256", - "name": "baseBurnCost", - "type": "uint256" - }, - { - "internalType": "uint64", - "name": "currentDifficulty", - "type": "uint64" - }, - { - "internalType": "uint16", - "name": "targetRegsPerInterval", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxRegsPerBlock", - "type": "uint16" - }, - { - "internalType": "uint64", - "name": "weightsRateLimit", - "type": "uint64" - }, - { - "internalType": "bool", - "name": "registrationAllowed", - "type": "bool" - }, - { - "internalType": "bool", - "name": "commitRevealEnabled", - "type": "bool" - }, - { - "internalType": "uint64", - "name": "commitRevealPeriod", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "servingRateLimit", - "type": "uint64" - }, - { - "internalType": "uint16", - "name": "validatorThreshold", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "neuronThreshold", - "type": "uint16" - } - ], - "internalType": "struct SubnetTypes.SubnetHyperparams", - "name": "", - "type": "tuple" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "user", - "type": "address" - } - ], - "name": "getUserSubnets", - "outputs": [ - { - "internalType": "uint16[]", - "name": "", - "type": "uint16[]" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "hetuToken", - "outputs": [ - { - "internalType": "contract IERC20", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "lockReductionInterval", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "networkLastLock", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "networkLastLockBlock", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "networkMinLock", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "networkRateLimit", - "outputs": [ - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "nextNetuid", - "outputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "owner", - "outputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "", - "type": "address" - }, - { - "internalType": "uint256", - "name": "", - "type": "uint256" - } - ], - "name": "ownerSubnets", - "outputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "internalType": "string", - "name": "description", - "type": "string" - }, - { - "internalType": "string", - "name": "tokenName", - "type": "string" - }, - { - "internalType": "string", - "name": "tokenSymbol", - "type": "string" - } - ], - "name": "registerNetwork", - "outputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "internalType": "string", - "name": "description", - "type": "string" - }, - { - "internalType": "string", - "name": "tokenName", - "type": "string" - }, - { - "internalType": "string", - "name": "tokenSymbol", - "type": "string" - }, - { - "components": [ - { - "internalType": "uint16", - "name": "rho", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "kappa", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "immunityPeriod", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "tempo", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxValidators", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "activityCutoff", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxAllowedUids", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxAllowedValidators", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "minAllowedWeights", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxWeightsLimit", - "type": "uint16" - }, - { - "internalType": "uint256", - "name": "baseBurnCost", - "type": "uint256" - }, - { - "internalType": "uint64", - "name": "currentDifficulty", - "type": "uint64" - }, - { - "internalType": "uint16", - "name": "targetRegsPerInterval", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxRegsPerBlock", - "type": "uint16" - }, - { - "internalType": "uint64", - "name": "weightsRateLimit", - "type": "uint64" - }, - { - "internalType": "bool", - "name": "registrationAllowed", - "type": "bool" - }, - { - "internalType": "bool", - "name": "commitRevealEnabled", - "type": "bool" - }, - { - "internalType": "uint64", - "name": "commitRevealPeriod", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "servingRateLimit", - "type": "uint64" - }, - { - "internalType": "uint16", - "name": "validatorThreshold", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "neuronThreshold", - "type": "uint16" - } - ], - "internalType": "struct SubnetTypes.SubnetHyperparams", - "name": "customHyperparams", - "type": "tuple" - }, - { - "internalType": "bool[21]", - "name": "useCustomFlags", - "type": "bool[21]" - } - ], - "name": "registerNetworkWithPartialCustom", - "outputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "internalType": "string", - "name": "description", - "type": "string" - }, - { - "internalType": "string", - "name": "tokenName", - "type": "string" - }, - { - "internalType": "string", - "name": "tokenSymbol", - "type": "string" - }, - { - "components": [ - { - "internalType": "uint16", - "name": "rho", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "kappa", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "immunityPeriod", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "tempo", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxValidators", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "activityCutoff", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxAllowedUids", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxAllowedValidators", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "minAllowedWeights", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxWeightsLimit", - "type": "uint16" - }, - { - "internalType": "uint256", - "name": "baseBurnCost", - "type": "uint256" - }, - { - "internalType": "uint64", - "name": "currentDifficulty", - "type": "uint64" - }, - { - "internalType": "uint16", - "name": "targetRegsPerInterval", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxRegsPerBlock", - "type": "uint16" - }, - { - "internalType": "uint64", - "name": "weightsRateLimit", - "type": "uint64" - }, - { - "internalType": "bool", - "name": "registrationAllowed", - "type": "bool" - }, - { - "internalType": "bool", - "name": "commitRevealEnabled", - "type": "bool" - }, - { - "internalType": "uint64", - "name": "commitRevealPeriod", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "servingRateLimit", - "type": "uint64" - }, - { - "internalType": "uint16", - "name": "validatorThreshold", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "neuronThreshold", - "type": "uint16" - } - ], - "internalType": "struct SubnetTypes.SubnetHyperparams", - "name": "customHyperparams", - "type": "tuple" - }, - { - "internalType": "bool[21]", - "name": "useCustomFlags", - "type": "bool[21]" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "name": "registerNetworkWithPartialCustomAndPermit", - "outputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "internalType": "string", - "name": "description", - "type": "string" - }, - { - "internalType": "string", - "name": "tokenName", - "type": "string" - }, - { - "internalType": "string", - "name": "tokenSymbol", - "type": "string" - }, - { - "internalType": "uint256", - "name": "deadline", - "type": "uint256" - }, - { - "internalType": "uint8", - "name": "v", - "type": "uint8" - }, - { - "internalType": "bytes32", - "name": "r", - "type": "bytes32" - }, - { - "internalType": "bytes32", - "name": "s", - "type": "bytes32" - } - ], - "name": "registerNetworkWithPermit", - "outputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - } - ], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "renounceOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [], - "name": "resetNetworkLockState", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "name": "subnetExists", - "outputs": [ - { - "internalType": "bool", - "name": "", - "type": "bool" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "name": "subnetHyperparams", - "outputs": [ - { - "internalType": "uint16", - "name": "rho", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "kappa", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "immunityPeriod", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "tempo", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxValidators", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "activityCutoff", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxAllowedUids", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxAllowedValidators", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "minAllowedWeights", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxWeightsLimit", - "type": "uint16" - }, - { - "internalType": "uint256", - "name": "baseBurnCost", - "type": "uint256" - }, - { - "internalType": "uint64", - "name": "currentDifficulty", - "type": "uint64" - }, - { - "internalType": "uint16", - "name": "targetRegsPerInterval", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxRegsPerBlock", - "type": "uint16" - }, - { - "internalType": "uint64", - "name": "weightsRateLimit", - "type": "uint64" - }, - { - "internalType": "bool", - "name": "registrationAllowed", - "type": "bool" - }, - { - "internalType": "bool", - "name": "commitRevealEnabled", - "type": "bool" - }, - { - "internalType": "uint64", - "name": "commitRevealPeriod", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "servingRateLimit", - "type": "uint64" - }, - { - "internalType": "uint16", - "name": "validatorThreshold", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "neuronThreshold", - "type": "uint16" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "name": "subnets", - "outputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "address", - "name": "owner", - "type": "address" - }, - { - "internalType": "address", - "name": "alphaToken", - "type": "address" - }, - { - "internalType": "address", - "name": "ammPool", - "type": "address" - }, - { - "internalType": "uint256", - "name": "lockedAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "poolInitialTao", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "burnedAmount", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "createdAt", - "type": "uint256" - }, - { - "internalType": "bool", - "name": "isActive", - "type": "bool" - }, - { - "internalType": "string", - "name": "name", - "type": "string" - }, - { - "internalType": "string", - "name": "description", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [], - "name": "totalNetworks", - "outputs": [ - { - "internalType": "uint16", - "name": "", - "type": "uint16" - } - ], - "stateMutability": "view", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "address", - "name": "newOwner", - "type": "address" - } - ], - "name": "transferOwnership", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint256", - "name": "_networkMinLock", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_networkRateLimit", - "type": "uint256" - }, - { - "internalType": "uint256", - "name": "_lockReductionInterval", - "type": "uint256" - } - ], - "name": "updateNetworkParams", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "components": [ - { - "internalType": "uint16", - "name": "rho", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "kappa", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "immunityPeriod", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "tempo", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxValidators", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "activityCutoff", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxAllowedUids", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxAllowedValidators", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "minAllowedWeights", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxWeightsLimit", - "type": "uint16" - }, - { - "internalType": "uint256", - "name": "baseBurnCost", - "type": "uint256" - }, - { - "internalType": "uint64", - "name": "currentDifficulty", - "type": "uint64" - }, - { - "internalType": "uint16", - "name": "targetRegsPerInterval", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "maxRegsPerBlock", - "type": "uint16" - }, - { - "internalType": "uint64", - "name": "weightsRateLimit", - "type": "uint64" - }, - { - "internalType": "bool", - "name": "registrationAllowed", - "type": "bool" - }, - { - "internalType": "bool", - "name": "commitRevealEnabled", - "type": "bool" - }, - { - "internalType": "uint64", - "name": "commitRevealPeriod", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "servingRateLimit", - "type": "uint64" - }, - { - "internalType": "uint16", - "name": "validatorThreshold", - "type": "uint16" - }, - { - "internalType": "uint16", - "name": "neuronThreshold", - "type": "uint16" - } - ], - "internalType": "struct SubnetTypes.SubnetHyperparams", - "name": "newHyperparams", - "type": "tuple" - } - ], - "name": "updateSubnetHyperparams", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - }, - { - "inputs": [ - { - "internalType": "uint16", - "name": "netuid", - "type": "uint16" - }, - { - "internalType": "string", - "name": "newName", - "type": "string" - }, - { - "internalType": "string", - "name": "newDescription", - "type": "string" - } - ], - "name": "updateSubnetInfo", - "outputs": [], - "stateMutability": "nonpayable", - "type": "function" - } + { + "inputs": [ + { + "internalType": "address", + "name": "_hetuToken", + "type": "address" + }, + { + "internalType": "address", + "name": "_ammFactory", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "OwnableInvalidOwner", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "OwnableUnauthorizedAccount", + "type": "error" + }, + { + "inputs": [], + "name": "ReentrancyGuardReentrantCall", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "string", + "name": "paramName", + "type": "string" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "oldValue", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "newValue", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "updater", + "type": "address" + } + ], + "name": "NetworkConfigUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "alphaToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "ammPool", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lockedAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "poolAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "burnedAmount", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "components": [ + { + "internalType": "uint16", + "name": "rho", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "kappa", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "immunityPeriod", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "tempo", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "activityCutoff", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "minAllowedWeights", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxWeightsLimit", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "baseNeuronCost", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "currentDifficulty", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "targetRegsPerInterval", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxRegsPerBlock", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "weightsRateLimit", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "registrationAllowed", + "type": "bool" + }, + { + "internalType": "bool", + "name": "commitRevealEnabled", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "commitRevealPeriod", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "servingRateLimit", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "validatorThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "neuronThreshold", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct SubnetTypes.SubnetHyperparams", + "name": "hyperparams", + "type": "tuple" + } + ], + "name": "NetworkRegistered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "SubnetActivated", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "activateSubnet", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "ammFactory", + "outputs": [ + { + "internalType": "contract SubnetAMMFactory", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNetworkLockCost", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNetworkParams", + "outputs": [ + { + "internalType": "uint256", + "name": "minLock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastLock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lastLockBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "rateLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reductionInterval", + "type": "uint256" + }, + { + "internalType": "uint16", + "name": "totalNets", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "nextId", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getNextNetuid", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetDetails", + "outputs": [ + { + "components": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "alphaToken", + "type": "address" + }, + { + "internalType": "address", + "name": "ammPool", + "type": "address" + }, + { + "internalType": "uint256", + "name": "lockedAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "poolInitialTao", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "burnedAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "createdAt", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "internalType": "struct SubnetTypes.SubnetInfo", + "name": "subnetInfo", + "type": "tuple" + }, + { + "internalType": "uint256", + "name": "currentPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalVolume", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "hetuReserve", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "alphaReserve", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetHyperparams", + "outputs": [ + { + "components": [ + { + "internalType": "uint16", + "name": "rho", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "kappa", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "immunityPeriod", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "tempo", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "activityCutoff", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "minAllowedWeights", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxWeightsLimit", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "baseNeuronCost", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "currentDifficulty", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "targetRegsPerInterval", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxRegsPerBlock", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "weightsRateLimit", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "registrationAllowed", + "type": "bool" + }, + { + "internalType": "bool", + "name": "commitRevealEnabled", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "commitRevealPeriod", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "servingRateLimit", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "validatorThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "neuronThreshold", + "type": "uint256" + } + ], + "internalType": "struct SubnetTypes.SubnetHyperparams", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetInfo", + "outputs": [ + { + "components": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "alphaToken", + "type": "address" + }, + { + "internalType": "address", + "name": "ammPool", + "type": "address" + }, + { + "internalType": "uint256", + "name": "lockedAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "poolInitialTao", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "burnedAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "createdAt", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "internalType": "struct SubnetTypes.SubnetInfo", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "getSubnetParams", + "outputs": [ + { + "components": [ + { + "internalType": "uint16", + "name": "rho", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "kappa", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "immunityPeriod", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "tempo", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "activityCutoff", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "minAllowedWeights", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxWeightsLimit", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "baseNeuronCost", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "currentDifficulty", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "targetRegsPerInterval", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxRegsPerBlock", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "weightsRateLimit", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "registrationAllowed", + "type": "bool" + }, + { + "internalType": "bool", + "name": "commitRevealEnabled", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "commitRevealPeriod", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "servingRateLimit", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "validatorThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "neuronThreshold", + "type": "uint256" + } + ], + "internalType": "struct SubnetTypes.SubnetHyperparams", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "user", + "type": "address" + } + ], + "name": "getUserSubnets", + "outputs": [ + { + "internalType": "uint16[]", + "name": "", + "type": "uint16[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "hetuToken", + "outputs": [ + { + "internalType": "contract IERC20", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "name": "isSubnetActive", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "lockReductionInterval", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "networkLastLock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "networkLastLockBlock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "networkMinLock", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "networkRateLimit", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "nextNetuid", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "ownerSubnets", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "string", + "name": "tokenName", + "type": "string" + }, + { + "internalType": "string", + "name": "tokenSymbol", + "type": "string" + } + ], + "name": "registerNetwork", + "outputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "string", + "name": "tokenName", + "type": "string" + }, + { + "internalType": "string", + "name": "tokenSymbol", + "type": "string" + }, + { + "components": [ + { + "internalType": "uint16", + "name": "rho", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "kappa", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "immunityPeriod", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "tempo", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "activityCutoff", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "minAllowedWeights", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxWeightsLimit", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "baseNeuronCost", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "currentDifficulty", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "targetRegsPerInterval", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxRegsPerBlock", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "weightsRateLimit", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "registrationAllowed", + "type": "bool" + }, + { + "internalType": "bool", + "name": "commitRevealEnabled", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "commitRevealPeriod", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "servingRateLimit", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "validatorThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "neuronThreshold", + "type": "uint256" + } + ], + "internalType": "struct SubnetTypes.SubnetHyperparams", + "name": "customHyperparams", + "type": "tuple" + }, + { + "internalType": "bool[21]", + "name": "useCustomFlags", + "type": "bool[21]" + } + ], + "name": "registerNetworkWithPartialCustom", + "outputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "string", + "name": "tokenName", + "type": "string" + }, + { + "internalType": "string", + "name": "tokenSymbol", + "type": "string" + }, + { + "components": [ + { + "internalType": "uint16", + "name": "rho", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "kappa", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "immunityPeriod", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "tempo", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "activityCutoff", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "minAllowedWeights", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxWeightsLimit", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "baseNeuronCost", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "currentDifficulty", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "targetRegsPerInterval", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxRegsPerBlock", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "weightsRateLimit", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "registrationAllowed", + "type": "bool" + }, + { + "internalType": "bool", + "name": "commitRevealEnabled", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "commitRevealPeriod", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "servingRateLimit", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "validatorThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "neuronThreshold", + "type": "uint256" + } + ], + "internalType": "struct SubnetTypes.SubnetHyperparams", + "name": "customHyperparams", + "type": "tuple" + }, + { + "internalType": "bool[21]", + "name": "useCustomFlags", + "type": "bool[21]" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "registerNetworkWithPartialCustomAndPermit", + "outputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + }, + { + "internalType": "string", + "name": "tokenName", + "type": "string" + }, + { + "internalType": "string", + "name": "tokenSymbol", + "type": "string" + }, + { + "internalType": "uint256", + "name": "deadline", + "type": "uint256" + }, + { + "internalType": "uint8", + "name": "v", + "type": "uint8" + }, + { + "internalType": "bytes32", + "name": "r", + "type": "bytes32" + }, + { + "internalType": "bytes32", + "name": "s", + "type": "bytes32" + } + ], + "name": "registerNetworkWithPermit", + "outputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "resetNetworkLockState", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "name": "subnetExists", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "name": "subnetHyperparams", + "outputs": [ + { + "internalType": "uint16", + "name": "rho", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "kappa", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "immunityPeriod", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "tempo", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "activityCutoff", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "minAllowedWeights", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxWeightsLimit", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "baseNeuronCost", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "currentDifficulty", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "targetRegsPerInterval", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxRegsPerBlock", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "weightsRateLimit", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "registrationAllowed", + "type": "bool" + }, + { + "internalType": "bool", + "name": "commitRevealEnabled", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "commitRevealPeriod", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "servingRateLimit", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "validatorThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "neuronThreshold", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "name": "subnets", + "outputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "alphaToken", + "type": "address" + }, + { + "internalType": "address", + "name": "ammPool", + "type": "address" + }, + { + "internalType": "uint256", + "name": "lockedAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "poolInitialTao", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "burnedAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "createdAt", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "isActive", + "type": "bool" + }, + { + "internalType": "string", + "name": "name", + "type": "string" + }, + { + "internalType": "string", + "name": "description", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalNetworks", + "outputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "newMinLock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "newRateLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "newInterval", + "type": "uint256" + } + ], + "name": "updateNetworkConfig", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "_networkMinLock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_networkRateLimit", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_lockReductionInterval", + "type": "uint256" + } + ], + "name": "updateNetworkParams", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "components": [ + { + "internalType": "uint16", + "name": "rho", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "kappa", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "immunityPeriod", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "tempo", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "activityCutoff", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedUids", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxAllowedValidators", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "minAllowedWeights", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxWeightsLimit", + "type": "uint16" + }, + { + "internalType": "uint256", + "name": "baseNeuronCost", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "currentDifficulty", + "type": "uint64" + }, + { + "internalType": "uint16", + "name": "targetRegsPerInterval", + "type": "uint16" + }, + { + "internalType": "uint16", + "name": "maxRegsPerBlock", + "type": "uint16" + }, + { + "internalType": "uint64", + "name": "weightsRateLimit", + "type": "uint64" + }, + { + "internalType": "bool", + "name": "registrationAllowed", + "type": "bool" + }, + { + "internalType": "bool", + "name": "commitRevealEnabled", + "type": "bool" + }, + { + "internalType": "uint64", + "name": "commitRevealPeriod", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "servingRateLimit", + "type": "uint64" + }, + { + "internalType": "uint256", + "name": "validatorThreshold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "neuronThreshold", + "type": "uint256" + } + ], + "internalType": "struct SubnetTypes.SubnetHyperparams", + "name": "newHyperparams", + "type": "tuple" + } + ], + "name": "updateSubnetHyperparams", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } ] \ No newline at end of file diff --git a/contracts/Weights.abi b/contracts/Weights.abi new file mode 100644 index 0000000..0817778 --- /dev/null +++ b/contracts/Weights.abi @@ -0,0 +1,98 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "indexed": true, + "internalType": "address", + "name": "validator", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "dest", + "type": "address" + }, + { + "internalType": "uint256", + "name": "weight", + "type": "uint256" + } + ], + "indexed": false, + "internalType": "struct Weights.Weight[]", + "name": "weights", + "type": "tuple[]" + } + ], + "name": "WeightsSet", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "netuid", + "type": "uint16" + }, + { + "components": [ + { + "internalType": "address", + "name": "dest", + "type": "address" + }, + { + "internalType": "uint256", + "name": "weight", + "type": "uint256" + } + ], + "internalType": "struct Weights.Weight[]", + "name": "newWeights", + "type": "tuple[]" + } + ], + "name": "setWeights", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint16", + "name": "", + "type": "uint16" + }, + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "weights", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } +] \ No newline at end of file diff --git a/hetu_pycli/cli.py b/hetu_pycli/cli.py index 28d9ec0..84f5395 100644 --- a/hetu_pycli/cli.py +++ b/hetu_pycli/cli.py @@ -6,10 +6,12 @@ from hetu_pycli.src.commands.config import config_app from hetu_pycli.src.hetu.erc20 import erc20_app from hetu_pycli.src.hetu.whetu import whetu_app +from hetu_pycli.src.hetu.hetu import hetu_app from hetu_pycli.src.hetu.staking import staking_app from hetu_pycli.src.hetu.subnet import subnet_app from hetu_pycli.src.hetu.amm import amm_app -from hetu_pycli.src.hetu.neuron import neuron_app +from hetu_pycli.src.hetu.dendron import dendron_app +from hetu_pycli.src.hetu.weights import weights_app from hetu_pycli.config import load_config, ensure_config_file, _epilog from hetu_pycli.version import __version__ @@ -73,11 +75,13 @@ def raise_exit(): app.add_typer(config_app, name="c", hidden=True, no_args_is_help=True) app.add_typer(config_app, name="conf", hidden=True, no_args_is_help=True) app.add_typer(erc20_app, name="erc20", help="ERC20 token operations", no_args_is_help=True, epilog=_epilog) +app.add_typer(hetu_app, name="hetu", help="Native HETU operations", no_args_is_help=True, epilog=_epilog) app.add_typer(whetu_app, name="whetu", help="WHETU contract operations", no_args_is_help=True, epilog=_epilog) app.add_typer(staking_app, name="stake", help="Global staking operations", no_args_is_help=True, epilog=_epilog) app.add_typer(subnet_app, name="subnet", help="Subnet manager operations", no_args_is_help=True, epilog=_epilog) app.add_typer(amm_app, name="amm", help="Subnet AMM operations", no_args_is_help=True, epilog=_epilog) -app.add_typer(neuron_app, name="neuron", help="Neuron manager operations", no_args_is_help=True, epilog=_epilog) +app.add_typer(dendron_app, name="dendron", help="Dendron manager operations", no_args_is_help=True, epilog=_epilog) +app.add_typer(weights_app, name="weights", help="Weights manager operations", no_args_is_help=True, epilog=_epilog) if __name__ == "__main__": app() diff --git a/hetu_pycli/config.py b/hetu_pycli/config.py index 779b54e..ce80df5 100644 --- a/hetu_pycli/config.py +++ b/hetu_pycli/config.py @@ -6,18 +6,19 @@ DEFAULT_CONFIG_PATH = os.path.expanduser("~/.hetucli/config.yml") DEFAULT_CONFIG = { - "chain": "ws://127.0.0.1:8545", - "json_rpc": "http://127.0.0.1:8545", + "chain": "ws://127.0.0.1:9090", + "json_rpc": "http://161.97.161.133:18545", "network": "local", "no_cache": False, "wallet_hotkey": "hotkey-user1", "wallet_name": "coldkey-user1", "wallet_path": os.path.expanduser("~/.hetucli/wallets"), - "whetu_address": "0x0000000000000000000000000000000000000000", - "subnet_address": "0x0000000000000000000000000000000000000000", - "staking_address": "0x0000000000000000000000000000000000000000", - "amm_address": "0x0000000000000000000000000000000000000000", - "neuron_address": "0x0000000000000000000000000000000000000000", + "whetu_address": "0xBC45C2511eA43F998E659b4722D6795C482a7E07", + "subnet_address": "0xaF856443EaF741eEcAD2b5Bb3ff6F9F57a00920F", + "staking_address": "0x9cCb4A38a208409422969737977696B8189eF96a", + "amm_address": "0x36607E8D2cb850E3b2d14b998A25c43611d710cE", + "dendron_address": "0x34d3911323Ef5576Ba84a5a68b814D189112020F", + "weights_address": "0x1011c3586a901FBea4DEB3df16cFC42922219D86", "metagraph_cols": { "ACTIVE": True, "AXON": True, diff --git a/hetu_pycli/src/commands/tx.py b/hetu_pycli/src/commands/tx.py index e6e0fbc..2b10f00 100644 --- a/hetu_pycli/src/commands/tx.py +++ b/hetu_pycli/src/commands/tx.py @@ -13,7 +13,7 @@ def send( sender: str = typer.Option(..., help="Wallet name or address (local keystore)"), to: str = typer.Option(..., help="Recipient address"), value: float = typer.Option(..., help="Transfer amount (HETU)"), - rpc: str = typer.Option(None, help="Ethereum node RPC URL"), + rpc: str = typer.Option(None, help="Ethereum node RPC URL (optional, uses default from config if not provided)"), wallet_path: str = typer.Option(None, help="Wallet path (default from config)"), password: str = typer.Option(None, help="Password for keystore (prompt if not set)"), ): @@ -54,7 +54,7 @@ def send_by_direct_key( private_key: str = typer.Option(..., help="Sender private key"), to: str = typer.Option(..., help="Recipient address"), value: float = typer.Option(..., help="Transfer amount (HETU)"), - rpc: str = typer.Option(None, help="Ethereum node RPC URL"), + rpc: str = typer.Option(None, help="Ethereum node RPC URL (optional, uses default from config if not provided)"), ): """Send HETU transfer directly by private key.""" config = getattr(ctx, "obj", {}) or {} diff --git a/hetu_pycli/src/commands/wallet.py b/hetu_pycli/src/commands/wallet.py index 6b4f8d6..b8a9c33 100644 --- a/hetu_pycli/src/commands/wallet.py +++ b/hetu_pycli/src/commands/wallet.py @@ -135,30 +135,32 @@ def export_privkey( ) def balance( ctx: typer.Context, - name_or_address: str = typer.Argument(..., help="Wallet name or address"), - rpc: str = typer.Option(None, help="Hetu node RPC URL"), + wallet_name: str = typer.Argument(..., help="Wallet name (username)"), + rpc: str = typer.Option(None, help="Hetu node RPC URL (optional, uses default from config if not provided)"), ): - """Query address balance by wallet name or address""" + """Query address balance by wallet name""" config = ctx.obj rpc_url = rpc or (config.get("json_rpc") if config else None) if not rpc_url: print("[red]No RPC URL provided or found in config.") raise typer.Exit(1) wallet_path = get_wallet_path(config) - # Try to resolve address if name is given - address = name_or_address - if not (address.startswith('0x') and len(address) == 42): - try: - keystore = load_keystore(name_or_address, wallet_path) - address = keystore.get("address") - except Exception: - print(f"[red]Wallet not found: {name_or_address}") - raise typer.Exit(1) + + # Load keystore by wallet name + try: + keystore = load_keystore(wallet_name, wallet_path) + address = keystore.get("address") + print(f"[yellow]Wallet '{wallet_name}' address: {address}") + except Exception: + print(f"[red]Wallet not found: {wallet_name}") + raise typer.Exit(1) + w3 = Web3(Web3.HTTPProvider(rpc_url)) bal = w3.eth.get_balance(address) ether = w3.from_wei(bal, 'ether') ether_str = f"{ether:,.18f}".rstrip('0').rstrip('.') - print(f"[cyan]Balance: {ether_str} HETU ({address})") + print(f"[cyan]Balance: {ether_str} HETU") + print(f"[cyan]Address: {address}") @wallet_app.command( diff --git a/hetu_pycli/src/hetu/amm.py b/hetu_pycli/src/hetu/amm.py index cadb7cf..a5d3f69 100644 --- a/hetu_pycli/src/hetu/amm.py +++ b/hetu_pycli/src/hetu/amm.py @@ -33,32 +33,90 @@ def get_contract_address(ctx, cli_contract_key: str, param_contract: str): print(f"[yellow]Using contract address: {contract_addr}") return contract_addr +def get_amm_contract_address(ctx, contract: str, netuid: int, subnet_contract: str = None): + """Get AMM contract address from either contract parameter or netuid""" + if contract and netuid: + print("[red]Cannot specify both contract and netuid. Please use only one.") + raise typer.Exit(1) + + if not contract and not netuid: + print("[red]Must specify either contract or netuid.") + raise typer.Exit(1) + + if contract: + # Use direct contract address + return get_contract_address(ctx, "amm_address", contract) + else: + # Get AMM contract address from netuid + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + + # Load subnet manager to get AMM pool address + from hetu_pycli.src.hetu.subnet import load_subnet_mgr + subnet_contract = get_contract_address(ctx, "subnet_address", subnet_contract) + subnet_mgr = load_subnet_mgr(subnet_contract, rpc) + + # Get subnet info to extract AMM pool address + subnet_info = subnet_mgr.getSubnetInfo(netuid) + amm_pool_address = subnet_info[3] # AMM Pool address is at index 3 + + if amm_pool_address == "0x0000000000000000000000000000000000000000": + print(f"[red]No AMM pool found for netuid {netuid}") + raise typer.Exit(1) + + print(f"[yellow]AMM Pool Address from netuid {netuid}: {amm_pool_address}") + return amm_pool_address + @amm_app.command() def alpha_price( ctx: typer.Context, contract: str = typer.Option(None, help="AMM contract address"), + netuid: int = typer.Option(None, help="Subnet netuid"), + subnet_contract: str = typer.Option(None, help="Subnet manager contract address (required when using netuid)"), ): """Query current alpha price""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "amm_address", contract) if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) - amm = load_amm(contract, rpc) - print(f"[green]Alpha Price: {amm.getAlphaPrice()}") + + amm_contract = get_amm_contract_address(ctx, contract, netuid, subnet_contract) + amm = load_amm(amm_contract, rpc) + alpha_price_wei = amm.getAlphaPrice() + + # Convert wei to ether for better readability + alpha_price_ether = amm.web3.from_wei(alpha_price_wei, "ether") + + print(f"[green]Alpha Price:") + print(f" Raw (wei): {alpha_price_wei}") + print(f" Formatted: {alpha_price_ether} ALPHA/HETU") + + # Show the exchange rate + if alpha_price_ether == 1.0: + print(f" Exchange Rate: 1 ALPHA = 1 HETU (1:1)") + else: + hetu_per_alpha = alpha_price_ether + alpha_per_hetu = 1 / alpha_price_ether + print(f" Exchange Rate: 1 ALPHA = {hetu_per_alpha:.6f} HETU") + print(f" 1 HETU = {alpha_per_hetu:.6f} ALPHA") @amm_app.command() def pool_info( ctx: typer.Context, contract: str = typer.Option(None, help="AMM contract address"), + netuid: int = typer.Option(None, help="Subnet netuid"), + subnet_contract: str = typer.Option(None, help="Subnet manager contract address (required when using netuid)"), ): """Query pool info""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "amm_address", contract) if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) - amm = load_amm(contract, rpc) + + amm_contract = get_amm_contract_address(ctx, contract, netuid, subnet_contract) + amm = load_amm(amm_contract, rpc) pool = amm.getPoolInfo() print(f"[green]Pool Info: \n- mechanism: {pool[0]}\n- subnetTAO: {pool[1]}\n- subnetAlphaIn: {pool[2]}\n- subnetAlphaOut: {pool[3]}\n- currentPrice: {pool[4]}\n- movingPrice: {pool[5]}\n- totalVolume: {pool[6]}\n- minimumLiquidity: {pool[7]}") @@ -66,30 +124,36 @@ def pool_info( def statistics( ctx: typer.Context, contract: str = typer.Option(None, help="AMM contract address"), + netuid: int = typer.Option(None, help="Subnet netuid"), + subnet_contract: str = typer.Option(None, help="Subnet manager contract address (required when using netuid)"), ): """Query pool statistics""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "amm_address", contract) if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) - amm = load_amm(contract, rpc) + + amm_contract = get_amm_contract_address(ctx, contract, netuid, subnet_contract) + amm = load_amm(amm_contract, rpc) print(f"[green]Statistics: {amm.getStatistics()}") @amm_app.command() def swap_preview( ctx: typer.Context, contract: str = typer.Option(None, help="AMM contract address"), + netuid: int = typer.Option(None, help="Subnet netuid"), + subnet_contract: str = typer.Option(None, help="Subnet manager contract address (required when using netuid)"), amount_in: float = typer.Option(..., help="Input amount (in HETU or ALPHA)"), is_hetu_to_alpha: bool = typer.Option(..., help="True for HETU->ALPHA, False for ALPHA->HETU"), ): """Preview swap result and price impact""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "amm_address", contract) if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) - amm = load_amm(contract, rpc) + + amm_contract = get_amm_contract_address(ctx, contract, netuid, subnet_contract) + amm = load_amm(amm_contract, rpc) amount_in_wei = amm.web3.to_wei(amount_in, "ether") print(f"[green]Swap Preview: {amm.getSwapPreview(amount_in_wei, is_hetu_to_alpha)}") @@ -97,15 +161,18 @@ def swap_preview( def sim_swap_alpha_for_hetu( ctx: typer.Context, contract: str = typer.Option(None, help="AMM contract address"), + netuid: int = typer.Option(None, help="Subnet netuid"), + subnet_contract: str = typer.Option(None, help="Subnet manager contract address (required when using netuid)"), alpha_amount: float = typer.Option(..., help="Alpha amount (in ALPHA)"), ): """Simulate swap ALPHA for HETU""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "amm_address", contract) if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) - amm = load_amm(contract, rpc) + + amm_contract = get_amm_contract_address(ctx, contract, netuid, subnet_contract) + amm = load_amm(amm_contract, rpc) alpha_amount_wei = amm.web3.to_wei(alpha_amount, "ether") print(f"[green]Simulated HETU Out: {amm.simSwapAlphaForHETU(alpha_amount_wei)}") @@ -113,15 +180,18 @@ def sim_swap_alpha_for_hetu( def sim_swap_hetu_for_alpha( ctx: typer.Context, contract: str = typer.Option(None, help="AMM contract address"), + netuid: int = typer.Option(None, help="Subnet netuid"), + subnet_contract: str = typer.Option(None, help="Subnet manager contract address (required when using netuid)"), hetu_amount: float = typer.Option(..., help="HETU amount (in HETU)"), ): """Simulate swap HETU for ALPHA""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "amm_address", contract) if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) - amm = load_amm(contract, rpc) + + amm_contract = get_amm_contract_address(ctx, contract, netuid, subnet_contract) + amm = load_amm(amm_contract, rpc) hetu_amount_wei = amm.web3.to_wei(hetu_amount, "ether") print(f"[green]Simulated ALPHA Out: {amm.simSwapHETUForAlpha(hetu_amount_wei)}") @@ -129,6 +199,8 @@ def sim_swap_hetu_for_alpha( def inject_liquidity( ctx: typer.Context, contract: str = typer.Option(None, help="AMM contract address"), + netuid: int = typer.Option(None, help="Subnet netuid"), + subnet_contract: str = typer.Option(None, help="Subnet manager contract address (required when using netuid)"), sender: str = typer.Option(..., help="Sender address (must match keystore address or wallet name)"), wallet_path: str = typer.Option(None, help="Wallet path (default from config)"), password: str = typer.Option(None, hide_input=True, help="Keystore password"), @@ -137,10 +209,11 @@ def inject_liquidity( ): """Inject liquidity into the pool""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "amm_address", contract) if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) + + amm_contract = get_amm_contract_address(ctx, contract, netuid, subnet_contract) config = ctx.obj wallet_path = wallet_path or get_wallet_path(config) keystore = load_keystore(sender, wallet_path) @@ -151,7 +224,7 @@ def inject_liquidity( except Exception as e: print(f"[red]Failed to decrypt keystore: {e}") raise typer.Exit(1) - amm = load_amm(contract, rpc) + amm = load_amm(amm_contract, rpc) from_address = keystore["address"] nonce = amm.web3.eth.get_transaction_count(from_address) hetu_amount_wei = amm.web3.to_wei(hetu_amount, "ether") @@ -178,19 +251,22 @@ def inject_liquidity( def withdraw_liquidity( ctx: typer.Context, contract: str = typer.Option(None, help="AMM contract address"), + netuid: int = typer.Option(None, help="Subnet netuid"), + subnet_contract: str = typer.Option(None, help="Subnet manager contract address (required when using netuid)"), sender: str = typer.Option(..., help="Sender address (must match keystore address or wallet name)"), wallet_path: str = typer.Option(None, help="Wallet path (default from config)"), password: str = typer.Option(None, hide_input=True, help="Keystore password"), hetu_amount: float = typer.Option(..., help="HETU amount to withdraw (in HETU)"), alpha_amount: float = typer.Option(..., help="ALPHA amount to withdraw (in ALPHA)"), - to: str = typer.Option(..., help="Recipient address"), + to: str = typer.Option(None, help="Recipient address (defaults to sender address)"), ): """Withdraw liquidity from the pool""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "amm_address", contract) if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) + + amm_contract = get_amm_contract_address(ctx, contract, netuid, subnet_contract) config = ctx.obj wallet_path = wallet_path or get_wallet_path(config) keystore = load_keystore(sender, wallet_path) @@ -201,12 +277,18 @@ def withdraw_liquidity( except Exception as e: print(f"[red]Failed to decrypt keystore: {e}") raise typer.Exit(1) - amm = load_amm(contract, rpc) + + # Use sender address as default recipient if not specified + recipient = to or keystore["address"] + if not to: + print(f"[yellow]Using sender address as recipient: {recipient}") + + amm = load_amm(amm_contract, rpc) from_address = keystore["address"] nonce = amm.web3.eth.get_transaction_count(from_address) hetu_amount_wei = amm.web3.to_wei(hetu_amount, "ether") alpha_amount_wei = amm.web3.to_wei(alpha_amount, "ether") - tx = amm.contract.functions.withdrawLiquidity(hetu_amount_wei, alpha_amount_wei, to).build_transaction( + tx = amm.contract.functions.withdrawLiquidity(hetu_amount_wei, alpha_amount_wei, recipient).build_transaction( { "from": from_address, "nonce": nonce, @@ -228,19 +310,22 @@ def withdraw_liquidity( def swap_alpha_for_hetu( ctx: typer.Context, contract: str = typer.Option(None, help="AMM contract address"), + netuid: int = typer.Option(None, help="Subnet netuid"), + subnet_contract: str = typer.Option(None, help="Subnet manager contract address (required when using netuid)"), sender: str = typer.Option(..., help="Sender address (must match keystore address or wallet name)"), wallet_path: str = typer.Option(None, help="Wallet path (default from config)"), password: str = typer.Option(None, hide_input=True, help="Keystore password"), alpha_amount_in: float = typer.Option(..., help="Alpha amount in (in ALPHA)"), hetu_amount_out_min: float = typer.Option(..., help="Minimum HETU out (in HETU)"), - to: str = typer.Option(..., help="Recipient address"), + to: str = typer.Option(None, help="Recipient address (defaults to sender address)"), ): """Swap ALPHA for HETU""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "amm_address", contract) if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) + + amm_contract = get_amm_contract_address(ctx, contract, netuid, subnet_contract) config = ctx.obj wallet_path = wallet_path or get_wallet_path(config) keystore = load_keystore(sender, wallet_path) @@ -251,12 +336,18 @@ def swap_alpha_for_hetu( except Exception as e: print(f"[red]Failed to decrypt keystore: {e}") raise typer.Exit(1) - amm = load_amm(contract, rpc) + + # Use sender address as default recipient if not specified + recipient = to or keystore["address"] + if not to: + print(f"[yellow]Using sender address as recipient: {recipient}") + + amm = load_amm(amm_contract, rpc) from_address = keystore["address"] nonce = amm.web3.eth.get_transaction_count(from_address) alpha_amount_in_wei = amm.web3.to_wei(alpha_amount_in, "ether") hetu_amount_out_min_wei = amm.web3.to_wei(hetu_amount_out_min, "ether") - tx = amm.contract.functions.swapAlphaForHETU(alpha_amount_in_wei, hetu_amount_out_min_wei, to).build_transaction( + tx = amm.contract.functions.swapAlphaForHETU(alpha_amount_in_wei, hetu_amount_out_min_wei, recipient).build_transaction( { "from": from_address, "nonce": nonce, @@ -278,19 +369,22 @@ def swap_alpha_for_hetu( def swap_hetu_for_alpha( ctx: typer.Context, contract: str = typer.Option(None, help="AMM contract address"), + netuid: int = typer.Option(None, help="Subnet netuid"), + subnet_contract: str = typer.Option(None, help="Subnet manager contract address (required when using netuid)"), sender: str = typer.Option(..., help="Sender address (must match keystore address or wallet name)"), wallet_path: str = typer.Option(None, help="Wallet path (default from config)"), password: str = typer.Option(None, hide_input=True, help="Keystore password"), hetu_amount_in: float = typer.Option(..., help="HETU amount in (in HETU)"), alpha_amount_out_min: float = typer.Option(..., help="Minimum ALPHA out (in ALPHA)"), - to: str = typer.Option(..., help="Recipient address"), + to: str = typer.Option(None, help="Recipient address (defaults to sender address)"), ): """Swap HETU for ALPHA""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "amm_address", contract) if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) + + amm_contract = get_amm_contract_address(ctx, contract, netuid, subnet_contract) config = ctx.obj wallet_path = wallet_path or get_wallet_path(config) keystore = load_keystore(sender, wallet_path) @@ -301,12 +395,18 @@ def swap_hetu_for_alpha( except Exception as e: print(f"[red]Failed to decrypt keystore: {e}") raise typer.Exit(1) - amm = load_amm(contract, rpc) + + # Use sender address as default recipient if not specified + recipient = to or keystore["address"] + if not to: + print(f"[yellow]Using sender address as recipient: {recipient}") + + amm = load_amm(amm_contract, rpc) from_address = keystore["address"] nonce = amm.web3.eth.get_transaction_count(from_address) hetu_amount_in_wei = amm.web3.to_wei(hetu_amount_in, "ether") alpha_amount_out_min_wei = amm.web3.to_wei(alpha_amount_out_min, "ether") - tx = amm.contract.functions.swapHETUForAlpha(hetu_amount_in_wei, alpha_amount_out_min_wei, to).build_transaction( + tx = amm.contract.functions.swapHETUForAlpha(hetu_amount_in_wei, alpha_amount_out_min_wei, recipient).build_transaction( { "from": from_address, "nonce": nonce, @@ -322,4 +422,273 @@ def swap_hetu_for_alpha( if receipt.status == 1: print(f"[green]Swap HETU for ALPHA succeeded in block {receipt.blockNumber}") else: - print(f"[red]Swap HETU for ALPHA failed in block {receipt.blockNumber}") \ No newline at end of file + print(f"[red]Swap HETU for ALPHA failed in block {receipt.blockNumber}") + +@amm_app.command() +def approve_tokens( + ctx: typer.Context, + contract: str = typer.Option(None, help="AMM contract address"), + netuid: int = typer.Option(None, help="Subnet netuid"), + subnet_contract: str = typer.Option(None, help="Subnet manager contract address (required when using netuid)"), + sender: str = typer.Option(..., help="Sender address (must match keystore address or wallet name)"), + wallet_path: str = typer.Option(None, help="Wallet path (default from config)"), + password: str = typer.Option(None, hide_input=True, help="Keystore password"), + hetu_amount: float = typer.Option(None, help="HETU amount to approve (in HETU)"), + alpha_amount: float = typer.Option(None, help="ALPHA amount to approve (in ALPHA)"), +): + """Approve AMM contract to spend your tokens""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + + amm_contract = get_amm_contract_address(ctx, contract, netuid, subnet_contract) + config = ctx.obj + wallet_path = wallet_path or get_wallet_path(config) + keystore = load_keystore(sender, wallet_path) + if not password: + password = getpass.getpass("Keystore password: ") + try: + private_key = Account.decrypt(keystore, password) + except Exception as e: + print(f"[red]Failed to decrypt keystore: {e}") + raise typer.Exit(1) + + amm = load_amm(amm_contract, rpc) + from_address = keystore["address"] + + # Get token addresses from AMM contract + hetu_token_address = amm.hetuToken() + alpha_token_address = amm.alphaToken() + + print(f"[yellow]AMM Contract: {amm_contract}") + print(f"[yellow]HETU Token: {hetu_token_address}") + print(f"[yellow]ALPHA Token: {alpha_token_address}") + + # Load token contracts + from hetu_pycli.src.hetu.erc20 import load_erc20 + + if hetu_amount is not None: + print(f"[green]Approving HETU tokens...") + hetu_token = load_erc20(hetu_token_address, rpc) + hetu_amount_wei = hetu_token.web3.to_wei(hetu_amount, "ether") + + nonce = hetu_token.web3.eth.get_transaction_count(from_address) + tx = hetu_token.contract.functions.approve(amm_contract, hetu_amount_wei).build_transaction( + { + "from": from_address, + "nonce": nonce, + "gas": 100000, + "gasPrice": hetu_token.web3.eth.gas_price, + } + ) + signed = hetu_token.web3.eth.account.sign_transaction(tx, private_key) + tx_hash = hetu_token.web3.eth.send_raw_transaction(signed.raw_transaction) + print(f"[green]Broadcasted HETU approve tx hash: {tx_hash.hex()}") + print("[yellow]Waiting for transaction receipt...") + receipt = hetu_token.web3.eth.wait_for_transaction_receipt(tx_hash) + if receipt.status == 1: + print(f"[green]HETU approve succeeded in block {receipt.blockNumber}") + else: + print(f"[red]HETU approve failed in block {receipt.blockNumber}") + + if alpha_amount is not None: + print(f"[green]Approving ALPHA tokens...") + alpha_token = load_erc20(alpha_token_address, rpc) + alpha_amount_wei = alpha_token.web3.to_wei(alpha_amount, "ether") + + nonce = alpha_token.web3.eth.get_transaction_count(from_address) + tx = alpha_token.contract.functions.approve(amm_contract, alpha_amount_wei).build_transaction( + { + "from": from_address, + "nonce": nonce, + "gas": 100000, + "gasPrice": alpha_token.web3.eth.gas_price, + } + ) + signed = alpha_token.web3.eth.account.sign_transaction(tx, private_key) + tx_hash = alpha_token.web3.eth.send_raw_transaction(signed.raw_transaction) + print(f"[green]Broadcasted ALPHA approve tx hash: {tx_hash.hex()}") + print("[yellow]Waiting for transaction receipt...") + receipt = alpha_token.web3.eth.wait_for_transaction_receipt(tx_hash) + if receipt.status == 1: + print(f"[green]ALPHA approve succeeded in block {receipt.blockNumber}") + else: + print(f"[red]ALPHA approve failed in block {receipt.blockNumber}") + + if hetu_amount is None and alpha_amount is None: + print("[yellow]No amounts specified. Use --hetu-amount or --alpha-amount to approve tokens.") + +@amm_app.command() +def check_approval( + ctx: typer.Context, + contract: str = typer.Option(None, help="AMM contract address"), + netuid: int = typer.Option(None, help="Subnet netuid"), + subnet_contract: str = typer.Option(None, help="Subnet manager contract address (required when using netuid)"), + sender: str = typer.Option(..., help="Sender address (must match keystore address or wallet name)"), + wallet_path: str = typer.Option(None, help="Wallet path (default from config)"), +): + """Check token approval and balance status""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + + amm_contract = get_amm_contract_address(ctx, contract, netuid, subnet_contract) + config = ctx.obj + wallet_path = wallet_path or get_wallet_path(config) + keystore = load_keystore(sender, wallet_path) + from_address = keystore["address"] + + amm = load_amm(amm_contract, rpc) + + # Get token addresses from AMM contract + hetu_token_address = amm.hetuToken() + alpha_token_address = amm.alphaToken() + + print(f"[yellow]AMM Contract: {amm_contract}") + print(f"[yellow]User Address: {from_address}") + print(f"[yellow]HETU Token: {hetu_token_address}") + print(f"[yellow]ALPHA Token: {alpha_token_address}") + + # Load token contracts + from hetu_pycli.src.hetu.erc20 import load_erc20 + + # Check HETU token + hetu_token = load_erc20(hetu_token_address, rpc) + hetu_balance = hetu_token.balanceOf(from_address) + hetu_allowance = hetu_token.allowance(from_address, amm_contract) + hetu_balance_ether = hetu_token.web3.from_wei(hetu_balance, "ether") + hetu_allowance_ether = hetu_token.web3.from_wei(hetu_allowance, "ether") + + print(f"\n[green]HETU Token Status:") + print(f" Balance: {hetu_balance_ether} HETU") + print(f" Allowance: {hetu_allowance_ether} HETU") + print(f" Raw Balance: {hetu_balance}") + print(f" Raw Allowance: {hetu_allowance}") + + # Check ALPHA token + alpha_token = load_erc20(alpha_token_address, rpc) + alpha_balance = alpha_token.balanceOf(from_address) + alpha_allowance = alpha_token.allowance(from_address, amm_contract) + alpha_balance_ether = alpha_token.web3.from_wei(alpha_balance, "ether") + alpha_allowance_ether = alpha_token.web3.from_wei(alpha_allowance, "ether") + + print(f"\n[green]ALPHA Token Status:") + print(f" Balance: {alpha_balance_ether} ALPHA") + print(f" Allowance: {alpha_allowance_ether} ALPHA") + print(f" Raw Balance: {alpha_balance}") + print(f" Raw Allowance: {alpha_allowance}") + + # Check AMM pool status + pool_info = amm.getPoolInfo() + print(f"\n[green]AMM Pool Status:") + print(f" Mechanism: {pool_info[0]}") + print(f" HETU Reserve: {pool_info[1]}") + print(f" ALPHA Reserve: {pool_info[2]}") + print(f" ALPHA Out: {pool_info[3]}") + print(f" Current Price: {pool_info[4]}") + print(f" Moving Price: {pool_info[5]}") + + # Simulate swap + hetu_amount_wei = hetu_token.web3.to_wei(10.0, "ether") + simulated_alpha = amm.simSwapHETUForAlpha(hetu_amount_wei) + simulated_alpha_ether = hetu_token.web3.from_wei(simulated_alpha, "ether") + + print(f"\n[green]Swap Simulation (10 HETU):") + print(f" Expected ALPHA Out: {simulated_alpha_ether}") + print(f" Raw Expected: {simulated_alpha}") + + # Check if swap would succeed + if hetu_balance < hetu_amount_wei: + print(f"[red]❌ Insufficient HETU balance") + else: + print(f"[green]✅ Sufficient HETU balance") + + if hetu_allowance < hetu_amount_wei: + print(f"[red]❌ Insufficient HETU allowance") + else: + print(f"[green]✅ Sufficient HETU allowance") + + if simulated_alpha == 0: + print(f"[red]❌ Swap simulation failed - no liquidity or other issue") + else: + print(f"[green]✅ Swap simulation successful") + +@amm_app.command() +def pool_status( + ctx: typer.Context, + contract: str = typer.Option(None, help="AMM contract address"), + netuid: int = typer.Option(None, help="Subnet netuid"), + subnet_contract: str = typer.Option(None, help="Subnet manager contract address (required when using netuid)"), +): + """Check AMM pool system status and initialization""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + + amm_contract = get_amm_contract_address(ctx, contract, netuid, subnet_contract) + amm = load_amm(amm_contract, rpc) + + print(f"[yellow]AMM Pool Status Check:") + print(f" Contract: {amm_contract}") + + # Get system info + system_info = amm.getSystemInfo() + print(f"\n[green]System Information:") + print(f" System Address: {system_info[0]}") + print(f" Subnet Contract: {system_info[1]}") + + # Get creator info + creator_info = amm.getCreatorInfo() + print(f"\n[green]Creator Information:") + print(f" Creator: {creator_info[0]}") + print(f" Created At: {creator_info[1]}") + print(f" Netuid: {creator_info[2]}") + + # Get pool info + pool_info = amm.getPoolInfo() + print(f"\n[green]Pool Information:") + print(f" Mechanism: {pool_info[0]} (0=Stable, 1=Dynamic)") + print(f" HETU Reserve: {pool_info[1]}") + print(f" ALPHA Reserve: {pool_info[2]}") + print(f" ALPHA Out: {pool_info[3]}") + print(f" Current Price: {pool_info[4]}") + print(f" Moving Price: {pool_info[5]}") + print(f" Total Volume: {pool_info[6]}") + print(f" Min Liquidity: {pool_info[7]}") + + # Get token addresses + hetu_token = amm.hetuToken() + alpha_token = amm.alphaToken() + print(f"\n[green]Token Addresses:") + print(f" HETU Token: {hetu_token}") + print(f" ALPHA Token: {alpha_token}") + + # Check if pool is initialized + hetu_reserve = pool_info[1] + alpha_reserve = pool_info[2] + min_liquidity = pool_info[7] + + print(f"\n[green]Initialization Status:") + if hetu_reserve >= min_liquidity and alpha_reserve >= min_liquidity: + print(f" ✅ Pool is properly initialized") + else: + print(f" ❌ Pool needs initialization") + print(f" Required minimum liquidity: {min_liquidity}") + print(f" Current HETU reserve: {hetu_reserve}") + print(f" Current ALPHA reserve: {alpha_reserve}") + + # Check mechanism type + mechanism = pool_info[0] + if mechanism == 0: + print(f" Mechanism: Stable (1:1 exchange)") + else: + print(f" Mechanism: Dynamic (AMM exchange)") + + # Check if trading is possible + if hetu_reserve > 0 and alpha_reserve > 0: + print(f" ✅ Trading is possible") + else: + print(f" ❌ Trading is not possible - insufficient liquidity") \ No newline at end of file diff --git a/hetu_pycli/src/hetu/dendron.py b/hetu_pycli/src/hetu/dendron.py new file mode 100644 index 0000000..183186e --- /dev/null +++ b/hetu_pycli/src/hetu/dendron.py @@ -0,0 +1,620 @@ +import typer +from rich import print +from web3 import Web3 +import json +import os +from hetu_pycli.src.hetu.wrapper.dendron_mgr import DendronMgr +from eth_account import Account +from hetu_pycli.src.commands.wallet import load_keystore, get_wallet_path +import getpass + +DENDRON_ABI_PATH = os.path.join( + os.path.dirname(__file__), "../../../contracts/DendronManager.abi" +) + +dendron_app = typer.Typer(help="Dendron manager contract operations") + +def get_contract_address(ctx, cli_contract_key: str, param_contract: str): + config = ctx.obj or {} + contract_addr = param_contract or config.get(cli_contract_key) + if not contract_addr: + print(f"[red]No contract address provided or found in config for {cli_contract_key}.") + raise typer.Exit(1) + print(f"[yellow]Using contract address: {contract_addr}") + return contract_addr + +def load_dendron_mgr(contract: str, rpc: str): + abi_path = os.path.abspath(DENDRON_ABI_PATH) + if not os.path.exists(abi_path): + print(f"[red]ABI file not found: {abi_path}") + raise typer.Exit(1) + with open(abi_path, "r") as f: + abi = json.load(f) + provider = Web3.HTTPProvider(rpc) + return DendronMgr(contract, provider, abi) + +@dendron_app.command() +def get_dendron_info( + ctx: typer.Context, + contract: str = typer.Option(None, help="Dendron manager contract address"), + netuid: int = typer.Option(..., help="Subnet netuid"), + account: str = typer.Option(..., help="Dendron account address or wallet name"), +): + """Query dendron info by netuid and account""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "dendron_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + + # 检查是否是钱包名称并转换为地址 + address = account + config = ctx.obj + wallet_path = get_wallet_path(config) + if not (address.startswith('0x') and len(address) == 42): + try: + keystore = load_keystore(account, wallet_path) + address = keystore.get("address") + print(f"[yellow]Converted wallet name '{account}' to address: {address}") + except Exception: + print(f"[red]Wallet not found: {account}") + raise typer.Exit(1) + account = address + + mgr = load_dendron_mgr(contract, rpc) + print(f"[green]Dendron Info: {mgr.getDendronInfo(netuid, account)}") + +@dendron_app.command() +def get_subnet_dendron_count( + ctx: typer.Context, + contract: str = typer.Option(None, help="Dendron manager contract address"), + netuid: int = typer.Option(..., help="Subnet netuid"), +): + """Query dendron count in a subnet""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "dendron_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + mgr = load_dendron_mgr(contract, rpc) + print(f"[green]Subnet Dendron Count: {mgr.getSubnetDendronCount(netuid)}") + +@dendron_app.command() +def get_subnet_dendrons( + ctx: typer.Context, + contract: str = typer.Option(None, help="Dendron manager contract address"), + netuid: int = typer.Option(..., help="Subnet netuid"), +): + """Query all dendron addresses in a subnet""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "dendron_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + mgr = load_dendron_mgr(contract, rpc) + print(f"[green]Subnet Dendrons: {mgr.getSubnetDendrons(netuid)}") + +@dendron_app.command() +def get_subnet_validator_count( + ctx: typer.Context, + contract: str = typer.Option(None, help="Dendron manager contract address"), + netuid: int = typer.Option(..., help="Subnet netuid"), +): + """Query validator count in a subnet""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "dendron_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + mgr = load_dendron_mgr(contract, rpc) + print(f"[green]Subnet Validator Count: {mgr.getSubnetValidatorCount(netuid)}") + +@dendron_app.command() +def get_subnet_validators( + ctx: typer.Context, + contract: str = typer.Option(None, help="Dendron manager contract address"), + netuid: int = typer.Option(..., help="Subnet netuid"), +): + """Query all validator addresses in a subnet""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "dendron_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + mgr = load_dendron_mgr(contract, rpc) + print(f"[green]Subnet Validators: {mgr.getSubnetValidators(netuid)}") + +@dendron_app.command() +def is_dendron( + ctx: typer.Context, + contract: str = typer.Option(None, help="Dendron manager contract address"), + netuid: int = typer.Option(..., help="Subnet netuid"), + account: str = typer.Option(..., help="Dendron account address or wallet name"), +): + """Check if account is a dendron in subnet""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "dendron_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + + # 检查是否是钱包名称并转换为地址 + address = account + config = ctx.obj + wallet_path = get_wallet_path(config) + if not (address.startswith('0x') and len(address) == 42): + try: + keystore = load_keystore(account, wallet_path) + address = keystore.get("address") + print(f"[yellow]Converted wallet name '{account}' to address: {address}") + except Exception: + print(f"[red]Wallet not found: {account}") + raise typer.Exit(1) + account = address + + mgr = load_dendron_mgr(contract, rpc) + print(f"[green]Is Dendron: {mgr.isDendron(netuid, account)}") + +@dendron_app.command() +def is_validator( + ctx: typer.Context, + contract: str = typer.Option(None, help="Dendron manager contract address"), + netuid: int = typer.Option(..., help="Subnet netuid"), + account: str = typer.Option(..., help="Dendron account address or wallet name"), +): + """Check if account is a validator in subnet""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "dendron_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + + # 检查是否是钱包名称并转换为地址 + address = account + config = ctx.obj + wallet_path = get_wallet_path(config) + if not (address.startswith('0x') and len(address) == 42): + try: + keystore = load_keystore(account, wallet_path) + address = keystore.get("address") + print(f"[yellow]Converted wallet name '{account}' to address: {address}") + except Exception: + print(f"[red]Wallet not found: {account}") + raise typer.Exit(1) + account = address + + mgr = load_dendron_mgr(contract, rpc) + print(f"[green]Is Validator: {mgr.isValidator(netuid, account)}") + +@dendron_app.command() +def dendron_list( + ctx: typer.Context, + contract: str = typer.Option(None, help="Dendron manager contract address"), + netuid: int = typer.Option(..., help="Subnet netuid"), + idx: int = typer.Option(..., help="Index (uint256)"), +): + """Query dendronList(netuid, idx)""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "dendron_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + mgr = load_dendron_mgr(contract, rpc) + print(f"[green]dendronList: {mgr.dendronList(netuid, idx)}") + +@dendron_app.command() +def dendrons( + ctx: typer.Context, + contract: str = typer.Option(None, help="Dendron manager contract address"), + netuid: int = typer.Option(..., help="Subnet netuid"), + account: str = typer.Option(..., help="Dendron account address"), +): + """Query dendrons(netuid, account)""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "dendron_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + mgr = load_dendron_mgr(contract, rpc) + print(f"[green]dendrons: {mgr.dendrons(netuid, account)}") + +@dendron_app.command() +def can_register_dendron( + ctx: typer.Context, + contract: str = typer.Option(None, help="Dendron manager contract address"), + user: str = typer.Option(..., help="User address to check"), + netuid: int = typer.Option(..., help="Subnet netuid"), + is_validator_role: bool = typer.Option(False, "--is-validator-role/--no-validator-role", help="Is validator role"), +): + """Check if a user can register as a dendron""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "dendron_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + mgr = load_dendron_mgr(contract, rpc) + + # 由于 ABI 中没有 canRegisterDendron 函数,我们显示基本信息 + print(f"[yellow]Checking registration eligibility for user {user} on subnet {netuid}") + print(f"[yellow]Validator role: {is_validator_role}") + print(f"[yellow]Note: canRegisterDendron function not available in current ABI") + print(f"[yellow]Please check subnet status and user stake allocation manually") + +@dendron_app.command( + name="regist" +) +def register_dendron( + ctx: typer.Context, + contract: str = typer.Option(None, help="Dendron manager contract address"), + sender: str = typer.Option(..., help="Sender address (must match keystore address or wallet name)"), + wallet_path: str = typer.Option(None, help="Wallet path (default from config)"), + password: str = typer.Option(None, hide_input=True, help="Keystore password"), + netuid: int = typer.Option(..., help="Subnet netuid"), + is_validator_role: bool = typer.Option(False, "--is-validator-role/--no-validator-role", help="Is validator role?"), + axon_endpoint: str = typer.Option(..., help="Axon endpoint"), + axon_port: int = typer.Option(..., help="Axon port (uint32)"), + prometheus_endpoint: str = typer.Option(..., help="Prometheus endpoint"), + prometheus_port: int = typer.Option(..., help="Prometheus port (uint32)"), +): + """Register a dendron with automatic cost and stake requirement detection""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "dendron_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + + config = ctx.obj + wallet_path = wallet_path or get_wallet_path(config) + keystore = load_keystore(sender, wallet_path) + if not password: + password = getpass.getpass("Keystore password: ") + try: + private_key = Account.decrypt(keystore, password) + except Exception as e: + print(f"[red]Failed to decrypt keystore: {e}") + raise typer.Exit(1) + + from_address = keystore["address"] + + # Load dendron manager + mgr = load_dendron_mgr(contract, rpc) + + # Get subnet manager contract to query parameters + from hetu_pycli.src.hetu.subnet import load_subnet_mgr, get_contract_address as get_subnet_contract + subnet_contract = get_subnet_contract(ctx, "subnet_address", None) + subnet_mgr = load_subnet_mgr(subnet_contract, rpc) + + # Get subnet parameters to determine required costs and thresholds + print(f"[yellow]Querying subnet {netuid} parameters...") + try: + subnet_params = subnet_mgr.getSubnetParams(netuid) + + # Extract required parameters based on ABI structure + # Note: These indices are based on the ABI structure shown in the wrapper + base_burn_cost = subnet_params[10] # baseBurnCost (might be baseDendronCost) + validator_threshold = subnet_params[19] # validatorThreshold + dendron_threshold = subnet_params[20] # dendronThreshold + + print(f"[green]Subnet {netuid} parameters:") + print(f"[green] - Base burn cost: {base_burn_cost} wei") + print(f"[green] - Validator threshold: {validator_threshold} wei") + print(f"[green] - Dendron threshold: {dendron_threshold} wei") + + # Determine required stake amount based on role + if is_validator_role: + required_stake_wei = validator_threshold + print(f"[green]Required stake (validator): {mgr.web3.from_wei(required_stake_wei, 'ether')} HETU") + else: + required_stake_wei = dendron_threshold + print(f"[green]Required stake (dendron): {mgr.web3.from_wei(required_stake_wei, 'ether')} HETU") + + # Total required = stake + registration cost + total_required_wei = required_stake_wei + base_burn_cost + total_required_hetu = mgr.web3.from_wei(total_required_wei, "ether") + print(f"[green]Total required (stake + registration): {total_required_hetu} HETU") + + except Exception as e: + print(f"[yellow]Warning: Could not get subnet parameters: {e}") + print(f"[yellow]Using default values:") + print(f"[yellow] - Required stake: 100 HETU") + print(f"[yellow] - Registration cost: 10 HETU") + print(f"[yellow] - Total required: 110 HETU") + required_stake_wei = mgr.web3.to_wei(100, "ether") + base_burn_cost = mgr.web3.to_wei(10, "ether") + total_required_wei = required_stake_wei + base_burn_cost + total_required_hetu = 110.0 + + # Check if user has sufficient global stake + print(f"[yellow]Checking global stake allocation...") + staking_contract = get_contract_address(ctx, "staking_address", None) + from hetu_pycli.src.hetu.staking import load_staking + staking = load_staking(staking_contract, rpc) + + # Use the correct getAvailableStake function which calculates: totalStaked - totalAllocated - totalCost + available_stake = staking.getAvailableStake(from_address) + available_stake_hetu = staking.web3.from_wei(available_stake, "ether") + + # Also get detailed stake info for display + stake_info = staking.getStakeInfo(from_address) + total_staked = stake_info[0] # totalStaked + total_allocated = stake_info[1] # totalAllocated + total_cost = stake_info[2] # totalCost + + print(f"[green]Stake info for {from_address}:") + print(f"[green] - Total staked: {staking.web3.from_wei(total_staked, 'ether')} HETU") + print(f"[green] - Total allocated: {staking.web3.from_wei(total_allocated, 'ether')} HETU") + print(f"[green] - Total cost consumed: {staking.web3.from_wei(total_cost, 'ether')} HETU") + print(f"[green] - Available for allocation: {available_stake_hetu} HETU") + + if available_stake < total_required_wei: + print(f"[red]Insufficient available stake!") + print(f"[red]Required: {total_required_hetu} HETU") + print(f"[red]Available: {available_stake_hetu} HETU") + print(f"[yellow]Please add more global stake using: hetucli stake add-stake --sender {sender} --amount {total_required_hetu}") + raise typer.Exit(1) + + print(f"[green]Available stake sufficient: {available_stake_hetu} HETU") + + # Load WHETU contract for allowance check + from hetu_pycli.src.hetu.whetu import load_whetu + whetu_contract = get_contract_address(ctx, "whetu_address", None) + whetu = load_whetu(whetu_contract, rpc) + + # Check current allowance for staking contract + print(f"[yellow]Checking WHETU allowance for staking...") + current_allowance = whetu.allowance(from_address, staking_contract) + decimals = whetu.decimals() + + if current_allowance < required_stake_wei: + print(f"[yellow]Insufficient allowance. Approving {mgr.web3.from_wei(required_stake_wei, 'ether')} WHETU for staking...") + nonce = whetu.web3.eth.get_transaction_count(from_address) + approve_tx = whetu.contract.functions.approve(staking_contract, required_stake_wei).build_transaction( + { + "from": from_address, + "nonce": nonce, + "gas": 100000, + "gasPrice": whetu.web3.eth.gas_price, + } + ) + signed_approve = whetu.web3.eth.account.sign_transaction(approve_tx, private_key) + approve_tx_hash = whetu.web3.eth.send_raw_transaction(signed_approve.raw_transaction) + print(f"[green]Broadcasted approve tx hash: {approve_tx_hash.hex()}") + print("[yellow]Waiting for approve transaction receipt...") + approve_receipt = whetu.web3.eth.wait_for_transaction_receipt(approve_tx_hash) + if approve_receipt.status == 1: + print(f"[green]Approve succeeded in block {approve_receipt.blockNumber}") + else: + print(f"[red]Approve failed in block {approve_receipt.blockNumber}") + raise typer.Exit(1) + else: + print(f"[green]Sufficient allowance already exists: {current_allowance / (10 ** decimals)} WHETU") + + # Now register the dendron + print(f"[yellow]Registering dendron with {mgr.web3.from_wei(required_stake_wei, 'ether')} HETU stake...") + nonce = mgr.web3.eth.get_transaction_count(from_address) + + tx = mgr.contract.functions.registerNeuronWithStakeAllocation( + netuid, required_stake_wei, is_validator_role, axon_endpoint, axon_port, prometheus_endpoint, prometheus_port + ).build_transaction( + { + "from": from_address, + "nonce": nonce, + "gas": 500000, + "gasPrice": mgr.web3.eth.gas_price, + } + ) + signed = mgr.web3.eth.account.sign_transaction(tx, private_key) + tx_hash = mgr.web3.eth.send_raw_transaction(signed.raw_transaction) + print(f"[green]Broadcasted register dendron tx hash: {tx_hash.hex()}") + print("[yellow]Waiting for transaction receipt...") + receipt = mgr.web3.eth.wait_for_transaction_receipt(tx_hash) + if receipt.status == 1: + print(f"[green]Register dendron succeeded in block {receipt.blockNumber}") + print(f"[green]Dendron registered with {mgr.web3.from_wei(required_stake_wei, 'ether')} HETU stake") + print(f"[green]Registration cost: {mgr.web3.from_wei(base_burn_cost, 'ether')} HETU") + print(f"[green]Total cost: {total_required_hetu} HETU") + else: + print(f"[red]Register dendron failed in block {receipt.blockNumber}, receipt {receipt}") + +@dendron_app.command() +def deregister_dendron( + ctx: typer.Context, + contract: str = typer.Option(None, help="Dendron manager contract address"), + sender: str = typer.Option(None, help="Sender address (must match keystore address or wallet name)"), + wallet_path: str = typer.Option(None, help="Wallet path (default from config)"), + password: str = typer.Option(None, hide_input=True, help="Keystore password"), + netuid: int = typer.Option(..., help="Subnet netuid to deregister from"), +): + """Deregister a dendron (write tx)""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "dendron_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + config = ctx.obj + wallet_path = wallet_path or get_wallet_path(config) + keystore = load_keystore(sender, wallet_path) + if not password: + password = getpass.getpass("Keystore password: ") + try: + private_key = Account.decrypt(keystore, password) + except Exception as e: + print(f"[red]Failed to decrypt keystore: {e}") + raise typer.Exit(1) + mgr = load_dendron_mgr(contract, rpc) + from_address = keystore["address"] + + # Use the wrapper method instead of direct contract call + try: + # First check if the dendron exists and is active + dendron_info = mgr.getDendronInfo(netuid, from_address) + if not dendron_info[2]: # isActive field + print(f"[yellow]Dendron in subnet {netuid} is not active or doesn't exist") + raise typer.Exit(1) + except Exception as e: + print(f"[red]Failed to get dendron info: {e}") + raise typer.Exit(1) + + nonce = mgr.web3.eth.get_transaction_count(from_address) + + # Build transaction using the wrapper method + tx = mgr.contract.functions.deregisterNeuron(netuid).build_transaction( + { + "from": from_address, + "nonce": nonce, + "gas": 200000, + "gasPrice": mgr.web3.eth.gas_price, + } + ) + signed = mgr.web3.eth.account.sign_transaction(tx, private_key) + tx_hash = mgr.web3.eth.send_raw_transaction(signed.raw_transaction) + print(f"[green]Broadcasted deregister dendron tx hash: {tx_hash.hex()}") + print("[yellow]Waiting for transaction receipt...") + receipt = mgr.web3.eth.wait_for_transaction_receipt(tx_hash) + if receipt.status == 1: + print(f"[green]Deregister dendron succeeded in block {receipt.blockNumber}") + else: + print(f"[red]Deregister dendron failed in block {receipt.blockNumber}, receipt {receipt}") + +@dendron_app.command() +def get_user_role( + ctx: typer.Context, + contract: str = typer.Option(None, help="Dendron manager contract address"), + netuid: int = typer.Option(..., help="Subnet netuid"), + user: str = typer.Option(..., help="User address or wallet name to check"), +): + """Get user role in a subnet (validator, miner, or not registered)""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "dendron_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + + # 检查是否是钱包名称并转换为地址 + address = user + config = ctx.obj + wallet_path = get_wallet_path(config) + if not (address.startswith('0x') and len(address) == 42): + try: + keystore = load_keystore(user, wallet_path) + address = keystore.get("address") + print(f"[yellow]Converted wallet name '{user}' to address: {address}") + except Exception: + print(f"[red]Wallet not found: {user}") + raise typer.Exit(1) + user = address + + mgr = load_dendron_mgr(contract, rpc) + + try: + # 检查用户是否在子网中注册 + dendron_info = mgr.getDendronInfo(netuid, address) + + # 检查是否激活 + if not dendron_info[2]: # isActive field + print(f"[yellow]User {address} is not active in subnet {netuid}") + print(f"[yellow]Status: Not registered or inactive") + return + + # 检查是否是验证者 + is_validator = dendron_info[3] # isValidator field + stake_amount = dendron_info[4] # stake field + registration_block = dendron_info[5] # registrationBlock field + + stake_hetu = mgr.web3.from_wei(stake_amount, "ether") + + if is_validator: + print(f"[green]User {address} in subnet {netuid}:") + print(f"[green] Role: Validator") + print(f"[green] Stake: {stake_hetu} HETU") + print(f"[green] Registration Block: {registration_block}") + else: + print(f"[green]User {address} in subnet {netuid}:") + print(f"[green] Role: Miner") + print(f"[green] Stake: {stake_hetu} HETU") + print(f"[green] Registration Block: {registration_block}") + + except Exception as e: + print(f"[yellow]User {address} is not registered in subnet {netuid}") + print(f"[yellow]Status: Not joined") + +@dendron_app.command() +def update_dendron_service( + ctx: typer.Context, + contract: str = typer.Option(None, help="Dendron manager contract address"), + sender: str = typer.Option(..., help="Sender address (must match keystore address or wallet name)"), + wallet_path: str = typer.Option(None, help="Wallet path (default from config)"), + password: str = typer.Option(None, hide_input=True, help="Keystore password"), + netuid: int = typer.Option(..., help="Subnet netuid"), + axon_endpoint: str = typer.Option(..., help="New axon endpoint"), + axon_port: int = typer.Option(..., help="New axon port (uint32)"), + prometheus_endpoint: str = typer.Option(..., help="New prometheus endpoint"), + prometheus_port: int = typer.Option(..., help="New prometheus port (uint32)"), +): + """Update dendron service information (endpoints and ports)""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "dendron_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + + config = ctx.obj + wallet_path = wallet_path or get_wallet_path(config) + keystore = load_keystore(sender, wallet_path) + if not password: + password = getpass.getpass("Keystore password: ") + try: + private_key = Account.decrypt(keystore, password) + except Exception as e: + print(f"[red]Failed to decrypt keystore: {e}") + raise typer.Exit(1) + + mgr = load_dendron_mgr(contract, rpc) + from_address = keystore["address"] + + # Check if the dendron exists and is active + try: + dendron_info = mgr.getDendronInfo(netuid, from_address) + if not dendron_info[2]: # isActive field + print(f"[red]Dendron in subnet {netuid} is not active or doesn't exist") + raise typer.Exit(1) + print(f"[green]Current dendron info for subnet {netuid}:") + print(f"[green] Axon endpoint: {dendron_info[7]}") # axonEndpoint + print(f"[green] Axon port: {dendron_info[8]}") # axonPort + print(f"[green] Prometheus endpoint: {dendron_info[9]}") # prometheusEndpoint + print(f"[green] Prometheus port: {dendron_info[10]}") # prometheusPort + except Exception as e: + print(f"[red]Failed to get dendron info: {e}") + raise typer.Exit(1) + + # Update the service information + print(f"[yellow]Updating dendron service information...") + print(f"[yellow]New axon endpoint: {axon_endpoint}:{axon_port}") + print(f"[yellow]New prometheus endpoint: {prometheus_endpoint}:{prometheus_port}") + + nonce = mgr.web3.eth.get_transaction_count(from_address) + tx = mgr.contract.functions.updateNeuronService( + netuid, axon_endpoint, axon_port, prometheus_endpoint, prometheus_port + ).build_transaction( + { + "from": from_address, + "nonce": nonce, + "gas": 200000, + "gasPrice": mgr.web3.eth.gas_price, + } + ) + + signed = mgr.web3.eth.account.sign_transaction(tx, private_key) + tx_hash = mgr.web3.eth.send_raw_transaction(signed.raw_transaction) + print(f"[green]Broadcasted update dendron service tx hash: {tx_hash.hex()}") + print("[yellow]Waiting for transaction receipt...") + + receipt = mgr.web3.eth.wait_for_transaction_receipt(tx_hash) + if receipt.status == 1: + print(f"[green]Update dendron service succeeded in block {receipt.blockNumber}") + print(f"[green]✅ Dendron service information updated successfully!") + print(f"[green]📋 Subnet: {netuid}") + print(f"[green]🔗 Transaction: {tx_hash.hex()}") + print(f"[green]📦 Block: {receipt.blockNumber}") + else: + print(f"[red]Update dendron service failed in block {receipt.blockNumber}, receipt {receipt}") \ No newline at end of file diff --git a/hetu_pycli/src/hetu/hetu.py b/hetu_pycli/src/hetu/hetu.py new file mode 100644 index 0000000..8ca5037 --- /dev/null +++ b/hetu_pycli/src/hetu/hetu.py @@ -0,0 +1,35 @@ +import typer +from rich import print +from web3 import Web3 +from hetu_pycli.src.commands.wallet import load_keystore, get_wallet_path + +hetu_app = typer.Typer(help="Native HETU operations") + +@hetu_app.command() +def balance_of( + ctx: typer.Context, + name: str = typer.Argument(..., help="Wallet name or address to query"), +): + """Query native HETU balance of an account""" + config = ctx.obj + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + + address = name + wallet_path = get_wallet_path(config) + if not (address.startswith('0x') and len(address) == 42): + try: + keystore = load_keystore(name, wallet_path) + address = keystore.get("address") + except Exception: + print(f"[red]Wallet not found: {name}") + raise typer.Exit(1) + + provider = Web3.HTTPProvider(rpc) + web3 = Web3(provider) + balance_wei = web3.eth.get_balance(address) + balance_hetu = web3.from_wei(balance_wei, "ether") + print(f"[green]Native HETU Balance: {balance_hetu} HETU") + print(f"[yellow]Raw balance: {balance_wei} wei") \ No newline at end of file diff --git a/hetu_pycli/src/hetu/neuron.py b/hetu_pycli/src/hetu/neuron.py deleted file mode 100644 index 45653ed..0000000 --- a/hetu_pycli/src/hetu/neuron.py +++ /dev/null @@ -1,292 +0,0 @@ -import typer -from rich import print -from web3 import Web3 -import json -import os -from hetu_pycli.src.hetu.wrapper.neuron_mgr import NeuronMgr -from eth_account import Account -from hetu_pycli.src.commands.wallet import load_keystore, get_wallet_path -import getpass - -NEURON_ABI_PATH = os.path.join( - os.path.dirname(__file__), "../../../contracts/NeuronManager.abi" -) - -neuron_app = typer.Typer(help="Neuron manager contract operations") - -def get_contract_address(ctx, cli_contract_key: str, param_contract: str): - config = ctx.obj or {} - contract_addr = param_contract or config.get(cli_contract_key) - if not contract_addr: - print(f"[red]No contract address provided or found in config for {cli_contract_key}.") - raise typer.Exit(1) - print(f"[yellow]Using contract address: {contract_addr}") - return contract_addr - -def load_neuron_mgr(contract: str, rpc: str): - abi_path = os.path.abspath(NEURON_ABI_PATH) - if not os.path.exists(abi_path): - print(f"[red]ABI file not found: {abi_path}") - raise typer.Exit(1) - with open(abi_path, "r") as f: - abi = json.load(f) - provider = Web3.HTTPProvider(rpc) - return NeuronMgr(contract, provider, abi) - -@neuron_app.command() -def get_neuron_info( - ctx: typer.Context, - contract: str = typer.Option(None, help="Neuron manager contract address"), - netuid: int = typer.Option(..., help="Subnet netuid"), - account: str = typer.Option(..., help="Neuron account address"), -): - """Query neuron info by netuid and account""" - rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "neuron_address", contract) - if not rpc: - print("[red]No RPC URL found in config or CLI.") - raise typer.Exit(1) - mgr = load_neuron_mgr(contract, rpc) - print(f"[green]Neuron Info: {mgr.getNeuronInfo(netuid, account)}") - -@neuron_app.command() -def get_subnet_neuron_count( - ctx: typer.Context, - contract: str = typer.Option(None, help="Neuron manager contract address"), - netuid: int = typer.Option(..., help="Subnet netuid"), -): - """Query neuron count in a subnet""" - rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "neuron_address", contract) - if not rpc: - print("[red]No RPC URL found in config or CLI.") - raise typer.Exit(1) - mgr = load_neuron_mgr(contract, rpc) - print(f"[green]Subnet Neuron Count: {mgr.getSubnetNeuronCount(netuid)}") - -@neuron_app.command() -def get_subnet_neurons( - ctx: typer.Context, - contract: str = typer.Option(None, help="Neuron manager contract address"), - netuid: int = typer.Option(..., help="Subnet netuid"), -): - """Query all neuron addresses in a subnet""" - rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "neuron_address", contract) - if not rpc: - print("[red]No RPC URL found in config or CLI.") - raise typer.Exit(1) - mgr = load_neuron_mgr(contract, rpc) - print(f"[green]Subnet Neurons: {mgr.getSubnetNeurons(netuid)}") - -@neuron_app.command() -def get_subnet_validator_count( - ctx: typer.Context, - contract: str = typer.Option(None, help="Neuron manager contract address"), - netuid: int = typer.Option(..., help="Subnet netuid"), -): - """Query validator count in a subnet""" - rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "neuron_address", contract) - if not rpc: - print("[red]No RPC URL found in config or CLI.") - raise typer.Exit(1) - mgr = load_neuron_mgr(contract, rpc) - print(f"[green]Subnet Validator Count: {mgr.getSubnetValidatorCount(netuid)}") - -@neuron_app.command() -def get_subnet_validators( - ctx: typer.Context, - contract: str = typer.Option(None, help="Neuron manager contract address"), - netuid: int = typer.Option(..., help="Subnet netuid"), -): - """Query all validator addresses in a subnet""" - rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "neuron_address", contract) - if not rpc: - print("[red]No RPC URL found in config or CLI.") - raise typer.Exit(1) - mgr = load_neuron_mgr(contract, rpc) - print(f"[green]Subnet Validators: {mgr.getSubnetValidators(netuid)}") - -@neuron_app.command() -def is_neuron( - ctx: typer.Context, - contract: str = typer.Option(None, help="Neuron manager contract address"), - netuid: int = typer.Option(..., help="Subnet netuid"), - account: str = typer.Option(..., help="Neuron account address"), -): - """Check if account is a neuron in subnet""" - rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "neuron_address", contract) - if not rpc: - print("[red]No RPC URL found in config or CLI.") - raise typer.Exit(1) - mgr = load_neuron_mgr(contract, rpc) - print(f"[green]Is Neuron: {mgr.isNeuron(netuid, account)}") - -@neuron_app.command() -def is_validator( - ctx: typer.Context, - contract: str = typer.Option(None, help="Neuron manager contract address"), - netuid: int = typer.Option(..., help="Subnet netuid"), - account: str = typer.Option(..., help="Neuron account address"), -): - """Check if account is a validator in subnet""" - rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "neuron_address", contract) - if not rpc: - print("[red]No RPC URL found in config or CLI.") - raise typer.Exit(1) - mgr = load_neuron_mgr(contract, rpc) - print(f"[green]Is Validator: {mgr.isValidator(netuid, account)}") - -@neuron_app.command() -def neuron_list( - ctx: typer.Context, - contract: str = typer.Option(None, help="Neuron manager contract address"), - netuid: int = typer.Option(..., help="Subnet netuid"), - idx: int = typer.Option(..., help="Index (uint256)"), -): - """Query neuronList(netuid, idx)""" - rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "neuron_address", contract) - if not rpc: - print("[red]No RPC URL found in config or CLI.") - raise typer.Exit(1) - mgr = load_neuron_mgr(contract, rpc) - print(f"[green]neuronList: {mgr.neuronList(netuid, idx)}") - -@neuron_app.command() -def neurons( - ctx: typer.Context, - contract: str = typer.Option(None, help="Neuron manager contract address"), - netuid: int = typer.Option(..., help="Subnet netuid"), - account: str = typer.Option(..., help="Neuron account address"), -): - """Query neurons(netuid, account)""" - rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "neuron_address", contract) - if not rpc: - print("[red]No RPC URL found in config or CLI.") - raise typer.Exit(1) - mgr = load_neuron_mgr(contract, rpc) - print(f"[green]neurons: {mgr.neurons(netuid, account)}") - -@neuron_app.command() -def can_register_neuron( - ctx: typer.Context, - contract: str = typer.Option(None, help="Neuron manager contract address"), - user: str = typer.Option(..., help="User address"), - netuid: int = typer.Option(..., help="Subnet netuid"), - is_validator_role: bool = typer.Option(..., help="Is validator role?"), -): - """Check if user can register neuron""" - rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "neuron_address", contract) - if not rpc: - print("[red]No RPC URL found in config or CLI.") - raise typer.Exit(1) - mgr = load_neuron_mgr(contract, rpc) - print(f"[green]Can Register Neuron: {mgr.canRegisterNeuron(user, netuid, is_validator_role)}") - -@neuron_app.command( - name="regist" -) -def register_neuron( - ctx: typer.Context, - contract: str = typer.Option(None, help="Neuron manager contract address"), - sender: str = typer.Option(..., help="Sender address (must match keystore address or wallet name)"), - wallet_path: str = typer.Option(None, help="Wallet path (default from config)"), - password: str = typer.Option(None, hide_input=True, help="Keystore password"), - netuid: int = typer.Option(..., help="Subnet netuid"), - is_validator_role: bool = typer.Option(..., help="Is validator role?"), - axon_endpoint: str = typer.Option(..., help="Axon endpoint"), - axon_port: int = typer.Option(..., help="Axon port (uint32)"), - prometheus_endpoint: str = typer.Option(..., help="Prometheus endpoint"), - prometheus_port: int = typer.Option(..., help="Prometheus port (uint32)"), -): - """Register a neuron (write tx)""" - rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "neuron_address", contract) - if not rpc: - print("[red]No RPC URL found in config or CLI.") - raise typer.Exit(1) - config = ctx.obj - wallet_path = wallet_path or get_wallet_path(config) - keystore = load_keystore(sender, wallet_path) - if not password: - password = getpass.getpass("Keystore password: ") - try: - private_key = Account.decrypt(keystore, password) - except Exception as e: - print(f"[red]Failed to decrypt keystore: {e}") - raise typer.Exit(1) - mgr = load_neuron_mgr(contract, rpc) - from_address = keystore["address"] - nonce = mgr.web3.eth.get_transaction_count(from_address) - tx = mgr.contract.functions.registerNeuron( - netuid, is_validator_role, axon_endpoint, axon_port, prometheus_endpoint, prometheus_port - ).build_transaction( - { - "from": from_address, - "nonce": nonce, - "gas": 500000, - "gasPrice": mgr.web3.eth.gas_price, - } - ) - signed = mgr.web3.eth.account.sign_transaction(tx, private_key) - tx_hash = mgr.web3.eth.send_raw_transaction(signed.raw_transaction) - print(f"[green]Broadcasted register neuron tx hash: {tx_hash.hex()}") - print("[yellow]Waiting for transaction receipt...") - receipt = mgr.web3.eth.wait_for_transaction_receipt(tx_hash) - if receipt.status == 1: - print(f"[green]Register neuron succeeded in block {receipt.blockNumber}") - else: - print(f"[red]Register neuron failed in block {receipt.blockNumber}, receipt {receipt}") - -@neuron_app.command() -def deregister_neuron( - ctx: typer.Context, - contract: str = typer.Option(None, help="Neuron manager contract address"), - sender: str = typer.Option(..., help="Sender address (must match keystore address or wallet name)"), - wallet_path: str = typer.Option(None, help="Wallet path (default from config)"), - password: str = typer.Option(None, hide_input=True, help="Keystore password"), - netuid: int = typer.Option(..., help="Subnet netuid to deregister from"), -): - """Deregister a neuron (write tx)""" - rpc = ctx.obj.get("json_rpc") if ctx.obj else None - contract = get_contract_address(ctx, "neuron_address", contract) - if not rpc: - print("[red]No RPC URL found in config or CLI.") - raise typer.Exit(1) - config = ctx.obj - wallet_path = wallet_path or get_wallet_path(config) - keystore = load_keystore(sender, wallet_path) - if not password: - password = getpass.getpass("Keystore password: ") - try: - private_key = Account.decrypt(keystore, password) - except Exception as e: - print(f"[red]Failed to decrypt keystore: {e}") - raise typer.Exit(1) - mgr = load_neuron_mgr(contract, rpc) - from_address = keystore["address"] - nonce = mgr.web3.eth.get_transaction_count(from_address) - tx = mgr.contract.functions.deregisterNeuron(netuid).build_transaction( - { - "from": from_address, - "nonce": nonce, - "gas": 200000, - "gasPrice": mgr.web3.eth.gas_price, - } - ) - signed = mgr.web3.eth.account.sign_transaction(tx, private_key) - tx_hash = mgr.web3.eth.send_raw_transaction(signed.raw_transaction) - print(f"[green]Broadcasted deregister neuron tx hash: {tx_hash.hex()}") - print("[yellow]Waiting for transaction receipt...") - receipt = mgr.web3.eth.wait_for_transaction_receipt(tx_hash) - if receipt.status == 1: - print(f"[green]Deregister neuron succeeded in block {receipt.blockNumber}") - else: - print(f"[red]Deregister neuron failed in block {receipt.blockNumber}, receipt {receipt}") \ No newline at end of file diff --git a/hetu_pycli/src/hetu/staking.py b/hetu_pycli/src/hetu/staking.py index 697eb02..f54fb14 100644 --- a/hetu_pycli/src/hetu/staking.py +++ b/hetu_pycli/src/hetu/staking.py @@ -38,21 +38,45 @@ def get_contract_address(ctx, cli_contract_key: str, param_contract: str): def total_staked( ctx: typer.Context, contract: str = typer.Option(None, help="Staking contract address"), + user: str = typer.Argument(None, help="User address or wallet name to query (optional, defaults to owner)"), ): - """Query total staked HETU""" + """Query total staked HETU for a user""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None contract = get_contract_address(ctx, "staking_address", contract) if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) staking = load_staking(contract, rpc) - all_staking = staking.getTotalStaked() + + # 如果没有指定用户,使用合约所有者 + if not user: + user = staking.owner() + print(f"[yellow]No user specified, using contract owner: {user}") + else: + # 如果指定了用户,检查是否是钱包名称并转换为地址 + address = user + config = ctx.obj + wallet_path = get_wallet_path(config) + if not (address.startswith('0x') and len(address) == 42): + try: + keystore = load_keystore(user, wallet_path) + address = keystore.get("address") + print(f"[yellow]Converted wallet name '{user}' to address: {address}") + except Exception: + print(f"[red]Wallet not found: {user}") + raise typer.Exit(1) + user = address + + # 获取用户的质押信息 + stake_info = staking.getStakeInfo(user) + total_staked = stake_info[0] # totalStaked 是第一个元素 + whetu = staking.hetuToken() erc20 = load_erc20(whetu, rpc) decimals = erc20.decimals() - value = all_staking / (10 ** decimals) + value = total_staked / (10 ** decimals) value_str = f"{value:,.{decimals}f}".rstrip('0').rstrip('.') - print(f"[green]Total Staked: {value_str} (raw: {all_staking}, decimals: {decimals})") + print(f"[green]Total Staked for {user}: {value_str} (raw: {total_staked}, decimals: {decimals})") @staking_app.command() def stake_info( @@ -67,6 +91,21 @@ def stake_info( raise typer.Exit(1) contract = get_contract_address(ctx, "staking_address", contract) staking = load_staking(contract, rpc) + + # 检查是否是钱包名称并转换为地址 + address = user + config = ctx.obj + wallet_path = get_wallet_path(config) + if not (address.startswith('0x') and len(address) == 42): + try: + keystore = load_keystore(user, wallet_path) + address = keystore.get("address") + print(f"[yellow]Converted wallet name '{user}' to address: {address}") + except Exception: + print(f"[red]Wallet not found: {user}") + raise typer.Exit(1) + user = address + print(f"[green]Stake Info: {staking.getStakeInfo(user)}") @staking_app.command() @@ -78,12 +117,13 @@ def add_stake( password: str = typer.Option(None, hide_input=True, help="Keystore password"), amount: float = typer.Option(..., help="Amount to stake (in HETU)"), ): - """Add global stake (stake HETU)""" + """Add global stake (stake HETU) with automatic WHETU approval""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None contract = get_contract_address(ctx, "staking_address", contract) if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) + config = ctx.obj wallet_path = wallet_path or get_wallet_path(config) keystore = load_keystore(sender, wallet_path) @@ -94,10 +134,68 @@ def add_stake( except Exception as e: print(f"[red]Failed to decrypt keystore: {e}") raise typer.Exit(1) - staking = load_staking(contract, rpc) + from_address = keystore["address"] - nonce = staking.web3.eth.get_transaction_count(from_address) + + # Load staking contract + staking = load_staking(contract, rpc) + + # Load WHETU contract to check balance and approve + from hetu_pycli.src.hetu.whetu import load_whetu + whetu_contract = get_contract_address(ctx, "whetu_address", None) + whetu = load_whetu(whetu_contract, rpc) + + # Check WHETU balance + print(f"[yellow]Checking WHETU balance...") + whetu_balance_raw = whetu.balanceOf(from_address) + decimals = whetu.decimals() + whetu_balance_human = whetu_balance_raw / (10 ** decimals) amount_wei = staking.web3.to_wei(amount, "ether") + + print(f"[green]Current WHETU balance: {whetu_balance_human} HETU") + print(f"[green]Amount to stake: {amount} HETU") + + if whetu_balance_raw < amount_wei: + print(f"[red]Insufficient WHETU balance!") + print(f"[red]Required: {amount} HETU") + print(f"[red]Available: {whetu_balance_human} HETU") + print(f"[yellow]Please deposit more WHETU using: hetucli whetu deposit --sender {sender} --value {amount}") + raise typer.Exit(1) + + print(f"[green]WHETU balance sufficient: {whetu_balance_human} HETU") + + # Check current allowance for staking contract + print(f"[yellow]Checking WHETU allowance for staking...") + current_allowance = whetu.allowance(from_address, contract) + current_allowance_human = current_allowance / (10 ** decimals) + + if current_allowance < amount_wei: + print(f"[yellow]Insufficient allowance. Approving {amount} WHETU for staking...") + nonce = whetu.web3.eth.get_transaction_count(from_address) + approve_tx = whetu.contract.functions.approve(contract, amount_wei).build_transaction( + { + "from": from_address, + "nonce": nonce, + "gas": 100000, + "gasPrice": whetu.web3.eth.gas_price, + } + ) + signed_approve = whetu.web3.eth.account.sign_transaction(approve_tx, private_key) + approve_tx_hash = whetu.web3.eth.send_raw_transaction(signed_approve.raw_transaction) + print(f"[green]Broadcasted approve tx hash: {approve_tx_hash.hex()}") + print("[yellow]Waiting for approve transaction receipt...") + approve_receipt = whetu.web3.eth.wait_for_transaction_receipt(approve_tx_hash) + if approve_receipt.status == 1: + print(f"[green]Approve succeeded in block {approve_receipt.blockNumber}") + else: + print(f"[red]Approve failed in block {approve_receipt.blockNumber}") + raise typer.Exit(1) + else: + print(f"[green]Sufficient allowance already exists: {current_allowance_human} HETU") + + # Now add the stake + print(f"[yellow]Adding {amount} HETU to global stake...") + nonce = staking.web3.eth.get_transaction_count(from_address) tx = staking.contract.functions.addGlobalStake(amount_wei).build_transaction( { "from": from_address, @@ -113,6 +211,7 @@ def add_stake( receipt = staking.web3.eth.wait_for_transaction_receipt(tx_hash) if receipt.status == 1: print(f"[green]Add stake succeeded in block {receipt.blockNumber}") + print(f"[green]Successfully added {amount} HETU to global stake") else: print(f"[red]Add stake failed in block {receipt.blockNumber}") @@ -215,14 +314,29 @@ def available_stake( user: str = typer.Option(..., help="User address to query"), netuid: int = typer.Option(..., help="Subnet netuid"), ): - """Query available stake for a user in a subnet""" + """Query available stake for a user in a specific subnet""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) contract = get_contract_address(ctx, "staking_address", contract) staking = load_staking(contract, rpc) - print(f"[green]Available Stake: {staking.getAvailableStake(user, netuid)}") + + # 检查是否是钱包名称并转换为地址 + address = user + config = ctx.obj + wallet_path = get_wallet_path(config) + if not (address.startswith('0x') and len(address) == 42): + try: + keystore = load_keystore(user, wallet_path) + address = keystore.get("address") + print(f"[yellow]Converted wallet name '{user}' to address: {address}") + except Exception: + print(f"[red]Wallet not found: {user}") + raise typer.Exit(1) + user = address + + print(f"[green]Available Stake: {staking.getAvailableStake(user)}") @staking_app.command() def effective_stake( @@ -231,13 +345,28 @@ def effective_stake( user: str = typer.Option(..., help="User address to query"), netuid: int = typer.Option(..., help="Subnet netuid"), ): - """Query effective stake for a user in a subnet""" + """Query effective stake for a user in a specific subnet""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) contract = get_contract_address(ctx, "staking_address", contract) staking = load_staking(contract, rpc) + + # 检查是否是钱包名称并转换为地址 + address = user + config = ctx.obj + wallet_path = get_wallet_path(config) + if not (address.startswith('0x') and len(address) == 42): + try: + keystore = load_keystore(user, wallet_path) + address = keystore.get("address") + print(f"[yellow]Converted wallet name '{user}' to address: {address}") + except Exception: + print(f"[red]Wallet not found: {user}") + raise typer.Exit(1) + user = address + print(f"[green]Effective Stake: {staking.getEffectiveStake(user, netuid)}") @staking_app.command() @@ -247,13 +376,28 @@ def locked_stake( user: str = typer.Option(..., help="User address to query"), netuid: int = typer.Option(..., help="Subnet netuid"), ): - """Query locked stake for a user in a subnet""" + """Query locked stake for a user in a specific subnet""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) contract = get_contract_address(ctx, "staking_address", contract) staking = load_staking(contract, rpc) + + # 检查是否是钱包名称并转换为地址 + address = user + config = ctx.obj + wallet_path = get_wallet_path(config) + if not (address.startswith('0x') and len(address) == 42): + try: + keystore = load_keystore(user, wallet_path) + address = keystore.get("address") + print(f"[yellow]Converted wallet name '{user}' to address: {address}") + except Exception: + print(f"[red]Wallet not found: {user}") + raise typer.Exit(1) + user = address + print(f"[green]Locked Stake: {staking.getLockedStake(user, netuid)}") @staking_app.command() @@ -318,4 +462,145 @@ def subnet_allocation( raise typer.Exit(1) contract = get_contract_address(ctx, "staking_address", contract) staking = load_staking(contract, rpc) - print(f"[green]Subnet Allocation: {staking.getSubnetAllocation(user, netuid)}") \ No newline at end of file + + # 检查是否是钱包名称并转换为地址 + address = user + config = ctx.obj + wallet_path = get_wallet_path(config) + if not (address.startswith('0x') and len(address) == 42): + try: + keystore = load_keystore(user, wallet_path) + address = keystore.get("address") + print(f"[yellow]Converted wallet name '{user}' to address: {address}") + except Exception: + print(f"[red]Wallet not found: {user}") + raise typer.Exit(1) + user = address + + print(f"[green]Subnet Allocation: {staking.getSubnetAllocation(user, netuid)}") + +@staking_app.command() +def approve( + ctx: typer.Context, + contract: str = typer.Option(None, help="Staking contract address"), + value: float = typer.Option(..., help="Amount to approve (in HETU)"), + sender: str = typer.Option(..., help="Sender address (must match keystore address or wallet name)"), + wallet_path: str = typer.Option(None, help="Wallet path (default from config)"), + password: str = typer.Option(None, hide_input=True, help="Keystore password"), +): + """Approve WHETU allowance for staking contract""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + staking_contract = get_contract_address(ctx, "staking_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + + config = ctx.obj + wallet_path = wallet_path or get_wallet_path(config) + keystore = load_keystore(sender, wallet_path) + if not password: + password = getpass.getpass("Keystore password: ") + try: + private_key = Account.decrypt(keystore, password) + except Exception as e: + print(f"[red]Failed to decrypt keystore: {e}") + raise typer.Exit(1) + + # Load staking contract to get WHETU token address + staking = load_staking(staking_contract, rpc) + whetu_token_address = staking.hetuToken() + + # Load WHETU contract to perform approve + from hetu_pycli.src.hetu.whetu import load_whetu + whetu_contract = get_contract_address(ctx, "whetu_address", None) + whetu = load_whetu(whetu_contract, rpc) + + decimals = whetu.decimals() + value_raw = int(value * (10 ** decimals)) + + print(f"[yellow]Approving {value} WHETU for staking contract {staking_contract}...") + print(f"[yellow]WHETU token address: {whetu_token_address}") + + from_address = keystore["address"] + nonce = whetu.web3.eth.get_transaction_count(from_address) + + approve_tx = whetu.contract.functions.approve(staking_contract, value_raw).build_transaction( + { + "from": from_address, + "nonce": nonce, + "gas": 100000, + "gasPrice": whetu.web3.eth.gas_price, + } + ) + + signed_approve = whetu.web3.eth.account.sign_transaction(approve_tx, private_key) + approve_tx_hash = whetu.web3.eth.send_raw_transaction(signed_approve.raw_transaction) + print(f"[green]Broadcasted approve tx hash: {approve_tx_hash.hex()}") + print("[yellow]Waiting for transaction receipt...") + + approve_receipt = whetu.web3.eth.wait_for_transaction_receipt(approve_tx_hash) + if approve_receipt.status == 1: + print(f"[green]Approve succeeded in block {approve_receipt.blockNumber}") + print(f"[green]You can now stake up to {value} WHETU using: hetucli stake add-stake --sender {sender} --amount {value}") + else: + print(f"[red]Approve failed in block {approve_receipt.blockNumber}") + +@staking_app.command() +def debug_stake_info( + ctx: typer.Context, + contract: str = typer.Option(None, help="Staking contract address"), + user: str = typer.Option(..., help="User address or wallet name to query"), +): + """Debug: Show raw stake info data structure""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + contract = get_contract_address(ctx, "staking_address", contract) + staking = load_staking(contract, rpc) + + # 检查是否是钱包名称并转换为地址 + address = user + config = ctx.obj + wallet_path = get_wallet_path(config) + if not (address.startswith('0x') and len(address) == 42): + try: + keystore = load_keystore(user, wallet_path) + address = keystore.get("address") + print(f"[yellow]Converted wallet name '{user}' to address: {address}") + except Exception: + print(f"[red]Wallet not found: {user}") + raise typer.Exit(1) + user = address + + print(f"[yellow]Debug: Raw getStakeInfo output for {user}") + print(f"[yellow]Contract address: {contract}") + + try: + stake_info = staking.getStakeInfo(user) + print(f"[green]Raw stake_info: {stake_info}") + print(f"[green]Type: {type(stake_info)}") + print(f"[green]Length: {len(stake_info)}") + + for i, item in enumerate(stake_info): + print(f"[green] [{i}]: {item} (type: {type(item)})") + + # Also show the individual values we're using + if len(stake_info) >= 3: + total_staked = stake_info[0] + total_allocated = stake_info[1] + available_for_allocation = stake_info[2] + + print(f"\n[yellow]Parsed values:") + print(f"[green] totalStaked[0]: {total_staked} wei = {staking.web3.from_wei(total_staked, 'ether')} HETU") + print(f"[green] totalAllocated[1]: {total_allocated} wei = {staking.web3.from_wei(total_allocated, 'ether')} HETU") + print(f"[green] availableForAllocation[2]: {available_for_allocation} wei = {staking.web3.from_wei(available_for_allocation, 'ether')} HETU") + + # Calculate what we expect + expected_available = total_staked - total_allocated + print(f"[yellow]Expected available: {total_staked} - {total_allocated} = {expected_available} wei = {staking.web3.from_wei(expected_available, 'ether')} HETU") + + except Exception as e: + print(f"[red]Error calling getStakeInfo: {e}") + import traceback + traceback.print_exc() \ No newline at end of file diff --git a/hetu_pycli/src/hetu/subnet.py b/hetu_pycli/src/hetu/subnet.py index 7de8ff5..96d9494 100644 --- a/hetu_pycli/src/hetu/subnet.py +++ b/hetu_pycli/src/hetu/subnet.py @@ -98,16 +98,40 @@ def subnet_params( def user_subnets( ctx: typer.Context, contract: str = typer.Option(None, help="Subnet manager contract address"), - user: str = typer.Option(..., help="User address to query"), + sender: str = typer.Option(..., help="Wallet name (username) to query"), ): - """Query all subnets for a user""" + """Query all subnets for a user by wallet name""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None contract = get_contract_address(ctx, "subnet_address", contract) if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) + + # 检查是否是钱包名称并转换为地址 + address = sender + config = ctx.obj + wallet_path = get_wallet_path(config) + if not (address.startswith('0x') and len(address) == 42): + try: + keystore = load_keystore(sender, wallet_path) + address = keystore.get("address") + print(f"[yellow]Wallet '{sender}' address: {address}") + except Exception: + print(f"[red]Wallet not found: {sender}") + raise typer.Exit(1) + subnet_mgr = load_subnet_mgr(contract, rpc) - print(f"[green]User Subnets: {subnet_mgr.getUserSubnets(user)}") + try: + user_subnets_list = subnet_mgr.getUserSubnets(address) + if user_subnets_list: + print(f"[green]User '{sender}' ({address}) created subnets:") + for i, netuid in enumerate(user_subnets_list, 1): + print(f"[green] {i}. Subnet ID: {netuid}") + else: + print(f"[yellow]User '{sender}' ({address}) has not created any subnets yet") + except Exception as e: + print(f"[red]Failed to get user subnets: {e}") + raise typer.Exit(1) @subnet_app.command() def total_networks( @@ -137,12 +161,13 @@ def register_network( token_name: str = typer.Option(..., help="Token name"), token_symbol: str = typer.Option(..., help="Token symbol"), ): - """Register a new network""" + """Register a new network with automatic cost checking and approval""" rpc = ctx.obj.get("json_rpc") if ctx.obj else None contract = get_contract_address(ctx, "subnet_address", contract) if not rpc: print("[red]No RPC URL found in config or CLI.") raise typer.Exit(1) + config = ctx.obj wallet_path = wallet_path or get_wallet_path(config) keystore = load_keystore(sender, wallet_path) @@ -153,8 +178,80 @@ def register_network( except Exception as e: print(f"[red]Failed to decrypt keystore: {e}") raise typer.Exit(1) - subnet_mgr = load_subnet_mgr(contract, rpc) + from_address = keystore["address"] + + # Load subnet manager and get network lock cost + print("[yellow]Checking network lock cost...") + subnet_mgr = load_subnet_mgr(contract, rpc) + lock_cost_raw = subnet_mgr.getNetworkLockCost() + whetu_token_address = subnet_mgr.hetuToken() + + # Load WHETU contract to check balance and approve + from hetu_pycli.src.hetu.whetu import load_whetu + whetu_contract = get_contract_address(ctx, "whetu_address", None) + whetu = load_whetu(whetu_contract, rpc) + + # Get decimals and convert to human readable format + decimals = whetu.decimals() + lock_cost_human = lock_cost_raw / (10 ** decimals) + lock_cost_str = f"{lock_cost_human:,.{decimals}f}".rstrip('0').rstrip('.') + print(f"[green]Network lock cost: {lock_cost_str} WHETU") + + # Check WHETU balance + print("[yellow]Checking WHETU balance...") + whetu_balance_raw = whetu.balanceOf(from_address) + whetu_balance_human = whetu_balance_raw / (10 ** decimals) + whetu_balance_str = f"{whetu_balance_human:,.{decimals}f}".rstrip('0').rstrip('.') + print(f"[green]Current WHETU balance: {whetu_balance_str} WHETU") + + # Check if balance is sufficient + if whetu_balance_raw < lock_cost_raw: + print(f"[red]Insufficient WHETU balance!") + print(f"[red]Required: {lock_cost_str} WHETU") + print(f"[red]Available: {whetu_balance_str} WHETU") + print(f"[yellow]Please deposit more WHETU using: hetucli whetu deposit --sender {sender} --value {lock_cost_human}") + raise typer.Exit(1) + + # Check current allowance + print("[yellow]Checking current allowance...") + current_allowance = whetu.allowance(from_address, contract) + current_allowance_human = current_allowance / (10 ** decimals) + current_allowance_str = f"{current_allowance_human:,.{decimals}f}".rstrip('0').rstrip('.') + print(f"[green]Current allowance: {current_allowance_str} WHETU") + + # Approve if needed + if current_allowance < lock_cost_raw: + print(f"[yellow]Insufficient allowance. Approving {lock_cost_str} WHETU...") + nonce = whetu.web3.eth.get_transaction_count(from_address) + approve_tx = whetu.contract.functions.approve(contract, lock_cost_raw).build_transaction( + { + "from": from_address, + "nonce": nonce, + "gas": 100000, + "gasPrice": whetu.web3.eth.gas_price, + } + ) + signed_approve = whetu.web3.eth.account.sign_transaction(approve_tx, private_key) + approve_tx_hash = whetu.web3.eth.send_raw_transaction(signed_approve.raw_transaction) + print(f"[green]Broadcasted approve tx hash: {approve_tx_hash.hex()}") + print("[yellow]Waiting for approve transaction receipt...") + approve_receipt = whetu.web3.eth.wait_for_transaction_receipt(approve_tx_hash) + if approve_receipt.status == 1: + print(f"[green]Approve succeeded in block {approve_receipt.blockNumber}") + else: + print(f"[red]Approve failed in block {approve_receipt.blockNumber}") + raise typer.Exit(1) + else: + print(f"[green]Sufficient allowance already exists: {current_allowance_str} WHETU") + + # Now register the network + print(f"[yellow]Registering network '{name}'...") + + # Get the next netuid before registration + next_netuid_before = subnet_mgr.getNextNetuid() + print(f"[yellow]Next netuid before registration: {next_netuid_before}") + nonce = subnet_mgr.web3.eth.get_transaction_count(from_address) tx = subnet_mgr.contract.functions.registerNetwork(name, description, token_name, token_symbol).build_transaction( { @@ -171,6 +268,17 @@ def register_network( receipt = subnet_mgr.web3.eth.wait_for_transaction_receipt(tx_hash) if receipt.status == 1: print(f"[green]Register network succeeded in block {receipt.blockNumber}") + + # Get the created subnet ID - it should be the current nextNetuid + created_netuid = next_netuid_before + print(f"[green]✅ Network '{name}' successfully created!") + print(f"[green]📋 Subnet ID: {created_netuid}") + print(f"[green]🔗 Transaction: {tx_hash.hex()}") + print(f"[green]📦 Block: {receipt.blockNumber}") + print(f"[yellow]💡 Next steps:") + print(f"[yellow] 1. Activate the subnet: hetucli subnet activate-subnet --netuid {created_netuid} --sender {sender}") + print(f"[yellow] 2. View subnet info: hetucli subnet subnet-info --netuid {created_netuid}") + print(f"[yellow] 3. View subnet params: hetucli subnet subnet-params --netuid {created_netuid}") else: print(f"[red]Register network failed in block {receipt.blockNumber}, receipt {receipt}") @@ -455,6 +563,20 @@ def network_last_lock_block( subnet_mgr = load_subnet_mgr(contract, rpc) print(f"[green]networkLastLockBlock: {subnet_mgr.networkLastLockBlock()}") +@subnet_app.command() +def network_rate_limit( + ctx: typer.Context, + contract: str = typer.Option(None, help="Subnet manager contract address"), +): + """Query networkRateLimit""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "subnet_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + subnet_mgr = load_subnet_mgr(contract, rpc) + print(f"[green]networkRateLimit: {subnet_mgr.networkRateLimit()}") + @subnet_app.command() def owner_subnets( ctx: typer.Context, diff --git a/hetu_pycli/src/hetu/weights.py b/hetu_pycli/src/hetu/weights.py new file mode 100644 index 0000000..250905a --- /dev/null +++ b/hetu_pycli/src/hetu/weights.py @@ -0,0 +1,317 @@ +import typer +from rich import print +from web3 import Web3 +import json +import os +from .wrapper.weights import Weights +from eth_account import Account +from hetu_pycli.src.commands.wallet import load_keystore, get_wallet_path +import getpass + +WEIGHTS_ABI_PATH = os.path.join( + os.path.dirname(__file__), "../../../contracts/Weights.abi" +) + +weights_app = typer.Typer(help="Weights contract operations for validator scoring") + +def get_contract_address(ctx, cli_contract_key: str, param_contract: str): + config = ctx.obj or {} + contract_addr = param_contract or config.get(cli_contract_key) + if not contract_addr: + print(f"[red]No contract address provided or found in config for {cli_contract_key}.") + raise typer.Exit(1) + print(f"[yellow]Using contract address: {contract_addr}") + return contract_addr + +def load_weights(contract: str, rpc: str): + abi_path = os.path.abspath(WEIGHTS_ABI_PATH) + if not os.path.exists(abi_path): + print(f"[red]ABI file not found: {abi_path}") + raise typer.Exit(1) + with open(abi_path, "r") as f: + abi = json.load(f) + provider = Web3.HTTPProvider(rpc) + return Weights(contract, provider, abi) + +@weights_app.command() +def set_weights( + ctx: typer.Context, + contract: str = typer.Option(None, help="Weights contract address (default from config)"), + sender: str = typer.Option(..., help="Sender address (must match keystore address or wallet name)"), + wallet_path: str = typer.Option(None, help="Wallet path (default from config)"), + password: str = typer.Option(None, hide_input=True, help="Keystore password"), + netuid: int = typer.Option(..., help="Subnet netuid"), + weights_file: str = typer.Option(None, help="JSON file containing weights data"), + weights_json: str = typer.Option(None, help="JSON string containing weights data"), +): + """Set weights for nodes in a subnet (validator only)""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "weights_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + + config = ctx.obj + wallet_path = wallet_path or get_wallet_path(config) + keystore = load_keystore(sender, wallet_path) + if not password: + password = getpass.getpass("Keystore password: ") + try: + private_key = Account.decrypt(keystore, password) + except Exception as e: + print(f"[red]Failed to decrypt keystore: {e}") + raise typer.Exit(1) + + from_address = keystore["address"] + + # Load weights contract + weights_contract = load_weights(contract, rpc) + + # Parse weights data + weights_data = [] + if weights_file: + try: + with open(weights_file, 'r') as f: + weights_data = json.load(f) + print(f"[green]Loaded weights from file: {weights_file}") + except Exception as e: + print(f"[red]Failed to load weights file: {e}") + raise typer.Exit(1) + elif weights_json: + try: + weights_data = json.loads(weights_json) + print(f"[green]Loaded weights from JSON string") + except Exception as e: + print(f"[red]Failed to parse weights JSON: {e}") + raise typer.Exit(1) + else: + print("[red]Please provide either --weights-file or --weights-json") + raise typer.Exit(1) + + # Validate weights data structure + if not isinstance(weights_data, list): + print("[red]Weights data must be a list") + raise typer.Exit(1) + + # Convert weights data to contract format + contract_weights = [] + for weight in weights_data: + if not isinstance(weight, dict) or 'dest' not in weight or 'weight' not in weight: + print(f"[red]Invalid weight format: {weight}") + print("[red]Each weight must have 'dest' (address) and 'weight' (uint256) fields") + raise typer.Exit(1) + + dest = weight['dest'] + weight_value = weight['weight'] + + # Validate address format + if not Web3.is_address(dest): + print(f"[red]Invalid destination address: {dest}") + raise typer.Exit(1) + + # Validate weight value (0 to 1e6 representing 0 to 1) + if not isinstance(weight_value, (int, float)) or weight_value < 0 or weight_value > 1000000: + print(f"[red]Invalid weight value: {weight_value}") + print("[red]Weight must be between 0 and 1000000 (representing 0 to 1)") + raise typer.Exit(1) + + contract_weights.append([dest, int(weight_value)]) + + print(f"[green]Setting weights for subnet {netuid}") + print(f"[green]Number of weights: {len(contract_weights)}") + for i, (dest, weight) in enumerate(contract_weights): + weight_normalized = weight / 1000000.0 + print(f"[green] {i+1}. {dest} -> {weight} ({weight_normalized:.6f})") + + # Build transaction + nonce = weights_contract.web3.eth.get_transaction_count(from_address) + + # Prepare weights data for contract call - combine dests and weights into Weight struct array + new_weights = [] + for dest, weight in contract_weights: + new_weights.append([dest, weight]) # [address, uint256] format for Weight struct + + tx = weights_contract.contract.functions.setWeights(netuid, new_weights).build_transaction( + { + "from": from_address, + "nonce": nonce, + "gas": 300000, + "gasPrice": weights_contract.web3.eth.gas_price, + } + ) + + signed = weights_contract.web3.eth.account.sign_transaction(tx, private_key) + tx_hash = weights_contract.web3.eth.send_raw_transaction(signed.raw_transaction) + print(f"[green]Broadcasted setWeights tx hash: {tx_hash.hex()}") + print("[yellow]Waiting for transaction receipt...") + + receipt = weights_contract.web3.eth.wait_for_transaction_receipt(tx_hash) + if receipt.status == 1: + print(f"[green]SetWeights succeeded in block {receipt.blockNumber}") + print(f"[green]Weights updated for subnet {netuid}") + else: + print(f"[red]SetWeights failed in block {receipt.blockNumber}, receipt {receipt}") + +@weights_app.command() +def get_weights( + ctx: typer.Context, + contract: str = typer.Option(None, help="Weights contract address (default from config)"), + netuid: int = typer.Option(..., help="Subnet netuid"), + validator: str = typer.Option(..., help="Validator address"), + dest: str = typer.Option(..., help="Destination address"), +): + """Query weights for a specific validator-destination pair in a subnet""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "weights_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + + weights_contract = load_weights(contract, rpc) + + # Validate addresses + if not Web3.is_address(validator): + print(f"[red]Invalid validator address: {validator}") + raise typer.Exit(1) + + if not Web3.is_address(dest): + print(f"[red]Invalid destination address: {dest}") + raise typer.Exit(1) + + try: + weight = weights_contract.weights(netuid, validator, dest) + weight_normalized = weight / 1000000.0 + print(f"[green]Weight for subnet {netuid}, validator {validator}, destination {dest}:") + print(f"[green] Raw weight: {weight}") + print(f"[green] Normalized weight: {weight_normalized:.6f} (0-1 scale)") + except Exception as e: + print(f"[red]Failed to get weight: {e}") + raise typer.Exit(1) + +@weights_app.command() +def create_weights_template( + ctx: typer.Context, + output_file: str = typer.Option("weights_template.json", help="Output file name"), +): + """Create a template JSON file for weights data""" + template = [ + { + "dest": "0x1234567890123456789012345678901234567890", + "weight": 500000 + }, + { + "dest": "0x0987654321098765432109876543210987654321", + "weight": 750000 + } + ] + + try: + with open(output_file, 'w') as f: + json.dump(template, f, indent=2) + print(f"[green]Weights template created: {output_file}") + print(f"[yellow]Template contains example weights:") + print(f"[yellow] - Weight 500000 = 0.5 (50%)") + print(f"[yellow] - Weight 750000 = 0.75 (75%)") + print(f"[yellow] - Weight range: 0-1000000 (representing 0-1)") + print(f"[yellow]Edit the file with your actual destination addresses and weights") + except Exception as e: + print(f"[red]Failed to create template: {e}") + raise typer.Exit(1) + +@weights_app.command() +def quick_score( + ctx: typer.Context, + contract: str = typer.Option(None, help="Weights contract address (default from config)"), + sender: str = typer.Option(..., help="Sender address (must match keystore address or wallet name)"), + wallet_path: str = typer.Option(None, help="Wallet path (default from config)"), + password: str = typer.Option(None, hide_input=True, help="Keystore password"), + netuid: int = typer.Option(..., help="Subnet netuid"), + targets: str = typer.Option(..., help="Comma-separated list of target addresses"), + scores: str = typer.Option(..., help="Comma-separated list of scores (0-1000000, representing 0-1)"), +): + """Quick score command for testing - directly input targets and scores""" + rpc = ctx.obj.get("json_rpc") if ctx.obj else None + contract = get_contract_address(ctx, "weights_address", contract) + if not rpc: + print("[red]No RPC URL found in config or CLI.") + raise typer.Exit(1) + + config = ctx.obj + wallet_path = wallet_path or get_wallet_path(config) + keystore = load_keystore(sender, wallet_path) + if not password: + password = getpass.getpass("Keystore password: ") + try: + private_key = Account.decrypt(keystore, password) + except Exception as e: + print(f"[red]Failed to decrypt keystore: {e}") + raise typer.Exit(1) + + from_address = keystore["address"] + + # Load weights contract + weights_contract = load_weights(contract, rpc) + + # Parse targets and scores + target_list = [addr.strip() for addr in targets.split(",")] + score_list = [score.strip() for score in scores.split(",")] + + if len(target_list) != len(score_list): + print("[red]Number of targets and scores must match") + raise typer.Exit(1) + + # Validate and convert data + contract_weights = [] + for i, (target, score) in enumerate(zip(target_list, score_list)): + # Validate address format + if not Web3.is_address(target): + print(f"[red]Invalid target address at position {i+1}: {target}") + raise typer.Exit(1) + + # Validate and convert score + try: + score_value = int(score) + if score_value < 0 or score_value > 1000000: + print(f"[red]Invalid score at position {i+1}: {score}") + print("[red]Score must be between 0 and 1000000 (representing 0 to 1)") + raise typer.Exit(1) + except ValueError: + print(f"[red]Invalid score format at position {i+1}: {score}") + raise typer.Exit(1) + + contract_weights.append([target, score_value]) + + print(f"[green]Setting weights for subnet {netuid}") + print(f"[green]Number of weights: {len(contract_weights)}") + for i, (target, score) in enumerate(contract_weights): + score_normalized = score / 1000000.0 + print(f"[green] {i+1}. {target} -> {score} ({score_normalized:.6f})") + + # Build transaction + nonce = weights_contract.web3.eth.get_transaction_count(from_address) + + # Prepare weights data for contract call - combine dests and weights into Weight struct array + new_weights = [] + for dest, weight in contract_weights: + new_weights.append([dest, weight]) # [address, uint256] format for Weight struct + + tx = weights_contract.contract.functions.setWeights(netuid, new_weights).build_transaction( + { + "from": from_address, + "nonce": nonce, + "gas": 300000, + "gasPrice": weights_contract.web3.eth.gas_price, + } + ) + + signed = weights_contract.web3.eth.account.sign_transaction(tx, private_key) + tx_hash = weights_contract.web3.eth.send_raw_transaction(signed.raw_transaction) + print(f"[green]Broadcasted setWeights tx hash: {tx_hash.hex()}") + print("[yellow]Waiting for transaction receipt...") + + receipt = weights_contract.web3.eth.wait_for_transaction_receipt(tx_hash) + if receipt.status == 1: + print(f"[green]SetWeights succeeded in block {receipt.blockNumber}") + print(f"[green]Weights updated for subnet {netuid}") + else: + print(f"[red]SetWeights failed in block {receipt.blockNumber}, receipt {receipt}") \ No newline at end of file diff --git a/hetu_pycli/src/hetu/wrapper/neuron_mgr.py b/hetu_pycli/src/hetu/wrapper/dendron_mgr.py similarity index 56% rename from hetu_pycli/src/hetu/wrapper/neuron_mgr.py rename to hetu_pycli/src/hetu/wrapper/dendron_mgr.py index 84d3bf5..b3025e8 100644 --- a/hetu_pycli/src/hetu/wrapper/neuron_mgr.py +++ b/hetu_pycli/src/hetu/wrapper/dendron_mgr.py @@ -3,69 +3,40 @@ from web3 import Web3 -class NeuronMgr: - def __init__(self, address, provider, abi): +class DendronMgr: + def __init__(self, contract_address, provider, abi): + self.contract_address = contract_address self.web3 = Web3(provider) - self.contract = self.web3.eth.contract(address=address, abi=abi) + self.contract = self.web3.eth.contract(address=contract_address, abi=abi) - def batchUpdateStakeAllocations(self, netuid, accounts, newStakes): + def getDendronCount(self, netuid): """ - Call batchUpdateStakeAllocations(netuid, accounts, newStakes) + Call getDendronCount(netuid) :param netuid: uint16 (solidity name: 'netuid') - :param accounts: address[] (solidity name: 'accounts') - :param newStakes: uint256[] (solidity name: 'newStakes') - :return: [] - """ - return self.contract.functions.batchUpdateStakeAllocations(netuid, accounts, newStakes).call() - - def canRegisterNeuron(self, user, netuid, isValidatorRole): - """ - Call canRegisterNeuron(user, netuid, isValidatorRole) - :param user: address (solidity name: 'user') - :param netuid: uint16 (solidity name: 'netuid') - :param isValidatorRole: bool (solidity name: 'isValidatorRole') - :return: [{'internalType': 'bool', 'name': '', 'type': 'bool'}] - """ - return self.contract.functions.canRegisterNeuron(user, netuid, isValidatorRole).call() - - def deregisterNeuron(self, netuid): - """ - Call deregisterNeuron(netuid) - :param netuid: uint16 (solidity name: 'netuid') - :return: [] - """ - return self.contract.functions.deregisterNeuron(netuid).call() - - def distributeRewards(self, netuid, accounts, amounts): - """ - Call distributeRewards(netuid, accounts, amounts) - :param netuid: uint16 (solidity name: 'netuid') - :param accounts: address[] (solidity name: 'accounts') - :param amounts: uint256[] (solidity name: 'amounts') - :return: [] + :return: [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}] """ - return self.contract.functions.distributeRewards(netuid, accounts, amounts).call() + return self.contract.functions.getDendronCount(netuid).call() - def getNeuronInfo(self, netuid, account): + def getDendronInfo(self, netuid, account): """ - Call getNeuronInfo(netuid, account) + Call getNeuronInfo(netuid, account) - keeping old ABI function name for compatibility :param netuid: uint16 (solidity name: 'netuid') :param account: address (solidity name: 'account') - :return: [{'components': [{'internalType': 'address', 'name': 'account', 'type': 'address'}, {'internalType': 'uint16', 'name': 'uid', 'type': 'uint16'}, {'internalType': 'uint16', 'name': 'netuid', 'type': 'uint16'}, {'internalType': 'bool', 'name': 'isActive', 'type': 'bool'}, {'internalType': 'bool', 'name': 'isValidator', 'type': 'bool'}, {'internalType': 'uint256', 'name': 'stake', 'type': 'uint256'}, {'internalType': 'uint64', 'name': 'registrationBlock', 'type': 'uint64'}, {'internalType': 'uint256', 'name': 'lastUpdate', 'type': 'uint256'}, {'internalType': 'string', 'name': 'axonEndpoint', 'type': 'string'}, {'internalType': 'uint32', 'name': 'axonPort', 'type': 'uint32'}, {'internalType': 'string', 'name': 'prometheusEndpoint', 'type': 'string'}, {'internalType': 'uint32', 'name': 'prometheusPort', 'type': 'uint32'}], 'internalType': 'struct SubnetTypes.NeuronInfo', 'name': '', 'type': 'tuple'}] + :return: [{'components': [{'internalType': 'address', 'name': 'account', 'type': 'address'}, {'internalType': 'uint16', 'name': 'netuid', 'type': 'uint16'}, {'internalType': 'bool', 'name': 'isActive', 'type': 'bool'}, {'internalType': 'bool', 'name': 'isValidator', 'type': 'bool'}, {'internalType': 'uint256', 'name': 'stake', 'type': 'uint256'}, {'internalType': 'uint64', 'name': 'registrationBlock', 'type': 'uint64'}, {'internalType': 'uint256', 'name': 'lastUpdate', 'type': 'uint256'}, {'internalType': 'string', 'name': 'axonEndpoint', 'type': 'string'}, {'internalType': 'uint32', 'name': 'axonPort', 'type': 'uint32'}, {'internalType': 'string', 'name': 'prometheusEndpoint', 'type': 'string'}, {'internalType': 'uint32', 'name': 'prometheusPort', 'type': 'uint32'}], 'internalType': 'struct SubnetTypes.DendronInfo', 'name': '', 'type': 'tuple'}] """ return self.contract.functions.getNeuronInfo(netuid, account).call() - def getSubnetNeuronCount(self, netuid): + def getSubnetDendronCount(self, netuid): """ - Call getSubnetNeuronCount(netuid) + Call getSubnetNeuronCount(netuid) - keeping old ABI function name for compatibility :param netuid: uint16 (solidity name: 'netuid') :return: [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}] """ return self.contract.functions.getSubnetNeuronCount(netuid).call() - def getSubnetNeurons(self, netuid): + def getSubnetDendrons(self, netuid): """ - Call getSubnetNeurons(netuid) + Call getSubnetNeurons(netuid) - keeping old ABI function name for compatibility :param netuid: uint16 (solidity name: 'netuid') :return: [{'internalType': 'address[]', 'name': '', 'type': 'address[]'}] """ @@ -94,9 +65,9 @@ def globalStaking(self, ): """ return self.contract.functions.globalStaking().call() - def isNeuron(self, netuid, account): + def isDendron(self, netuid, account): """ - Call isNeuron(netuid, account) + Call isNeuron(netuid, account) - keeping old ABI function name for compatibility :param netuid: uint16 (solidity name: 'netuid') :param account: address (solidity name: 'account') :return: [{'internalType': 'bool', 'name': '', 'type': 'bool'}] @@ -112,24 +83,15 @@ def isValidator(self, netuid, account): """ return self.contract.functions.isValidator(netuid, account).call() - def neuronList(self, arg0, arg1): + def dendronList(self, arg0, arg1): """ - Call neuronList(arg0, arg1) + Call neuronList(arg0, arg1) - keeping old ABI function name for compatibility :param arg0: uint16 (solidity name: 'arg0') :param arg1: uint256 (solidity name: 'arg1') :return: [{'internalType': 'address', 'name': '', 'type': 'address'}] """ return self.contract.functions.neuronList(arg0, arg1).call() - def neurons(self, arg0, arg1): - """ - Call neurons(arg0, arg1) - :param arg0: uint16 (solidity name: 'arg0') - :param arg1: address (solidity name: 'arg1') - :return: [{'internalType': 'address', 'name': 'account', 'type': 'address'}, {'internalType': 'uint16', 'name': 'uid', 'type': 'uint16'}, {'internalType': 'uint16', 'name': 'netuid', 'type': 'uint16'}, {'internalType': 'bool', 'name': 'isActive', 'type': 'bool'}, {'internalType': 'bool', 'name': 'isValidator', 'type': 'bool'}, {'internalType': 'uint256', 'name': 'stake', 'type': 'uint256'}, {'internalType': 'uint64', 'name': 'registrationBlock', 'type': 'uint64'}, {'internalType': 'uint256', 'name': 'lastUpdate', 'type': 'uint256'}, {'internalType': 'string', 'name': 'axonEndpoint', 'type': 'string'}, {'internalType': 'uint32', 'name': 'axonPort', 'type': 'uint32'}, {'internalType': 'string', 'name': 'prometheusEndpoint', 'type': 'string'}, {'internalType': 'uint32', 'name': 'prometheusPort', 'type': 'uint32'}] - """ - return self.contract.functions.neurons(arg0, arg1).call() - def owner(self, ): """ Call owner() @@ -137,10 +99,11 @@ def owner(self, ): """ return self.contract.functions.owner().call() - def registerNeuron(self, netuid, isValidatorRole, axonEndpoint, axonPort, prometheusEndpoint, prometheusPort): + def registerDendronWithStakeAllocation(self, netuid, stakeAmount, isValidatorRole, axonEndpoint, axonPort, prometheusEndpoint, prometheusPort): """ - Call registerNeuron(netuid, isValidatorRole, axonEndpoint, axonPort, prometheusEndpoint, prometheusPort) + Call registerNeuronWithStakeAllocation(netuid, stakeAmount, isValidatorRole, axonEndpoint, axonPort, prometheusEndpoint, prometheusPort) - keeping old ABI function name for compatibility :param netuid: uint16 (solidity name: 'netuid') + :param stakeAmount: uint256 (solidity name: 'stakeAmount') :param isValidatorRole: bool (solidity name: 'isValidatorRole') :param axonEndpoint: string (solidity name: 'axonEndpoint') :param axonPort: uint32 (solidity name: 'axonPort') @@ -148,7 +111,7 @@ def registerNeuron(self, netuid, isValidatorRole, axonEndpoint, axonPort, promet :param prometheusPort: uint32 (solidity name: 'prometheusPort') :return: [] """ - return self.contract.functions.registerNeuron(netuid, isValidatorRole, axonEndpoint, axonPort, prometheusEndpoint, prometheusPort).call() + return self.contract.functions.registerNeuronWithStakeAllocation(netuid, stakeAmount, isValidatorRole, axonEndpoint, axonPort, prometheusEndpoint, prometheusPort).call() def renounceOwnership(self, ): """ @@ -187,9 +150,9 @@ def transferOwnership(self, newOwner): """ return self.contract.functions.transferOwnership(newOwner).call() - def updateService(self, netuid, axonEndpoint, axonPort, prometheusEndpoint, prometheusPort): + def updateDendronService(self, netuid, axonEndpoint, axonPort, prometheusEndpoint, prometheusPort): """ - Call updateService(netuid, axonEndpoint, axonPort, prometheusEndpoint, prometheusPort) + Call updateNeuronService(netuid, axonEndpoint, axonPort, prometheusEndpoint, prometheusPort) - keeping old ABI function name for compatibility :param netuid: uint16 (solidity name: 'netuid') :param axonEndpoint: string (solidity name: 'axonEndpoint') :param axonPort: uint32 (solidity name: 'axonPort') @@ -197,7 +160,7 @@ def updateService(self, netuid, axonEndpoint, axonPort, prometheusEndpoint, prom :param prometheusPort: uint32 (solidity name: 'prometheusPort') :return: [] """ - return self.contract.functions.updateService(netuid, axonEndpoint, axonPort, prometheusEndpoint, prometheusPort).call() + return self.contract.functions.updateNeuronService(netuid, axonEndpoint, axonPort, prometheusEndpoint, prometheusPort).call() def updateStakeAllocation(self, netuid, account, newStake): """ @@ -207,4 +170,21 @@ def updateStakeAllocation(self, netuid, account, newStake): :param newStake: uint256 (solidity name: 'newStake') :return: [] """ - return self.contract.functions.updateStakeAllocation(netuid, account, newStake).call() \ No newline at end of file + return self.contract.functions.updateStakeAllocation(netuid, account, newStake).call() + + def deregisterDendron(self, netuid): + """ + Call deregisterNeuron(netuid) - keeping old ABI function name for compatibility + :param netuid: uint16 (solidity name: 'netuid') + :return: [] + """ + return self.contract.functions.deregisterNeuron(netuid).call() + + def dendrons(self, netuid, account): + """ + Call neurons(netuid, account) - keeping old ABI function name for compatibility + :param netuid: uint16 (solidity name: 'netuid') + :param account: address (solidity name: 'account') + :return: [{'internalType': 'struct SubnetTypes.DendronInfo', 'name': '', 'type': 'tuple'}] + """ + return self.contract.functions.neurons(netuid, account).call() \ No newline at end of file diff --git a/hetu_pycli/src/hetu/wrapper/global_staking.py b/hetu_pycli/src/hetu/wrapper/global_staking.py index 4489106..a69a2bc 100644 --- a/hetu_pycli/src/hetu/wrapper/global_staking.py +++ b/hetu_pycli/src/hetu/wrapper/global_staking.py @@ -75,14 +75,13 @@ def claimRewards(self, ): """ return self.contract.functions.claimRewards().call() - def getAvailableStake(self, user, netuid): + def getAvailableStake(self, user): """ - Call getAvailableStake(user, netuid) + Call getAvailableStake(user) :param user: address (solidity name: 'user') - :param netuid: uint16 (solidity name: 'netuid') :return: [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}] """ - return self.contract.functions.getAvailableStake(user, netuid).call() + return self.contract.functions.getAvailableStake(user).call() def getEffectiveStake(self, user, netuid): """ diff --git a/hetu_pycli/src/hetu/wrapper/weights.py b/hetu_pycli/src/hetu/wrapper/weights.py new file mode 100644 index 0000000..641f729 --- /dev/null +++ b/hetu_pycli/src/hetu/wrapper/weights.py @@ -0,0 +1,26 @@ +from web3 import Web3 + +class Weights: + def __init__(self, contract_address, provider, abi): + self.contract_address = contract_address + self.web3 = Web3(provider) + self.contract = self.web3.eth.contract(address=contract_address, abi=abi) + + def setWeights(self, netuid, newWeights): + """ + Call setWeights(netuid, newWeights) + :param netuid: uint16 (solidity name: 'netuid') + :param newWeights: Weight[] (solidity name: 'newWeights') + :return: [] + """ + return self.contract.functions.setWeights(netuid, newWeights).call() + + def weights(self, netuid, validator, dest): + """ + Call weights(netuid, validator, dest) + :param netuid: uint16 (solidity name: 'netuid') + :param validator: address (solidity name: 'validator') + :param dest: address (solidity name: 'dest') + :return: [{'internalType': 'uint256', 'name': '', 'type': 'uint256'}] + """ + return self.contract.functions.weights(netuid, validator, dest).call() \ No newline at end of file diff --git a/introduction.md b/introduction.md new file mode 100644 index 0000000..4fa03b6 --- /dev/null +++ b/introduction.md @@ -0,0 +1,209 @@ +# Hetu Client Introduction + +## Overview + +Hetu is a decentralized protocol designed to create a network of specialized subnets where participants can stake HETU tokens, register as nodes (miners or validators), and contribute to the network's computational capabilities. The protocol establishes an economic incentive structure that rewards participants for contributing computational resources while maintaining network security through staking mechanisms. + +## System Architecture + +### Core System Overview + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Hetu Network │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ Subnet 1 │ │ Subnet 2 │ │ Subnet N │ │ +│ │ (AI/ML) │ │ (DeFi) │ │ (Gaming) │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ Miners │ │ Miners │ │ Miners │ │ +│ │ (Xylem) │ │ (Xylem) │ │ (Xylem) │ │ +│ │ • Compute │ │ • Compute │ │ • Compute │ │ +│ │ • Services │ │ • Services │ │ • Services │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ ▲ ▲ ▲ │ +│ │ │ │ │ +│ ┌─────────────────────────────────────────────────────────────┐ │ +│ │ Metastruct │ │ +│ │ (Service Discovery) │ │ +│ │ • Dendron Registry │ │ +│ │ • Service Endpoints │ │ +│ │ • Stake Information │ │ +│ └─────────────────────────────────────────────────────────────┘ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ Validators │ │ Validators │ │ Validators │ │ +│ │(Phloem) │ │(Phloem) │ │(Phloem) │ │ +│ │ • Discovery │ │ • Discovery │ │ • Discovery │ │ +│ │ • Requests │ │ • Requests │ │ • Requests │ │ +│ │ • Validation│ │ • Validation│ │ • Validation│ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ +│ ┌─────────────────────────────────────────────────────────────┐ │ +│ │ Blockchain Layer │ │ +│ │ (EVM Smart Contracts) │ │ +│ │ • Dendron Manager │ │ +│ │ • Subnet Manager │ │ +│ │ • Global Staking │ │ +│ └─────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### User Registration Flow + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Node Registration Process │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ Phase 1 │ │ Phase 2 │ │ Phase 3 │ │ +│ │ Stake HETU │ │ Allocate │ │ Register │ │ +│ │ │ │ to Subnet │ │ Dendron │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ User │ │ User │ │ User │ │ +│ │ • CLI │────────▶│ • CLI │────────▶│ • CLI │ │ +│ │ • Wallet │ │ • Wallet │ │ • Wallet │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ Smart │ │ Smart │ │ Smart │ │ +│ │ Contracts │ │ Contracts │ │ Contracts │ │ +│ │ • Staking │ │ • Subnet │ │ • Dendron │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +│ │ │ │ │ +│ ▼ ▼ ▼ │ +│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ +│ │ Blockchain │ │ Blockchain │ │ Blockchain │ │ +│ │ • Execute │ │ • Execute │ │ • Execute │ │ +│ │ • Confirm │ │ • Confirm │ │ • Confirm │ │ +│ └─────────────┘ └─────────────┘ └─────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### Smart Contract Interaction + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Smart Contract Architecture │ +├─────────────────────────────────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────────────────────────────────────┐ │ +│ │ User Interface │ │ +│ │ • CLI Commands │ │ +│ │ • Wallet Management │ │ +│ │ • Parameter Validation │ │ +│ └─────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────────┐ │ +│ │ Processing Layer │ │ +│ │ • Transaction Builder │ │ +│ │ • Gas Estimation │ │ +│ │ • Nonce Management │ │ +│ └─────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────────┐ │ +│ │ Mainnet Smart Contracts │ │ +│ │ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ │ +│ │ │ Subnet │ │ Global │ │ Dendron │ │ │ +│ │ │ Manager │ │ Staking │ │ Manager │ │ │ +│ │ │ │ │ │ │ │ │ │ +│ │ │ • Create │ │ • Stake │ │ • Register │ │ │ +│ │ │ • Activate │ │ • Allocate │ │ • Manage │ │ │ +│ │ │ • Configure │ │ • Lock │ │ • Validate │ │ │ +│ │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ +│ └─────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ │ +│ ┌─────────────────────────────────────────────────────────────┐ │ +│ │ Blockchain Layer │ │ +│ │ • EVM Execution │ │ +│ │ • Transaction Pool │ │ +│ │ • Block Production │ │ +│ └─────────────────────────────────────────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Core Functionality + +### Subnet Management +The protocol enables users to create specialized subnets, each with its own AMM pool and Alpha tokens. Subnets can be customized for specific computational tasks like AI vision, data processing, or machine learning. Each subnet operates as an independent network with its own governance parameters, staking requirements, and reward distribution mechanisms. The SubnetManager contract handles the creation, activation, and lifecycle management of these specialized networks. + +### Staking and Economic Security +Participants stake HETU tokens to participate in the network. The staking mechanism provides economic security and determines participation rights across different subnets. The GlobalStaking contract manages the global staking pool, allowing users to allocate their staked tokens to specific subnets based on their interests and computational capabilities. This creates a dynamic ecosystem where capital flows to the most valuable and efficient subnets. + +### Dendron Registration System +Users can register as either miners (computational contributors) or validators (network validators), each with different stake requirements and responsibilities. Miners are responsible for providing computational resources and executing tasks, while validators ensure network integrity and validate operations. The DendronManager contract handles the registration process, role assignment, and ongoing management of network participants. + +### AMM Trading and Liquidity +Automated market makers enable seamless trading between HETU and subnet-specific Alpha tokens, providing liquidity and price discovery. Each subnet maintains its own AMM pool where users can trade HETU for Alpha tokens or vice versa. This creates a dynamic pricing mechanism that reflects the perceived value of each subnet's computational capabilities and the demand for its services. + +## How Users Register as Nodes + +### 1. Staking Requirements and Preparation +- **Miners**: Require 500 HETU minimum stake allocation to ensure sufficient economic commitment +- **Validators**: Require 200 HETU minimum stake allocation, reflecting their different role and responsibility level +- Users must first stake HETU globally through the GlobalStaking contract, then allocate specific amounts to target subnets +- The staking process involves locking tokens for a specified period, creating economic incentives for long-term participation + +### 2. Registration Process and Technical Requirements +Users register through the CLI by providing comprehensive information: +- **Wallet credentials and stake amount**: Ensuring sufficient economic commitment and identity verification +- **Network endpoint information**: Including Xylem endpoints for communication and Cambium endpoints for monitoring +- **Role selection**: Choosing between miner (computational contributor) or validator (network validator) based on capabilities +- **Subnet identification**: Specifying the target subnet using its unique netuid identifier +- **Technical specifications**: Providing network configuration details for seamless integration + +### 3. Node Activation and Network Integration +Once registered, nodes become active participants in the subnet, contributing computational resources or validating network operations based on their role. The activation process involves: +- **Smart contract verification**: Ensuring all registration parameters meet subnet requirements +- **Network synchronization**: Establishing connections with other nodes and the subnet infrastructure +- **Role assignment**: Activating specific functions based on the chosen role (miner or validator) +- **Performance monitoring**: Enabling real-time tracking of node contributions and network health + +## Technical Architecture and Smart Contract Design + +### Core Smart Contracts +The protocol consists of several interconnected smart contracts that work together to create a robust and scalable network: + +- **SubnetManager**: Manages subnet creation, lifecycle, and governance parameters. Handles subnet activation, parameter updates, and overall network coordination. + +- **GlobalStaking**: Manages the global staking pool and token allocation. Provides mechanisms for users to stake HETU tokens and allocate them across different subnets based on their preferences and risk tolerance. + +- **DendronManager**: Manages dendron registration, role assignment, and ongoing participation. Handles the technical aspects of node integration and ensures proper role-based access control. + +- **SubnetAMM**: Provides liquidity pools for token trading between HETU and subnet-specific Alpha tokens. Implements automated market making algorithms to ensure fair pricing and sufficient liquidity. + +### Network Interaction Patterns +The protocol implements several key interaction patterns that ensure efficient operation: + +- **Stake Allocation**: Users can dynamically allocate their staked tokens across multiple subnets, creating a flexible investment strategy +- **Role-based Access**: Different roles (miner/validator) have different permissions and responsibilities within the network +- **Economic Incentives**: Reward mechanisms ensure that participants are compensated for their contributions and network security +- **Liquidity Management**: AMM pools provide continuous liquidity for token trading, enabling efficient capital allocation + +## Economic Model and Incentive Structure + +### Token Economics +The protocol uses a dual-token system where HETU serves as the base staking and governance token, while Alpha tokens represent subnet-specific value and utility. This creates a layered economic model where: + +- **HETU tokens** provide network-wide security and governance rights +- **Alpha tokens** represent the value of specific subnet services and computational capabilities +- **Staking rewards** incentivize long-term participation and network security +- **Trading fees** from AMM pools provide additional revenue streams for participants + +### Network Security +The staking mechanism ensures network security by requiring participants to lock significant capital. This creates economic disincentives for malicious behavior while encouraging honest participation. The validator role provides an additional layer of security through consensus mechanisms and transaction validation. + +This comprehensive design creates a flexible, scalable network where participants can contribute computational resources, validate network operations, and trade tokens in a decentralized manner. The protocol's architecture enables continuous innovation and adaptation to changing computational needs while maintaining robust economic incentives and security measures. \ No newline at end of file