Skip to content

Commit

Permalink
Address comments
Browse files Browse the repository at this point in the history
  • Loading branch information
m30m committed Feb 5, 2024
1 parent b265a8f commit a7b5d66
Show file tree
Hide file tree
Showing 3 changed files with 31 additions and 15 deletions.
1 change: 1 addition & 0 deletions auction-server/src/api/marketplace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ pub async fn bid_opportunity(
));
}

// TODO: move this logic to searcher side
if liquidation.bidders.contains(&opportunity_bid.liquidator) {
return Err(RestError::BadParameters(
"Liquidator already bid on this opportunity".to_string(),
Expand Down
37 changes: 24 additions & 13 deletions auction-server/src/liquidation_adapter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,14 +84,20 @@ impl From<(Address, U256)> for TokenQty {
}
}

pub enum VerificationResult {
Success,
UnableToSpoof,
}

/// Verify an opportunity by simulating the liquidation call and checking the result
/// Simulation is done by spoofing the balances and allowances of a random liquidator
/// Returns Ok(()) if the simulation is successful or if the tokens cannot be spoofed
/// Returns Ok(VerificationResult) if the simulation is successful or if the tokens cannot be spoofed
/// Returns Err if the simulation fails despite spoofing or if any other error occurs
pub async fn verify_opportunity(
opportunity: VerifiedLiquidationOpportunity,
chain_store: &ChainStore,
per_operator: Address,
) -> Result<()> {
) -> Result<VerificationResult> {
let client = Arc::new(chain_store.provider.clone());
let fake_wallet = LocalWallet::new(&mut rand::thread_rng());
let mut fake_bid = OpportunityBid {
Expand Down Expand Up @@ -153,7 +159,7 @@ pub async fn verify_opportunity(
}
};
match spoof_info {
SpoofInfo::UnableToSpoof => return Ok(()), // unable to spoof, so we can't verify
SpoofInfo::UnableToSpoof => return Ok(VerificationResult::UnableToSpoof),
SpoofInfo::Spoofed {
balance_slot,
allowance_slot,
Expand Down Expand Up @@ -188,7 +194,7 @@ pub async fn verify_opportunity(
}
Err(e) => return Err(anyhow!(format!("Error decoding multicall result: {:?}", e))),
}
Ok(())
Ok(VerificationResult::Success)
}

fn get_liquidation_digest(params: liquidation_adapter::LiquidationCallParams) -> Result<H256> {
Expand Down Expand Up @@ -299,18 +305,24 @@ async fn verify_with_store(
opportunity: VerifiedLiquidationOpportunity,
store: &Store,
) -> Result<()> {
let current_time = SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as UnixTimestamp;

if current_time - opportunity.creation_time > MAX_STALE_OPPORTUNITY_SECS {
return Err(anyhow!("Opportunity is stale"));
}

let chain_store = store
.chains
.get(&opportunity.chain_id)
.ok_or(anyhow!("Chain not found: {}", opportunity.chain_id))?;
let per_operator = store.per_operator.address();
verify_opportunity(opportunity.clone(), chain_store, per_operator).await
match verify_opportunity(opportunity.clone(), chain_store, per_operator).await {
Ok(VerificationResult::Success) => Ok(()),
Ok(VerificationResult::UnableToSpoof) => {
let current_time =
SystemTime::now().duration_since(UNIX_EPOCH)?.as_secs() as UnixTimestamp;
if current_time - opportunity.creation_time > MAX_STALE_OPPORTUNITY_SECS {
Err(anyhow!("Opportunity is stale and unverifiable"))
} else {
Ok(())
}
}
Err(e) => Err(e),
}
}

/// Run an infinite loop to verify opportunities in the store and remove invalid ones
Expand All @@ -323,8 +335,7 @@ pub async fn run_verification_loop(store: Arc<Store>) {
while !SHOULD_EXIT.load(Ordering::Acquire) {
let all_opportunities = store.liquidation_store.opportunities.read().await.clone();
for (permission_key, opportunity) in all_opportunities.iter() {
let should_remove = verify_with_store(opportunity.clone(), &store).await;
match should_remove {
match verify_with_store(opportunity.clone(), &store).await {
Ok(_) => {}
Err(e) => {
store
Expand Down
8 changes: 6 additions & 2 deletions auction-server/src/token_spoof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
/// The spoofing is done by finding the storage slot of the balance and allowance of an address
/// This approach is just a heuristic and will not work for all tokens, specially if the token
/// has a custom storage layout or logic to calculate the balance or allowance
/// Finding the storage slot is done by brute forcing the storage slots (only the first 32 slots)
/// and checking if the output of the balance or allowance is the expected value
use ethers::addressbook::Address;
use {
crate::{
Expand Down Expand Up @@ -69,6 +71,8 @@ pub fn calculate_allowance_storage_key(
}


const MAX_SLOT_FOR_BRUTEFORCE: i32 = 32;

/// Find the balance slot of an ERC20 token that can be used to spoof the balance of an address
/// Returns an error if no slot is found or if the network calls fail
///
Expand All @@ -82,7 +86,7 @@ async fn find_spoof_balance_slot(
) -> anyhow::Result<U256> {
let contract = ERC20::new(token, client.clone());
let fake_owner = LocalWallet::new(&mut rand::thread_rng());
for balance_slot in 0..32 {
for balance_slot in 0..MAX_SLOT_FOR_BRUTEFORCE {
let tx = contract.balance_of(fake_owner.address()).tx;
let mut state = spoof::State::default();
let balance_storage_key =
Expand Down Expand Up @@ -115,7 +119,7 @@ async fn find_spoof_allowance_slot(
let fake_owner = LocalWallet::new(&mut rand::thread_rng());
let fake_spender = LocalWallet::new(&mut rand::thread_rng());

for allowance_slot in 0..32 {
for allowance_slot in 0..MAX_SLOT_FOR_BRUTEFORCE {
let tx = contract
.allowance(fake_owner.address(), fake_spender.address())
.tx;
Expand Down

0 comments on commit a7b5d66

Please sign in to comment.