Skip to content

Commit

Permalink
Flashloan appdata (#3236)
Browse files Browse the repository at this point in the history
# Description
Fixes: #3214

# Changes
Extend appdata format with `flashloan` field explained in the linked
issue.

## How to test
added an e2e test that shows it's now possible to create an order that
contains flashloan hints in the appdata.
  • Loading branch information
MartinquaXD authored Jan 16, 2025
1 parent b4e2bbd commit 50f7c9a
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 1 deletion.
21 changes: 20 additions & 1 deletion crates/app-data/src/app_data.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use {
crate::{app_data_hash::hash_full_app_data, AppDataHash, Hooks},
anyhow::{anyhow, Context, Result},
primitive_types::H160,
primitive_types::{H160, U256},
serde::{de, Deserialize, Deserializer, Serialize, Serializer},
std::{fmt, fmt::Display},
};
Expand All @@ -24,6 +24,24 @@ pub struct ProtocolAppData {
pub signer: Option<H160>,
pub replaced_order: Option<ReplacedOrder>,
pub partner_fee: Option<PartnerFee>,
pub flashloan: Option<Flashloan>,
}

/// Contains information to hint at how a solver could make
/// use of flashloans to settle the associated order.
/// Since using flashloans introduces a bunch of complexities
/// all these hints are not binding for the solver.
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
pub struct Flashloan {
/// Which contract to request the flashloan from.
lender: Option<H160>,
/// Who should receive the borrowed tokens. If this is not
/// set the order owner will get the tokens.
borrower: Option<H160>,
/// Which token to flashloan.
token: H160,
/// How much of the token to flashloan.
amount: U256,
}

#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)]
Expand Down Expand Up @@ -217,6 +235,7 @@ impl From<BackendAppData> for ProtocolAppData {
signer: None,
replaced_order: None,
partner_fee: None,
flashloan: None,
}
}
}
Expand Down
112 changes: 112 additions & 0 deletions crates/e2e/tests/e2e/app_data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ async fn local_node_app_data() {
run_test(app_data).await;
}

#[tokio::test]
#[ignore]
async fn local_node_app_data_full_format() {
run_test(app_data_full_format).await;
}

// Test that orders can be placed with the new app data format.
async fn app_data(web3: Web3) {
let mut onchain = OnchainComponents::deploy(web3).await;
Expand Down Expand Up @@ -177,3 +183,109 @@ async fn app_data(web3: Web3) {
.unwrap_err();
dbg!(err);
}

/// Tests that orders can be placed with an app data JSON containing
/// all supported features.
async fn app_data_full_format(web3: Web3) {
let mut onchain = OnchainComponents::deploy(web3).await;
let [solver] = onchain.make_solvers(to_wei(1)).await;
let [trader] = onchain.make_accounts(to_wei(1)).await;
let [token_a, token_b] = onchain
.deploy_tokens_with_weth_uni_v2_pools(to_wei(1_000), to_wei(1_000))
.await;

token_a.mint(trader.address(), to_wei(10)).await;
tx!(
trader.account(),
token_a.approve(onchain.contracts().allowance, to_wei(10))
);

let mut valid_to: u32 = model::time::now_in_epoch_seconds() + 300;
let mut create_order = |app_data| {
let order = OrderCreation {
app_data,
sell_token: token_a.address(),
sell_amount: to_wei(2),
buy_token: token_b.address(),
buy_amount: to_wei(1),
valid_to,
kind: OrderKind::Sell,
..Default::default()
}
.sign(
EcdsaSigningScheme::Eip712,
&onchain.contracts().domain_separator,
SecretKeyRef::from(&SecretKey::from_slice(trader.private_key()).unwrap()),
);
// Adjust valid to make sure we get unique UIDs.
valid_to += 1;
order
};

let services = Services::new(&onchain).await;
services.start_protocol(solver).await;

// Unknown hashes are not accepted.
let order0 = create_order(OrderCreationAppData::Hash {
hash: AppDataHash([0; 32]),
});
let order_uid = services.create_order(&order0).await.unwrap();

// appdata using all features
let app_data = format!(
r#"{{
"version": "0.9.0",
"appCode": "CoW Swap",
"environment": "barn",
"metadata": {{
"quote": {{
"slippageBps": "50"
}},
"hooks": {{
"pre": [
{{
"target": "0x0000000000000000000000000000000000000000",
"callData": "0x12345678",
"gasLimit": "21000"
}}
],
"post": [
{{
"target": "0x0000000000000000000000000000000000000000",
"callData": "0x12345678",
"gasLimit": "21000"
}}
]
}},
"flashloan": {{
"lender": "0x1111111111111111111111111111111111111111",
"borrower": "0x2222222222222222222222222222222222222222",
"token": "0x3333333333333333333333333333333333333333",
"amount": "1234"
}},
"signer": "{:?}",
"replacedOrder": {{
"uid": "{}"
}},
"partnerFee": {{
"bps": 1,
"recipient": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
}}
}}
}}"#,
trader.address(),
order_uid
);

let app_data_hash = AppDataHash(hash_full_app_data(app_data.as_bytes()));
let order1 = create_order(OrderCreationAppData::Full {
full: app_data.to_string(),
});
let uid = services.create_order(&order1).await.unwrap();
let order1_ = services.get_order(&uid).await.unwrap();
assert_eq!(order1_.data.app_data, app_data_hash);
assert_eq!(order1_.metadata.full_app_data, Some(app_data.to_string()));

let app_data_ = services.get_app_data(app_data_hash).await.unwrap();
assert_eq!(app_data_, app_data);
}

0 comments on commit 50f7c9a

Please sign in to comment.