From 77cc0fd0c77762b3351f31cc68cbf0389b61f586 Mon Sep 17 00:00:00 2001 From: tomos <55086152+86667@users.noreply.github.com> Date: Fri, 7 Feb 2025 11:50:42 +0000 Subject: [PATCH] Add z2 commands for depositTopUp, Unstake and Withdraw (#2305) * feat: depositTopUp, unstake and withdraw transaction creators * feat: z2 plumbing for new commands * docs: write up usage for other z2 deposit contract interactions * chore: rename deposit.md -> staking.md * chore: satisfy clippy --- z2/docs/deposit.md | 74 ----------------- z2/docs/staking.md | 158 ++++++++++++++++++++++++++++++++++++ z2/src/bin/z2.rs | 105 +++++++++++++++++++++--- z2/src/deployer.rs | 9 ++- z2/src/validators.rs | 187 ++++++++++++++++++++++++++++++++++--------- 5 files changed, 408 insertions(+), 125 deletions(-) delete mode 100644 z2/docs/deposit.md create mode 100644 z2/docs/staking.md diff --git a/z2/docs/deposit.md b/z2/docs/deposit.md deleted file mode 100644 index f74514593..000000000 --- a/z2/docs/deposit.md +++ /dev/null @@ -1,74 +0,0 @@ -# z2 deposit - -The `z2 deposit` command deposits ZIL tokens to the deposit smart contract to promote a node as a validator. - - -```bash -z2 deposit \ - --chain \ - --public-key \ - --peer-id \ - --private-key \ - --amount \ - --reward-address \ - --signing-address \ - --deposit-auth-signature - -Usage: z2 deposit --chain --public-key --peer-id --private-key --amount --reward-address --signing-address --deposit-auth-signature -``` -## Parameters -* `--chain `: The name of the chain. Possible values are zq2-devnet, zq2-prototestnet, zq2-protomainnet, zq2-testnet, zq2-mainnet. -* `--public-key `: The BLS public key of the validator node. -* `--peer-id `: The peer ID of the validator node. -* `--private-key `: The private key of the wallet that has a minimum stake amount of 10 million. -* `--amount `: The amount in ZIL to deposit. The valid range is from 10 million to 255 million ZIL, allowing a deposit of up to 255 million ZIL. -* `--reward-address `: Specifies the address to receive rewards. You can generate a new wallet address to receive the rewards. -* `--signing-address `: Specifies the address which signs cross-chain events. -* `--deposit-auth-signature `: BLS signature of the validator node signing over control address and chain Id. - -**Note**: The `--private-key` parameter should be the private key of a wallet that has secured a minimum stake amount of 10 million ZILs. - -### Generating Required Values -To generate the `public-key`, `deposit-auth-signature` and `peer-id`, use the following command inside the zq2 folder. Please pass `PRIVATE_KEY_OF_VALIDATOR` and `CHAIN_ID` to the command input. - - -```bash -echo '{"secret_key":"", "chain_id": }' | cargo run --bin convert-key -``` - -By default this tool signs over the address derived from the given secret key to generate the `deposit-auth-signature`, you may want to override this for example if deploying via a Delegation contract. To override pass in `"control_address": ""`. - - -#### Sample run -```bash -$ echo '{"secret_key":"96252e38af375be21d9eb30a6b88abc3836acecaeb2240731fb42e0299e14419", "chain_id": 33469}' | cargo run --bin convert-key - Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.67s - Running `target/debug/convert-key` -{"bls_public_key":"825124961d51c99816848875fa505b75f2e62e69937fe9bfa5fa97711845abd667f05bdc3756f7dba6b7e9e0467a3804","control_address":"0x3946f9872247af2eb4fe44c81c463e801925b8d4","deposit_auth_signature":"a53efd8bad058e4e551b7e9681613a278b782acfe05fb98d536bc95029278704adbb85891cdc7fa384ab7d5008fdd42c0ea70404ab4ec07bcf5e738c92b2be88debdc33014852ead1d9976fcbf7760043615ba74f36181fc87db21f8f8997a44","peer_id":"12D3KooWGu8PBoj6vMPafnhA2P7sLumSV1NhQJZ2W2AGiBgc5ATW","tx_pubkey":{"Ecdsa":["3056301006072A8648CE3D020106052B8104000A03420004B7C457DC36C75EADA5675629F1CE0FA93534FB76ADFC49840CC050AE2995FC87764AEB8975D049D19FDA6BFF2B3FF51608034A3FC6708F476A0C9306BA5CBE14",true]}} -``` - -Or with delegation contract control address: - -```bash -$ echo '{"secret_key":"96252e38af375be21d9eb30a6b88abc3836acecaeb2240731fb42e0299e14419", "chain_id": 33469, "control_address": "0x81fbbe8916a4e986735296e130a87a97226480c5"}' | cargo run --bin convert-key - Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.67s - Running `target/debug/convert-key` -{"bls_public_key":"825124961d51c99816848875fa505b75f2e62e69937fe9bfa5fa97711845abd667f05bdc3756f7dba6b7e9e0467a3804","control_address":"0x81fbbe8916a4e986735296e130a87a97226480c5","deposit_auth_signature":"b4770471f1b6b798b3a5cf19b6f574724777f2fbf7b7f520e75fc8461cafcfd84114316fe2aeaf35b52b9ca519310f8c0bf5cd941426e4a78cc7e10c6da80f245a9ddadc42de3f8a35db42d633b2b03847b33883f702eb13c332988d34d68d90","peer_id":"12D3KooWGu8PBoj6vMPafnhA2P7sLumSV1NhQJZ2W2AGiBgc5ATW","tx_pubkey":{"Ecdsa":["3056301006072A8648CE3D020106052B8104000A03420004B7C457DC36C75EADA5675629F1CE0FA93534FB76ADFC49840CC050AE2995FC87764AEB8975D049D19FDA6BFF2B3FF51608034A3FC6708F476A0C9306BA5CBE14",true]}} - -``` - -### Run z2 deposit - -#### Sample run - - -```bash - z2 deposit --chain zq2-prototestnet \ - --peer-id 12D3KooWGu8PBoj6vMPafnhA2P7sLumSV1NhQJZ2W2AGiBgc5ATW \ - --private-key 96252e38af375be21d9eb30a6b88abc3836acecaeb2240731fb42e0299e14419 \ - --reward-address 0xe29a3e99a6997B1571DA24d6517e7b3acaFB5d9e \ - --signing-address 0x3946f9872247af2eb4fe44c81c463e801925b8d4 \ - --amount 100 \ - --public-key 825124961d51c99816848875fa505b75f2e62e69937fe9bfa5fa97711845abd667f05bdc3756f7dba6b7e9e0467a3804 \ - --deposit-auth-signature b12ab1e18e2393f165c083f9685f708aa7a1578d2685e18f4f19d950ad27c10c8dd0cf4cb637b7b215687afe861906ec064a7d89acbba718e6590cfd3baebe06bc7779028207909fff9c9c3db34f0ce812969e37d252907f9496e50bd725bb5e -``` \ No newline at end of file diff --git a/z2/docs/staking.md b/z2/docs/staking.md new file mode 100644 index 000000000..729c779a7 --- /dev/null +++ b/z2/docs/staking.md @@ -0,0 +1,158 @@ +# z2 deposit + +The `z2 deposit` command deposits ZIL tokens to the deposit smart contract to promote a node as a validator. + + +```bash +z2 deposit \ + --chain \ + --private-key \ + --public-key \ + --peer-id \ + --deposit-auth-signature \ + --amount \ + --reward-address \ + --signing-address + + +Usage: z2 deposit --chain --private-key --public-key --peer-id --deposit-auth-signature --amount --reward-address --signing-address +``` +## Parameters +* `--chain `: The name of the chain. Possible values are zq2-devnet, zq2-prototestnet, zq2-protomainnet, zq2-testnet, zq2-mainnet. +* `--private-key `: The private key of the wallet that has a minimum stake amount of 10 million. +* `--public-key `: The BLS public key of the validator node. +* `--peer-id `: The peer ID of the validator node. +* `--deposit-auth-signature `: BLS signature of the validator node signing over control address and chain Id. +* `--amount `: The amount in ZIL to deposit. The valid range is from 10 million to 255 million ZIL, allowing a deposit of up to 255 million ZIL. +* `--reward-address `: Specifies the address to receive rewards. You can generate a new wallet address to receive the rewards. +* `--signing-address `: Specifies the address which signs cross-chain events. + +**Note**: The `--private-key` parameter should be the private key of a wallet that has secured a minimum stake amount of 10 million ZILs. + +### Generating Required Values +To generate the `public-key`, `deposit-auth-signature` and `peer-id`, use the following command inside the zq2 folder. Please pass `PRIVATE_KEY_OF_VALIDATOR` and `CHAIN_ID` to the command input. + + +```bash +echo '{"secret_key":"", "chain_id": }' | cargo run --bin convert-key +``` + +By default this tool signs over the address derived from the given secret key to generate the `deposit-auth-signature`, you may want to override this for example if deploying via a Delegation contract. To override pass in `"control_address": ""`. + + +#### Sample run +```bash +$ echo '{"secret_key":"96252e38af375be21d9eb30a6b88abc3836acecaeb2240731fb42e0299e14419", "chain_id": 33469}' | cargo run --bin convert-key + Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.67s + Running `target/debug/convert-key` +{"bls_public_key":"825124961d51c99816848875fa505b75f2e62e69937fe9bfa5fa97711845abd667f05bdc3756f7dba6b7e9e0467a3804","control_address":"0x3946f9872247af2eb4fe44c81c463e801925b8d4","deposit_auth_signature":"a53efd8bad058e4e551b7e9681613a278b782acfe05fb98d536bc95029278704adbb85891cdc7fa384ab7d5008fdd42c0ea70404ab4ec07bcf5e738c92b2be88debdc33014852ead1d9976fcbf7760043615ba74f36181fc87db21f8f8997a44","peer_id":"12D3KooWGu8PBoj6vMPafnhA2P7sLumSV1NhQJZ2W2AGiBgc5ATW","tx_pubkey":{"Ecdsa":["3056301006072A8648CE3D020106052B8104000A03420004B7C457DC36C75EADA5675629F1CE0FA93534FB76ADFC49840CC050AE2995FC87764AEB8975D049D19FDA6BFF2B3FF51608034A3FC6708F476A0C9306BA5CBE14",true]}} +``` + +Or with delegation contract control address: + +```bash +$ echo '{"secret_key":"96252e38af375be21d9eb30a6b88abc3836acecaeb2240731fb42e0299e14419", "chain_id": 33469, "control_address": "0x3946f9872247af2eb4fe44c81c463e801925b8d4"}' | cargo run --bin convert-key + Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.67s + Running `target/debug/convert-key` +{"bls_public_key":"825124961d51c99816848875fa505b75f2e62e69937fe9bfa5fa97711845abd667f05bdc3756f7dba6b7e9e0467a3804","peer_id":"12D3KooWGu8PBoj6vMPafnhA2P7sLumSV1NhQJZ2W2AGiBgc5ATW","tx_pubkey":{"Ecdsa":["3056301006072A8648CE3D020106052B8104000A03420004B7C457DC36C75EADA5675629F1CE0FA93534FB76ADFC49840CC050AE2995FC87764AEB8975D049D19FDA6BFF2B3FF51608034A3FC6708F476A0C9306BA5CBE14",true]},"control_address":"0x3946f9872247af2eb4fe44c81c463e801925b8d4","deposit_auth_signature":"a53efd8bad058e4e551b7e9681613a278b782acfe05fb98d536bc95029278704adbb85891cdc7fa384ab7d5008fdd42c0ea70404ab4ec07bcf5e738c92b2be88debdc33014852ead1d9976fcbf7760043615ba74f36181fc87db21f8f8997a44"} + + +``` + +### Run z2 deposit + +#### Sample run +```bash + z2 deposit --chain zq2-prototestnet \ + --peer-id 12D3KooWGu8PBoj6vMPafnhA2P7sLumSV1NhQJZ2W2AGiBgc5ATW \ + --private-key 96252e38af375be21d9eb30a6b88abc3836acecaeb2240731fb42e0299e14419 \ + --reward-address 0xe29a3e99a6997B1571DA24d6517e7b3acaFB5d9e \ + --signing-address 0x3946f9872247af2eb4fe44c81c463e801925b8d4 \ + --amount 100 \ + --public-key 825124961d51c99816848875fa505b75f2e62e69937fe9bfa5fa97711845abd667f05bdc3756f7dba6b7e9e0467a3804 \ + --deposit-auth-signature b4770471f1b6b798b3a5cf19b6f574724777f2fbf7b7f520e75fc8461cafcfd84114316fe2aeaf35b52b9ca519310f8c0bf5cd941426e4a78cc7e10c6da80f245a9ddadc42de3f8a35db42d633b2b03847b33883f702eb13c332988d34d68d90 +``` + + +# z2 deposit top up +Top up a Staker with more ZIL. + +```bash +z2 deposit-top-up \ + --chain + --private-key + --public-key + --amount + +Usage: z2 deposit-top-up --chain --private-key --public-key --amount +``` + +## Parameters +* `--chain `: The name of the chain. Possible values are zq2-devnet, zq2-prototestnet, zq2-protomainnet, zq2-testnet, zq2-mainnet. +* `--private-key `: The private key of the wallet. +* `--public-key `: The BLS public key of the validator node. +* `--amount `: The amount in ZIL to top up. + +#### Sample run +```bash +z2 deposit-top-up --chain zq2-prototestnet \ + --private-key 96252e38af375be21d9eb30a6b88abc3836acecaeb2240731fb42e0299e14419 \ + --public-key 825124961d51c99816848875fa505b75f2e62e69937fe9bfa5fa97711845abd667f05bdc3756f7dba6b7e9e0467a3804 \ + --amount 10 +``` + + +# z2 unstake +Unstake ZIL from Staker. + +```bash +z2 unstake \ + --chain + --private-key + --public-key + --amount + +Usage: z2 deposit-top-up --chain --private-key --public-key --amount +``` + +## Parameters +* `--chain `: The name of the chain. Possible values are zq2-devnet, zq2-prototestnet, zq2-protomainnet, zq2-testnet, zq2-mainnet. +* `--private-key `: The private key of the wallet. +* `--public-key `: The BLS public key of the validator node. +* `--amount `: The amount in ZIL to unstake. + +#### Sample run +```bash +z2 unstake --chain zq2-prototestnet \ + --private-key 96252e38af375be21d9eb30a6b88abc3836acecaeb2240731fb42e0299e14419 \ + --public-key 825124961d51c99816848875fa505b75f2e62e69937fe9bfa5fa97711845abd667f05bdc3756f7dba6b7e9e0467a3804 \ + --amount 10 +``` + + +# z2 withdraw +Withdraw unstaked ZIL from deposit contract. + +```bash +z2 withdraw \ + --chain + --private-key + --public-key + --count + +Usage: z2 deposit-top-up --chain --private-key --public-key --count +``` + +## Parameters +* `--chain `: The name of the chain. Possible values are zq2-devnet, zq2-prototestnet, zq2-protomainnet, zq2-testnet, zq2-mainnet. +* `--private-key `: The private key of the wallet. +* `--public-key `: The BLS public key of the validator node. +* `--count `: Number of withdrawals to process. This is useful in scenarios when the processing a large number of withdrawals would take the transcation over the block gas limit. Set to `0` to process all withdrawals. + +#### Sample run +```bash +z2 withdraw --chain zq2-prototestnet \ + --private-key 96252e38af375be21d9eb30a6b88abc3836acecaeb2240731fb42e0299e14419 \ + --public-key 825124961d51c99816848875fa505b75f2e62e69937fe9bfa5fa97711845abd667f05bdc3756f7dba6b7e9e0467a3804 \ + --count 0 +``` \ No newline at end of file diff --git a/z2/src/bin/z2.rs b/z2/src/bin/z2.rs index 82baf6744..1410abd22 100644 --- a/z2/src/bin/z2.rs +++ b/z2/src/bin/z2.rs @@ -50,8 +50,14 @@ enum Commands { Depends(DependsCommands), /// Join a ZQ2 network Join(JoinStruct), - /// Deposit stake amount to validators + /// Deposit stake amount to a validator Deposit(DepositStruct), + /// Top up stake + DepositTopUp(DepositTopUpStruct), + /// Unstake funds + Unstake(UnstakeStruct), + /// Withdraw unstaked funds + Withdraw(WithdrawStruct), Kpi(KpiStruct), /// Print out the ports in use (otherwise they scroll off the top too fast) Ports(RunStruct), @@ -488,15 +494,18 @@ struct DepositStruct { /// Specify the ZQ2 deposit chain #[clap(long = "chain")] chain_name: chain::Chain, + /// Specify the private_key to fund the deposit + #[clap(long, short)] + private_key: String, /// Specify the Validator Public Key #[clap(long)] public_key: String, /// Specify the Validator PeerId #[clap(long)] peer_id: String, - /// Specify the private_key to fund the deposit - #[clap(long, short)] - private_key: String, + /// Specify the Validator deposit signature + #[clap(long)] + deposit_auth_signature: String, /// Specify the stake amount you want provide #[clap(long, short)] amount: u8, @@ -506,9 +515,54 @@ struct DepositStruct { /// Specify the signing address #[clap(long, short)] signing_address: String, - /// Specify the Validator deposit signature +} + +#[derive(Args, Debug)] +struct DepositTopUpStruct { + /// Specify the ZQ2 deposit chain + #[clap(long = "chain")] + chain_name: chain::Chain, + /// Specify the private_key to fund the deposit + #[clap(long, short)] + private_key: String, + /// Specify the Validator Public Key #[clap(long)] - deposit_auth_signature: String, + public_key: String, + /// Specify the stake amount you want provide + #[clap(long, short)] + amount: u8, +} + +#[derive(Args, Debug)] +struct UnstakeStruct { + /// Specify the ZQ2 deposit chain + #[clap(long = "chain")] + chain_name: chain::Chain, + /// Specify the private_key to fund the deposit + #[clap(long, short)] + private_key: String, + /// Specify the Validator Public Key + #[clap(long)] + public_key: String, + /// Specify the stake amount you want provide + #[clap(long, short)] + amount: u8, +} + +#[derive(Args, Debug)] +struct WithdrawStruct { + /// Specify the ZQ2 deposit chain + #[clap(long = "chain")] + chain_name: chain::Chain, + /// Specify the private_key to fund the deposit + #[clap(long, short)] + private_key: String, + /// Specify the Validator Public Key + #[clap(long)] + public_key: String, + /// Specify the number of withdrawals + #[clap(long, short)] + count: u8, } #[derive(Args, Debug)] @@ -1083,15 +1137,46 @@ async fn main() -> Result<()> { .unwrap(), BlsSignature::from_string(&args.deposit_auth_signature).unwrap(), )?; - let stake = validators::StakeDeposit::new( - node, - args.amount, + let client_config = validators::ClientConfig::new( &args.chain_name.get_api_endpoint()?, &args.private_key, + )?; + let deposit_params = validators::DepositParams::new( + args.amount, &args.reward_address, &args.signing_address, )?; - validators::deposit_stake(&stake).await + validators::deposit(&node, &client_config, &deposit_params).await + } + Commands::DepositTopUp(ref args) => { + let bls_public_key = + NodePublicKey::from_bytes(hex::decode(&args.public_key).unwrap().as_slice()) + .unwrap(); + let client_config = validators::ClientConfig::new( + &args.chain_name.get_api_endpoint()?, + &args.private_key, + )?; + validators::deposit_top_up(&client_config, &bls_public_key, args.amount).await + } + Commands::Unstake(ref args) => { + let bls_public_key = + NodePublicKey::from_bytes(hex::decode(&args.public_key).unwrap().as_slice()) + .unwrap(); + let client_config = validators::ClientConfig::new( + &args.chain_name.get_api_endpoint()?, + &args.private_key, + )?; + validators::unstake(&client_config, &bls_public_key, args.amount).await + } + Commands::Withdraw(ref args) => { + let bls_public_key = + NodePublicKey::from_bytes(hex::decode(&args.public_key).unwrap().as_slice()) + .unwrap(); + let client_config = validators::ClientConfig::new( + &args.chain_name.get_api_endpoint()?, + &args.private_key, + )?; + validators::withdraw(&client_config, &bls_public_key, args.count).await } Commands::Nodes(ref args) => { let spec = Composition::parse(&args.nodes)?; diff --git a/z2/src/deployer.rs b/z2/src/deployer.rs index 99264a523..76604abfd 100644 --- a/z2/src/deployer.rs +++ b/z2/src/deployer.rs @@ -303,16 +303,17 @@ pub async fn run_deposit(config_file: &str, node_selection: bool) -> Result<()> node_ethereum_address.bls_public_key, deposit_auth_signature, )?; - let stake = validators::StakeDeposit::new( - validator, - VALIDATOR_DEPOSIT_IN_MILLIONS, + let client_config = validators::ClientConfig::new( &chain.chain()?.get_api_endpoint()?, &genesis_private_key, + )?; + let deposit_params = validators::DepositParams::new( + VALIDATOR_DEPOSIT_IN_MILLIONS, ZERO_ACCOUNT, ZERO_ACCOUNT, )?; - let result = validators::deposit_stake(&stake).await; + let result = validators::deposit(&validator, &client_config, &deposit_params).await; match result { Ok(()) => successes.push(node.name()), diff --git a/z2/src/validators.rs b/z2/src/validators.rs index 705058519..856c34686 100644 --- a/z2/src/validators.rs +++ b/z2/src/validators.rs @@ -46,31 +46,16 @@ impl Validator { } #[derive(Debug)] -pub struct StakeDeposit { - validator: Validator, - amount: u8, +pub struct ClientConfig { chain_endpoint: String, private_key: String, - reward_address: H160, - signing_address: H160, } -impl StakeDeposit { - pub fn new( - validator: Validator, - amount: u8, - chain_endpoint: &str, - private_key: &str, - reward_address: &str, - signing_address: &str, - ) -> Result { +impl ClientConfig { + pub fn new(chain_endpoint: &str, private_key: &str) -> Result { Ok(Self { - validator, - amount, chain_endpoint: chain_endpoint.to_owned(), private_key: private_key.to_owned(), - reward_address: H160(hex_string_to_u8_20(reward_address).unwrap()), - signing_address: H160(hex_string_to_u8_20(signing_address).unwrap()), }) } } @@ -113,6 +98,23 @@ impl ChainConfig { } } +#[derive(Debug)] +pub struct DepositParams { + amount: u8, + reward_address: H160, + signing_address: H160, +} + +impl DepositParams { + pub fn new(amount: u8, reward_address: &str, signing_address: &str) -> Result { + Ok(Self { + amount, + reward_address: H160(hex_string_to_u8_20(reward_address).unwrap()), + signing_address: H160(hex_string_to_u8_20(signing_address).unwrap()), + }) + } +} + fn hex_string_to_u8_20(hex_str: &str) -> Result<[u8; 20], &'static str> { // Convert the hex string to a byte vector let bytes = hex::decode(hex_str.strip_prefix("0x").unwrap_or(hex_str)) @@ -182,37 +184,148 @@ pub async fn gen_validator_startup_script( Ok(()) } -pub async fn deposit_stake(stake: &StakeDeposit) -> Result<()> { +async fn build_client( + client_config: &ClientConfig, +) -> Result, LocalWallet>> { + let provider = Provider::::try_from(client_config.chain_endpoint.clone())?; + + let wallet: LocalWallet = client_config + .private_key + .as_str() + .parse::()? + .with_chain_id(provider.get_chainid().await?.as_u64()); + + Ok(SignerMiddleware::new(provider, wallet)) +} + +pub async fn deposit( + validator: &Validator, + client_config: &ClientConfig, + params: &DepositParams, +) -> Result<()> { println!( "Deposit: add {} M $ZIL to {}", - stake.amount, stake.validator.peer_id + params.amount, validator.peer_id ); - let network_api = stake.chain_endpoint.clone(); - let provider = Provider::::try_from(network_api)?; + let client = build_client(client_config).await?; - let chain_id = provider.get_chainid().await?; + // Stake the new validator's funds. + let tx = TransactionRequest::new() + .to(H160(contract_addr::DEPOSIT_PROXY.into_array())) + .value(params.amount as u128 * 1_000_000u128 * 10u128.pow(18)) + .data( + contracts::deposit_v4::DEPOSIT + .encode_input(&[ + Token::Bytes(validator.public_key.as_bytes()), + Token::Bytes(validator.peer_id.to_bytes()), + Token::Bytes(validator.deposit_auth_signature.to_bytes()), + Token::Address(params.reward_address), + Token::Address(params.signing_address), + ]) + .unwrap(), + ); - let wallet: LocalWallet = stake - .private_key - .as_str() - .parse::()? - .with_chain_id(chain_id.as_u64()); + // send it! + let pending_tx = client.send_transaction(tx, None).await?; - let client = SignerMiddleware::new(provider, wallet); + // get the mined tx + let receipt = pending_tx + .await? + .ok_or_else(|| anyhow::anyhow!("tx dropped from mempool"))?; + let tx = client.get_transaction(receipt.transaction_hash).await?; - // Stake the new validator's funds. + println!("Sent tx: {}\n", serde_json::to_string(&tx)?); + println!("Tx receipt: {}", serde_json::to_string(&receipt)?); + + Ok(()) +} + +pub async fn deposit_top_up( + client_config: &ClientConfig, + bls_public_key: &NodePublicKey, + amount: u8, +) -> Result<()> { + println!("DepositTopUp: add {} M $ZIL stake", amount,); + + let client = build_client(client_config).await?; + + // Topup the validator's funds. + let tx = TransactionRequest::new() + .to(H160(contract_addr::DEPOSIT_PROXY.into_array())) + .value(amount as u128 * 1_000_000u128 * 10u128.pow(18)) + .data( + contracts::deposit_v4::DEPOSIT_TOPUP + .encode_input(&[Token::Bytes(bls_public_key.as_bytes())]) + .unwrap(), + ); + + // send it! + let pending_tx = client.send_transaction(tx, None).await?; + + // get the mined tx + let receipt = pending_tx + .await? + .ok_or_else(|| anyhow::anyhow!("tx dropped from mempool"))?; + let tx = client.get_transaction(receipt.transaction_hash).await?; + + println!("Sent tx: {}\n", serde_json::to_string(&tx)?); + println!("Tx receipt: {}", serde_json::to_string(&receipt)?); + + Ok(()) +} + +pub async fn unstake( + client_config: &ClientConfig, + bls_public_key: &NodePublicKey, + amount: u8, +) -> Result<()> { + println!("Unstake: {} M $ZIL unstaked", amount,); + + let client = build_client(client_config).await?; + // Unstake the validator's funds. + let tx = TransactionRequest::new() + .to(H160(contract_addr::DEPOSIT_PROXY.into_array())) + .data( + contracts::deposit_v4::UNSTAKE + .encode_input(&[ + Token::Bytes(bls_public_key.as_bytes()), + Token::Uint((amount as u128 * 1_000_000u128 * 10u128.pow(18)).into()), + ]) + .unwrap(), + ); + + // send it! + let pending_tx = client.send_transaction(tx, None).await?; + + // get the mined tx + let receipt = pending_tx + .await? + .ok_or_else(|| anyhow::anyhow!("tx dropped from mempool"))?; + let tx = client.get_transaction(receipt.transaction_hash).await?; + + println!("Sent tx: {}\n", serde_json::to_string(&tx)?); + println!("Tx receipt: {}", serde_json::to_string(&receipt)?); + + Ok(()) +} + +pub async fn withdraw( + client_config: &ClientConfig, + bls_public_key: &NodePublicKey, + count: u8, +) -> Result<()> { + println!("Withdraw: pulling available unstaked funds from deposit contract"); + + let client = build_client(client_config).await?; + // Withdraw the validator's funds. let tx = TransactionRequest::new() .to(H160(contract_addr::DEPOSIT_PROXY.into_array())) - .value(stake.amount as u128 * 1_000_000u128 * 10u128.pow(18)) .data( - contracts::deposit_v3::DEPOSIT + contracts::deposit_v4::UNSTAKE .encode_input(&[ - Token::Bytes(stake.validator.public_key.as_bytes()), - Token::Bytes(stake.validator.peer_id.to_bytes()), - Token::Bytes(stake.validator.deposit_auth_signature.to_bytes()), - Token::Address(stake.reward_address), - Token::Address(stake.signing_address), + Token::Bytes(bls_public_key.as_bytes()), + Token::Uint((count as u128).into()), ]) .unwrap(), );