diff --git a/.github/workflows/rust_lint.yml b/.github/workflows/rust_lint.yml index bdf495b4..69867cde 100644 --- a/.github/workflows/rust_lint.yml +++ b/.github/workflows/rust_lint.yml @@ -2,10 +2,14 @@ name: Rust Lint on: push: + branches: + - main pull_request: merge_group: jobs: + # Check if files in the /contracts, /test, /crates, or /lib directories or the + # /foundry.toml, /Cargo.lock, or /Cargo.toml files were changed in this PR. detect-changes: uses: ./.github/workflows/check_diff.yaml with: @@ -17,21 +21,32 @@ jobs: needs: detect-changes if: needs.detect-changes.outputs.changed == 'true' steps: - - uses: actions/checkout@v3 + - name: Checkout hyperdrive-rs + uses: actions/checkout@v4 with: submodules: recursive + token: ${{ secrets.GITHUB_TOKEN }} + + # NOTE: This is needed to ensure that hyperdrive-wrappers builds correctly. + - name: Install foundry + uses: foundry-rs/foundry-toolchain@v1 + with: + version: nightly + + - name: Fetch latest release tag from Hyperdrive + run: ./scripts/fetch-latest-tag.sh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: check out hyperdrive - uses: actions/checkout@main + uses: actions/checkout@v4 with: repository: delvtech/hyperdrive - ref: "latest" + ref: ${{ env.latest_tag }} path: "./hyperdrive" - - name: install foundry - uses: foundry-rs/foundry-toolchain@v1 - with: - version: nightly + - name: build hyperdrive + run: cd hyperdrive && make build-sol - uses: actions-rs/toolchain@v1 with: diff --git a/.github/workflows/rust_test.yml b/.github/workflows/rust_test.yml index 4804bc2d..32bbfc56 100644 --- a/.github/workflows/rust_test.yml +++ b/.github/workflows/rust_test.yml @@ -2,6 +2,8 @@ name: Rust Test on: push: + branches: + - main pull_request: merge_group: @@ -19,28 +21,38 @@ jobs: needs: detect-changes if: needs.detect-changes.outputs.changed == 'true' steps: - - uses: actions/checkout@v3 + - name: Checkout hyperdrive-rs + uses: actions/checkout@v4 with: submodules: recursive token: ${{ secrets.GITHUB_TOKEN }} - - name: check out hyperdrive - uses: actions/checkout@main - with: - repository: delvtech/hyperdrive - ref: "latest" - path: "./hyperdrive" - # NOTE: This is needed to ensure that hyperdrive-wrappers builds correctly. - name: install foundry uses: foundry-rs/foundry-toolchain@v1 with: version: nightly + - name: Fetch latest release tag from Hyperdrive + run: ./scripts/fetch-latest-tag.sh + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: check out hyperdrive + uses: actions/checkout@v4 + with: + repository: delvtech/hyperdrive + ref: ${{ env.latest_tag }} + path: "./hyperdrive" + + - name: build hyperdrive + run: cd hyperdrive && make build-sol + - uses: actions-rs/toolchain@v1 with: toolchain: nightly override: true + components: rustfmt, clippy - name: test run: make test diff --git a/crates/hyperdrive-math/src/long/close.rs b/crates/hyperdrive-math/src/long/close.rs index 7cf012a7..4335a1ec 100644 --- a/crates/hyperdrive-math/src/long/close.rs +++ b/crates/hyperdrive-math/src/long/close.rs @@ -5,6 +5,27 @@ use fixed_point_macros::fixed; use crate::{State, YieldSpace}; impl State { + /// Calculates the amount of shares the trader will receive after fees for closing a long + pub fn calculate_close_long>( + &self, + bond_amount: F, + maturity_time: U256, + current_time: U256, + ) -> FixedPoint { + let bond_amount = bond_amount.into(); + + if bond_amount < self.config.minimum_transaction_amount.into() { + // TODO would be nice to return a `Result` here instead of a panic. + panic!("MinimumTransactionAmount: Input amount too low"); + } + + // Subtract the fees from the trade + self.calculate_close_long_flat_plus_curve(bond_amount, maturity_time, current_time) + - self.close_long_curve_fee(bond_amount, maturity_time, current_time) + - self.close_long_flat_fee(bond_amount, maturity_time, current_time) + } + + /// Calculate the amount of shares returned when selling bonds without considering fees. fn calculate_close_long_flat_plus_curve>( &self, bond_amount: F, @@ -31,26 +52,6 @@ impl State { flat + curve } - - /// Calculates the amount of shares the trader will receive after fees for closing a long - pub fn calculate_close_long>( - &self, - bond_amount: F, - maturity_time: U256, - current_time: U256, - ) -> FixedPoint { - let bond_amount = bond_amount.into(); - - if bond_amount < self.config.minimum_transaction_amount.into() { - // TODO would be nice to return a `Result` here instead of a panic. - panic!("MinimumTransactionAmount: Input amount too low"); - } - - // Subtract the fees from the trade - self.calculate_close_long_flat_plus_curve(bond_amount, maturity_time, current_time) - - self.close_long_curve_fee(bond_amount, maturity_time, current_time) - - self.close_long_flat_fee(bond_amount, maturity_time, current_time) - } } #[cfg(test)] diff --git a/crates/hyperdrive-math/src/long/fees.rs b/crates/hyperdrive-math/src/long/fees.rs index b081280d..7ab9904e 100644 --- a/crates/hyperdrive-math/src/long/fees.rs +++ b/crates/hyperdrive-math/src/long/fees.rs @@ -10,7 +10,7 @@ impl State { /// The curve fee $c(x)$ paid by longs is paid in bonds and is given by: /// /// $$ - /// c(x) = \phi_{c} \cdot \left( \tfrac{1}{p} - 1 \right) \cdot x + /// c(x) = \phi_c \cdot \left( \tfrac{1}{p} - 1 \right) \cdot x /// $$ pub fn open_long_curve_fees(&self, base_amount: FixedPoint) -> FixedPoint { self.curve_fee() @@ -25,7 +25,7 @@ impl State { /// is given by: /// /// $$ - /// g(x) = \phi_{g} \cdot p \cdot c(x) + /// g(x) = \phi_g \cdot p \cdot c(x) /// $$ pub fn open_long_governance_fee(&self, base_amount: FixedPoint) -> FixedPoint { self.governance_lp_fee() @@ -43,7 +43,7 @@ impl State { ) -> FixedPoint { let normalized_time_remaining = self.calculate_normalized_time_remaining(maturity_time, current_time); - // curve_fee = ((1 - p) * phi_curve * d_y * t) / c + // curve_fee = ((1 - p) * phi_c * d_y * t) / c self.curve_fee() * (fixed!(1e18) - self.calculate_spot_price()) * bond_amount.mul_div_down(normalized_time_remaining, self.vault_share_price()) @@ -59,7 +59,7 @@ impl State { ) -> FixedPoint { let normalized_time_remaining = self.calculate_normalized_time_remaining(maturity_time, current_time); - // flat_fee = (d_y * (1 - t) * phi_flat) / c + // flat_fee = (d_y * (1 - t) * phi_f) / c bond_amount.mul_div_down( fixed!(1e18) - normalized_time_remaining, self.vault_share_price(), diff --git a/crates/hyperdrive-math/src/long/open.rs b/crates/hyperdrive-math/src/long/open.rs index 50ba5d37..e8703b58 100644 --- a/crates/hyperdrive-math/src/long/open.rs +++ b/crates/hyperdrive-math/src/long/open.rs @@ -70,7 +70,7 @@ impl State { /// /// We calculate the rate for a fixed length of time as: /// $$ - /// r(x) = (1 - p(x)) / (p(x) t) + /// r(\Delta y) = (1 - p(\Delta y)) / (p(\Delta y) t) /// $$ /// /// where $p(x)$ is the spot price after a long for `delta_base`$= x$ and diff --git a/crates/hyperdrive-math/src/short/close.rs b/crates/hyperdrive-math/src/short/close.rs index 37e3c928..237da623 100644 --- a/crates/hyperdrive-math/src/short/close.rs +++ b/crates/hyperdrive-math/src/short/close.rs @@ -88,10 +88,11 @@ impl State { /// it is possible for traders to receive a negative interest rate even /// if curve's spot price is less than or equal to 1. // - /// Given the curve fee `phi_c` and the starting spot price `p_0`, the + /// Given the curve fee $\phi_c$ and the starting spot price `p_0`, the /// maximum spot price is given by: + /// /// $$ - /// p_max = 1 - phi_c * (1 - p_0) + /// p_max = 1 - \phi_c * (1 - p_0) /// $$ fn calculate_close_short_max_spot_price(&self) -> FixedPoint { fixed!(1e18) @@ -119,7 +120,7 @@ impl State { } // Ensure that the trader didn't purchase bonds at a negative interest - // rate after accounting for fees + // rate after accounting for fees. let share_curve_delta = self.calculate_close_short_curve(bond_amount, maturity_time, current_time); let bond_reserves_delta = bond_amount diff --git a/crates/hyperdrive-math/src/short/fees.rs b/crates/hyperdrive-math/src/short/fees.rs index 757a59ea..a9497580 100644 --- a/crates/hyperdrive-math/src/short/fees.rs +++ b/crates/hyperdrive-math/src/short/fees.rs @@ -24,7 +24,7 @@ impl State { } /// Calculates the curve fee paid by shorts for a given bond amount. - /// Returns the fee in shares + /// Returns the fee in shares. pub fn close_short_curve_fee( &self, bond_amount: FixedPoint, @@ -34,7 +34,7 @@ impl State { let normalized_time_remaining = self.calculate_normalized_time_remaining(maturity_time, current_time); - // ((1 - p) * phi_curve * d_y * t) / c + // ((1 - p) * phi_c * d_y * t) / c self.curve_fee() * (fixed!(1e18) - self.calculate_spot_price()) * bond_amount.mul_div_down(normalized_time_remaining, self.vault_share_price()) @@ -54,8 +54,8 @@ impl State { .mul_down(self.governance_lp_fee()) } - /// Calculates the flat fee paid by shorts for a given bond amount - /// Returns the fee in shares + /// Calculates the flat fee paid by shorts for a given bond amount. + /// Returns the fee in shares. pub fn close_short_flat_fee( &self, bond_amount: FixedPoint, @@ -64,7 +64,7 @@ impl State { ) -> FixedPoint { let normalized_time_remaining = self.calculate_normalized_time_remaining(maturity_time, current_time); - // flat fee = (d_y * (1 - t) * phi_flat) / c + // flat_fee = (d_y * (1 - t) * phi_f) / c bond_amount.mul_div_down( fixed!(1e18) - normalized_time_remaining, self.vault_share_price(), diff --git a/crates/hyperdrive-wrappers/build.rs b/crates/hyperdrive-wrappers/build.rs index 06561a57..45856cc5 100644 --- a/crates/hyperdrive-wrappers/build.rs +++ b/crates/hyperdrive-wrappers/build.rs @@ -1,4 +1,4 @@ -use std::{io::Write, path::Path, process::Command}; +use std::{fs::create_dir_all, io::Write, path::Path, process::Command}; use ethers::prelude::Abigen; use eyre::Result; @@ -57,6 +57,9 @@ const TARGETS: &[&str] = &[ ]; fn get_artifacts(artifacts_path: &Path) -> Result> { + if !artifacts_path.exists() { + create_dir_all(artifacts_path)?; + } let mut artifacts = Vec::new(); for entry in std::fs::read_dir(artifacts_path)? { let entry = entry?; diff --git a/scripts/fetch-latest-tag.sh b/scripts/fetch-latest-tag.sh new file mode 100755 index 00000000..b5dbac7c --- /dev/null +++ b/scripts/fetch-latest-tag.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# GitHub Token provided by GitHub Actions +TOKEN=${GITHUB_TOKEN} + +# Repository from which to fetch the latest tag +REPO='delvtech/hyperdrive' + +# Fetch the latest release from GitHub API +TAG=$(curl -sH "Authorization: token $TOKEN" "https://api.github.com/repos/$REPO/releases/latest" | jq -r '.tag_name') + +# Output the tag for subsequent steps +echo "latest_tag=$TAG" >> $GITHUB_ENV