Skip to content

Commit

Permalink
Merge pull request #498 from chainbound/jonas/feat/cli-deregister
Browse files Browse the repository at this point in the history
feat(cli): add operator deregistration commands
  • Loading branch information
thedevbirb authored Nov 29, 2024
2 parents 060e849 + 1879a9a commit 2619485
Show file tree
Hide file tree
Showing 5 changed files with 246 additions and 8 deletions.
88 changes: 85 additions & 3 deletions bolt-cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,12 @@ Available commands:
- [`delegate`](#delegate) - Generate BLS delegation messages for the Constraints API.
- [`pubkeys`](#pubkeys) - List available BLS public keys from various key sources.
- [`send`](#send) - Send a preconfirmation request to a Bolt sidecar.
- [`validators`](#validators) - Subcommand for bolt validators.
- [`operators`](#operators) - Subcommand for bolt operators.

---

### `Delegate`
### `delegate`

The `delegate` command generates signed delegation messages for the Constraints API.
To learn more about the Constraints API, please refer to the [Bolt documentation][bolt-docs].
Expand Down Expand Up @@ -135,7 +137,7 @@ bolt delegate \

---

### `Pubkeys`
### `pubkeys`

The `pubkeys` command lists available BLS public keys from different key sources:

Expand Down Expand Up @@ -197,7 +199,7 @@ bolt pubkeys dirk --url https://localhost:9091 \

---

### `Send`
### `send`

The `send` command sends a preconfirmation request to a Bolt sidecar.

Expand Down Expand Up @@ -260,6 +262,86 @@ bolt send --private-key $(openssl rand -hex 32)

---

### `validators`

The `validators` subcommand contains functionality for bolt validators.

<details>
<summary>Usage</summary>

```text
❯ bolt validators --help
Handle validators in the bolt network
Usage: bolt validators <COMMAND>
Commands:
register Register a batch of validators
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
```

</details>

---

### `operators`

The `operators` subcommand contains functionality for bolt operators and interacting with restaking protocols like Symbiotic and EigenLayer.

<details>
<summary>Usage</summary>

```text
❯ bolt operators --help
Handle operators in the bolt network
Usage: bolt operators <COMMAND>
Commands:
eigenlayer Commands to interact with EigenLayer and bolt
symbiotic Commands to interact with Symbiotic and bolt
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
❯ bolt operators eigenlayer --help
Commands to interact with EigenLayer and bolt
Usage: bolt operators eigenlayer <COMMAND>
Commands:
deposit Deposit into a strategy
register Register an operator into the bolt AVS
deregister Deregister an EigenLayer operator from the bolt AVS
status Check the status of an operator in the bolt AVS
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
❯ bolt operators symbiotic --help
Commands to interact with Symbiotic and bolt
Usage: bolt operators symbiotic <COMMAND>
Commands:
register Register into the bolt manager contract as a Symbiotic operator
deregister Deregister a Symbiotic operator from bolt
status Check the status of a Symbiotic operator
help Print this message or the help of the given subcommand(s)
Options:
-h, --help Print help
```

</details>

---

## Security

The Bolt CLI is designed to be used offline. It does not require any network connections
Expand Down
27 changes: 24 additions & 3 deletions bolt-cli/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ pub enum OperatorsSubcommand {

#[derive(Debug, Clone, Parser)]
pub enum EigenLayerSubcommand {
/// Step 1: Deposit into a strategy.
/// Deposit into a strategy.
Deposit {
/// The URL of the RPC to broadcast the transaction.
#[clap(long, env = "RPC_URL")]
Expand All @@ -209,7 +209,7 @@ pub enum EigenLayerSubcommand {
amount: U256,
},

/// Step 2: Register into the bolt AVS.
/// Register an operator into the bolt AVS.
Register {
/// The URL of the RPC to broadcast the transaction.
#[clap(long, env = "RPC_URL")]
Expand All @@ -228,7 +228,17 @@ pub enum EigenLayerSubcommand {
expiry: U256,
},

/// Step 3: Check your operation registration in bolt
/// Deregister an EigenLayer operator from the bolt AVS.
Deregister {
/// The URL of the RPC to broadcast the transaction.
#[clap(long, env = "RPC_URL")]
rpc_url: Url,
/// The private key of the operator.
#[clap(long, env = "OPERATOR_PRIVATE_KEY")]
operator_private_key: B256,
},

/// Check the status of an operator in the bolt AVS.
Status {
/// The URL of the RPC to broadcast the transaction.
#[clap(long, env = "RPC_URL")]
Expand All @@ -253,6 +263,17 @@ pub enum SymbioticSubcommand {
#[clap(long, env = "OPERATOR_RPC")]
operator_rpc: Url,
},

/// Deregister a Symbiotic operator from bolt.
Deregister {
/// The URL of the RPC to broadcast the transaction.
#[clap(long, env = "RPC_URL")]
rpc_url: Url,
/// The private key of the operator.
#[clap(long, env = "OPERATOR_PRIVATE_KEY")]
operator_private_key: B256,
},

/// Check the status of a Symbiotic operator.
Status {
/// The URL of the RPC to broadcast the transaction.
Expand Down
2 changes: 1 addition & 1 deletion bolt-cli/src/commands/delegate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,7 +383,7 @@ mod tests {
let signed_delegations = generate_from_keystore(
&keys_path,
keystore_secret,
delegatee_pubkey.clone(),
delegatee_pubkey,
chain,
Action::Delegate,
)?;
Expand Down
128 changes: 127 additions & 1 deletion bolt-cli/src/commands/operators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,47 @@ impl OperatorsCommand {
eyre::bail!("Transaction failed: {:?}", receipt)
}

info!("Succesfully registered Symbiotic operator");
info!("Succesfully registered EigenLayer operator");

Ok(())
}
EigenLayerSubcommand::Deregister { rpc_url, operator_private_key } => {
let signer = PrivateKeySigner::from_bytes(&operator_private_key)
.wrap_err("valid private key")?;
let address = signer.address();

let provider = ProviderBuilder::new()
.with_recommended_fillers()
.wallet(EthereumWallet::from(signer))
.on_http(rpc_url);

let chain_id = provider.get_chain_id().await?;
let chain = Chain::from_id(chain_id)
.unwrap_or_else(|| panic!("chain id {} not supported", chain_id));

info!(operator = %address, ?chain, "Deregistering EigenLayer operator");

request_confirmation();

let deployments = deployments_for_chain(chain);

let bolt_avs_address = deployments.bolt.eigenlayer_middleware;
let bolt_eigenlayer_middleware =
BoltEigenLayerMiddleware::new(bolt_avs_address, provider);

let result = bolt_eigenlayer_middleware.deregisterOperator().send().await?;

info!(
hash = ?result.tx_hash(),
"deregisterOperator transaction sent, awaiting receipt..."
);

let receipt = result.get_receipt().await?;
if !receipt.status() {
eyre::bail!("Transaction failed: {:?}", receipt)
}

info!("Succesfully deregistered EigenLayer operator");

Ok(())
}
Expand Down Expand Up @@ -245,6 +285,47 @@ impl OperatorsCommand {

Ok(())
}
SymbioticSubcommand::Deregister { rpc_url, operator_private_key } => {
let signer = PrivateKeySigner::from_bytes(&operator_private_key)
.wrap_err("valid private key")?;
let address = signer.address();

let provider = ProviderBuilder::new()
.with_recommended_fillers()
.wallet(EthereumWallet::from(signer))
.on_http(rpc_url);

let chain_id = provider.get_chain_id().await?;
let chain = Chain::from_id(chain_id)
.unwrap_or_else(|| panic!("chain id {} not supported", chain_id));

let deployments = deployments_for_chain(chain);

info!(operator = %address, ?chain, "Deregistering Symbiotic operator");

request_confirmation();

let middleware = BoltSymbioticMiddleware::new(
deployments.bolt.symbiotic_middleware,
provider,
);

let pending = middleware.deregisterOperator().send().await?;

info!(
hash = ?pending.tx_hash(),
"deregisterOperator transaction sent, awaiting receipt..."
);

let receipt = pending.get_receipt().await?;
if !receipt.status() {
eyre::bail!("Transaction failed: {:?}", receipt)
}

info!("Succesfully deregistered Symbiotic operator");

Ok(())
}
SymbioticSubcommand::Status { rpc_url, address } => {
let provider = ProviderBuilder::new().on_http(rpc_url.clone());
let chain_id = provider.get_chain_id().await?;
Expand Down Expand Up @@ -292,6 +373,7 @@ mod tests {

#[tokio::test]
async fn test_eigenlayer_flow() {
let _ = tracing_subscriber::fmt().try_init();
let mut rnd = rand::thread_rng();
let secret_key = B256::from(rnd.gen::<[u8; 32]>());
let wallet = PrivateKeySigner::from_bytes(&secret_key).expect("valid private key");
Expand Down Expand Up @@ -401,6 +483,28 @@ mod tests {
};

check_operator_registration.run().await.expect("to check operator registration");

let deregister_operator = OperatorsCommand {
subcommand: OperatorsSubcommand::EigenLayer {
subcommand: EigenLayerSubcommand::Deregister {
rpc_url: anvil_url.parse().expect("valid url"),
operator_private_key: secret_key,
},
},
};

deregister_operator.run().await.expect("to deregister operator");

let check_operator_registration = OperatorsCommand {
subcommand: OperatorsSubcommand::EigenLayer {
subcommand: EigenLayerSubcommand::Status {
rpc_url: anvil_url.parse().expect("valid url"),
address: account,
},
},
};

check_operator_registration.run().await.expect("to check operator registration");
}

/// Ignored since it requires Symbiotic CLI: https://docs.symbiotic.fi/guides/cli/#installation
Expand Down Expand Up @@ -527,5 +631,27 @@ mod tests {
};

check_status.run().await.expect("to check operator status");

let deregister_command = OperatorsCommand {
subcommand: OperatorsSubcommand::Symbiotic {
subcommand: SymbioticSubcommand::Deregister {
rpc_url: anvil_url.parse().expect("valid url"),
operator_private_key: secret_key,
},
},
};

deregister_command.run().await.expect("to deregister operator");

let check_status = OperatorsCommand {
subcommand: OperatorsSubcommand::Symbiotic {
subcommand: SymbioticSubcommand::Status {
rpc_url: anvil_url.parse().expect("valid url"),
address: account,
},
},
};

check_status.run().await.expect("to check operator status");
}
}
9 changes: 9 additions & 0 deletions bolt-cli/src/contracts/bolt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,11 @@ sol! {
/// EigenLayer internally contains a mapping from `msg.sender` (our AVS contract) to the operator.
/// The msg.sender of this call will be the operator address.
function registerOperator(string calldata rpc, SignatureWithSaltAndExpiry calldata operatorSignature) public;

/// @notice Deregister an EigenLayer operator from working in Bolt Protocol.
/// @dev This requires calling the EigenLayer AVS Directory contract to deregister the operator.
/// EigenLayer internally contains a mapping from `msg.sender` (our AVS contract) to the operator.
function deregisterOperator() public;
}

#[allow(missing_docs)]
Expand All @@ -45,6 +50,10 @@ sol! {
/// msg.sender must be an operator in the Symbiotic network.
function registerOperator(string calldata rpc) public;

/// @notice Deregister a Symbiotic operator from working in Bolt Protocol.
/// @dev This does NOT deregister the operator from the Symbiotic network.
function deregisterOperator() public;

/// @notice Get the collaterals and amounts staked by an operator across the supported strategies.
///
/// @param operator The operator address to get the collaterals and amounts staked for.
Expand Down

0 comments on commit 2619485

Please sign in to comment.