Skip to content

Commit

Permalink
chore: add nonfungible position manager example (#122)
Browse files Browse the repository at this point in the history
Introduce a new example demonstrating minting liquidity with the Uniswap Nonfungible Position Manager. Refactor transaction handling by replacing deprecated `register()` calls with `watch()` for better event monitoring. Adjust `get_erc20_state_overrides` to use provider references for improved API consistency.
  • Loading branch information
shuhuiluo authored Dec 29, 2024
1 parent 6f4da2d commit 24ac093
Show file tree
Hide file tree
Showing 5 changed files with 142 additions and 11 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,7 @@ required-features = ["extensions"]
[[example]]
name = "self_permit"
required-features = ["extensions"]

[[example]]
name = "nonfungible_position_manager"
required-features = ["extensions"]
132 changes: 132 additions & 0 deletions examples/nonfungible_position_manager.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
use alloy::{
eips::BlockId,
providers::{ext::AnvilApi, Provider, ProviderBuilder},
rpc::types::TransactionRequest,
transports::{http::reqwest::Url, Transport},
};
use alloy_primitives::{address, Address, U256};
use uniswap_lens::bindings::ierc721enumerable::IERC721Enumerable;
use uniswap_sdk_core::{prelude::*, token};
use uniswap_v3_sdk::prelude::*;

#[tokio::main]
async fn main() {
dotenv::dotenv().ok();
let rpc_url: Url = std::env::var("MAINNET_RPC_URL").unwrap().parse().unwrap();
let block_id = BlockId::from(17000000);
let wbtc = token!(
1,
address!("2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599"),
8,
"WBTC"
);
let weth = WETH9::on_chain(1).unwrap();
let npm = *NONFUNGIBLE_POSITION_MANAGER_ADDRESSES.get(&1).unwrap();

// Create an Anvil fork
let provider = ProviderBuilder::new()
.with_recommended_fillers()
.on_anvil_with_config(|anvil| {
anvil
.fork(rpc_url)
.fork_block_number(block_id.as_u64().unwrap())
});
let account = provider.get_accounts().await.unwrap()[0];

let pool = Pool::from_pool_key(
1,
FACTORY_ADDRESS,
wbtc.address(),
weth.address(),
FeeAmount::LOW,
provider.clone(),
None,
)
.await
.unwrap();
let mut position = Position::new(
pool.clone(),
pool.liquidity,
nearest_usable_tick(pool.tick_current - pool.tick_spacing(), pool.tick_spacing()),
nearest_usable_tick(pool.tick_current + pool.tick_spacing(), pool.tick_spacing()),
);

// Set the state of the account to allow the position to be minted
let MintAmounts { amount0, amount1 } = position.mint_amounts().unwrap();
let mut overrides = get_erc20_state_overrides(
position.pool.token0.address(),
account,
npm,
amount0,
&provider,
)
.await
.unwrap();
overrides.extend(
get_erc20_state_overrides(
position.pool.token1.address(),
account,
npm,
amount1,
&provider,
)
.await
.unwrap(),
);
for (token, account_override) in overrides {
for (slot, value) in account_override.state_diff.unwrap() {
provider
.anvil_set_storage_at(token, U256::from_be_bytes(slot.0), value)
.await
.unwrap();
}
}

let minted_position = mint_liquidity(&mut position, account, &provider).await;

assert_eq!(minted_position.liquidity, position.liquidity);
assert_eq!(minted_position.tick_lower, position.tick_lower);
assert_eq!(minted_position.tick_upper, position.tick_upper);
}

async fn mint_liquidity<T, P>(position: &mut Position, from: Address, provider: &P) -> Position
where
T: Transport + Clone,
P: Provider<T>,
{
let npm = *NONFUNGIBLE_POSITION_MANAGER_ADDRESSES.get(&1).unwrap();

let options = AddLiquidityOptions {
slippage_tolerance: Percent::default(),
deadline: U256::MAX,
use_native: None,
token0_permit: None,
token1_permit: None,
specific_opts: AddLiquiditySpecificOptions::Mint(MintSpecificOptions {
recipient: from,
create_pool: false,
}),
};
let params = add_call_parameters(position, options).unwrap();
let tx = TransactionRequest::default()
.from(from)
.to(npm)
.input(params.calldata.into());
provider
.send_transaction(tx)
.await
.unwrap()
.watch()
.await
.unwrap();

let token_id = IERC721Enumerable::new(npm, provider)
.tokenOfOwnerByIndex(from, U256::ZERO)
.call()
.await
.unwrap()
._0;
Position::from_token_id(1, npm, token_id, provider, None)
.await
.unwrap()
}
4 changes: 1 addition & 3 deletions examples/self_permit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,7 @@ async fn main() {
.send_transaction(tx)
.await
.unwrap()
.register()
.await
.unwrap()
.watch()
.await
.unwrap();

Expand Down
4 changes: 1 addition & 3 deletions examples/swap_router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,7 @@ async fn main() {
.send_transaction(tx)
.await
.unwrap()
.register()
.await
.unwrap()
.watch()
.await
.unwrap();

Expand Down
9 changes: 4 additions & 5 deletions src/extensions/state_overrides.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ pub async fn get_erc20_state_overrides<T, P>(
owner: Address,
spender: Address,
amount: U256,
provider: P,
provider: &P,
) -> Result<StateOverride, Error>
where
T: Transport + Clone,
Expand Down Expand Up @@ -90,10 +90,9 @@ mod tests {
let owner = address!("88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640");
let npm = *NONFUNGIBLE_POSITION_MANAGER_ADDRESSES.get(&1).unwrap();
let amount = U256::from(1_000_000);
let overrides =
get_erc20_state_overrides(USDC.address(), owner, npm, amount, provider.clone())
.await
.unwrap();
let overrides = get_erc20_state_overrides(USDC.address(), owner, npm, amount, &provider)
.await
.unwrap();
let usdc = IERC20::new(USDC.address(), provider);
let balance = usdc
.balanceOf(owner)
Expand Down

0 comments on commit 24ac093

Please sign in to comment.