diff --git a/db/migrations/1712766721-events_seawater.sql b/db/migrations/1712766721-events_seawater.sql index 09476039..d5332d90 100644 --- a/db/migrations/1712766721-events_seawater.sql +++ b/db/migrations/1712766721-events_seawater.sql @@ -41,7 +41,7 @@ CREATE TABLE events_seawater_mintPosition ( upper BIGINT NOT NULL ); -CREATE UNIQUE INDEX ON events_seawater_mintPosition (pos_id, pool); +CREATE UNIQUE INDEX ON events_seawater_mintPosition (pos_id); CREATE INDEX ON events_seawater_mintPosition (owner, pool); CREATE TABLE events_seawater_transferPosition ( diff --git a/lib/math/concentrated-liq_test.go b/lib/math/concentrated-liq_test.go index 4e1bc0d5..475ff42e 100644 --- a/lib/math/concentrated-liq_test.go +++ b/lib/math/concentrated-liq_test.go @@ -90,7 +90,7 @@ func TestGetPriceAtSqrtRatio(t *testing.T) { t.Fatal("unimplemented") } -var getAmountForsLiqTestTable = map[string]struct { +var getAmountsForLiqTestTable = map[string]struct { sqrtRatioX96, sqrtRatioAX96, sqrtRatioBX96 *big.Int liq int @@ -116,10 +116,34 @@ var getAmountForsLiqTestTable = map[string]struct { encodePriceSqrt(111, 100), encodePriceSqrt(100, 110), encodePriceSqrt(110, 100), 2097, new(big.Rat).SetInt64(0), new(big.Rat).SetInt64(200), }, + "contract alex_0f08c379a": { + GetSqrtRatioAtTick(new(big.Int).SetInt64(2970)), // Current tick + GetSqrtRatioAtTick(new(big.Int).SetInt64(2100)), // Lower tick + GetSqrtRatioAtTick(new(big.Int).SetInt64(4080)), // Upper tick + 18117952900, // Liquidity (delta) + new(big.Rat).SetInt64(842893567), // Amount0 + new(big.Rat).SetInt64(842893567), // Amount1 + }, + "contract alex_0f08c379a without tick conversion in the go code": { + mustIntFromStr("91911338314972375132734921679"), // Current tick + mustIntFromStr("87999098777895760865233273050"), // Lower tick + mustIntFromStr("97156358459122590463153608088"), // Upper tick + 18117952900, // Liquidity (delta) + new(big.Rat).SetInt64(842893567), // Amount0 + new(big.Rat).SetInt64(842893567), // Amount1 + }, +} + +func mustIntFromStr(x string) *big.Int { + i, ok := new(big.Int).SetString(x, 10) + if !ok { + panic(x) + } + return i } func TestGetAmountsForLiq(t *testing.T) { - for k, test := range getAmountForsLiqTestTable { + for k, test := range getAmountsForLiqTestTable { test := test t.Run(k, func(t *testing.T) { t.Parallel() diff --git a/pkg/Cargo.lock b/pkg/Cargo.lock index 87453d56..2cebbb70 100644 --- a/pkg/Cargo.lock +++ b/pkg/Cargo.lock @@ -925,6 +925,7 @@ dependencies = [ "keccak-const", "lol_alloc", "maplit", + "num-traits", "rand", "ruint", "ruint-macro", diff --git a/pkg/Cargo.toml b/pkg/Cargo.toml index a474d0b2..5b758b7d 100644 --- a/pkg/Cargo.toml +++ b/pkg/Cargo.toml @@ -21,7 +21,8 @@ incremental = false stylus-sdk = "0.5.0" thiserror = "1.0.48" tiny-keccak = "2.0.2" -ruint = "1.11.0" +ruint = { version = "1.11.0", features = ["num-traits"] } +num-traits = "0.2.19" alloy-sol-types = "0.3.1" ruint-macro = "1.1.0" keccak-const = "0.2.0" diff --git a/pkg/seawater/Cargo.toml b/pkg/seawater/Cargo.toml index c71890b7..2cdfac72 100644 --- a/pkg/seawater/Cargo.toml +++ b/pkg/seawater/Cargo.toml @@ -23,6 +23,7 @@ tiny-keccak.workspace = true lol_alloc.workspace = true keccak-const.workspace = true const-hex.workspace = true +num-traits.workspace = true [dev-dependencies] rand = "0.8.5" diff --git a/pkg/seawater/src/error.rs b/pkg/seawater/src/error.rs index 95acde69..e01044b7 100644 --- a/pkg/seawater/src/error.rs +++ b/pkg/seawater/src/error.rs @@ -200,6 +200,12 @@ pub enum Error { #[error("Invalid tick spacing")] InvalidTickSpacing, + + #[error("Amount 0 too low")] + Amount0TooLow, + + #[error("Amount 1 too low")] + Amount1TooLow } impl From for Vec { diff --git a/pkg/seawater/src/host_erc20.rs b/pkg/seawater/src/host_erc20.rs index db79f027..f5d96d4d 100644 --- a/pkg/seawater/src/host_erc20.rs +++ b/pkg/seawater/src/host_erc20.rs @@ -22,41 +22,29 @@ pub fn decimals(_token: Address) -> Result { ///! Assumes a single token is in use for the life of this test. pub fn take_transfer_from(_token: Address, _amount: U256) -> Result<(), Error> { #[cfg(feature = "testing-dbg")] - { - dbg!(("take_transfer_from", current_test!(), _token, _amount)); - return host_test_shims::take_caller_bal(_token, _amount).map_err( - |_| Error::Erc20RevertNoData, // follow the trace! - ); - } - #[allow(unreachable_code)] - Ok(()) + dbg!(("take_transfer_from", current_test!(), _token, _amount)); + host_test_shims::take_caller_bal(_token, _amount).map_err( + |_| Error::Erc20RevertNoData, // follow the trace! + ) } /// Pretends to give users tokens. Only useful for testing. pub fn give(_token: Address, _amount: U256) -> Result<(), Error> { #[cfg(feature = "testing-dbg")] - { - dbg!(("give", current_test!(), _token, _amount)); - return host_test_shims::take_amm_bal(_token, _amount).map_err( - |_| Error::Erc20RevertNoData, // follow the trace! - ); - } - #[allow(unreachable_code)] - Ok(()) + dbg!(("give", current_test!(), _token, _amount)); + host_test_shims::take_amm_bal(_token, _amount).map_err( + |_| Error::Erc20RevertNoData, // follow the trace! + ) } /// Pretends to take ERC20 tokens from the user, only happening if the underlying /// environment is not WASM. Only useful for testing. pub fn take_permit2(_token: Address, _amount: U256, _details: Permit2Args) -> Result<(), Error> { #[cfg(feature = "testing-dbg")] - { - dbg!(("take_permit2", current_test!(), _token, _amount, _details)); - return host_test_shims::take_caller_bal(_token, _amount).map_err( - |_| Error::Erc20RevertNoData, // follow the trace! - ); - } - #[allow(unreachable_code)] - Ok(()) + dbg!(("take_permit2", current_test!(), _token, _amount, _details)); + host_test_shims::take_caller_bal(_token, _amount).map_err( + |_| Error::Erc20RevertNoData, // follow the trace! + ) } /// Pretends to take ERC20 tokens from the user, only happening if the underlying @@ -67,14 +55,10 @@ pub fn take( _permit2_details: Option, ) -> Result<(), Error> { #[cfg(feature = "testing-dbg")] - { - dbg!(("take", current_test!(), _token, _amount, _permit2_details)); - return host_test_shims::take_caller_bal(_token, _amount).map_err( - |_| Error::Erc20RevertNoData, // follow the trace! - ); - } - #[allow(unreachable_code)] - Ok(()) + dbg!(("take", current_test!(), _token, _amount, _permit2_details)); + host_test_shims::take_caller_bal(_token, _amount).map_err( + |_| Error::Erc20RevertNoData, // follow the trace! + ) } /// Pretends to construct a revert string from a message, only happening if the underlying diff --git a/pkg/seawater/src/lib.rs b/pkg/seawater/src/lib.rs index cc35a885..8e601f32 100644 --- a/pkg/seawater/src/lib.rs +++ b/pkg/seawater/src/lib.rs @@ -271,7 +271,7 @@ impl Pools { /// Swap functions. Only enabled when the `swaps` feature is set. #[cfg_attr(feature = "swaps", external)] impl Pools { - pub fn swap( + pub fn swap_eb3a17( &mut self, pool: Address, zero_for_one: bool, @@ -282,7 +282,7 @@ impl Pools { } /// Performs a two stage swap, using approvals to transfer tokens. See [Self::swap_2_internal]. - pub fn swap_2_exact_in( + pub fn swap_2_exact_in_57968f( &mut self, from: Address, to: Address, @@ -299,7 +299,8 @@ impl Pools { /// Quote a [Self::swap]. Will revert with the result of the swap /// as a decimal number as the message of an `Error(string)`. /// Returns a `Result` as Stylus expects but will always only fill the `Revert`. - pub fn quote( + #[allow(non_snake_case)] + pub fn quote____006eac( &mut self, pool: Address, zero_for_one: bool, @@ -326,7 +327,8 @@ impl Pools { /// Quote a [Self::swap_2_exact_in]. Will revert with the result of the swap /// as a decimal number as the message of an `Error(string)`. /// Returns a `Result` as Stylus expects but will always only fill the `Revert`. - pub fn quote2( + #[allow(non_snake_case)] + pub fn quote2__cc9e21( &mut self, from: Address, to: Address, @@ -352,9 +354,9 @@ impl Pools { // slight hack - we cfg out the whole function, since the `selector` and `raw` attributes don't // actually exist, so we can't `cfg_attr` them in #[cfg(feature = "swap_permit2")] - #[selector(id = "swapPermit2(address,bool,int256,uint256,uint256,uint256,uint256,bytes)")] + #[selector(id = "swapPermit2_d5d578(address,bool,int256,uint256,uint256,uint256,uint256,bytes)")] #[raw] - pub fn swap_permit2(&mut self, data: &[u8]) -> RawArbResult { + pub fn swap_permit2_d5d578(&mut self, data: &[u8]) -> RawArbResult { let (pool, data) = eth_serde::parse_addr(data); let (zero_for_one, data) = eth_serde::parse_bool(data); let (amount, data) = eth_serde::parse_i256(data); @@ -387,9 +389,9 @@ impl Pools { /// Performs a two stage swap, using permit2 to transfer tokens. See [Self::swap_2_internal]. #[cfg(feature = "swap_permit2")] - #[selector(id = "swap2ExactInPermit2(address,address,uint256,uint256,uint256,uint256,bytes)")] + #[selector(id = "swap2ExactInPermit2_15755d(address,address,uint256,uint256,uint256,uint256,bytes)")] #[raw] - pub fn swap_2_permit2(&mut self, data: &[u8]) -> RawArbResult { + pub fn swap_2_permit2_15755d(&mut self, data: &[u8]) -> RawArbResult { let (from, data) = eth_serde::parse_addr(data); let (to, data) = eth_serde::parse_addr(data); let (amount, data) = eth_serde::parse_u256(data); @@ -447,7 +449,7 @@ impl Pools { /// /// # Errors /// Requires the pool to exist and be enabled. - pub fn mint_position(&mut self, pool: Address, lower: i32, upper: i32) -> Result<(), Revert> { + pub fn mint_position_05ca11(&mut self, pool: Address, lower: i32, upper: i32) -> Result { let id = self.next_position_id.get(); self.pools.setter(pool).create_position(id, lower, upper)?; @@ -465,7 +467,7 @@ impl Pools { upper, }); - Ok(()) + Ok(id) } /// Burns a position. Only usable by the position owner. @@ -474,7 +476,7 @@ impl Pools { /// /// # Errors /// Requires the position be owned by the caller. Requires the pool to be enabled. - pub fn burn_position(&mut self, id: U256) -> Result<(), Revert> { + pub fn burn_position_f7639f(&mut self, id: U256) -> Result<(), Revert> { let owner = msg::sender(); assert_eq_or!( self.position_owners.get(id), @@ -497,7 +499,7 @@ impl Pools { /// /// # Errors /// Requires the caller be the NFT manager. - pub fn transfer_position( + pub fn transfer_position_5bf9a4( &mut self, id: U256, from: Address, @@ -514,17 +516,18 @@ impl Pools { } /// Returns the current owner of a position. - pub fn position_owner(&self, id: U256) -> Result { + #[allow(non_snake_case)] + pub fn position_owner__0602c2(&self, id: U256) -> Result { Ok(self.position_owners.get(id)) } - /// Returns the number of positions owned by an account. - pub fn position_balance(&self, user: Address) -> Result { + /// Returns the number of positions owned by an address. + pub fn position_balance_f285b1(&self, user: Address) -> Result { Ok(self.owned_positions.get(user)) } /// Returns the amount of liquidity in a position. - pub fn position_liquidity(&self, pool: Address, id: U256) -> Result { + pub fn position_liquidity_0f1547(&self, pool: Address, id: U256) -> Result { let liquidity = self.pools.getter(pool).get_position_liquidity(id); Ok(liquidity.sys()) @@ -543,7 +546,7 @@ impl Pools { /// # Errors /// Requires the caller to be the position owner. Requires the pool to be enabled. /// Requires the length of the pools and ids to be equal. - pub fn collect( + pub fn collect_7472a7( &mut self, pools: Vec
, ids: Vec @@ -613,6 +616,9 @@ impl Pools { let (token_0, token_1) = self.pools.setter(pool).update_position(id, delta)?; + #[cfg(feature = "testing-dbg")] + dbg!(("update position taking", current_test!(), token_0, token_1)); + if delta < 0 { erc20::give(pool, token_0.abs_neg()?)?; erc20::give(FUSDC_ADDR, token_1.abs_neg()?)?; @@ -652,11 +658,11 @@ impl Pools { #[cfg(feature = "update_positions")] #[raw] #[selector( - id = "updatePositionPermit2(address,uint256,int128,uint256,uint256,uint256,bytes,uint256,uint256,uint256,bytes)" + id = "updatePositionPermit2_9936af(address,uint256,int128,uint256,uint256,uint256,bytes,uint256,uint256,uint256,bytes)" )] /// Refreshes and updates liquidity in a position, using permit2 to transfer tokens. /// See [Self::update_position_internal]. - pub fn update_position_permit2(&mut self, data: &[u8]) -> RawArbResult { + pub fn update_position_permit2_9936af(&mut self, data: &[u8]) -> RawArbResult { let (pool, data) = eth_serde::parse_addr(data); let (id, data) = eth_serde::parse_u256(data); let (delta, data) = eth_serde::parse_i128(data); @@ -739,7 +745,7 @@ impl Pools { /// /// # Errors /// Requires the caller to be the seawater admin. Requires the pool to not exist. - pub fn create_pool( + pub fn create_pool_816c1f( &mut self, pool: Address, price: U256, @@ -772,33 +778,33 @@ impl Pools { } /// Getter method for the sqrt price - pub fn sqrt_price_x96(&self, pool: Address) -> Result { + pub fn sqrt_price_x96_6ed516(&self, pool: Address) -> Result { Ok(self.pools.getter(pool).get_sqrt_price()) } /// Getter method for the current tick - pub fn cur_tick(&self, pool: Address) -> Result { + pub fn cur_tick_84a60b(&self, pool: Address) -> Result { // converted to i32 for automatic abi encoding Ok(self.pools.getter(pool).get_cur_tick().sys()) } - pub fn fees_owed(&self, pool: Address, id: U256) -> Result<(u128, u128), Revert> { + pub fn fees_owed_5ec605(&self, pool: Address, id: U256) -> Result<(u128, u128), Revert> { Ok(self.pools.getter(pool).get_fees_owed(id)) } /// Getter method for the tick spacing of the pool given. - pub fn tick_spacing(&self, pool: Address) -> Result { + pub fn tick_spacing_52bd17(&self, pool: Address) -> Result { // converted to i32 for automatic abi encoding Ok(self.pools.getter(pool).get_tick_spacing().sys()) } /// Getter method for getting the fee growth for token 0 - pub fn fee_growth_global_0(&self, pool: Address) -> Result { + pub fn fee_growth_global_0_7fb790(&self, pool: Address) -> Result { Ok(self.pools.getter(pool).get_fee_growth_global_0()) } /// Getter method for getting the fee growth for token 1 - pub fn fee_growth_global_1(&self, pool: Address) -> Result { + pub fn fee_growth_global_1_d2e217(&self, pool: Address) -> Result { Ok(self.pools.getter(pool).get_fee_growth_global_1()) } @@ -806,7 +812,7 @@ impl Pools { /// /// # Errors /// Requires the user to be the seawater admin. Requires the pool to be enabled. - pub fn collect_protocol( + pub fn collect_protocol_fd4241( &mut self, pool: Address, amount_0: u128, @@ -840,7 +846,7 @@ impl Pools { /// /// # Errors /// Requires the user to be the seawater admin. - pub fn enable_pool(&mut self, pool: Address, enabled: bool) -> Result<(), Revert> { + pub fn enable_pool_a60d25(&mut self, pool: Address, enabled: bool) -> Result<(), Revert> { assert_eq_or!( msg::sender(), self.seawater_admin.get(), @@ -864,7 +870,7 @@ impl test_utils::StorageNew for Pools { #[cfg(test)] mod test { - use crate::{eth_serde, test_utils, types::I256Extension, types::*, Pools}; + use crate::{eth_serde, test_utils, types::I256Extension, types::*, Pools, tick_math}; use maplit::hashmap; use ruint_macro::uint; use stylus_sdk::{ @@ -1128,17 +1134,45 @@ mod test { "0xdc03f6203d56cf5fe49270519e5a797eebcd9be54de9070150d36d99795813bf" => "0x0000000000000000000000000000000000000000000000000000000000000000" }), Some(hashmap! { - address!("6437fdc89ced41941b97a9f1f8992d88718c81c5") => U256::from(842893567) + address!("6437fdc89ced41941b97a9f1f8992d88718c81c5") => U256::from(777444371) }), // caller balances None, // amm balances |contract| { use core::str::FromStr; - let pool = address!("6437fdc89cED41941b97A9f1f8992D88718C81c5"); + let pool_addr = address!("6437fdc89cED41941b97A9f1f8992D88718C81c5"); let id = U256::from(33252); let delta = i128::from_str("18117952900").unwrap(); - let (_amount_0, _amount_1) = contract.update_position(pool, id, delta).unwrap(); + let pool = contract.pools.get(pool_addr); + + let liq = pool.get_position_liquidity(id); + let sqrt_price = pool.get_sqrt_price(); + let tick_current = pool.get_cur_tick().as_i32(); + + let position = pool.get_position(id); + let tick_lower = position.lower.get().as_i32(); + let tick_upper = position.upper.get().as_i32(); + + let sqrt_current = tick_math::get_sqrt_ratio_at_tick(tick_current)?; + let sqrt_lower = tick_math::get_sqrt_ratio_at_tick(tick_lower)?; + let sqrt_upper = tick_math::get_sqrt_ratio_at_tick(tick_upper)?; + + dbg!(("update_position", liq, sqrt_price, tick_current, id, delta, tick_lower, tick_upper, sqrt_lower, sqrt_upper, sqrt_current)); + + // liquidity 0 + // sqrt price 91912741289436239605563425905 + // current tick 2970 + // id 33252 + // delta 18117952900 + // tick lower 2100 + // tick upper 4080 + // sqrt current 91911338314972375132734921679 + // sqrt lower 87999098777895760865233273050 + // sqrt upper 97156358459122590463153608088 + + let (_amount_0, _amount_1) = contract.update_position(pool_addr, id, delta).unwrap(); + Ok(()) }, ) diff --git a/pkg/seawater/src/maths/sqrt_price_math.rs b/pkg/seawater/src/maths/sqrt_price_math.rs index c9456989..1995aab8 100644 --- a/pkg/seawater/src/maths/sqrt_price_math.rs +++ b/pkg/seawater/src/maths/sqrt_price_math.rs @@ -9,6 +9,7 @@ use crate::{ }, }; use ruint_macro::uint; +use num_traits::cast::ToPrimitive; /// The maximum value storable in a 160-bit unsigned integer. pub const MAX_U160: U256 = @@ -239,6 +240,49 @@ pub fn get_amount_1_delta( } } +fn get_liquidity_for_amount_0( + sqrt_ratio_a_x_96: U256, + sqrt_ratio_b_x_96: U256, + amount: U256, +) -> Result { + let intermediate = mul_div(sqrt_ratio_a_x_96, sqrt_ratio_b_x_96, Q96)?; + mul_div(amount, intermediate, sqrt_ratio_b_x_96 - sqrt_ratio_a_x_96) +} + +fn get_liquidity_for_amount_1( + sqrt_ratio_a_x_96: U256, + sqrt_ratio_b_x_96: U256, + amount: U256, +) -> Result { + mul_div(amount, Q96, sqrt_ratio_b_x_96 - sqrt_ratio_a_x_96) +} + +pub fn get_liquidity_for_amounts( + sqrt_ratio_x_96: U256, + sqrt_ratio_a_x_96: U256, + sqrt_ratio_b_x_96: U256, + amount_0: U256, + amount_1: U256, +) -> Result { + let (sqrt_ratio_0_x_96, sqrt_ratio_1_x_96) = if sqrt_ratio_a_x_96 > sqrt_ratio_b_x_96 { + (sqrt_ratio_a_x_96, sqrt_ratio_b_x_96) + } else { + (sqrt_ratio_b_x_96, sqrt_ratio_a_x_96) + }; + + let delta = if sqrt_ratio_x_96 <= sqrt_ratio_0_x_96 { + get_liquidity_for_amount_0(sqrt_ratio_0_x_96, sqrt_ratio_1_x_96, amount_0)? + } else if sqrt_ratio_x_96 < sqrt_ratio_1_x_96 { + let liq0 = get_liquidity_for_amount_0(sqrt_ratio_x_96, sqrt_ratio_1_x_96, amount_0)?; + let liq1 = get_liquidity_for_amount_1(sqrt_ratio_0_x_96, sqrt_ratio_x_96, amount_1)?; + if liq0 > liq1 { liq0 } else { liq1 } + } else { + get_liquidity_for_amount_1(sqrt_ratio_0_x_96, sqrt_ratio_1_x_96, amount_1)? + }; + + Ok(delta.to_i128().unwrap()) +} + #[cfg(test)] mod test { use std::ops::{Add, Sub}; diff --git a/pkg/seawater/src/pool.rs b/pkg/seawater/src/pool.rs index fe544d59..2f13deb1 100644 --- a/pkg/seawater/src/pool.rs +++ b/pkg/seawater/src/pool.rs @@ -184,6 +184,39 @@ impl StoragePool { } } + pub fn incr_position( + &mut self, + id: U256, + amount_0_min: U256, + amount_1_min: U256, + amount_0_max: U256, + amount_1_max: U256 + ) -> Result<(I256, I256), Revert> { + // calculate the delta using the amounts that we have here, guaranteeing + // that we don't dip below the amount that's supplied as the minimum. + + let position = self.positions.positions.get(id); + + let sqrt_ratio_x_96 = tick_math::get_sqrt_ratio_at_tick(self.get_cur_tick().as_i32())?; + let sqrt_ratio_a_x_96 = tick_math::get_sqrt_ratio_at_tick(position.lower.get().as_i32())?; + let sqrt_ratio_b_x_96 = tick_math::get_sqrt_ratio_at_tick(position.upper.get().as_i32())?; + + let delta = sqrt_price_math::get_liquidity_for_amounts( + sqrt_ratio_x_96, // cur_tick + sqrt_ratio_a_x_96, // lower_tick + sqrt_ratio_b_x_96, // upper_tick + amount_0_max, // amount_0 + amount_1_max // amount_1 + )?; + + let (amount_0, amount_1) = self.update_position(id, delta)?; + + assert_or!(amount_0.abs_pos()? >= amount_0_min, Error::Amount0TooLow); + assert_or!(amount_1.abs_pos()? >= amount_1_min, Error::Amount1TooLow); + + Ok((amount_0, amount_1)) + } + /// Performs a swap on this pool. pub fn swap( &mut self, @@ -442,6 +475,11 @@ impl StoragePool { self.cur_tick.get() } + ///! Get a position given. This is a helper function for testing. + pub fn get_position(&self, id: U256) -> StorageGuard<'_, position::StoragePositionInfo> { + self.positions.positions.get(id) + } + pub fn get_fees_owed(&self, id: U256) -> (u128, u128) { self.positions.fees_owed(id) } diff --git a/pkg/sol/ISeawater.sol b/pkg/sol/ISeawater.sol index 764ddc00..b3f74885 100644 --- a/pkg/sol/ISeawater.sol +++ b/pkg/sol/ISeawater.sol @@ -1,8 +1,6 @@ // SPDX-Identifier: MIT pragma solidity 0.8.16; -import "./ISeawaterOwnership.sol"; import "./ISeawaterAMM.sol"; -interface ISeawater is ISeawaterOwnership, ISeawaterAMM { -} +interface ISeawater is ISeawaterAMM {} diff --git a/pkg/sol/ISeawaterAMM.sol b/pkg/sol/ISeawaterAMM.sol index 7d700254..cdc16bfb 100644 --- a/pkg/sol/ISeawaterAMM.sol +++ b/pkg/sol/ISeawaterAMM.sol @@ -19,7 +19,7 @@ interface ISeawaterAMM is /// @param _amount input amount (token) /// @param _minOut the minimum output amount (usdc), reverting if the actual output is lower /// @return amount of usdc out - function swapIn( + function swapIn_33a1f7( address _token, uint256 _amount, uint256 _minOut @@ -34,7 +34,7 @@ interface ISeawaterAMM is /// @param _sig the signature for the token /// @param _maxAmount the max amount of the token /// @return amount of usdc out - function swapInPermit2( + function swapInPermit2_3e9dd6( address _token, uint256 _amount, uint256 _minOut, @@ -49,7 +49,7 @@ interface ISeawaterAMM is /// @param _amount input amount (usdc) /// @param _minOut the minimum output amount (token), reverting if the actual output is lower /// @return amount of token out - function swapOut( + function swapOut_63c461( address _token, uint256 _amount, uint256 _minOut @@ -64,7 +64,7 @@ interface ISeawaterAMM is /// @param _sig the signature for the token /// @param _maxAmount the max amount of the token /// @return amount of token out - function swapOutPermit2( + function swapOutPermit2_5dec48( address _token, uint256 _amount, uint256 _minOut, @@ -80,7 +80,7 @@ interface ISeawaterAMM is /// @param _amount input amount (tokenA) /// @param _minOut the minimum output amount (tokenB), reverting if the actual output is lower /// @return amount of token A in, amount of token B out - function swap2ExactIn( + function swap2ExactIn_f4bd2d( address _tokenA, address _tokenB, uint256 _amount, diff --git a/pkg/sol/ISeawaterExecutors.sol b/pkg/sol/ISeawaterExecutors.sol index 12a3dfb6..012e40a3 100644 --- a/pkg/sol/ISeawaterExecutors.sol +++ b/pkg/sol/ISeawaterExecutors.sol @@ -8,7 +8,7 @@ interface ISeawaterExecutorSwap { /// @param amount the amount of token to swap, positive if exactIn, negative if exactOut /// @param priceLimit the price limit for swaps, encoded as a sqrtX96 price /// @return (token0, token1) delta - function swap( + function swap_eb3a17( address pool, bool zeroForOne, int256 amount, @@ -21,7 +21,7 @@ interface ISeawaterExecutorSwap { /// @param amount the amount of the input token to use /// @param minOut the minimum valid amount of the output token, reverts if not reached /// @return (amount in, amount out) - function swap2ExactIn( + function swap2ExactIn_f4bd2d( address from, address to, uint256 amount, @@ -40,7 +40,7 @@ interface ISeawaterExecutorSwapPermit2 { /// @param maxAmount the permit2 maxAmount /// @param sig the permit2 signature /// @return (token0, token1) delta - function swapPermit2( + function swapPermit2_d5d578( address pool, bool zeroForOne, int256 amount, @@ -61,7 +61,7 @@ interface ISeawaterExecutorSwapPermit2 { /// @param sig the permit2 signature /// @notice permit2's max amount must be set to `amount` /// @return (amount in, amount out) - function swap2ExactInPermit2( + function swap2ExactInPermit2_15755d( address from, address to, uint256 amount, @@ -79,7 +79,7 @@ interface ISeawaterExecutorQuote { /// @param amount the amount of token to swap, positive if exactIn, negative if exactOut /// @param priceLimit the price limit for swaps, encoded as a sqrtX96 price /// @notice always revert with Error(string(amountOut)) - function quote( + function quote____006eac( address pool, bool zeroForOne, int256 amount, @@ -92,7 +92,7 @@ interface ISeawaterExecutorQuote { /// @param amount the amount of the input token to use /// @param minOut the minimum valid amount of the output token, reverts if not reached /// @notice always revert with Error(string(amountOut)) - function quote2( + function quote2__cc9e21( address from, address to, uint256 amount, @@ -105,37 +105,37 @@ interface ISeawaterExecutorPosition { /// @param pool the pool to create the position on /// @param lower the lower tick of the position (for concentrated liquidity) /// @param upper the upper tick of the position - function mintPosition( + function mintPosition_05ca11( address pool, int32 lower, int32 upper - ) external; + ) external returns (uint256 id); /// @notice burns a position, leaving the liquidity in it inaccessible /// @notice id the id of the position to burn - function burnPosition(uint256 id) external; + function burnPosition_f7639f(uint256 id) external; /// @notice transferPosition transfers a position. usable only by the NFT manager /// @param id the id of the position to transfer /// @param from the user to transfer the position from /// @param to the user to transfer the position to - function transferPosition(uint256 id, address from, address to) external; + function transferPosition_5bf9a4(uint256 id, address from, address to) external; /// @notice gets the owner of a position /// @param id the id of the position /// @return the owner of the position - function positionOwner(uint256 id) external returns (address); + function positionOwner__0602c2(uint256 id) external returns (address); /// @notice gets the number of positions owned by a user /// @param user the user to get position balance for /// @return the number of positions owned by the user - function positionBalance(address user) external returns (uint256); + function positionBalance_f285b1(address user) external returns (uint256); /// @notice gets the amount of liquidity in a position /// @param pool the pool the position belongs to /// @param id the id of the position /// @return the amount of liquidity contained in the position - function positionLiquidity(address pool, uint256 id) external returns (uint128); + function positionLiquidity_0f1547(address pool, uint256 id) external returns (uint128); /// @notice collects fees from a position /// @param pool the pool the position belongs to @@ -143,7 +143,7 @@ interface ISeawaterExecutorPosition { /// @param amount0 the maximum amount of token0 to claim /// @param amount1 the maximum amount of token1 to claim /// @return the amount of token0 and token1 collected - function collect( + function collect_7472a7( address pool, uint256 id, uint128 amount0, @@ -157,7 +157,7 @@ interface ISeawaterExecutorUpdatePosition { /// @param id the id of the position /// @param delta the amount of liquidity to add or remove /// @return the deltas for token0 and token1 for the user - function updatePosition( + function updatePosition_957a57( address pool, uint256 id, int128 delta @@ -176,7 +176,7 @@ interface ISeawaterExecutorUpdatePosition { /// @param maxAmount1 the max amount for token 1 /// @param sig1 the signature for token 1 /// @return the deltas for token0 and token1 for the user - function updatePositionPermit2( + function updatePositionPermit2__0c5637( address pool, uint256 id, int128 delta, @@ -199,7 +199,7 @@ interface ISeawaterExecutorAdminExposed { /// @param fee the fee to use /// @param tickSpacing the spacing for valid liquidity ticks /// @param maxLiquidityPerTick the maximum amount of liquidity allowed in a single tick - function createPool( + function createPool_816c1f( address pool, uint256 sqrtPriceX96, uint32 fee, @@ -212,7 +212,7 @@ interface ISeawaterExecutorAdminExposed { /// @param amount0 the maximum amount of token0 fees to collect /// @param amount1 the maximum amount of token1 fees to collect /// @return the amount of token0 and token1 fees collected - function collectProtocol( + function collectProtocol_fd4241( address pool, uint128 amount0, uint128 amount1 @@ -222,37 +222,37 @@ interface ISeawaterExecutorAdminExposed { /// @param pool to get the fees owed for /// @param id of the position to check for /// @return the amount of token0 and token1 to get in return - function feesOwed(address pool, uint256 id) external returns (uint128, uint128); + function feesOwed_5ec605(address pool, uint256 id) external returns (uint128, uint128); /// @notice gets the current sqrt price of the pool /// @param pool to get from /// @return the current sqrtPriceX96 for the pool - function sqrtPriceX96(address pool) external returns (uint256); + function sqrtPriceX96_6ed516(address pool) external returns (uint256); /// @notice gets the currently used tick of the pool /// @param pool to get from /// @return the current active tick in the pool - function curTick(address pool) external returns (int32); + function curTick_84a60b(address pool) external returns (int32); /// @notice gets the tick spacing of the pool /// @param pool to get from /// @return the tick spacing of the pool - function tickSpacing(address pool) external returns (uint8); + function tickSpacing_52bd17(address pool) external returns (uint8); /// @notice gets the fee growth for token 0 /// @param pool to get from /// @return the fee growth for the other token - function feeGrowthGlobal0(address pool) external returns (uint256); + function feeGrowthGlobal0_7fb790(address pool) external returns (uint256); /// @notice gets the fee growth for token 1 /// @param pool to get from /// @return the fee growth for fUSDC - function feeGrowthGlobal1(address pool) external returns (uint256); + function feeGrowthGlobal1_d2e217(address pool) external returns (uint256); /// @notice enables or disables a pool /// @param pool the pool to enable or disable /// @param enabled true to enable to pool, false to disable it - function setPoolEnabled(address pool, bool enabled) external; + function enablePool___e9825f(address pool, bool enabled) external; } interface ISeawaterExecutorAdmin is ISeawaterExecutorAdminExposed { @@ -262,6 +262,4 @@ interface ISeawaterExecutorAdmin is ISeawaterExecutorAdminExposed { function ctor(address seawaterAdmin, address nftManager) external; } -interface ISeawaterExecutorFallback { - -} +interface ISeawaterExecutorFallback {} diff --git a/pkg/sol/ISeawaterOwnership.sol b/pkg/sol/ISeawaterOwnership.sol deleted file mode 100644 index 8bae5f87..00000000 --- a/pkg/sol/ISeawaterOwnership.sol +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-Identifier: MIT - -pragma solidity 0.8.16; - -interface ISeawaterOwnership { - function ownerOf(uint256 _tokenId) external view returns (address); - - function transfer(address _sender, uint256 _tokenId, address _recipient) external; - - function balanceOf(address _spender) external view returns (uint256); -} diff --git a/pkg/sol/OwnershipNFTs.sol b/pkg/sol/OwnershipNFTs.sol index faacf709..b9b5e03a 100644 --- a/pkg/sol/OwnershipNFTs.sol +++ b/pkg/sol/OwnershipNFTs.sol @@ -2,15 +2,16 @@ pragma solidity 0.8.16; import "./IERC721Metadata.sol"; +import "./ISeawaterAMM.sol"; + import "./IERC721TokenReceiver.sol"; -import "./ISeawaterOwnership.sol"; /* * OwnershipNFTs is a simple interface for tracking ownership of * positions in the Seawater Stylus contract. */ contract OwnershipNFTs is IERC721Metadata { - ISeawaterOwnership immutable public SEAWATER; + ISeawaterAMM immutable public SEAWATER; /** * @notice TOKEN_URI to set as the default token URI for every NFT @@ -38,7 +39,7 @@ contract OwnershipNFTs is IERC721Metadata { string memory _name, string memory _symbol, string memory _tokenURI, - ISeawaterOwnership _seawater + ISeawaterAMM _seawater ) { name = _name; symbol = _symbol; @@ -51,7 +52,13 @@ contract OwnershipNFTs is IERC721Metadata { * @param _tokenId to look up */ function ownerOf(uint256 _tokenId) public view returns (address) { - return SEAWATER.ownerOf(_tokenId); + (bool ok, bytes memory rc) = address(SEAWATER).staticcall(abi.encodeWithSelector( + SEAWATER.positionOwner__0602c2.selector, + _tokenId + )); + require(ok, "position owner revert"); + (address owner) = abi.decode(rc, (address)); + return owner; } /** @@ -105,7 +112,7 @@ contract OwnershipNFTs is IERC721Metadata { uint256 _tokenId ) internal { _requireAuthorised(_from, _tokenId); - SEAWATER.transfer(_from, _tokenId, _to); + SEAWATER.transferPosition_5bf9a4(_tokenId, _from, _to); } function transferFrom( @@ -162,7 +169,13 @@ contract OwnershipNFTs is IERC721Metadata { /// @inheritdoc IERC721Metadata function balanceOf(address _spender) external view returns (uint256) { - return SEAWATER.balanceOf(_spender); + (bool ok, bytes memory rc) = address(SEAWATER).staticcall(abi.encodeWithSelector( + SEAWATER.positionBalance_f285b1.selector, + _spender + )); + require(ok, "position balance revert"); + (uint256 balance) = abi.decode(rc, (uint256)); + return balance; } /// @inheritdoc IERC721Metadata diff --git a/pkg/sol/SeawaterAMM.sol b/pkg/sol/SeawaterAMM.sol index 3abb89b4..2355ef21 100644 --- a/pkg/sol/SeawaterAMM.sol +++ b/pkg/sol/SeawaterAMM.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.16; import "./ISeawaterExecutors.sol"; + import "./ISeawaterAMM.sol"; // slots to store proxy data in @@ -155,7 +156,7 @@ contract SeawaterAMM is ISeawaterAMM { // admin functions /// @inheritdoc ISeawaterExecutorAdminExposed - function createPool( + function createPool_816c1f( address /* token */, uint256 /* sqrtPriceX96 */, uint32 /* fee */, @@ -166,7 +167,7 @@ contract SeawaterAMM is ISeawaterAMM { } /// @inheritdoc ISeawaterExecutorAdminExposed - function collectProtocol( + function collectProtocol_fd4241( address /* pool */, uint128 /* amount0 */, uint128 /* amount1 */ @@ -175,7 +176,7 @@ contract SeawaterAMM is ISeawaterAMM { } /// @inheritdoc ISeawaterExecutorAdminExposed - function setPoolEnabled( + function enablePool___e9825f( address /* pool */, bool /* enabled */ ) external { @@ -185,23 +186,23 @@ contract SeawaterAMM is ISeawaterAMM { // swap functions /// @inheritdoc ISeawaterExecutorSwap - function swap(address /* pool */, bool /* zeroForOne */, int256 /* amount */, uint256 /* priceLimit */) external returns (int256, int256) { + function swap_eb3a17(address /* pool */, bool /* zeroForOne */, int256 /* amount */, uint256 /* priceLimit */) external returns (int256, int256) { directDelegate(_getExecutorSwap()); } /// @inheritdoc ISeawaterExecutorQuote - function quote(address /* pool */, bool /* zeroForOne */, int256 /* amount */, uint256 /* priceLimit */) external { + function quote____006eac(address /* pool */, bool /* zeroForOne */, int256 /* amount */, uint256 /* priceLimit */) external { directDelegate(_getExecutorQuote()); } /// @inheritdoc ISeawaterExecutorQuote - function quote2(address /* to */, address /* from */, uint256 /* amount */, uint256 /* minOut*/) external { + function quote2__cc9e21(address /* to */, address /* from */, uint256 /* amount */, uint256 /* minOut*/) external { directDelegate(_getExecutorQuote()); } /// @inheritdoc ISeawaterExecutorSwapPermit2 - function swapPermit2( + function swapPermit2_d5d578( address /* pool */, bool /* zeroForOne */, int256 /* amount */, @@ -215,12 +216,12 @@ contract SeawaterAMM is ISeawaterAMM { } /// @inheritdoc ISeawaterExecutorSwap - function swap2ExactIn(address /* tokenA */, address /* tokenB */, uint256 /* amountIn */, uint256 /* minAmountOut */) external returns (uint256, uint256) { + function swap2ExactIn_f4bd2d(address /* tokenA */, address /* tokenB */, uint256 /* amountIn */, uint256 /* minAmountOut */) external returns (uint256, uint256) { directDelegate(_getExecutorSwap()); } /// @inheritdoc ISeawaterExecutorSwapPermit2 - function swap2ExactInPermit2( + function swap2ExactInPermit2_15755d( address /* from */, address /* to */, uint256 /* amount */, @@ -233,9 +234,9 @@ contract SeawaterAMM is ISeawaterAMM { } /// @inheritdoc ISeawaterAMM - function swapIn(address token, uint256 amountIn, uint256 minOut) external returns (int256, int256) { + function swapIn_33a1f7(address token, uint256 amountIn, uint256 minOut) external returns (int256, int256) { (bool success, bytes memory data) = _getExecutorSwap().delegatecall(abi.encodeCall( - ISeawaterExecutorSwap.swap, + ISeawaterExecutorSwap.swap_eb3a17, ( token, true, @@ -252,9 +253,9 @@ contract SeawaterAMM is ISeawaterAMM { } /// @inheritdoc ISeawaterAMM - function swapInPermit2(address token, uint256 amountIn, uint256 minOut, uint256 nonce, uint256 deadline, uint256 maxAmount, bytes memory sig) external returns (int256, int256) { + function swapInPermit2_3e9dd6(address token, uint256 amountIn, uint256 minOut, uint256 nonce, uint256 deadline, uint256 maxAmount, bytes memory sig) external returns (int256, int256) { (bool success, bytes memory data) = _getExecutorSwapPermit2().delegatecall(abi.encodeCall( - ISeawaterExecutorSwapPermit2.swapPermit2, + ISeawaterExecutorSwapPermit2.swapPermit2_d5d578, ( token, true, @@ -275,9 +276,9 @@ contract SeawaterAMM is ISeawaterAMM { } /// @inheritdoc ISeawaterAMM - function swapOut(address token, uint256 amountIn, uint256 minOut) external returns (int256, int256) { + function swapOut_63c461(address token, uint256 amountIn, uint256 minOut) external returns (int256, int256) { (bool success, bytes memory data) = _getExecutorSwap().delegatecall(abi.encodeCall( - ISeawaterExecutorSwap.swap, + ISeawaterExecutorSwap.swap_eb3a17, ( token, false, @@ -293,9 +294,9 @@ contract SeawaterAMM is ISeawaterAMM { } /// @inheritdoc ISeawaterAMM - function swapOutPermit2(address token, uint256 amountIn, uint256 minOut, uint256 nonce, uint256 deadline, uint256 maxAmount, bytes memory sig) external returns (int256, int256) { + function swapOutPermit2_5dec48(address token, uint256 amountIn, uint256 minOut, uint256 nonce, uint256 deadline, uint256 maxAmount, bytes memory sig) external returns (int256, int256) { (bool success, bytes memory data) = _getExecutorSwapPermit2().delegatecall(abi.encodeCall( - ISeawaterExecutorSwapPermit2.swapPermit2, + ISeawaterExecutorSwapPermit2.swapPermit2_d5d578, ( token, false, @@ -317,68 +318,68 @@ contract SeawaterAMM is ISeawaterAMM { // position functions /// @inheritdoc ISeawaterExecutorPosition - function mintPosition(address /* token */, int32 /* lower */, int32 /* upper */) external { + function mintPosition_05ca11(address /* token */, int32 /* lower */, int32 /* upper */) external returns (uint256 /* id */) { directDelegate(_getExecutorPosition()); } /// @inheritdoc ISeawaterExecutorPosition - function burnPosition(uint256 /* id */) external { + function burnPosition_f7639f(uint256 /* id */) external { directDelegate(_getExecutorPosition()); } /// @inheritdoc ISeawaterExecutorPosition - function positionOwner(uint256 /* id */) external returns (address) { + function positionOwner__0602c2(uint256 /* id */) external returns (address) { directDelegate(_getExecutorPosition()); } // called by the position manager contract!! /// @inheritdoc ISeawaterExecutorPosition - function transferPosition(uint256 /* id */, address /* from */, address /* to */) external { + function transferPosition_5bf9a4(uint256 /* id */, address /* from */, address /* to */) external { directDelegate(_getExecutorPosition()); } /// @inheritdoc ISeawaterExecutorPosition - function positionBalance(address /* user */) external returns (uint256) { + function positionBalance_f285b1(address /* user */) external returns (uint256) { directDelegate(_getExecutorPosition()); } /// @inheritdoc ISeawaterExecutorPosition - function positionLiquidity(address /* pool */, uint256 /* id */) external returns (uint128) { + function positionLiquidity_0f1547(address /* pool */, uint256 /* id */) external returns (uint128) { directDelegate(_getExecutorPosition()); } /// @inheritdoc ISeawaterExecutorAdminExposed - function sqrtPriceX96(address /* pool */) external returns (uint256) { + function sqrtPriceX96_6ed516(address /* pool */) external returns (uint256) { directDelegate(_getExecutorAdmin()); } /// @inheritdoc ISeawaterExecutorAdminExposed - function feesOwed(address /* pool */, uint256 /* position */) external returns (uint128, uint128) { + function feesOwed_5ec605(address /* pool */, uint256 /* position */) external returns (uint128, uint128) { directDelegate(_getExecutorAdmin()); } /// @inheritdoc ISeawaterExecutorAdminExposed - function curTick(address /* pool */) external returns (int32) { + function curTick_84a60b(address /* pool */) external returns (int32) { directDelegate(_getExecutorAdmin()); } /// @inheritdoc ISeawaterExecutorAdminExposed - function tickSpacing(address /* pool */) external returns (uint8) { + function tickSpacing_52bd17(address /* pool */) external returns (uint8) { directDelegate(_getExecutorAdmin()); } /// @inheritdoc ISeawaterExecutorAdminExposed - function feeGrowthGlobal0(address /* pool */) external returns (uint256) { + function feeGrowthGlobal0_7fb790(address /* pool */) external returns (uint256) { directDelegate(_getExecutorAdmin()); } /// @inheritdoc ISeawaterExecutorAdminExposed - function feeGrowthGlobal1(address /* pool */) external returns (uint256) { + function feeGrowthGlobal1_d2e217(address /* pool */) external returns (uint256) { directDelegate(_getExecutorAdmin()); } /// @inheritdoc ISeawaterExecutorPosition - function collect( + function collect_7472a7( address /* pool */, uint256 /* id */, uint128 /* amount0 */, @@ -388,7 +389,7 @@ contract SeawaterAMM is ISeawaterAMM { } /// @inheritdoc ISeawaterExecutorUpdatePosition - function updatePosition( + function updatePosition_957a57( address /* pool */, uint256 /* id */, int128 /* delta */ @@ -397,7 +398,7 @@ contract SeawaterAMM is ISeawaterAMM { } /// @inheritdoc ISeawaterExecutorUpdatePosition - function updatePositionPermit2( + function updatePositionPermit2__0c5637( address /* pool */, uint256 /* id */, int128 /* delta */, @@ -415,8 +416,20 @@ contract SeawaterAMM is ISeawaterAMM { // fallback! fallback() external { - // this will revert if the fallback executor is not set! - directDelegate(_getExecutorFallback()); + require(msg.data.length > 3); + // swaps + if (uint8(msg.data[2]) == 0) directDelegate(_getExecutorSwap()); + // update positions + else if (uint8(msg.data[2]) == 1) directDelegate(_getExecutorUpdatePosition()); + // positions + else if (uint8(msg.data[2]) == 2) directDelegate(_getExecutorPosition()); + // admin + else if (uint8(msg.data[2]) == 3) directDelegate(_getExecutorAdmin()); + // swap permit 2 + else if (uint8(msg.data[2]) == 4) directDelegate(_getExecutorSwapPermit2()); + // quotes + else if (uint8(msg.data[2]) == 5) directDelegate(_getExecutorQuote()); + else directDelegate(_getExecutorFallback()); } // internal functions diff --git a/pkg/tests.sh b/pkg/tests.sh index eff00bd5..0a653e47 100755 --- a/pkg/tests.sh +++ b/pkg/tests.sh @@ -4,7 +4,7 @@ export \ SPN_GETH_URL=http://127.0.0.1:8547 \ RUST_BACKTRACE=1 -cargo test --features testing +cargo test --features testing,testing-dbg #forge test