Skip to content

Commit

Permalink
Circuit breaker remove solver (#2705)
Browse files Browse the repository at this point in the history
# Description
Related to #2667

POC implementation for using "Roles" safe module to grant special role
to an EOA to sign and execute "removeSolver" function on behalf of the
gpv2_authenticator manager/owner safe.

Need to add tests to see if this actually works.

# Changes
<!-- List of detailed changes (how the change is accomplished) -->

- [ ] Added `Roles` smart contract
- [ ] Added EOA account as configuration
- [ ] Implemented `remove_solver` function

## How to test
todo

---------

Co-authored-by: Mateo <[email protected]>
Co-authored-by: Mateo-mro <[email protected]>
  • Loading branch information
3 people authored May 31, 2024
1 parent 9f25c0e commit debfa39
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 10 deletions.
87 changes: 87 additions & 0 deletions crates/autopilot/src/infra/blockchain/authenticator.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use {
crate::{
domain::{self, eth},
infra::blockchain::{
contracts::{deployment_address, Contracts},
ChainId,
},
},
ethcontract::{dyns::DynWeb3, GasPrice},
};

#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct Manager {
/// The authenticator contract that decides which solver is allowed to
/// submit settlements.
authenticator: contracts::GPv2AllowListAuthentication,
/// The safe module that is used to provide special role to EOA.
authenticator_role: contracts::Roles,
/// The EOA that is allowed to remove solvers.
authenticator_eoa: ethcontract::Account,
}

impl Manager {
/// Creates an authenticator which can remove solvers from the allow-list
pub async fn new(
web3: DynWeb3,
chain: ChainId,
contracts: Contracts,
authenticator_pk: eth::H256,
) -> Self {
let authenticator_role = contracts::Roles::at(
&web3,
deployment_address(contracts::Roles::raw_contract(), &chain).expect("roles address"),
);

Self {
authenticator: contracts.authenticator().clone(),
authenticator_role,
authenticator_eoa: ethcontract::Account::Offline(
ethcontract::PrivateKey::from_raw(authenticator_pk.0).unwrap(),
None,
),
}
}

/// Fire and forget: Removes solver from the allow-list in the authenticator
/// contract. This solver will no longer be able to settle.
#[allow(dead_code)]
fn remove_solver(&self, solver: domain::eth::Address) {
let calldata = self
.authenticator
.methods()
.remove_solver(solver.into())
.tx
.data
.expect("missing calldata");
let authenticator_eoa = self.authenticator_eoa.clone();
let authenticator_address = self.authenticator.address();
let authenticator_role = self.authenticator_role.clone();
tokio::task::spawn(async move {
// This value comes from the TX posted in the issue: https://github.com/cowprotocol/services/issues/2667
let mut byte_array = [0u8; 32];
byte_array[31] = 1;
authenticator_role
.methods()
.exec_transaction_with_role(
authenticator_address,
0.into(),
ethcontract::Bytes(calldata.0),
0,
ethcontract::Bytes(byte_array),
true,
)
.from(authenticator_eoa)
.gas_price(GasPrice::Eip1559 {
// These are arbitrary high numbers that should be enough for a tx to be settled
// anytime.
max_fee_per_gas: 1000.into(),
max_priority_fee_per_gas: 5.into(),
})
.send()
.await
.inspect_err(|err| tracing::error!(?solver, ?err, "failed to remove the solver"))
});
}
}
6 changes: 4 additions & 2 deletions crates/autopilot/src/infra/blockchain/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ pub struct Contracts {
settlement: contracts::GPv2Settlement,
weth: contracts::WETH9,
chainalysis_oracle: Option<contracts::ChainalysisOracle>,
authenticator: contracts::GPv2AllowListAuthentication,

/// The authenticator contract that decides which solver is allowed to
/// submit settlements.
authenticator: contracts::GPv2AllowListAuthentication,
/// The domain separator for settlement contract used for signing orders.
settlement_domain_separator: domain::eth::DomainSeparator,
}

#[derive(Debug, Default, Clone, Copy)]
#[derive(Debug, Clone)]
pub struct Addresses {
pub settlement: Option<H160>,
pub weth: Option<H160>,
Expand Down
15 changes: 13 additions & 2 deletions crates/autopilot/src/infra/blockchain/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use {
url::Url,
};

pub mod authenticator;
pub mod contracts;

/// Chain ID as defined by EIP-155.
Expand Down Expand Up @@ -62,6 +63,11 @@ impl Rpc {
pub fn web3(&self) -> &DynWeb3 {
&self.web3
}

/// Returns a reference to the underlying RPC URL.
pub fn url(&self) -> &Url {
&self.url
}
}

/// The Ethereum blockchain.
Expand All @@ -80,8 +86,13 @@ impl Ethereum {
///
/// Since this type is essential for the program this method will panic on
/// any initialization error.
pub async fn new(rpc: Rpc, addresses: contracts::Addresses, poll_interval: Duration) -> Self {
let Rpc { web3, chain, url } = rpc;
pub async fn new(
web3: DynWeb3,
chain: ChainId,
url: Url,
addresses: contracts::Addresses,
poll_interval: Duration,
) -> Self {
let contracts = Contracts::new(&web3, &chain, addresses).await;

Self {
Expand Down
19 changes: 13 additions & 6 deletions crates/autopilot/src/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,14 @@ use {
},
domain,
event_updater::EventUpdater,
infra::{self},
infra::{self, blockchain::ChainId},
run_loop::RunLoop,
shadow,
solvable_orders::SolvableOrdersCache,
},
clap::Parser,
contracts::{BalancerV2Vault, IUniswapV3Factory},
ethcontract::{errors::DeployError, BlockNumber},
ethcontract::{dyns::DynWeb3, errors::DeployError, BlockNumber},
ethrpc::current_block::block_number_to_block_number_hash,
futures::StreamExt,
model::DomainSeparator,
Expand Down Expand Up @@ -87,11 +87,13 @@ async fn ethrpc(url: &Url) -> infra::blockchain::Rpc {
}

async fn ethereum(
ethrpc: infra::blockchain::Rpc,
web3: DynWeb3,
chain: ChainId,
url: Url,
contracts: infra::blockchain::contracts::Addresses,
poll_interval: Duration,
) -> infra::Ethereum {
infra::Ethereum::new(ethrpc, contracts, poll_interval).await
infra::Ethereum::new(web3, chain, url, contracts, poll_interval).await
}

pub async fn start(args: impl Iterator<Item = String>) {
Expand Down Expand Up @@ -149,13 +151,18 @@ pub async fn run(args: Arguments) {
}

let ethrpc = ethrpc(&args.shared.node_url).await;
let chain = ethrpc.chain();
let web3 = ethrpc.web3().clone();
let url = ethrpc.url().clone();
let contracts = infra::blockchain::contracts::Addresses {
settlement: args.shared.settlement_contract_address,
weth: args.shared.native_token_address,
};
let eth = ethereum(
ethrpc,
contracts,
web3.clone(),
chain,
url,
contracts.clone(),
args.shared.current_block.block_stream_poll_interval,
)
.await;
Expand Down
Loading

0 comments on commit debfa39

Please sign in to comment.