Skip to content

Commit

Permalink
Merge branch 'main' into verification
Browse files Browse the repository at this point in the history
  • Loading branch information
m30m committed Feb 4, 2024
2 parents 6040dba + fe7e0c6 commit 67536bc
Show file tree
Hide file tree
Showing 16 changed files with 230 additions and 138 deletions.
26 changes: 26 additions & 0 deletions auction-server/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
use std::process::Command;

fn main() {
let contract_setup = r#"
cd ../per_multicall
forge build --via-ir
"#;
println!("cargo:rerun-if-changed=../per_multicall");

// Build the contracts and generate the ABIs. This is required for abigen! macro expansions to work.
let output = Command::new("sh")
.args(["-c", contract_setup])
.output()
.expect("Failed to run build contracts command");
if !output.status.success() {
panic!(
"Failed to build contracts: {}",
String::from_utf8_lossy(&output.stderr)
);
} else {
println!(
"Built all solidity contracts {}",
String::from_utf8_lossy(&output.stdout)
);
}
}
1 change: 1 addition & 0 deletions auction-server/config.sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ chains:
per_contract: 0xa513E6E4b8f2a923D98304ec87F64353C4D5C853
adapter_contract: 0xB7f8BC63BbcaD18155201308C8f3540b07f84F5e
legacy_tx: false
poll_interval: 1
2 changes: 2 additions & 0 deletions auction-server/rust-toolchain.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[toolchain]
channel = "stable"
2 changes: 1 addition & 1 deletion auction-server/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ pub async fn start_server(run_options: RunOptions) -> Result<()> {
rpc_addr = chain_config.geth_rpc_addr
)
})?;
provider.set_interval(Duration::from_secs(1));
provider.set_interval(Duration::from_secs(chain_config.poll_interval));
let id = provider.get_chainid().await?.as_u64();
Ok((
chain_id.clone(),
Expand Down
100 changes: 40 additions & 60 deletions auction-server/src/api/marketplace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ use {
rest::handle_bid,
RestError,
},
config::ChainId,
liquidation_adapter::{
make_liquidator_calldata,
parse_revert_error,
Expand Down Expand Up @@ -33,7 +34,6 @@ use {
Serialize,
},
std::{
str::FromStr,
sync::Arc,
time::{
SystemTime,
Expand All @@ -50,36 +50,39 @@ pub struct TokenQty {
#[schema(example = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2",value_type=String)]
contract: Address,
/// Token amount
#[schema(example = "1000")]
amount: String,
#[schema(example = "1000", value_type=String)]
#[serde(with = "crate::serde::u256")]
amount: U256,
}

/// A liquidation opportunity ready to be executed.
/// If a searcher signs the opportunity and have approved enough tokens to liquidation adapter, by calling this contract with the given calldata and structures, they will receive the tokens specified in the receipt_tokens field, and will send the tokens specified in the repay_tokens field.
/// If a searcher signs the opportunity and have approved enough tokens to liquidation adapter,
/// by calling this contract with the given calldata and structures, they will receive the tokens specified
/// in the receipt_tokens field, and will send the tokens specified in the repay_tokens field.
#[derive(Serialize, Deserialize, ToSchema, Clone)]
pub struct LiquidationOpportunity {
/// The permission key required for succesful execution of the liquidation.
#[schema(example = "0xdeadbeefcafe", value_type=String)]
permission_key: Bytes,
/// The chain id where the liquidation will be executed.
#[schema(example = "sepolia")]
chain_id: String,
chain_id: ChainId,
/// The contract address to call for execution of the liquidation.
#[schema(example = "0xcA11bde05977b3631167028862bE2a173976CA11", value_type=String)]
contract: Address,
/// Calldata for the contract call.
#[schema(example = "0xdeadbeef", value_type=String)]
calldata: Bytes,
/// The value to send with the contract call.
#[schema(example = "1")]
value: String,
#[schema(example = "1", value_type=String)]
#[serde(with = "crate::serde::u256")]
value: U256,

repay_tokens: Vec<TokenQty>,
receipt_tokens: Vec<TokenQty>,
}

/// A submitted liquidation opportunity ready to be executed.
/// If a searcher signs the opportunity and have approved enough tokens to liquidation adapter, by calling this contract with the given calldata and structures, they will receive the tokens specified in the receipt_tokens field, and will send the tokens specified in the repay_tokens field.
/// Similar to LiquidationOpportunity, but with the opportunity id included.
#[derive(Serialize, Deserialize, ToSchema, Clone)]
pub struct LiquidationOpportunityWithId {
/// The opportunity unique id
Expand All @@ -93,28 +96,25 @@ impl From<(Address, U256)> for TokenQty {
fn from(token: (Address, U256)) -> Self {
TokenQty {
contract: token.0,
amount: token.1.to_string(),
amount: token.1,
}
}
}

impl TryFrom<TokenQty> for (Address, U256) {
type Error = RestError;

fn try_from(token: TokenQty) -> Result<Self, Self::Error> {
let amount = U256::from_dec_str(token.amount.as_str())
.map_err(|_| RestError::BadParameters("Invalid token amount".to_string()))?;
Ok((token.contract, amount))
impl From<TokenQty> for (Address, U256) {
fn from(token: TokenQty) -> Self {
(token.contract, token.amount)
}
}

fn parse_tokens(tokens: Vec<TokenQty>) -> Result<Vec<(Address, U256)>, RestError> {
tokens.into_iter().map(|token| token.try_into()).collect()
fn parse_tokens(tokens: Vec<TokenQty>) -> Vec<(Address, U256)> {
tokens.into_iter().map(|token| token.into()).collect()
}

/// Submit a liquidation opportunity ready to be executed.
///
/// The opportunity will be verified by the server. If the opportunity is valid, it will be stored in the database and will be available for bidding.
/// The opportunity will be verified by the server. If the opportunity is valid, it will be stored in the database
/// and will be available for bidding.
#[utoipa::path(post, path = "/liquidation/submit_opportunity", request_body = LiquidationOpportunity, responses(
(status = 200, description = "Opportunity was stored succesfuly with the returned uuid", body = String),
(status = 400, response=RestError)
Expand All @@ -128,8 +128,8 @@ pub async fn submit_opportunity(
.get(&opportunity.chain_id)
.ok_or(RestError::InvalidChainId)?;

let repay_tokens = parse_tokens(opportunity.repay_tokens)?;
let receipt_tokens = parse_tokens(opportunity.receipt_tokens)?;
let repay_tokens = parse_tokens(opportunity.repay_tokens);
let receipt_tokens = parse_tokens(opportunity.receipt_tokens);

let id = Uuid::new_v4();
let verified_opportunity = VerifiedLiquidationOpportunity {
Expand All @@ -142,8 +142,7 @@ pub async fn submit_opportunity(
permission_key: opportunity.permission_key.clone(),
contract: opportunity.contract,
calldata: opportunity.calldata,
value: U256::from_dec_str(opportunity.value.as_str())
.map_err(|_| RestError::BadParameters("Invalid value".to_string()))?,
value: opportunity.value,
repay_tokens,
receipt_tokens,
bidders: Default::default(),
Expand Down Expand Up @@ -189,7 +188,7 @@ pub async fn fetch_opportunities(
chain_id: opportunity.chain_id,
contract: opportunity.contract,
calldata: opportunity.calldata,
value: opportunity.value.to_string(),
value: opportunity.value,
repay_tokens: opportunity
.repay_tokens
.into_iter()
Expand All @@ -211,31 +210,26 @@ pub async fn fetch_opportunities(
pub struct OpportunityBid {
/// The opportunity id to bid on.
#[schema(example = "f47ac10b-58cc-4372-a567-0e02b2c3d479",value_type=String)]
opportunity_id: Uuid,
pub opportunity_id: Uuid,
/// The opportunity permission key
#[schema(example = "0xdeadbeefcafe", value_type=String)]
permission_key: Bytes,
pub permission_key: Bytes,
/// The bid amount in wei.
#[schema(example = "1000000000000000000")]
bid_amount: String,
#[schema(example = "1000000000000000000", value_type=String)]
#[serde(with = "crate::serde::u256")]
pub amount: U256,
/// How long the bid will be valid for.
#[schema(example = "1000000000000000000")]
valid_until: String,
#[schema(example = "1000000000000000000", value_type=String)]
#[serde(with = "crate::serde::u256")]
pub valid_until: U256,
/// Liquidator address
#[schema(example = "0x5FbDB2315678afecb367f032d93F642f64180aa2", value_type=String)]
liquidator: Address,
pub liquidator: Address,
#[schema(
example = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12"
example = "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef12",
value_type=String
)]
signature: String,
}

#[derive(Clone, Copy)]
pub struct VerifiedOpportunityBid {
pub opportunity_id: Uuid,
pub bid_amount: U256,
pub valid_until: U256,
pub liquidator: Address,
#[serde(with = "crate::serde::signature")]
pub signature: Signature,
}

Expand Down Expand Up @@ -275,36 +269,22 @@ pub async fn bid_opportunity(
.get(&liquidation.chain_id)
.ok_or(RestError::InvalidChainId)?;

let bid_amount = U256::from_dec_str(opportunity_bid.bid_amount.as_str())
.map_err(|_| RestError::BadParameters("Invalid bid_amount".to_string()))?;
let valid_until = U256::from_dec_str(opportunity_bid.valid_until.as_str())
.map_err(|_| RestError::BadParameters("Invalid valid_until".to_string()))?;
let signature = Signature::from_str(opportunity_bid.signature.as_str())
.map_err(|_| RestError::BadParameters("Invalid signature".to_string()))?;
let verified_liquidation_bid = VerifiedOpportunityBid {
opportunity_id: opportunity_bid.opportunity_id,
bid_amount,
valid_until,
liquidator: opportunity_bid.liquidator,
signature,
};

let per_calldata = make_liquidator_calldata(
liquidation.clone(),
verified_liquidation_bid,
opportunity_bid.clone(),
chain_store.provider.clone(),
chain_store.config.adapter_contract,
)
.await
.map_err(|e| RestError::BadParameters(e.to_string()))?;
match handle_bid(
store.clone(),
crate::api::rest::ParsedBid {
crate::api::rest::Bid {
permission_key: liquidation.permission_key.clone(),
chain_id: liquidation.chain_id.clone(),
contract: chain_store.config.adapter_contract,
calldata: per_calldata,
bid_amount: verified_liquidation_bid.bid_amount,
amount: opportunity_bid.amount,
},
)
.await
Expand All @@ -319,7 +299,7 @@ pub async fn bid_opportunity(
}
Err(e) => match e {
RestError::SimulationError { result, reason } => {
let parsed = parse_revert_error(result.clone());
let parsed = parse_revert_error(&result);
match parsed {
Some(decoded) => Err(RestError::BadParameters(decoded)),
None => {
Expand Down
70 changes: 20 additions & 50 deletions auction-server/src/api/rest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ use {
crate::{
api::RestError,
auction::{
per::MulticallStatus,
simulate_bids,
SimulationError,
},
state::{
SimulatedBid,
Expand Down Expand Up @@ -36,30 +36,23 @@ use {
pub struct Bid {
/// The permission key to bid on.
#[schema(example = "0xdeadbeef", value_type=String)]
permission_key: Bytes,
pub permission_key: Bytes,
/// The chain id to bid on.
#[schema(example = "sepolia")]
chain_id: String,
pub chain_id: String,
/// The contract address to call.
#[schema(example = "0xcA11bde05977b3631167028862bE2a173976CA11",value_type = String)]
contract: Address,
pub contract: Address,
/// Calldata for the contract call.
#[schema(example = "0xdeadbeef", value_type=String)]
calldata: Bytes,
/// Amount of bid in wei.
#[schema(example = "1000000000000000000")]
bid: String,
}

pub struct ParsedBid {
pub permission_key: Bytes,
pub chain_id: String,
pub contract: Address,
pub calldata: Bytes,
pub bid_amount: U256,
/// Amount of bid in wei.
#[schema(example = "10", value_type=String)]
#[serde(with = "crate::serde::u256")]
pub amount: U256,
}

pub async fn handle_bid(store: Arc<Store>, bid: ParsedBid) -> Result<String, RestError> {
pub async fn handle_bid(store: Arc<Store>, bid: Bid) -> Result<String, RestError> {
let chain_store = store
.chains
.get(&bid.chain_id)
Expand All @@ -71,27 +64,15 @@ pub async fn handle_bid(store: Arc<Store>, bid: ParsedBid) -> Result<String, Res
bid.permission_key.clone(),
vec![bid.contract],
vec![bid.calldata.clone()],
vec![bid.bid_amount],
vec![bid.amount],
);

match call.await {
Ok(result) => {
let multicall_results: Vec<MulticallStatus> = result;
if !multicall_results.iter().all(|x| x.external_success) {
let first_reason = multicall_results
.first()
.cloned()
.unwrap()
.multicall_revert_reason;
let first_result = multicall_results.first().cloned().unwrap().external_result;
return Err(RestError::SimulationError {
result: first_result,
reason: first_reason,
});
if let Err(e) = call.await {
return match e {
SimulationError::LogicalError { result, reason } => {
Err(RestError::SimulationError { result, reason })
}
}
Err(e) => {
return match e {
SimulationError::ContractError(e) => match e {
ContractError::Revert(reason) => Err(RestError::BadParameters(format!(
"Contract Revert Error: {}",
String::decode_with_selector(&reason)
Expand All @@ -100,8 +81,8 @@ pub async fn handle_bid(store: Arc<Store>, bid: ParsedBid) -> Result<String, Res
ContractError::MiddlewareError { e: _ } => Err(RestError::TemporarilyUnavailable),
ContractError::ProviderError { e: _ } => Err(RestError::TemporarilyUnavailable),
_ => Err(RestError::BadParameters(format!("Error: {}", e))),
}
}
},
};
};

chain_store
Expand All @@ -113,7 +94,7 @@ pub async fn handle_bid(store: Arc<Store>, bid: ParsedBid) -> Result<String, Res
.push(SimulatedBid {
contract: bid.contract,
calldata: bid.calldata.clone(),
bid: bid.bid_amount,
bid: bid.amount,
});
Ok("OK".to_string())
}
Expand All @@ -129,22 +110,11 @@ pub async fn bid(
State(store): State<Arc<Store>>,
Json(bid): Json<Bid>,
) -> Result<String, RestError> {
let bid = bid.clone();
store
.chains
.get(&bid.chain_id)
.ok_or(RestError::InvalidChainId)?;

let bid_amount = U256::from_dec_str(bid.bid.as_str())
.map_err(|_| RestError::BadParameters("Invalid bid amount".to_string()))?;
handle_bid(
store,
ParsedBid {
permission_key: bid.permission_key,
chain_id: bid.chain_id,
contract: bid.contract,
calldata: bid.calldata,
bid_amount,
},
)
.await
handle_bid(store, bid).await
}
Loading

0 comments on commit 67536bc

Please sign in to comment.