Skip to content

Commit

Permalink
Merge branch 'main' into fix/3159
Browse files Browse the repository at this point in the history
  • Loading branch information
squadgazzz authored Jan 2, 2025
2 parents f8ac38e + de86178 commit ca517b0
Show file tree
Hide file tree
Showing 97 changed files with 1,107 additions and 386 deletions.
87 changes: 87 additions & 0 deletions .github/workflows/hotfix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
name: Hotfix Release

permissions:
contents: write

on:
pull_request_target:
types: [closed]
branches:
- main

jobs:
hotfix_release:
if: ${{ github.event.pull_request.merged == true && contains(github.event.pull_request.labels.*.name, 'hotfix') }}
runs-on: ubuntu-latest
steps:
- name: Check out
uses: actions/checkout@v4
with:
token: "${{ secrets.HOTFIX_ACTION_JOB }}"
fetch-depth: 0

- name: Configure git
run: |
git config user.name 'github-actions-bot'
git config user.email '[email protected]'
git fetch --tags
- name: Get latest release version tag
id: fetch_tag
run: |
LATEST_VERSION=$(curl -s https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r '.tag_name')
if ! [[ "$LATEST_VERSION" =~ ^v[0-9]+\.[0-9]+\..* ]]; then
echo "Invalid tag format, cannot bump version of: $LATEST_VERSION"
exit 1
fi
echo "latest=$LATEST_VERSION" >> $GITHUB_OUTPUT
- name: Determine next patch version
id: bump
run: |
VERSION="${{ steps.fetch_tag.outputs.latest }}"
VERSION_NO_PREFIX="${VERSION#v}"
IFS='.' read -r MAJOR MINOR PATCH <<< "$VERSION_NO_PREFIX"
NEW_PATCH=$((PATCH + 1))
NEW_TAG="v$MAJOR.$MINOR.$NEW_PATCH"
echo "tag=$NEW_TAG" >> $GITHUB_OUTPUT
- name: Create and switch to hotfix branch
run: |
git checkout "${{ steps.fetch_tag.outputs.latest }}"
git checkout -b "hotfix/${{ steps.bump.outputs.tag }}"
- name: Cherry-pick merged commit
run: |
MERGE_COMMIT_SHA="${{ github.event.pull_request.merge_commit_sha }}"
if ! git cherry-pick "$MERGE_COMMIT_SHA"; then
echo "Cherry-pick failed. Please resolve conflicts manually."
exit 1
fi
- name: Create and push tag
id: tag_version
run: |
git tag "${{ steps.bump.outputs.tag }}"
git push origin "${{ steps.bump.outputs.tag }}"
- name: "Create hotfix release"
uses: actions/github-script@v6
with:
github-token: "${{ secrets.HOTFIX_ACTION_TOKEN }}"
script: |
try {
const response = await github.rest.repos.createRelease({
draft: false,
generate_release_notes: true,
name: "Hotfix ${{ steps.bump.outputs.tag }}",
owner: context.repo.owner,
prerelease: false,
repo: context.repo.repo,
tag_name: "${{ steps.bump.outputs.tag }}",
});
core.exportVariable('RELEASE_ID', response.data.id);
core.exportVariable('RELEASE_UPLOAD_URL', response.data.upload_url);
} catch (error) {
core.setFailed(error.message);
}
4 changes: 3 additions & 1 deletion .github/workflows/pull-request.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,8 @@ jobs:
- run: cargo nextest run -p e2e local_node --test-threads 1 --failure-output final --run-ignored ignored-only

test-forked-node:
# Do not run this job on forks since some secrets are required for it.
if: ${{ github.event.pull_request.head.repo.full_name == github.repository }}
timeout-minutes: 60
runs-on: ubuntu-latest
env:
Expand Down Expand Up @@ -167,7 +169,7 @@ jobs:
# Build the driver's tests.
- run: cargo build -p driver --tests
# Don't spawn any docker containers. The driver's tests will spawn anvil itself.
- run: cargo nextest run -p driver --run-ignored ignored-only
- run: cargo nextest run -p driver --test-threads 1 --run-ignored ignored-only

openapi:
timeout-minutes: 60
Expand Down
4 changes: 2 additions & 2 deletions crates/autopilot/src/domain/auction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl Price {
/// The base Ether amount for pricing.
const BASE: u128 = 10_u128.pow(18);

pub fn new(value: eth::Ether) -> Result<Self, InvalidPrice> {
pub fn try_new(value: eth::Ether) -> Result<Self, InvalidPrice> {
if value.0.is_zero() {
Err(InvalidPrice)
} else {
Expand All @@ -70,7 +70,7 @@ impl Price {
/// use autopilot::domain::{auction::Price, eth};
///
/// let amount = eth::TokenAmount::from(eth::U256::exp10(18));
/// let price = Price::new(eth::Ether::from(eth::U256::exp10(15))).unwrap(); // 0.001 ETH
/// let price = Price::try_new(eth::Ether::from(eth::U256::exp10(15))).unwrap(); // 0.001 ETH
///
/// let eth = price.in_eth(amount);
/// assert_eq!(eth, eth::Ether::from(eth::U256::exp10(15)));
Expand Down
2 changes: 1 addition & 1 deletion crates/autopilot/src/domain/competition/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ pub struct TradedOrder {
pub struct Score(eth::Ether);

impl Score {
pub fn new(score: eth::Ether) -> Result<Self, ZeroScore> {
pub fn try_new(score: eth::Ether) -> Result<Self, ZeroScore> {
if score.0.is_zero() {
Err(ZeroScore)
} else {
Expand Down
51 changes: 37 additions & 14 deletions crates/autopilot/src/domain/settlement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,10 +89,27 @@ impl Settlement {
}

/// Per order fees breakdown. Contains all orders from the settlement
pub fn fee_breakdown(&self) -> HashMap<domain::OrderUid, Option<trade::FeeBreakdown>> {
pub fn fee_breakdown(&self) -> HashMap<domain::OrderUid, trade::FeeBreakdown> {
self.trades
.iter()
.map(|trade| (*trade.uid(), trade.fee_breakdown(&self.auction).ok()))
.map(|trade| {
let fee_breakdown = trade.fee_breakdown(&self.auction).unwrap_or_else(|err| {
tracing::warn!(
?err,
trade = %trade.uid(),
"possible incomplete fee breakdown calculation",
);
trade::FeeBreakdown {
total: eth::Asset {
// TODO surplus token
token: trade.sell_token(),
amount: num::zero(),
},
protocol: vec![],
}
});
(*trade.uid(), fee_breakdown)
})
.collect()
}

Expand Down Expand Up @@ -308,13 +325,14 @@ mod tests {
eth::TokenAddress(eth::H160::from_slice(&hex!(
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
))),
auction::Price::new(eth::U256::from(1000000000000000000u128).into()).unwrap(),
auction::Price::try_new(eth::U256::from(1000000000000000000u128).into())
.unwrap(),
),
(
eth::TokenAddress(eth::H160::from_slice(&hex!(
"c52fafdc900cb92ae01e6e4f8979af7f436e2eb2"
))),
auction::Price::new(eth::U256::from(537359915436704u128).into()).unwrap(),
auction::Price::try_new(eth::U256::from(537359915436704u128).into()).unwrap(),
),
]),
surplus_capturing_jit_order_owners: Default::default(),
Expand All @@ -329,7 +347,7 @@ mod tests {
trade.surplus_in_ether(&auction.prices).unwrap().0,
eth::U256::from(52937525819789126u128)
);
// fee read from "executedSurplusFee" https://api.cow.fi/mainnet/api/v1/orders/0x10dab31217bb6cc2ace0fe601c15d342f7626a1ee5ef0495449800e73156998740a50cf069e992aa4536211b23f286ef88752187ffffffff
// fee read from "executedFee" https://api.cow.fi/mainnet/api/v1/orders/0x10dab31217bb6cc2ace0fe601c15d342f7626a1ee5ef0495449800e73156998740a50cf069e992aa4536211b23f286ef88752187ffffffff
// but not equal to 6890975030480504 anymore, since after this tx we switched to
// convert the fee from surplus token directly to ether
assert_eq!(
Expand Down Expand Up @@ -438,15 +456,17 @@ mod tests {
eth::TokenAddress(eth::H160::from_slice(&hex!(
"dac17f958d2ee523a2206206994597c13d831ec7"
))),
auction::Price::new(eth::U256::from(321341140475275961528483840u128).into())
auction::Price::try_new(eth::U256::from(321341140475275961528483840u128).into())
.unwrap(),
),
(
eth::TokenAddress(eth::H160::from_slice(&hex!(
"056fd409e1d7a124bd7017459dfea2f387b6d5cd"
))),
auction::Price::new(eth::U256::from(3177764302250520038326415654912u128).into())
.unwrap(),
auction::Price::try_new(
eth::U256::from(3177764302250520038326415654912u128).into(),
)
.unwrap(),
),
]);

Expand Down Expand Up @@ -604,14 +624,14 @@ mod tests {
eth::TokenAddress(eth::H160::from_slice(&hex!(
"a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"
))),
auction::Price::new(eth::U256::from(374263465721452989998170112u128).into())
auction::Price::try_new(eth::U256::from(374263465721452989998170112u128).into())
.unwrap(),
),
(
eth::TokenAddress(eth::H160::from_slice(&hex!(
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
))),
auction::Price::new(eth::U256::from(1000000000000000000u128).into()).unwrap(),
auction::Price::try_new(eth::U256::from(1000000000000000000u128).into()).unwrap(),
),
]);

Expand Down Expand Up @@ -778,19 +798,19 @@ mod tests {
eth::TokenAddress(eth::H160::from_slice(&hex!(
"812Ba41e071C7b7fA4EBcFB62dF5F45f6fA853Ee"
))),
auction::Price::new(eth::U256::from(400373909534592401408u128).into()).unwrap(),
auction::Price::try_new(eth::U256::from(400373909534592401408u128).into()).unwrap(),
),
(
eth::TokenAddress(eth::H160::from_slice(&hex!(
"a21Af1050F7B26e0cfF45ee51548254C41ED6b5c"
))),
auction::Price::new(eth::U256::from(127910593u128).into()).unwrap(),
auction::Price::try_new(eth::U256::from(127910593u128).into()).unwrap(),
),
(
eth::TokenAddress(eth::H160::from_slice(&hex!(
"c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"
))),
auction::Price::new(eth::U256::from(1000000000000000000u128).into()).unwrap(),
auction::Price::try_new(eth::U256::from(1000000000000000000u128).into()).unwrap(),
),
]);

Expand All @@ -810,7 +830,10 @@ mod tests {
let jit_trade = super::trade::Trade::new(transaction.trades[1].clone(), &auction, 0);
assert_eq!(jit_trade.fee_in_ether(&auction.prices).unwrap().0, 0.into());
assert_eq!(jit_trade.score(&auction).unwrap().0, 0.into());
assert_eq!(jit_trade.fee_breakdown(&auction).unwrap().total.0, 0.into());
assert_eq!(
jit_trade.fee_breakdown(&auction).unwrap().total.amount.0,
0.into()
);
assert!(jit_trade
.fee_breakdown(&auction)
.unwrap()
Expand Down
6 changes: 5 additions & 1 deletion crates/autopilot/src/domain/settlement/trade/math.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,9 +134,13 @@ impl Trade {

/// Total fee (protocol fee + network fee). Equal to a surplus difference
/// before and after applying the fees.
pub fn fee_in_sell_token(&self) -> Result<eth::SellTokenAmount, Error> {
pub fn fee_in_sell_token(&self) -> Result<eth::Asset, Error> {
let fee = self.fee()?;
self.fee_into_sell_token(fee.amount)
.map(|amount| eth::Asset {
token: self.sell.token,
amount: amount.into(),
})
}

/// Total fee (protocol fee + network fee). Equal to a surplus difference
Expand Down
11 changes: 9 additions & 2 deletions crates/autopilot/src/domain/settlement/trade/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ impl Trade {
Ok(FeeBreakdown { total, protocol })
}

pub fn sell_token(&self) -> eth::TokenAddress {
match self {
Self::Fulfillment(trade) => trade.sell.token,
Self::Jit(trade) => trade.sell.token,
}
}

pub fn new(trade: transaction::EncodedTrade, auction: &super::Auction, created: u32) -> Self {
if auction.orders.contains_key(&trade.uid) {
Trade::Fulfillment(Fulfillment {
Expand Down Expand Up @@ -161,8 +168,8 @@ pub struct Jit {
#[derive(Debug, Clone)]
pub struct FeeBreakdown {
/// Total fee the trade was charged (network fee + protocol fee)
// TODO: express in surplus token
pub total: eth::SellTokenAmount,
// TODO surplus token
pub total: eth::Asset,
/// Breakdown of protocol fees.
pub protocol: Vec<ExecutedProtocolFee>,
}
Expand Down
2 changes: 1 addition & 1 deletion crates/autopilot/src/domain/settlement/transaction/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ impl Transaction {
clearing_prices,
trades: decoded_trades,
interactions: _interactions,
} = tokenized::Tokenized::new(&crate::util::Bytes(data.to_vec()))?;
} = tokenized::Tokenized::try_new(&crate::util::Bytes(data.to_vec()))?;

let mut trades = Vec::with_capacity(decoded_trades.len());
for trade in decoded_trades {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub(super) struct Tokenized {
}

impl Tokenized {
pub fn new(calldata: &eth::Calldata) -> Result<Self, error::Decoding> {
pub fn try_new(calldata: &eth::Calldata) -> Result<Self, error::Decoding> {
let function = contracts::GPv2Settlement::raw_contract()
.interface
.abi
Expand Down
2 changes: 1 addition & 1 deletion crates/autopilot/src/infra/persistence/dto/auction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ impl Auction {
.prices
.into_iter()
.map(|(key, value)| {
Price::new(value.into()).map(|price| (eth::TokenAddress(key), price))
Price::try_new(value.into()).map(|price| (eth::TokenAddress(key), price))
})
.collect::<Result<_, _>>()?,
surplus_capturing_jit_order_owners: self
Expand Down
31 changes: 13 additions & 18 deletions crates/autopilot/src/infra/persistence/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -354,7 +354,7 @@ impl Persistence {
let token = eth::H160(price.token.0).into();
let price = big_decimal_to_u256(&price.price)
.ok_or(domain::auction::InvalidPrice)
.and_then(|p| domain::auction::Price::new(p.into()))
.and_then(|p| domain::auction::Price::try_new(p.into()))
.map_err(|_err| error::Auction::InvalidPrice(token));
price.map(|price| (token, price))
})
Expand Down Expand Up @@ -701,28 +701,23 @@ impl Persistence {
.await;

for (order, order_fee) in fee_breakdown {
let total_fee = order_fee
.as_ref()
.map(|fee| u256_to_big_decimal(&fee.total.0))
.unwrap_or_default();
let executed_protocol_fees = order_fee
.map(|fee| {
fee.protocol
.into_iter()
.map(|executed| Asset {
token: ByteArray(executed.fee.token.0 .0),
amount: u256_to_big_decimal(&executed.fee.amount.0),
})
.collect::<Vec<_>>()
})
.unwrap_or_default();
database::order_execution::save(
&mut ex,
&ByteArray(order.0),
auction_id,
block_number,
&total_fee,
&executed_protocol_fees,
Asset {
token: ByteArray(order_fee.total.token.0 .0),
amount: u256_to_big_decimal(&order_fee.total.amount.0),
},
&order_fee
.protocol
.into_iter()
.map(|executed| Asset {
token: ByteArray(executed.fee.token.0 .0),
amount: u256_to_big_decimal(&executed.fee.amount.0),
})
.collect::<Vec<_>>(),
)
.await?;
}
Expand Down
4 changes: 2 additions & 2 deletions crates/autopilot/src/infra/solvers/dto/solve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,15 @@ impl Solution {
Ok(domain::competition::Solution::new(
self.solution_id,
self.submission_address.into(),
domain::competition::Score::new(self.score.into())?,
domain::competition::Score::try_new(self.score.into())?,
self.orders
.into_iter()
.map(|(o, amounts)| (o.into(), amounts.into_domain()))
.collect(),
self.clearing_prices
.into_iter()
.map(|(token, price)| {
domain::auction::Price::new(price.into()).map(|price| (token.into(), price))
domain::auction::Price::try_new(price.into()).map(|price| (token.into(), price))
})
.collect::<Result<_, _>>()?,
))
Expand Down
Loading

0 comments on commit ca517b0

Please sign in to comment.