From 31d5f4fd572db781c34ff363f4b4ad44eec16ca2 Mon Sep 17 00:00:00 2001 From: Yash Sharma Date: Mon, 15 Jun 2026 16:23:04 +0530 Subject: [PATCH] feat: add uniswap v3 as a native router venue PoolType::UniswapV3 executor (QuoterV2 quotes, SwapRouter02 swaps), governable addresses in pallet-parameters, UniswapV3Api runtime API, and a lark deploy + mixed-swap validation CLI. --- Cargo.lock | 12 +- integration-tests/src/lib.rs | 1 + integration-tests/src/uniswap_v3_router.rs | 130 + integration-tests/uniswap-snapshot/README.md | 68 + pallets/broadcast/Cargo.toml | 2 +- pallets/broadcast/src/types.rs | 1 + pallets/parameters/Cargo.toml | 2 +- pallets/parameters/src/lib.rs | 32 + runtime/hydradx/Cargo.toml | 2 +- runtime/hydradx/src/assets.rs | 11 +- runtime/hydradx/src/evm/mod.rs | 1 + .../src/evm/uniswap_v3_trade_executor.rs | 560 +++ runtime/hydradx/src/lib.rs | 21 +- scripts/uniswap-v3-lark/.env.example | 31 + scripts/uniswap-v3-lark/.gitignore | 4 + scripts/uniswap-v3-lark/package-lock.json | 2988 +++++++++++++++++ scripts/uniswap-v3-lark/package.json | 37 + scripts/uniswap-v3-lark/src/cli.ts | 61 + .../uniswap-v3-lark/src/commands/deploy.ts | 43 + scripts/uniswap-v3-lark/src/commands/fund.ts | 45 + .../uniswap-v3-lark/src/commands/preflight.ts | 66 + scripts/uniswap-v3-lark/src/commands/seed.ts | 79 + .../src/commands/seedConcentrated.ts | 120 + .../src/commands/setAddresses.ts | 34 + .../uniswap-v3-lark/src/commands/swapTest.ts | 94 + .../uniswap-v3-lark/src/commands/whitelist.ts | 29 + scripts/uniswap-v3-lark/src/config.ts | 93 + scripts/uniswap-v3-lark/src/core/abis.ts | 22 + scripts/uniswap-v3-lark/src/core/assets.ts | 18 + scripts/uniswap-v3-lark/src/core/evm.ts | 43 + scripts/uniswap-v3-lark/src/core/math.ts | 32 + scripts/uniswap-v3-lark/src/core/substrate.ts | 146 + scripts/uniswap-v3-lark/tests/assets.test.ts | 31 + scripts/uniswap-v3-lark/tests/math.test.ts | 44 + scripts/uniswap-v3-lark/tsconfig.json | 18 + traits/Cargo.toml | 2 +- traits/src/router.rs | 3 +- 37 files changed, 4913 insertions(+), 13 deletions(-) create mode 100644 integration-tests/src/uniswap_v3_router.rs create mode 100644 integration-tests/uniswap-snapshot/README.md create mode 100644 runtime/hydradx/src/evm/uniswap_v3_trade_executor.rs create mode 100644 scripts/uniswap-v3-lark/.env.example create mode 100644 scripts/uniswap-v3-lark/.gitignore create mode 100644 scripts/uniswap-v3-lark/package-lock.json create mode 100644 scripts/uniswap-v3-lark/package.json create mode 100644 scripts/uniswap-v3-lark/src/cli.ts create mode 100644 scripts/uniswap-v3-lark/src/commands/deploy.ts create mode 100644 scripts/uniswap-v3-lark/src/commands/fund.ts create mode 100644 scripts/uniswap-v3-lark/src/commands/preflight.ts create mode 100644 scripts/uniswap-v3-lark/src/commands/seed.ts create mode 100644 scripts/uniswap-v3-lark/src/commands/seedConcentrated.ts create mode 100644 scripts/uniswap-v3-lark/src/commands/setAddresses.ts create mode 100644 scripts/uniswap-v3-lark/src/commands/swapTest.ts create mode 100644 scripts/uniswap-v3-lark/src/commands/whitelist.ts create mode 100644 scripts/uniswap-v3-lark/src/config.ts create mode 100644 scripts/uniswap-v3-lark/src/core/abis.ts create mode 100644 scripts/uniswap-v3-lark/src/core/assets.ts create mode 100644 scripts/uniswap-v3-lark/src/core/evm.ts create mode 100644 scripts/uniswap-v3-lark/src/core/math.ts create mode 100644 scripts/uniswap-v3-lark/src/core/substrate.ts create mode 100644 scripts/uniswap-v3-lark/tests/assets.test.ts create mode 100644 scripts/uniswap-v3-lark/tests/math.test.ts create mode 100644 scripts/uniswap-v3-lark/tsconfig.json diff --git a/Cargo.lock b/Cargo.lock index 534867c07f..a115ed9b6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5780,7 +5780,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "428.0.0" +version = "429.0.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", @@ -5878,7 +5878,7 @@ dependencies = [ "pallet-omnipool-liquidity-mining", "pallet-otc", "pallet-otc-settlements", - "pallet-parameters 1.2.0", + "pallet-parameters 1.3.0", "pallet-preimage", "pallet-proxy", "pallet-referenda", @@ -5945,7 +5945,7 @@ dependencies = [ [[package]] name = "hydradx-traits" -version = "4.11.0" +version = "4.12.0" dependencies = [ "frame-support", "frame-system", @@ -8912,7 +8912,7 @@ dependencies = [ [[package]] name = "pallet-broadcast" -version = "1.7.0" +version = "1.8.0" dependencies = [ "frame-support", "frame-support-procedural", @@ -9336,7 +9336,7 @@ dependencies = [ "pallet-balances", "pallet-currencies", "pallet-evm", - "pallet-parameters 1.2.0", + "pallet-parameters 1.3.0", "pallet-transaction-payment", "parity-scale-codec", "primitives", @@ -10302,7 +10302,7 @@ dependencies = [ [[package]] name = "pallet-parameters" -version = "1.2.0" +version = "1.3.0" dependencies = [ "frame-support", "frame-system", diff --git a/integration-tests/src/lib.rs b/integration-tests/src/lib.rs index 19dba1dc29..e38a854f38 100644 --- a/integration-tests/src/lib.rs +++ b/integration-tests/src/lib.rs @@ -47,6 +47,7 @@ mod stableswap; mod stableswap_curve_comparison; mod staking; mod transact_call_filter; +mod uniswap_v3_router; mod utility; pub mod utils; mod vesting; diff --git a/integration-tests/src/uniswap_v3_router.rs b/integration-tests/src/uniswap_v3_router.rs new file mode 100644 index 0000000000..10ace1886b --- /dev/null +++ b/integration-tests/src/uniswap_v3_router.rs @@ -0,0 +1,130 @@ +#![cfg(test)] + +use crate::polkadot_test_net::*; +use frame_support::assert_ok; +use hex_literal::hex; +use hydradx_runtime::evm::uniswap_v3_trade_executor::UniswapV3; +use hydradx_runtime::{AssetId, Currencies, Parameters, Router, Runtime, RuntimeEvent, RuntimeOrigin}; +use hydradx_traits::router::{PoolType, Trade}; +use orml_traits::MultiCurrency; +use pallet_broadcast::types::Filler; +use pallet_route_executor::TradeExecution; +use primitives::{Balance, EvmAddress}; +use sp_core::H160; + +pub const PATH_TO_SNAPSHOT: &str = "uniswap-snapshot/SNAPSHOT"; + +const UNISWAP_V3_FACTORY: EvmAddress = H160(hex!("0000000000000000000000000000000000000000")); +const UNISWAP_V3_SWAP_ROUTER: EvmAddress = H160(hex!("0000000000000000000000000000000000000000")); +const UNISWAP_V3_QUOTER: EvmAddress = H160(hex!("0000000000000000000000000000000000000000")); + +const ASSET_IN: AssetId = 0; +const ASSET_OUT: AssetId = 20; +const FEE_TIER: u32 = 3000; +const SELL_AMOUNT: Balance = 1_000_000_000_000; + +fn with_uniswap_v3(execution: impl FnOnce()) { + TestNet::reset(); + hydra_live_ext(PATH_TO_SNAPSHOT).execute_with(|| { + assert_ok!(Parameters::set_uniswap_v3_addresses( + RuntimeOrigin::root(), + UNISWAP_V3_FACTORY, + UNISWAP_V3_SWAP_ROUTER, + UNISWAP_V3_QUOTER, + )); + execution(); + }); +} + +fn uniswap_route() -> Vec> { + vec![Trade { + pool: PoolType::UniswapV3(FEE_TIER), + asset_in: ASSET_IN, + asset_out: ASSET_OUT, + }] +} + +#[test] +#[ignore = "requires uniswap-snapshot/SNAPSHOT and deployment constants; see uniswap-snapshot/README.md"] +fn calculate_out_given_in_should_return_positive_quote_when_pool_has_liquidity() { + with_uniswap_v3(|| { + let amount_out = + UniswapV3::calculate_out_given_in(PoolType::UniswapV3(FEE_TIER), ASSET_IN, ASSET_OUT, SELL_AMOUNT) + .expect("quote should succeed"); + assert!(amount_out > 0); + }); +} + +#[test] +#[ignore = "requires uniswap-snapshot/SNAPSHOT and deployment constants; see uniswap-snapshot/README.md"] +fn calculate_in_given_out_should_exceed_output_when_pool_charges_fee() { + with_uniswap_v3(|| { + let amount_out = SELL_AMOUNT / 2; + let amount_in = + UniswapV3::calculate_in_given_out(PoolType::UniswapV3(FEE_TIER), ASSET_IN, ASSET_OUT, amount_out) + .expect("quote should succeed"); + assert!(amount_in > amount_out); + }); +} + +#[test] +#[ignore = "requires uniswap-snapshot/SNAPSHOT and deployment constants; see uniswap-snapshot/README.md"] +fn router_sell_should_increase_output_balance_when_routed_through_uniswap_v3() { + with_uniswap_v3(|| { + let before = Currencies::free_balance(ASSET_OUT, &ALICE.into()); + assert_ok!(Router::sell( + RuntimeOrigin::signed(ALICE.into()), + ASSET_IN, + ASSET_OUT, + SELL_AMOUNT, + 0, + uniswap_route().try_into().unwrap(), + )); + let after = Currencies::free_balance(ASSET_OUT, &ALICE.into()); + assert!(after > before); + }); +} + +#[test] +#[ignore = "requires uniswap-snapshot/SNAPSHOT and deployment constants; see uniswap-snapshot/README.md"] +fn router_buy_should_deliver_exact_output_when_routed_through_uniswap_v3() { + with_uniswap_v3(|| { + let buy_amount = SELL_AMOUNT / 2; + let before = Currencies::free_balance(ASSET_OUT, &ALICE.into()); + assert_ok!(Router::buy( + RuntimeOrigin::signed(ALICE.into()), + ASSET_IN, + ASSET_OUT, + buy_amount, + u128::MAX, + uniswap_route().try_into().unwrap(), + )); + let after = Currencies::free_balance(ASSET_OUT, &ALICE.into()); + assert_eq!(after - before, buy_amount); + }); +} + +#[test] +#[ignore = "requires uniswap-snapshot/SNAPSHOT and deployment constants; see uniswap-snapshot/README.md"] +fn router_sell_should_emit_uniswap_v3_filler_event() { + with_uniswap_v3(|| { + assert_ok!(Router::sell( + RuntimeOrigin::signed(ALICE.into()), + ASSET_IN, + ASSET_OUT, + SELL_AMOUNT, + 0, + uniswap_route().try_into().unwrap(), + )); + let emitted = frame_system::Pallet::::events().into_iter().any(|record| { + matches!( + record.event, + RuntimeEvent::Broadcast(pallet_broadcast::Event::Swapped3 { + filler_type: Filler::UniswapV3, + .. + }) + ) + }); + assert!(emitted); + }); +} diff --git a/integration-tests/uniswap-snapshot/README.md b/integration-tests/uniswap-snapshot/README.md new file mode 100644 index 0000000000..26d5a57e3b --- /dev/null +++ b/integration-tests/uniswap-snapshot/README.md @@ -0,0 +1,68 @@ +# uniswap-snapshot + +Snapshot-backed integration tests for the Uniswap v3 router venue +(`src/uniswap_v3_router.rs`). The tests load a scraped EVM state snapshot that +already contains a deployed Uniswap v3 stack (Factory, SwapRouter02, QuoterV2, +a pool with liquidity), then drive the runtime router against it — the same +pattern as `aave_router.rs` / `evm-snapshot/`. + +The tests are `#[ignore]`d until the `SNAPSHOT` artifact and the deployment +constants are committed (the binary is too large to ship by default, and the +addresses depend on the deployment used). Follow the steps below to produce them. + +## 1. Bring up a chain with Uniswap v3 deployed + +Use the local zombienet + Uniswap v3 deploy from the `ys-` branches (the +`uniswap-v3-deploy` sibling repo). The chain must run **this** runtime +(`feat/uniswap-v3-router`) so the snapshot is loadable, and must have: + +- Factory, SwapRouter02, QuoterV2 deployed. +- At least one pool created **and initialized** for an asset pair that maps to + two registered Hydration assets, with liquidity minted (via the + NonfungiblePositionManager) so quotes and swaps return non-zero. +- `ALICE` funded with `ASSET_IN`. + +Record the deployed addresses (the deploy writes them to +`uniswap-v3-deploy/deployments//_addresses.json`). + +## 2. Build the scraper + +```bash +cargo build --release -p scraper +``` + +## 3. Scrape the EVM + registry + balances into a snapshot + +```bash +./target/release/scraper save-storage \ + --pallet EVM AssetRegistry System Tokens Omnipool Timestamp Parameters \ + --uri ws://127.0.0.1:9988 \ + --path integration-tests/uniswap-snapshot +``` + +This writes `integration-tests/uniswap-snapshot/SNAPSHOT_`. Rename it to +`SNAPSHOT` (or update `PATH_TO_SNAPSHOT` in `src/uniswap_v3_router.rs`). Add +`--slim` to drop most user accounts and keep the file small. + +> Including the `Parameters` pallet means that if you call +> `parameters.setUniswapV3Addresses` on the source chain **before** scraping, +> the addresses are baked into the snapshot and `with_uniswap_v3` doesn't need +> to set them — in that case the `UNISWAP_V3_*` constants below are only used as +> a fallback. + +## 4. Fill in the deployment constants + +In `src/uniswap_v3_router.rs` set, from the deploy's `_addresses.json` and your +asset registry: + +- `UNISWAP_V3_FACTORY`, `UNISWAP_V3_SWAP_ROUTER`, `UNISWAP_V3_QUOTER` +- `ASSET_IN`, `ASSET_OUT`, `FEE_TIER` — the pair + fee tier of the seeded pool +- `SELL_AMOUNT` — comfortably below the pool's depth + +## 5. Run the tests + +```bash +cargo test -p runtime-integration-tests uniswap_v3_router -- --ignored +``` + +Drop the `#[ignore]` attributes once `SNAPSHOT` and the constants are committed. diff --git a/pallets/broadcast/Cargo.toml b/pallets/broadcast/Cargo.toml index 2ee0b1366e..32467be906 100644 --- a/pallets/broadcast/Cargo.toml +++ b/pallets/broadcast/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-broadcast" -version = "1.7.0" +version = "1.8.0" authors = ["GalacticCouncil"] edition = "2021" license = "Apache 2.0" diff --git a/pallets/broadcast/src/types.rs b/pallets/broadcast/src/types.rs index ea20104596..db730552c6 100644 --- a/pallets/broadcast/src/types.rs +++ b/pallets/broadcast/src/types.rs @@ -18,6 +18,7 @@ pub enum Filler { OTC(OtcOrderId), AAVE, // ICE(solution_id/block id), swapper: alice, filler: solver HSM, + UniswapV3, } #[derive(Encode, Decode, DecodeWithMemTracking, Clone, Copy, Debug, Eq, PartialEq, TypeInfo, MaxEncodedLen)] diff --git a/pallets/parameters/Cargo.toml b/pallets/parameters/Cargo.toml index 542cd3db38..658d44bf94 100644 --- a/pallets/parameters/Cargo.toml +++ b/pallets/parameters/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pallet-parameters" -version = "1.2.0" +version = "1.3.0" authors = ['GalacticCouncil'] edition = "2021" license = "Apache-2.0" diff --git a/pallets/parameters/src/lib.rs b/pallets/parameters/src/lib.rs index 2d69d9490a..bdf6d0d684 100644 --- a/pallets/parameters/src/lib.rs +++ b/pallets/parameters/src/lib.rs @@ -37,6 +37,8 @@ mod tests; pub use pallet::*; use frame_support::pallet_prelude::*; +use frame_system::pallet_prelude::OriginFor; +use sp_core::H160; #[frame_support::pallet] pub mod pallet { @@ -56,6 +58,18 @@ pub mod pallet { #[pallet::getter(fn relay_parent_offset_override)] pub type RelayParentOffsetOverride = StorageValue<_, bool, ValueQuery>; + #[pallet::storage] + #[pallet::getter(fn uniswap_v3_factory)] + pub type UniswapV3Factory = StorageValue<_, H160, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn uniswap_v3_swap_router)] + pub type UniswapV3SwapRouter = StorageValue<_, H160, OptionQuery>; + + #[pallet::storage] + #[pallet::getter(fn uniswap_v3_quoter)] + pub type UniswapV3Quoter = StorageValue<_, H160, OptionQuery>; + #[pallet::genesis_config] pub struct GenesisConfig { pub is_testnet: bool, @@ -81,6 +95,24 @@ pub mod pallet { } } + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(T::DbWeight::get().writes(3))] + pub fn set_uniswap_v3_addresses( + origin: OriginFor, + factory: H160, + swap_router: H160, + quoter: H160, + ) -> DispatchResult { + frame_system::ensure_root(origin)?; + UniswapV3Factory::::put(factory); + UniswapV3SwapRouter::::put(swap_router); + UniswapV3Quoter::::put(quoter); + Ok(()) + } + } + impl Pallet { /// Set the flag. Only used for tests. #[cfg(feature = "std")] diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index dc597f6fce..24cad0a21d 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "428.0.0" +version = "429.0.0" authors = ["GalacticCouncil"] edition = "2021" license = "Apache 2.0" diff --git a/runtime/hydradx/src/assets.rs b/runtime/hydradx/src/assets.rs index c3ff7fb00c..a4281334bb 100644 --- a/runtime/hydradx/src/assets.rs +++ b/runtime/hydradx/src/assets.rs @@ -1076,6 +1076,7 @@ impl AmmTradeWeights> for RouterWeightInfo { PoolType::XYK => weights::pallet_xyk::HydraWeight::::router_execution_sell(c, e) .saturating_add(::AMMHandler::on_trade_weight()), PoolType::Aave => Aave::trade_weight(), + PoolType::UniswapV3(_) => UniswapV3::trade_weight(), PoolType::HSM => { let mut hsm_weight = weights::pallet_hsm::HydraWeight::::calculate_sell().saturating_mul(c as u64); @@ -1111,6 +1112,7 @@ impl AmmTradeWeights> for RouterWeightInfo { PoolType::XYK => weights::pallet_xyk::HydraWeight::::router_execution_buy(c, e) .saturating_add(::AMMHandler::on_trade_weight()), PoolType::Aave => Aave::trade_weight(), + PoolType::UniswapV3(_) => UniswapV3::trade_weight(), PoolType::HSM => { let mut hsm_weight = weights::pallet_hsm::HydraWeight::::calculate_buy().saturating_mul(c as u64); @@ -1143,6 +1145,7 @@ impl AmmTradeWeights> for RouterWeightInfo { PoolType::XYK => weights::pallet_xyk::HydraWeight::::router_execution_buy(c, e) .saturating_add(::AMMHandler::on_trade_weight()), PoolType::Aave => Weight::zero(), + PoolType::UniswapV3(_) => UniswapV3::trade_weight(), PoolType::HSM => { let mut hsm_weight = weights::pallet_hsm::HydraWeight::::calculate_buy().saturating_mul(c as u64); @@ -1173,6 +1176,7 @@ impl AmmTradeWeights> for RouterWeightInfo { PoolType::XYK => weights::pallet_xyk::HydraWeight::::router_execution_sell(c, e) .saturating_add(::AMMHandler::on_trade_weight()), PoolType::Aave => Aave::trade_weight(), + PoolType::UniswapV3(_) => UniswapV3::trade_weight(), PoolType::HSM => { let mut hsm_weight = weights::pallet_hsm::HydraWeight::::calculate_sell().saturating_mul(c as u64); @@ -1206,6 +1210,7 @@ impl AmmTradeWeights> for RouterWeightInfo { PoolType::XYK => weights::pallet_xyk::HydraWeight::::router_execution_buy(c, e) .saturating_add(::AMMHandler::on_trade_weight()), PoolType::Aave => Aave::trade_weight(), + PoolType::UniswapV3(_) => UniswapV3::trade_weight(), PoolType::HSM => { let mut hsm_weight = weights::pallet_hsm::HydraWeight::::calculate_buy().saturating_mul(c as u64); @@ -1244,6 +1249,7 @@ impl AmmTradeWeights> for RouterWeightInfo { PoolType::Stableswap(_) => weights::pallet_stableswap::HydraWeight::::router_execution_sell(0), PoolType::XYK => weights::pallet_xyk::HydraWeight::::router_execution_sell(1, 0), PoolType::Aave => Aave::trade_weight(), + PoolType::UniswapV3(_) => UniswapV3::trade_weight(), PoolType::HSM => weights::pallet_hsm::HydraWeight::::calculate_sell(), }; weight.saturating_accrue(amm_weight); @@ -1257,6 +1263,7 @@ impl AmmTradeWeights> for RouterWeightInfo { PoolType::Stableswap(_) => weights::pallet_stableswap::HydraWeight::::router_execution_sell(0), PoolType::XYK => weights::pallet_xyk::HydraWeight::::router_execution_sell(1, 0), PoolType::Aave => Aave::trade_weight(), + PoolType::UniswapV3(_) => UniswapV3::trade_weight(), PoolType::HSM => weights::pallet_hsm::HydraWeight::::calculate_sell(), }; weight.saturating_accrue(amm_weight); @@ -1291,6 +1298,7 @@ impl AmmTradeWeights> for RouterWeightInfo { } PoolType::XYK => weights::pallet_xyk::HydraWeight::::calculate_spot_price_with_fee(), PoolType::Aave => Weight::zero(), + PoolType::UniswapV3(_) => UniswapV3::trade_weight(), PoolType::HSM => weights::pallet_hsm::HydraWeight::::calculate_spot_price_with_fee(), }; weight.saturating_accrue(amm_weight); @@ -1314,7 +1322,7 @@ impl pallet_route_executor::Config for Runtime { type Balance = Balance; type Currency = FungibleCurrencies; type WeightInfo = RouterWeightInfo; - type AMM = (Omnipool, Stableswap, XYK, LBP, Aave, HSM); + type AMM = (Omnipool, Stableswap, XYK, LBP, Aave, HSM, UniswapV3); type DefaultRoutePoolType = DefaultRoutePoolType; type NativeAssetId = NativeAssetId; type ForceInsertOrigin = EitherOf, EitherOf>; @@ -2090,6 +2098,7 @@ impl GetByKey for ReferralsLevelVolumeAndRewa } use crate::evm::aave_trade_executor::Aave; +use crate::evm::uniswap_v3_trade_executor::UniswapV3; #[cfg(feature = "runtime-benchmarks")] use crate::helpers::benchmark_helpers::CircuitBreakerBenchmarkHelper; use pallet_xyk::types::AssetPair; diff --git a/runtime/hydradx/src/evm/mod.rs b/runtime/hydradx/src/evm/mod.rs index ba6d3607f5..5f1ff15a21 100644 --- a/runtime/hydradx/src/evm/mod.rs +++ b/runtime/hydradx/src/evm/mod.rs @@ -62,6 +62,7 @@ mod gas_to_weight_mapping; pub mod permit; pub mod precompiles; mod runner; +pub mod uniswap_v3_trade_executor; use crate::circuit_breaker::IgnoreWithdrawFuse; pub use erc20_currency::Erc20Currency; diff --git a/runtime/hydradx/src/evm/uniswap_v3_trade_executor.rs b/runtime/hydradx/src/evm/uniswap_v3_trade_executor.rs new file mode 100644 index 0000000000..fe1ea5bad7 --- /dev/null +++ b/runtime/hydradx/src/evm/uniswap_v3_trade_executor.rs @@ -0,0 +1,560 @@ +use crate::evm::executor::{BalanceOf, NonceIdOf}; +use crate::evm::precompiles::erc20_mapping::HydraErc20Mapping; +use crate::evm::precompiles::handle::EvmDataWriter; +use crate::evm::Executor; +use ethabi::{decode, ParamType}; +use evm::ExitReason::Succeed; +use evm::ExitSucceed; +use frame_support::ensure; +use frame_support::pallet_prelude::RuntimeDebug; +use frame_support::weights::Weight; +use frame_system::ensure_signed; +use frame_system::pallet_prelude::OriginFor; +use hydradx_traits::evm::{CallContext, Erc20Mapping, InspectEvmAccounts, EVM}; +use hydradx_traits::router::{ExecutorError, PoolType, TradeExecution}; +use num_enum::{IntoPrimitive, TryFromPrimitive}; +use pallet_broadcast::types::Asset; +use pallet_evm::{AddressMapping, GasWeightMapping}; +use primitive_types::U256; +use primitives::{AccountId, AssetId, Balance, EvmAddress}; +use sp_arithmetic::traits::{SaturatedConversion, Saturating}; +use sp_arithmetic::FixedPointNumber; +use sp_arithmetic::FixedU128; +use sp_runtime::DispatchError; +use sp_std::marker::PhantomData; +use sp_std::vec; + +pub struct UniswapV3TradeExecutor(PhantomData); + +pub type UniswapV3 = UniswapV3TradeExecutor; + +#[module_evm_utility_macro::generate_function_selector] +#[derive(RuntimeDebug, Eq, PartialEq, TryFromPrimitive, IntoPrimitive)] +#[repr(u32)] +pub enum Function { + GetPool = "getPool(address,address,uint24)", + Slot0 = "slot0()", + Liquidity = "liquidity()", + Token0 = "token0()", + Token1 = "token1()", + BalanceOf = "balanceOf(address)", + QuoteExactInputSingle = "quoteExactInputSingle((address,address,uint256,uint24,uint160))", + QuoteExactOutputSingle = "quoteExactOutputSingle((address,address,uint256,uint24,uint160))", + ExactInputSingle = "exactInputSingle((address,address,uint24,address,uint256,uint256,uint160))", + ExactOutputSingle = "exactOutputSingle((address,address,uint24,address,uint256,uint256,uint160))", + Approve = "approve(address,uint256)", +} + +const VIEW_GAS_LIMIT: u64 = 500_000; +const QUOTE_GAS_LIMIT: u64 = 1_000_000; +const TRADE_GAS_LIMIT: u64 = 1_000_000; +const IN_GIVEN_OUT_ROUNDING: Balance = 1; +const FEE_DENOMINATOR: u128 = 1_000_000; + +pub fn evm_token_address(asset: AssetId) -> EvmAddress { + HydraErc20Mapping::asset_address(asset) +} + +pub fn sort_tokens(a: EvmAddress, b: EvmAddress) -> (EvmAddress, EvmAddress) { + if a < b { + (a, b) + } else { + (b, a) + } +} + +fn price_token1_per_token0(sqrt_price_x96: U256) -> FixedU128 { + let scaled = sqrt_price_x96 + .checked_mul(U256::from(1_000_000_000u64)) + .unwrap_or(U256::MAX) + >> 96; + let inner = scaled.checked_mul(scaled).unwrap_or(U256::MAX).saturated_into::(); + FixedU128::from_inner(inner) +} + +impl UniswapV3TradeExecutor +where + T: frame_system::Config + + pallet_evm::Config + + pallet_dispatcher::Config + + pallet_parameters::Config + + pallet_evm_accounts::Config, + ::AccountId: AsRef<[u8; 32]> + frame_support::traits::IsType, + BalanceOf: TryFrom + Into + Default, + NonceIdOf: Into, + T::AddressMapping: AddressMapping, + pallet_evm::AccountIdOf: From, +{ + fn factory() -> Result> { + pallet_parameters::Pallet::::uniswap_v3_factory() + .ok_or(ExecutorError::Error("uniswapv3: factory not configured".into())) + } + + fn quoter() -> Result> { + pallet_parameters::Pallet::::uniswap_v3_quoter() + .ok_or(ExecutorError::Error("uniswapv3: quoter not configured".into())) + } + + pub fn pool_address( + factory: EvmAddress, + asset_a: AssetId, + asset_b: AssetId, + fee: u32, + ) -> Result, ExecutorError> { + let (token0, token1) = sort_tokens(evm_token_address(asset_a), evm_token_address(asset_b)); + let context = CallContext::new_view(factory); + let data = EvmDataWriter::new_with_selector(Function::GetPool) + .write(token0) + .write(token1) + .write(U256::from(fee)) + .build(); + let result = Executor::::view(context, data, VIEW_GAS_LIMIT); + ensure!( + matches!(result.exit_reason, Succeed(ExitSucceed::Returned)), + ExecutorError::Error("uniswapv3: getPool failed".into()) + ); + let decoded = decode(&[ParamType::Address], result.value.as_ref()) + .map_err(|_| ExecutorError::Error("uniswapv3: getPool decode failed".into()))?; + let pool = decoded + .first() + .and_then(|token| token.clone().into_address()) + .map(|addr| EvmAddress::from_slice(addr.as_bytes())) + .ok_or(ExecutorError::Error("uniswapv3: getPool returned no address".into()))?; + Ok((pool != EvmAddress::zero()).then_some(pool)) + } + + pub fn quote_out_given_in( + asset_in: AssetId, + asset_out: AssetId, + fee: u32, + amount_in: Balance, + ) -> Result> { + let quoter = Self::quoter()?; + let token_in = evm_token_address(asset_in); + let token_out = evm_token_address(asset_out); + let data = EvmDataWriter::new_with_selector(Function::QuoteExactInputSingle) + .write(token_in) + .write(token_out) + .write(U256::from(amount_in)) + .write(U256::from(fee)) + .write(U256::zero()) + .build(); + let result = Executor::::view(CallContext::new_view(quoter), data, QUOTE_GAS_LIMIT); + ensure!( + matches!(result.exit_reason, Succeed(ExitSucceed::Returned)), + ExecutorError::Error("uniswapv3: quote failed".into()) + ); + ensure!( + result.value.len() >= 32, + ExecutorError::Error("uniswapv3: quote returned no data".into()) + ); + let amount_out = U256::from_big_endian(&result.value[0..32]); + Ok(amount_out.saturated_into::()) + } + + pub fn quote_in_given_out( + asset_in: AssetId, + asset_out: AssetId, + fee: u32, + amount_out: Balance, + ) -> Result> { + let quoter = Self::quoter()?; + let token_in = evm_token_address(asset_in); + let token_out = evm_token_address(asset_out); + let data = EvmDataWriter::new_with_selector(Function::QuoteExactOutputSingle) + .write(token_in) + .write(token_out) + .write(U256::from(amount_out)) + .write(U256::from(fee)) + .write(U256::zero()) + .build(); + let result = Executor::::view(CallContext::new_view(quoter), data, QUOTE_GAS_LIMIT); + ensure!( + matches!(result.exit_reason, Succeed(ExitSucceed::Returned)), + ExecutorError::Error("uniswapv3: quote failed".into()) + ); + ensure!( + result.value.len() >= 32, + ExecutorError::Error("uniswapv3: quote returned no data".into()) + ); + let amount_in = U256::from_big_endian(&result.value[0..32]).saturated_into::(); + Ok(amount_in.saturating_add(IN_GIVEN_OUT_ROUNDING)) + } + + pub fn spot_price_with_fee( + asset_a: AssetId, + asset_b: AssetId, + fee: u32, + ) -> Result> { + let factory = Self::factory()?; + let pool = Self::pool_address(factory, asset_a, asset_b, fee)? + .ok_or(ExecutorError::Error("uniswapv3: pool not found".into()))?; + let data = EvmDataWriter::new_with_selector(Function::Slot0).build(); + let result = Executor::::view(CallContext::new_view(pool), data, VIEW_GAS_LIMIT); + ensure!( + matches!(result.exit_reason, Succeed(ExitSucceed::Returned)), + ExecutorError::Error("uniswapv3: slot0 failed".into()) + ); + ensure!( + result.value.len() >= 32, + ExecutorError::Error("uniswapv3: slot0 returned no data".into()) + ); + let sqrt_price_x96 = U256::from_big_endian(&result.value[0..32]); + let price = price_token1_per_token0(sqrt_price_x96); + let raw = if evm_token_address(asset_a) < evm_token_address(asset_b) { + price + .reciprocal() + .ok_or(ExecutorError::Error("uniswapv3: zero price".into()))? + } else { + price + }; + let fee_factor = FixedU128::from_inner( + FEE_DENOMINATOR + .saturating_sub(fee as u128) + .saturating_mul(1_000_000_000_000u128), + ); + Ok(raw.saturating_mul(fee_factor)) + } + + pub fn liquidity_depth( + asset_a: AssetId, + asset_b: AssetId, + fee: u32, + ) -> Result> { + let factory = Self::factory()?; + let pool = Self::pool_address(factory, asset_a, asset_b, fee)? + .ok_or(ExecutorError::Error("uniswapv3: pool not found".into()))?; + let token_a = evm_token_address(asset_a); + let data = EvmDataWriter::new_with_selector(Function::BalanceOf) + .write(pool) + .build(); + let result = Executor::::view(CallContext::new_view(token_a), data, VIEW_GAS_LIMIT); + ensure!( + matches!(result.exit_reason, Succeed(ExitSucceed::Returned)), + ExecutorError::Error("uniswapv3: balanceOf failed".into()) + ); + ensure!( + result.value.len() >= 32, + ExecutorError::Error("uniswapv3: balanceOf returned no data".into()) + ); + Ok(U256::from_big_endian(&result.value[0..32]).saturated_into::()) + } + + pub fn find_pool( + asset_a: AssetId, + asset_b: AssetId, + fee: u32, + ) -> Result, ExecutorError> { + let factory = Self::factory()?; + Self::pool_address(factory, asset_a, asset_b, fee) + } + + pub fn trade_weight() -> Weight { + ::GasWeightMapping::gas_to_weight(TRADE_GAS_LIMIT + QUOTE_GAS_LIMIT, true) + } + + fn swap_router() -> Result> { + pallet_parameters::Pallet::::uniswap_v3_swap_router() + .ok_or(ExecutorError::Error("uniswapv3: swap router not configured".into())) + } + + fn do_sell( + who: OriginFor, + asset_in: AssetId, + asset_out: AssetId, + fee: u32, + amount_in: Balance, + min_limit: Balance, + ) -> Result> { + let who_account = + ensure_signed(who.clone()).map_err(|_| ExecutorError::Error("uniswapv3: bad origin".into()))?; + let _ = pallet_evm_accounts::Pallet::::bind_evm_address(who); + let trader = pallet_evm_accounts::Pallet::::evm_address(&who_account); + let router = Self::swap_router()?; + let token_in = evm_token_address(asset_in); + let token_out = evm_token_address(asset_out); + + let approve = EvmDataWriter::new_with_selector(Function::Approve) + .write(router) + .write(U256::from(amount_in)) + .build(); + let approve_result = Executor::::call( + CallContext::new_call(token_in, trader), + approve, + U256::zero(), + TRADE_GAS_LIMIT, + ); + ensure!( + matches!(approve_result.exit_reason, Succeed(_)), + ExecutorError::Error("uniswapv3: approve failed".into()) + ); + + let swap = EvmDataWriter::new_with_selector(Function::ExactInputSingle) + .write(token_in) + .write(token_out) + .write(U256::from(fee)) + .write(trader) + .write(U256::from(amount_in)) + .write(U256::from(min_limit)) + .write(U256::zero()) + .build(); + let swap_result = Executor::::call( + CallContext::new_call(router, trader), + swap, + U256::zero(), + TRADE_GAS_LIMIT, + ); + ensure!( + matches!(swap_result.exit_reason, Succeed(_)), + ExecutorError::Error("uniswapv3: swap failed".into()) + ); + + let amount_out = if swap_result.value.len() >= 32 { + U256::from_big_endian(&swap_result.value[0..32]).saturated_into::() + } else { + min_limit + }; + Ok(amount_out) + } + + fn do_buy( + who: OriginFor, + asset_in: AssetId, + asset_out: AssetId, + fee: u32, + amount_out: Balance, + max_limit: Balance, + ) -> Result> { + let who_account = + ensure_signed(who.clone()).map_err(|_| ExecutorError::Error("uniswapv3: bad origin".into()))?; + let _ = pallet_evm_accounts::Pallet::::bind_evm_address(who); + let trader = pallet_evm_accounts::Pallet::::evm_address(&who_account); + let router = Self::swap_router()?; + let token_in = evm_token_address(asset_in); + let token_out = evm_token_address(asset_out); + + let approve = EvmDataWriter::new_with_selector(Function::Approve) + .write(router) + .write(U256::from(max_limit)) + .build(); + let approve_result = Executor::::call( + CallContext::new_call(token_in, trader), + approve, + U256::zero(), + TRADE_GAS_LIMIT, + ); + ensure!( + matches!(approve_result.exit_reason, Succeed(_)), + ExecutorError::Error("uniswapv3: approve failed".into()) + ); + + let swap = EvmDataWriter::new_with_selector(Function::ExactOutputSingle) + .write(token_in) + .write(token_out) + .write(U256::from(fee)) + .write(trader) + .write(U256::from(amount_out)) + .write(U256::from(max_limit)) + .write(U256::zero()) + .build(); + let swap_result = Executor::::call( + CallContext::new_call(router, trader), + swap, + U256::zero(), + TRADE_GAS_LIMIT, + ); + ensure!( + matches!(swap_result.exit_reason, Succeed(_)), + ExecutorError::Error("uniswapv3: swap failed".into()) + ); + + let amount_in = if swap_result.value.len() >= 32 { + U256::from_big_endian(&swap_result.value[0..32]).saturated_into::() + } else { + max_limit + }; + Ok(amount_in) + } +} + +impl TradeExecution, AccountId, AssetId, Balance> for UniswapV3TradeExecutor +where + T: frame_system::Config + + pallet_evm::Config + + pallet_dispatcher::Config + + pallet_parameters::Config + + pallet_evm_accounts::Config + + pallet_broadcast::Config, + ::AccountId: AsRef<[u8; 32]> + frame_support::traits::IsType, + BalanceOf: TryFrom + Into + Default, + NonceIdOf: Into, + T::AddressMapping: AddressMapping, + pallet_evm::AccountIdOf: From, +{ + type Error = DispatchError; + + fn calculate_out_given_in( + pool_type: PoolType, + asset_in: AssetId, + asset_out: AssetId, + amount_in: Balance, + ) -> Result> { + let PoolType::UniswapV3(fee) = pool_type else { + return Err(ExecutorError::NotSupported); + }; + Self::quote_out_given_in(asset_in, asset_out, fee, amount_in) + } + + fn calculate_in_given_out( + pool_type: PoolType, + asset_in: AssetId, + asset_out: AssetId, + amount_out: Balance, + ) -> Result> { + let PoolType::UniswapV3(fee) = pool_type else { + return Err(ExecutorError::NotSupported); + }; + Self::quote_in_given_out(asset_in, asset_out, fee, amount_out) + } + + fn execute_sell( + who: OriginFor, + pool_type: PoolType, + asset_in: AssetId, + asset_out: AssetId, + amount_in: Balance, + min_limit: Balance, + ) -> Result<(), ExecutorError> { + let PoolType::UniswapV3(fee) = pool_type else { + return Err(ExecutorError::NotSupported); + }; + let amount_out = Self::do_sell(who.clone(), asset_in, asset_out, fee, amount_in, min_limit)?; + let trader = ensure_signed(who).map_err(|_| ExecutorError::Error("uniswapv3: bad origin".into()))?; + let filler = pallet_evm_accounts::Pallet::::truncated_account_id(Self::swap_router().unwrap_or_default()); + pallet_broadcast::Pallet::::deposit_trade_event( + trader, + filler, + pallet_broadcast::types::Filler::UniswapV3, + pallet_broadcast::types::TradeOperation::ExactIn, + vec![Asset::new(asset_in, amount_in)], + vec![Asset::new(asset_out, amount_out)], + vec![], + ); + Ok(()) + } + + fn execute_buy( + who: OriginFor, + pool_type: PoolType, + asset_in: AssetId, + asset_out: AssetId, + amount_out: Balance, + max_limit: Balance, + ) -> Result<(), ExecutorError> { + let PoolType::UniswapV3(fee) = pool_type else { + return Err(ExecutorError::NotSupported); + }; + let amount_in = Self::do_buy(who.clone(), asset_in, asset_out, fee, amount_out, max_limit)?; + let trader = ensure_signed(who).map_err(|_| ExecutorError::Error("uniswapv3: bad origin".into()))?; + let filler = pallet_evm_accounts::Pallet::::truncated_account_id(Self::swap_router().unwrap_or_default()); + pallet_broadcast::Pallet::::deposit_trade_event( + trader, + filler, + pallet_broadcast::types::Filler::UniswapV3, + pallet_broadcast::types::TradeOperation::ExactOut, + vec![Asset::new(asset_in, amount_in)], + vec![Asset::new(asset_out, amount_out)], + vec![], + ); + Ok(()) + } + + fn get_liquidity_depth( + pool_type: PoolType, + asset_a: AssetId, + asset_b: AssetId, + ) -> Result> { + let PoolType::UniswapV3(fee) = pool_type else { + return Err(ExecutorError::NotSupported); + }; + Self::liquidity_depth(asset_a, asset_b, fee) + } + + fn calculate_spot_price_with_fee( + pool_type: PoolType, + asset_a: AssetId, + asset_b: AssetId, + ) -> Result> { + let PoolType::UniswapV3(fee) = pool_type else { + return Err(ExecutorError::NotSupported); + }; + Self::spot_price_with_fee(asset_a, asset_b, fee) + } +} + +pub mod runtime_api { + use super::AssetId; + use super::EvmAddress; + use codec::Codec; + use sp_runtime::traits::MaybeDisplay; + + sp_api::decl_runtime_apis! { + pub trait UniswapV3Api + where Balance: Codec + MaybeDisplay + { + fn pool(asset_a: AssetId, asset_b: AssetId, fee: u32) -> Option; + fn quote_sell(asset_in: AssetId, asset_out: AssetId, fee: u32, amount_in: Balance) -> Option; + fn quote_buy(asset_in: AssetId, asset_out: AssetId, fee: u32, amount_out: Balance) -> Option; + fn liquidity_depth(asset_in: AssetId, asset_out: AssetId, fee: u32) -> Option; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn evm_token_address_should_map_registry_asset_to_precompile() { + sp_io::TestExternalities::default().execute_with(|| { + assert_eq!(evm_token_address(2), EvmAddress::from_low_u64_be(0x1_0000_0002)); + }); + } + + #[test] + fn sort_tokens_should_order_ascending() { + let lo = EvmAddress::from_low_u64_be(0x1_0000_0001); + let hi = EvmAddress::from_low_u64_be(0x1_0000_0002); + assert_eq!(sort_tokens(hi, lo), (lo, hi)); + assert_eq!(sort_tokens(lo, hi), (lo, hi)); + } + + #[test] + fn price_at_sqrt_two_pow_96_should_be_one() { + assert_eq!(price_token1_per_token0(U256::from(1) << 96), FixedU128::from(1)); + } + + #[test] + fn price_at_sqrt_two_pow_97_should_be_four() { + assert_eq!(price_token1_per_token0(U256::from(1) << 97), FixedU128::from(4)); + } + + #[test] + fn price_at_sqrt_two_pow_95_should_be_one_quarter() { + assert_eq!( + price_token1_per_token0(U256::from(1) << 95), + FixedU128::from_rational(1, 4) + ); + } + + #[test] + fn price_should_saturate_when_sqrt_price_overflows() { + assert_eq!(price_token1_per_token0(U256::MAX), FixedU128::from_inner(u128::MAX)); + } + + #[test] + fn sort_tokens_should_return_same_pair_when_equal() { + let a = EvmAddress::from_low_u64_be(0x1_0000_0007); + assert_eq!(sort_tokens(a, a), (a, a)); + } +} diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index ab85011d5a..063a2d5ec4 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -129,7 +129,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: Cow::Borrowed("hydradx"), impl_name: Cow::Borrowed("hydradx"), authoring_version: 1, - spec_version: 428, + spec_version: 429, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, @@ -502,6 +502,7 @@ impl fp_rpc::ConvertTransaction for TransactionConv use crate::evm::aave_trade_executor::AaveTradeExecutor; use crate::evm::aave_trade_executor::PoolData; use crate::evm::precompiles::erc20_mapping::HydraErc20Mapping; +use crate::evm::uniswap_v3_trade_executor::UniswapV3TradeExecutor; use frame_support::{ genesis_builder_helper::{build_state, get_preset}, sp_runtime::{ @@ -1206,6 +1207,24 @@ impl_runtime_apis! { } } + impl evm::uniswap_v3_trade_executor::runtime_api::UniswapV3Api for Runtime { + fn pool(asset_a: AssetId, asset_b: AssetId, fee: u32) -> Option { + UniswapV3TradeExecutor::::find_pool(asset_a, asset_b, fee).ok().flatten() + } + + fn quote_sell(asset_in: AssetId, asset_out: AssetId, fee: u32, amount_in: Balance) -> Option { + UniswapV3TradeExecutor::::quote_out_given_in(asset_in, asset_out, fee, amount_in).ok() + } + + fn quote_buy(asset_in: AssetId, asset_out: AssetId, fee: u32, amount_out: Balance) -> Option { + UniswapV3TradeExecutor::::quote_in_given_out(asset_in, asset_out, fee, amount_out).ok() + } + + fn liquidity_depth(asset_in: AssetId, asset_out: AssetId, fee: u32) -> Option { + UniswapV3TradeExecutor::::liquidity_depth(asset_in, asset_out, fee).ok() + } + } + #[cfg(feature = "runtime-benchmarks")] impl frame_benchmarking::Benchmark for Runtime { diff --git a/scripts/uniswap-v3-lark/.env.example b/scripts/uniswap-v3-lark/.env.example new file mode 100644 index 0000000000..e97aab572d --- /dev/null +++ b/scripts/uniswap-v3-lark/.env.example @@ -0,0 +1,31 @@ +RPC_WS=wss://1.lark.hydration.cloud +RPC_EVM=https://1.lark.hydration.cloud + +SURI=//Alice +EVM_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + +UNISWAP_V3_FACTORY= +UNISWAP_V3_SWAP_ROUTER= +UNISWAP_V3_QUOTER= +UNISWAP_V3_NPM= +UNISWAP_DEPLOY_CLI= + +TOKEN_A=16 +TOKEN_B=9 +FEE=3000 +AMOUNT_A=1000000000000000000000000 +AMOUNT_B=1000000000000000000000000 + +GAS_ASSET_ID=20 +FUND_GAS_AMOUNT=100000000000000000000 +FUND_AMOUNT_A=2000000000000000000000000 +FUND_AMOUNT_B=2000000000000000000000000 + +SELL_ASSET_IN=0 +QUOTE_ASSET=9 +SELL_AMOUNT=1000000000000000 +FEES=500,3000,10000 + +VOTE_HDX=3000000000 +ENACT_AFTER=10 +EVM_GAS_LIMIT=15000000 diff --git a/scripts/uniswap-v3-lark/.gitignore b/scripts/uniswap-v3-lark/.gitignore new file mode 100644 index 0000000000..7a167883dc --- /dev/null +++ b/scripts/uniswap-v3-lark/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +.env +state.json diff --git a/scripts/uniswap-v3-lark/package-lock.json b/scripts/uniswap-v3-lark/package-lock.json new file mode 100644 index 0000000000..b18ed782c3 --- /dev/null +++ b/scripts/uniswap-v3-lark/package-lock.json @@ -0,0 +1,2988 @@ +{ + "name": "uniswap-v3-lark", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "uniswap-v3-lark", + "version": "1.0.0", + "dependencies": { + "@polkadot/api": "^16.4.1", + "@polkadot/util": "^14.0.0", + "@polkadot/util-crypto": "^14.0.0", + "commander": "^12.1.0", + "dotenv": "^16.4.7", + "ethers": "^6.13.5", + "zod": "^3.24.1" + }, + "devDependencies": { + "@types/node": "^22.10.5", + "tsx": "^4.19.2", + "typescript": "^5.7.3", + "vitest": "^2.1.8" + }, + "engines": { + "node": ">=20.6" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.1.tgz", + "integrity": "sha512-96Z2IP3mYmF1Xg2cDm8f1gWGf/HUVedQ3FMifV4kG/PQ4yEP51xDtRAEfhVNt5f/uzpNkZHwWQuUcu6D6K+Ekw==", + "license": "MIT" + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.0.tgz", + "integrity": "sha512-lhRUCeuOyJQURhTxl4WkpFTjIsbDayJHih5kZC1giwE+MhIzAb7mEsQMqMf18rHLsrb5qI1tafG20mLxEWcWlA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@noble/curves": { + "version": "1.9.7", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.9.7.tgz", + "integrity": "sha512-gbKGcRUYIjA3/zCCNaWDciTMFI0dCkvou3TL8Zmy5Nc7sJ47a0jtOeZoTaMxkuqRo9cRhjOdZJXegxYE5FN/xw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.8.0" + }, + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", + "integrity": "sha512-jCs9ldd7NwzpgXDIf6P3+NrHh9/sD6CQdxHyjQI+h/6rDNo88ypBxxz45UDuZHz9r3tNz7N/VInSVoVdtXEI4A==", + "license": "MIT", + "engines": { + "node": "^14.21.3 || >=16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@polkadot-api/json-rpc-provider": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider/-/json-rpc-provider-0.0.1.tgz", + "integrity": "sha512-/SMC/l7foRjpykLTUTacIH05H3mr9ip8b5xxfwXlVezXrNVLp3Cv0GX6uItkKd+ZjzVPf3PFrDF2B2/HLSNESA==", + "license": "MIT", + "optional": true + }, + "node_modules/@polkadot-api/json-rpc-provider-proxy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/json-rpc-provider-proxy/-/json-rpc-provider-proxy-0.1.0.tgz", + "integrity": "sha512-8GSFE5+EF73MCuLQm8tjrbCqlgclcHBSRaswvXziJ0ZW7iw3UEMsKkkKvELayWyBuOPa2T5i1nj6gFOeIsqvrg==", + "license": "MIT", + "optional": true + }, + "node_modules/@polkadot-api/metadata-builders": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@polkadot-api/metadata-builders/-/metadata-builders-0.3.2.tgz", + "integrity": "sha512-TKpfoT6vTb+513KDzMBTfCb/ORdgRnsS3TDFpOhAhZ08ikvK+hjHMt5plPiAX/OWkm1Wc9I3+K6W0hX5Ab7MVg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/substrate-bindings": "0.6.0", + "@polkadot-api/utils": "0.1.0" + } + }, + "node_modules/@polkadot-api/observable-client": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@polkadot-api/observable-client/-/observable-client-0.3.2.tgz", + "integrity": "sha512-HGgqWgEutVyOBXoGOPp4+IAq6CNdK/3MfQJmhCJb8YaJiaK4W6aRGrdQuQSTPHfERHCARt9BrOmEvTXAT257Ug==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/metadata-builders": "0.3.2", + "@polkadot-api/substrate-bindings": "0.6.0", + "@polkadot-api/utils": "0.1.0" + }, + "peerDependencies": { + "@polkadot-api/substrate-client": "0.1.4", + "rxjs": ">=7.8.0" + } + }, + "node_modules/@polkadot-api/substrate-bindings": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-bindings/-/substrate-bindings-0.6.0.tgz", + "integrity": "sha512-lGuhE74NA1/PqdN7fKFdE5C1gNYX357j1tWzdlPXI0kQ7h3kN0zfxNOpPUN7dIrPcOFZ6C0tRRVrBylXkI6xPw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@noble/hashes": "^1.3.1", + "@polkadot-api/utils": "0.1.0", + "@scure/base": "^1.1.1", + "scale-ts": "^1.6.0" + } + }, + "node_modules/@polkadot-api/substrate-client": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@polkadot-api/substrate-client/-/substrate-client-0.1.4.tgz", + "integrity": "sha512-MljrPobN0ZWTpn++da9vOvt+Ex+NlqTlr/XT7zi9sqPtDJiQcYl+d29hFAgpaeTqbeQKZwz3WDE9xcEfLE8c5A==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/json-rpc-provider": "0.0.1", + "@polkadot-api/utils": "0.1.0" + } + }, + "node_modules/@polkadot-api/utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@polkadot-api/utils/-/utils-0.1.0.tgz", + "integrity": "sha512-MXzWZeuGxKizPx2Xf/47wx9sr/uxKw39bVJUptTJdsaQn/TGq+z310mHzf1RCGvC1diHM8f593KrnDgc9oNbJA==", + "license": "MIT", + "optional": true + }, + "node_modules/@polkadot/api": { + "version": "16.5.6", + "resolved": "https://registry.npmjs.org/@polkadot/api/-/api-16.5.6.tgz", + "integrity": "sha512-5h/X3pY8WpqGk4XTaiIUjKD6Pnk8k4bJ6EIwPKLP8/kfFWKSOenpN6ggZxANr+Qj+RgXrp4TxJVcuhXSiBh9Sg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api-augment": "16.5.6", + "@polkadot/api-base": "16.5.6", + "@polkadot/api-derive": "16.5.6", + "@polkadot/keyring": "^14.0.3", + "@polkadot/rpc-augment": "16.5.6", + "@polkadot/rpc-core": "16.5.6", + "@polkadot/rpc-provider": "16.5.6", + "@polkadot/types": "16.5.6", + "@polkadot/types-augment": "16.5.6", + "@polkadot/types-codec": "16.5.6", + "@polkadot/types-create": "16.5.6", + "@polkadot/types-known": "16.5.6", + "@polkadot/util": "^14.0.3", + "@polkadot/util-crypto": "^14.0.3", + "eventemitter3": "^5.0.1", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-augment": { + "version": "16.5.6", + "resolved": "https://registry.npmjs.org/@polkadot/api-augment/-/api-augment-16.5.6.tgz", + "integrity": "sha512-bunJF1c3nIuDtU6iwa+reTt9U47Y8iOC8Gw7PfANlZmLJmO/XVXnWc3JJLM+g9ESDn2raHJELeWBFVOXQrbtUw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api-base": "16.5.6", + "@polkadot/rpc-augment": "16.5.6", + "@polkadot/types": "16.5.6", + "@polkadot/types-augment": "16.5.6", + "@polkadot/types-codec": "16.5.6", + "@polkadot/util": "^14.0.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-base": { + "version": "16.5.6", + "resolved": "https://registry.npmjs.org/@polkadot/api-base/-/api-base-16.5.6.tgz", + "integrity": "sha512-eBLIv86ZZY4t5OrobVoGC+QXbErOGlBpI2rJI5OMvTNPoVvtEoI++u+wwRScjkOZaUhXyQikd+0Uv71qr3xnsA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-core": "16.5.6", + "@polkadot/types": "16.5.6", + "@polkadot/util": "^14.0.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/api-derive": { + "version": "16.5.6", + "resolved": "https://registry.npmjs.org/@polkadot/api-derive/-/api-derive-16.5.6.tgz", + "integrity": "sha512-cHdvPvhYFch18uPTcuOZJ8VceOfercod2fi4xCnHJAmattzlgj9qCgnOoxdmBS9GZ403ZyRHOjBuUwZy/IsUWQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/api": "16.5.6", + "@polkadot/api-augment": "16.5.6", + "@polkadot/api-base": "16.5.6", + "@polkadot/rpc-core": "16.5.6", + "@polkadot/types": "16.5.6", + "@polkadot/types-codec": "16.5.6", + "@polkadot/util": "^14.0.3", + "@polkadot/util-crypto": "^14.0.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/keyring": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-14.0.3.tgz", + "integrity": "sha512-ozp1dQwaHCjgX/fpTTORmHjxdUNQnyiTVJszpzUaUpvtH/IGZhSU/mSHXMqNETS/g57vQa7NatIDcWfyR9abyA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "14.0.3", + "@polkadot/util-crypto": "14.0.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "14.0.3", + "@polkadot/util-crypto": "14.0.3" + } + }, + "node_modules/@polkadot/networks": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-14.0.3.tgz", + "integrity": "sha512-/VqTLUDn+Wm8S2L/yaGFddo3oW4vRYav0Rg4pLg/semMZLaN8PJ6h927ucn9JyWdH82QfZfyiIPORt0ZF3isyw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "14.0.3", + "@substrate/ss58-registry": "^1.51.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-augment": { + "version": "16.5.6", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-augment/-/rpc-augment-16.5.6.tgz", + "integrity": "sha512-vlrNvl2VtU09jZV/AvH7jBb/cNUO+dWu8Xj9pId5ctSUnZHm8o8wRk9ekyieKP57OUoKMd8+VScwMKd624SxTw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-core": "16.5.6", + "@polkadot/types": "16.5.6", + "@polkadot/types-codec": "16.5.6", + "@polkadot/util": "^14.0.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-core": { + "version": "16.5.6", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-core/-/rpc-core-16.5.6.tgz", + "integrity": "sha512-l6od++WlvKH4mw5mtsIh2AhiBs3H+TtdOoUHVLCx/R9il7+gl+arltzZ8vBuffyh/O+uQ36lI8yUoD1g4gi1tA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/rpc-augment": "16.5.6", + "@polkadot/rpc-provider": "16.5.6", + "@polkadot/types": "16.5.6", + "@polkadot/util": "^14.0.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/rpc-provider": { + "version": "16.5.6", + "resolved": "https://registry.npmjs.org/@polkadot/rpc-provider/-/rpc-provider-16.5.6.tgz", + "integrity": "sha512-46sHIjKYr4aSzBCfbyqtCwuP8MMJ3jOp0xx9eggOGbKyP8Z0j0Cp+1nNkZUYzehcdGjjrmCxCbQp17wc6cj4zA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/keyring": "^14.0.3", + "@polkadot/types": "16.5.6", + "@polkadot/types-support": "16.5.6", + "@polkadot/util": "^14.0.3", + "@polkadot/util-crypto": "^14.0.3", + "@polkadot/x-fetch": "^14.0.3", + "@polkadot/x-global": "^14.0.3", + "@polkadot/x-ws": "^14.0.3", + "eventemitter3": "^5.0.1", + "mock-socket": "^9.3.1", + "nock": "^13.5.5", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@substrate/connect": "0.8.11" + } + }, + "node_modules/@polkadot/types": { + "version": "16.5.6", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-16.5.6.tgz", + "integrity": "sha512-X/sfMHJS4RkRhnsc4CQqzUy7BM/s2y71TrBFHPYAjs2q/rbZ/BwvBk70SrUiSa0+iRRn3RewbBZm+AB8CbkdKw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/keyring": "^14.0.3", + "@polkadot/types-augment": "16.5.6", + "@polkadot/types-codec": "16.5.6", + "@polkadot/types-create": "16.5.6", + "@polkadot/util": "^14.0.3", + "@polkadot/util-crypto": "^14.0.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-augment": { + "version": "16.5.6", + "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-16.5.6.tgz", + "integrity": "sha512-QN5UrluUZCVgknUDW0gps/FRQ13Qgm24w53pCd2HgD0nmTtXDt9D4psjWwx5JkGTkUAvpzFWwN41bkxAeCiV6g==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/types": "16.5.6", + "@polkadot/types-codec": "16.5.6", + "@polkadot/util": "^14.0.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-codec": { + "version": "16.5.6", + "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-16.5.6.tgz", + "integrity": "sha512-3tzUv1LZOL97IlQmko4dqbfRC0cg9IQ2QAHRVoDIWsXrVovp1V3kPdP0o6e3I8T2XB9IlbabK91v+ZiIxhGMZw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "^14.0.3", + "@polkadot/x-bigint": "^14.0.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-create": { + "version": "16.5.6", + "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-16.5.6.tgz", + "integrity": "sha512-g7g3hrjpz4KgqQqei9PU0JY9fsFHBmThWALZk5pWB32vyDyDcXZiyhH3agDhqfmzQiolTW2FuvcNJxgS634J1w==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/types-codec": "16.5.6", + "@polkadot/util": "^14.0.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-known": { + "version": "16.5.6", + "resolved": "https://registry.npmjs.org/@polkadot/types-known/-/types-known-16.5.6.tgz", + "integrity": "sha512-c78NcVO3LIvi4xzxB39WewE+80I4jOYUtPBaB4AzSMespEwIr92VTeX3KzFWuutxDXLSPqeVfXhaAhBB0NssiQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/networks": "^14.0.3", + "@polkadot/types": "16.5.6", + "@polkadot/types-codec": "16.5.6", + "@polkadot/types-create": "16.5.6", + "@polkadot/util": "^14.0.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-support": { + "version": "16.5.6", + "resolved": "https://registry.npmjs.org/@polkadot/types-support/-/types-support-16.5.6.tgz", + "integrity": "sha512-Hqpa/hCvXZXUTUiJMAE55UXpzAeCVLaFlzzXQXLkne0vhmv3/JkWcBnX755a/b9+C4b3MKEz2i0tSKLsa3DldA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "^14.0.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-14.0.3.tgz", + "integrity": "sha512-mg1NR7ixHlNiz2zbvdcdy1OXZmca2tVA4DpewGpY/qFkW/gq9HdDrHLu7g0k90QnunDcFW4emb7NB60sGJQ0bw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-bigint": "14.0.3", + "@polkadot/x-global": "14.0.3", + "@polkadot/x-textdecoder": "14.0.3", + "@polkadot/x-textencoder": "14.0.3", + "@types/bn.js": "^5.1.6", + "bn.js": "^5.2.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/util-crypto": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-14.0.3.tgz", + "integrity": "sha512-V00BI6XnZLCkrAmV8uN0eSB6fy48CkxdDZT29cgSMSwHPtY6oKUNgd1ST07PGCL5x8XflwjoA7CTlhdbp1Y9gw==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "14.0.3", + "@polkadot/util": "14.0.3", + "@polkadot/wasm-crypto": "^7.5.3", + "@polkadot/wasm-util": "^7.5.3", + "@polkadot/x-bigint": "14.0.3", + "@polkadot/x-randomvalues": "14.0.3", + "@scure/base": "^1.1.7", + "@scure/sr25519": "^0.2.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "14.0.3" + } + }, + "node_modules/@polkadot/wasm-bridge": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.5.4.tgz", + "integrity": "sha512-6xaJVvoZbnbgpQYXNw9OHVNWjXmtcoPcWh7hlwx3NpfiLkkjljj99YS+XGZQlq7ks2fVCg7FbfknkNb8PldDaA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-util": "7.5.4", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.5.4.tgz", + "integrity": "sha512-1seyClxa7Jd7kQjfnCzTTTfYhTa/KUTDUaD3DMHBk5Q4ZUN1D1unJgX+v1aUeXSPxmzocdZETPJJRZjhVOqg9g==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-bridge": "7.5.4", + "@polkadot/wasm-crypto-asmjs": "7.5.4", + "@polkadot/wasm-crypto-init": "7.5.4", + "@polkadot/wasm-crypto-wasm": "7.5.4", + "@polkadot/wasm-util": "7.5.4", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-asmjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.5.4.tgz", + "integrity": "sha512-ZYwxQHAJ8pPt6kYk9XFmyuFuSS+yirJLonvP+DYbxOrARRUHfN4nzp4zcZNXUuaFhpbDobDSFn6gYzye6BUotA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-init": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.5.4.tgz", + "integrity": "sha512-U6s4Eo2rHs2n1iR01vTz/sOQ7eOnRPjaCsGWhPV+ZC/20hkVzwPAhiizu/IqMEol4tO2yiSheD4D6bn0KxUJhg==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-bridge": "7.5.4", + "@polkadot/wasm-crypto-asmjs": "7.5.4", + "@polkadot/wasm-crypto-wasm": "7.5.4", + "@polkadot/wasm-util": "7.5.4", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*", + "@polkadot/x-randomvalues": "*" + } + }, + "node_modules/@polkadot/wasm-crypto-wasm": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.5.4.tgz", + "integrity": "sha512-PsHgLsVTu43eprwSvUGnxybtOEuHPES6AbApcs7y5ZbM2PiDMzYbAjNul098xJK/CPtrxZ0ePDFnaQBmIJyTFw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/wasm-util": "7.5.4", + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/wasm-util": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.5.4.tgz", + "integrity": "sha512-hqPpfhCpRAqCIn/CYbBluhh0TXmwkJnDRjxrU9Bnqtw9nMNa97D8JuOjdd2pi0rxm+eeLQ/f1rQMp71RMM9t4w==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "*" + } + }, + "node_modules/@polkadot/x-bigint": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-14.0.3.tgz", + "integrity": "sha512-U0al6BKgldFrEbmSObRAlzv9VDs5SMa/rbvZKvvkVec0sWTzYPWQZU1ZC/biXLYdjdKML89BeuCKmXZtCcGhUQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "14.0.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-fetch": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-fetch/-/x-fetch-14.0.3.tgz", + "integrity": "sha512-695c5aPBPtYcnn2zM+u0mXgyNHINlO0qGlGcJq3/0t5NVRZv5KZhk7NNm6antOay9uUjGG40F/r+LPzDT3QamA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "14.0.3", + "node-fetch": "^3.3.2", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-global": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-14.0.3.tgz", + "integrity": "sha512-MzMEynJ7HMTy/plLmdyP8rv14RS/6s29HZodUG9aCOscBnEiEDxVEax/ztRJqxhhQuHeYdx0LYDwVbdQDTkqNw==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-randomvalues": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-14.0.3.tgz", + "integrity": "sha512-qTPcrk0nIHL2tIu5e0cLj3puQvjCK7onehnqO2fvlmWeIlvDel66fwWs06Ipsib+CwLJdmE6WgNy+8Jv74r6YA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "14.0.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "14.0.3", + "@polkadot/wasm-util": "*" + } + }, + "node_modules/@polkadot/x-textdecoder": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-14.0.3.tgz", + "integrity": "sha512-4RJYDG00iUzQ7YAuS/yvkWRZlkjYU8PUNdJHRfqtJ+SjrSPB7LYYxFhLgw43TZUtHmIueNTsml2Ukv3xXTr2kA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "14.0.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-textencoder": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-14.0.3.tgz", + "integrity": "sha512-9HH6o2L+r99wEfXhPb5g+Xwn7qouqD32PsMux7B0dFGR2KNqP4KwO19Hu+gdij6wsEhy7delhZwzHenrWwDfhQ==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "14.0.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/x-ws": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/@polkadot/x-ws/-/x-ws-14.0.3.tgz", + "integrity": "sha512-tOPdkMye3iuXnuFtdNg5+iSu7Cz9LRL8z5psMuZpUpThMYChGsS2pDFtNvXOKU8ohhO+frY9VdJ9VBg1WL9Iug==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-global": "14.0.3", + "tslib": "^2.8.0", + "ws": "^8.18.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.61.1.tgz", + "integrity": "sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.61.1.tgz", + "integrity": "sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.61.1.tgz", + "integrity": "sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.61.1.tgz", + "integrity": "sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.61.1.tgz", + "integrity": "sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.61.1.tgz", + "integrity": "sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.61.1.tgz", + "integrity": "sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.61.1.tgz", + "integrity": "sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.61.1.tgz", + "integrity": "sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.61.1.tgz", + "integrity": "sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.61.1.tgz", + "integrity": "sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.61.1.tgz", + "integrity": "sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.61.1.tgz", + "integrity": "sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.61.1.tgz", + "integrity": "sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.61.1.tgz", + "integrity": "sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.61.1.tgz", + "integrity": "sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.61.1.tgz", + "integrity": "sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.61.1.tgz", + "integrity": "sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.61.1.tgz", + "integrity": "sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.61.1.tgz", + "integrity": "sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.61.1.tgz", + "integrity": "sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.61.1.tgz", + "integrity": "sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.61.1.tgz", + "integrity": "sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.61.1.tgz", + "integrity": "sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.61.1.tgz", + "integrity": "sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@scure/base": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", + "integrity": "sha512-g/nm5FgUa//MCj1gV09zTJTaM6KBAHqLN907YVQqf7zC49+DcO4B1so4ZX07Ef10Twr6nuqYEH9GEggFXA4Fmg==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/sr25519": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@scure/sr25519/-/sr25519-0.2.0.tgz", + "integrity": "sha512-uUuLP7Z126XdSizKtrCGqYyR3b3hYtJ6Fg/XFUXmc2//k2aXHDLqZwFeXxL97gg4XydPROPVnuaHGF2+xriSKg==", + "license": "MIT", + "dependencies": { + "@noble/curves": "~1.9.2", + "@noble/hashes": "~1.8.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@substrate/connect": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@substrate/connect/-/connect-0.8.11.tgz", + "integrity": "sha512-ofLs1PAO9AtDdPbdyTYj217Pe+lBfTLltdHDs3ds8no0BseoLeAGxpz1mHfi7zB4IxI3YyAiLjH6U8cw4pj4Nw==", + "deprecated": "versions below 1.x are no longer maintained", + "license": "GPL-3.0-only", + "optional": true, + "dependencies": { + "@substrate/connect-extension-protocol": "^2.0.0", + "@substrate/connect-known-chains": "^1.1.5", + "@substrate/light-client-extension-helpers": "^1.0.0", + "smoldot": "2.0.26" + } + }, + "node_modules/@substrate/connect-extension-protocol": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@substrate/connect-extension-protocol/-/connect-extension-protocol-2.2.2.tgz", + "integrity": "sha512-t66jwrXA0s5Goq82ZtjagLNd7DPGCNjHeehRlE/gcJmJ+G56C0W+2plqOMRicJ8XGR1/YFnUSEqUFiSNbjGrAA==", + "license": "GPL-3.0-only", + "optional": true + }, + "node_modules/@substrate/connect-known-chains": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/@substrate/connect-known-chains/-/connect-known-chains-1.10.3.tgz", + "integrity": "sha512-OJEZO1Pagtb6bNE3wCikc2wrmvEU5x7GxFFLqqbz1AJYYxSlrPCGu4N2og5YTExo4IcloNMQYFRkBGue0BKZ4w==", + "license": "GPL-3.0-only", + "optional": true + }, + "node_modules/@substrate/light-client-extension-helpers": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@substrate/light-client-extension-helpers/-/light-client-extension-helpers-1.0.0.tgz", + "integrity": "sha512-TdKlni1mBBZptOaeVrKnusMg/UBpWUORNDv5fdCaJklP4RJiFOzBCrzC+CyVI5kQzsXBisZ+2pXm+rIjS38kHg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@polkadot-api/json-rpc-provider": "^0.0.1", + "@polkadot-api/json-rpc-provider-proxy": "^0.1.0", + "@polkadot-api/observable-client": "^0.3.0", + "@polkadot-api/substrate-client": "^0.1.2", + "@substrate/connect-extension-protocol": "^2.0.0", + "@substrate/connect-known-chains": "^1.1.5", + "rxjs": "^7.8.1" + }, + "peerDependencies": { + "smoldot": "2.x" + } + }, + "node_modules/@substrate/ss58-registry": { + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/@substrate/ss58-registry/-/ss58-registry-1.51.0.tgz", + "integrity": "sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ==", + "license": "Apache-2.0" + }, + "node_modules/@types/bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-DLbJ1BPqxvQhIGbeu8VbUC1DiAiahHtAYvA0ZEAa4P31F7IaArc8z3C3BRQdWX4mtLQuABG4yzp76ZrS02Ui1Q==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.19.21", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.21.tgz", + "integrity": "sha512-VMeFBSCKQKmm2swI2kW51SFusDqekC6q9trBCvJ/JliDchFSuoYYKN7yVNjPthP1HKZcx3U1gI/wTcEBjEFKTA==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitest/expect": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-2.1.9.tgz", + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-2.1.9.tgz", + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz", + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.9", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-2.1.9.tgz", + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "license": "MIT" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/bn.js": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.3.tgz", + "integrity": "sha512-EAcmnPkxpntVL+DS7bO1zhcZNvCkxqtkd0ZY53h06GNQ3DEkkGZ/gKgmDv6DdZQGj9BgfSPKtJJ7Dp1GPP8f7w==", + "license": "MIT" + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/ethers": { + "version": "6.16.0", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.16.0.tgz", + "integrity": "sha512-U1wulmetNymijEhpSEQ7Ct/P/Jw9/e7R1j5XIbPRydgV2DjLVMsULDlNksq3RQnFgKoLlZf88ijYtWEXcPa07A==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "license": "MIT", + "dependencies": { + "@adraffy/ens-normalize": "1.10.1", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "22.7.5", + "aes-js": "4.0.0-beta.5", + "tslib": "2.7.0", + "ws": "8.17.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "license": "MIT", + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "license": "MIT", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "22.7.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.7.5.tgz", + "integrity": "sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.19.2" + } + }, + "node_modules/ethers/node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, + "node_modules/ethers/node_modules/undici-types": { + "version": "6.19.8", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", + "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "license": "MIT" + }, + "node_modules/ethers/node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "license": "MIT" + }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "license": "ISC" + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mock-socket": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/mock-socket/-/mock-socket-9.3.1.tgz", + "integrity": "sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/nock": { + "version": "13.5.6", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.5.6.tgz", + "integrity": "sha512-o2zOYiCpzRqSzPj0Zt/dQ/DqZeYoaQ7TUonc/xUPjCGl9WeHpNbxgVvOquXYAaJzI0M9BXV3HTzG0p8IUAbBTQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/rollup": { + "version": "4.61.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.61.1.tgz", + "integrity": "sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.61.1", + "@rollup/rollup-android-arm64": "4.61.1", + "@rollup/rollup-darwin-arm64": "4.61.1", + "@rollup/rollup-darwin-x64": "4.61.1", + "@rollup/rollup-freebsd-arm64": "4.61.1", + "@rollup/rollup-freebsd-x64": "4.61.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.61.1", + "@rollup/rollup-linux-arm-musleabihf": "4.61.1", + "@rollup/rollup-linux-arm64-gnu": "4.61.1", + "@rollup/rollup-linux-arm64-musl": "4.61.1", + "@rollup/rollup-linux-loong64-gnu": "4.61.1", + "@rollup/rollup-linux-loong64-musl": "4.61.1", + "@rollup/rollup-linux-ppc64-gnu": "4.61.1", + "@rollup/rollup-linux-ppc64-musl": "4.61.1", + "@rollup/rollup-linux-riscv64-gnu": "4.61.1", + "@rollup/rollup-linux-riscv64-musl": "4.61.1", + "@rollup/rollup-linux-s390x-gnu": "4.61.1", + "@rollup/rollup-linux-x64-gnu": "4.61.1", + "@rollup/rollup-linux-x64-musl": "4.61.1", + "@rollup/rollup-openbsd-x64": "4.61.1", + "@rollup/rollup-openharmony-arm64": "4.61.1", + "@rollup/rollup-win32-arm64-msvc": "4.61.1", + "@rollup/rollup-win32-ia32-msvc": "4.61.1", + "@rollup/rollup-win32-x64-gnu": "4.61.1", + "@rollup/rollup-win32-x64-msvc": "4.61.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/rxjs": { + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz", + "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/scale-ts": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/scale-ts/-/scale-ts-1.6.1.tgz", + "integrity": "sha512-PBMc2AWc6wSEqJYBDPcyCLUj9/tMKnLX70jLOSndMtcUoLQucP/DM0vnQo1wJAYjTrQiq8iG9rD0q6wFzgjH7g==", + "license": "MIT", + "optional": true + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/smoldot": { + "version": "2.0.26", + "resolved": "https://registry.npmjs.org/smoldot/-/smoldot-2.0.26.tgz", + "integrity": "sha512-F+qYmH4z2s2FK+CxGj8moYcd1ekSIKH8ywkdqlOz88Dat35iB1DIYL11aILN46YSGMzQW/lbJNS307zBSDN5Ig==", + "license": "GPL-3.0-or-later WITH Classpath-exception-2.0", + "optional": true, + "dependencies": { + "ws": "^8.8.1" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tsx": { + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.4.tgz", + "integrity": "sha512-X8EX+XV4QR5xCsrgxaED954zTDfY8KqlDtskKEL0cHhyS/P8b4IFOvGDQpsC9Q1XnLq915wEfwwY/zzskCtmhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-2.1.9.tgz", + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitest": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-2.1.9.tgz", + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.9", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/scripts/uniswap-v3-lark/package.json b/scripts/uniswap-v3-lark/package.json new file mode 100644 index 0000000000..372e3e6a08 --- /dev/null +++ b/scripts/uniswap-v3-lark/package.json @@ -0,0 +1,37 @@ +{ + "name": "uniswap-v3-lark", + "version": "1.0.0", + "private": true, + "type": "module", + "description": "Deploy and validate Uniswap v3 as a native router venue on Hydration (lark)", + "engines": { + "node": ">=20.6" + }, + "scripts": { + "cli": "tsx src/cli.ts", + "fund": "tsx src/cli.ts fund", + "whitelist": "tsx src/cli.ts whitelist", + "deploy": "tsx src/cli.ts deploy", + "seed": "tsx src/cli.ts seed", + "set-addresses": "tsx src/cli.ts set-addresses", + "preflight": "tsx src/cli.ts preflight", + "swap-test": "tsx src/cli.ts swap-test", + "typecheck": "tsc --noEmit", + "test": "vitest run" + }, + "dependencies": { + "@polkadot/api": "^16.4.1", + "@polkadot/util": "^14.0.0", + "@polkadot/util-crypto": "^14.0.0", + "commander": "^12.1.0", + "dotenv": "^16.4.7", + "ethers": "^6.13.5", + "zod": "^3.24.1" + }, + "devDependencies": { + "@types/node": "^22.10.5", + "tsx": "^4.19.2", + "typescript": "^5.7.3", + "vitest": "^2.1.8" + } +} diff --git a/scripts/uniswap-v3-lark/src/cli.ts b/scripts/uniswap-v3-lark/src/cli.ts new file mode 100644 index 0000000000..db3c442fed --- /dev/null +++ b/scripts/uniswap-v3-lark/src/cli.ts @@ -0,0 +1,61 @@ +import { Command } from 'commander' +import { deploy } from './commands/deploy' +import { fund } from './commands/fund' +import { preflight } from './commands/preflight' +import { seed } from './commands/seed' +import { seedConcentrated } from './commands/seedConcentrated' +import { setAddresses } from './commands/setAddresses' +import { swapTest } from './commands/swapTest' +import { whitelist } from './commands/whitelist' + +function run(action: () => Promise): () => Promise { + return async () => { + try { + await action() + process.exit(0) + } catch (error) { + console.error('FAIL:', error instanceof Error ? error.message : error) + process.exit(1) + } + } +} + +const program = new Command() +program + .name('uniswap-v3-lark') + .description('Deploy and validate Uniswap v3 as a native router venue on Hydration (lark)') + +program + .command('fund') + .description('mint gas + pool assets to the deployer (Root referendum)') + .action(run(fund)) +program + .command('whitelist') + .description('whitelist the deployer for EVM contract creation (Root referendum)') + .action(run(whitelist)) +program + .command('deploy') + .description('deploy the Uniswap v3 stack via @uniswap/deploy-v3') + .action(run(deploy)) +program + .command('seed') + .description('create + initialize + fund the pool over precompile tokens') + .action(run(seed)) +program + .command('seed-concentrated') + .description('create + seed a concentrated-liquidity v3 pool (nested narrow positions)') + .action(run(seedConcentrated)) +program + .command('set-addresses') + .description('set the runtime parameters to the deployed contracts (Root referendum)') + .action(run(setAddresses)) +program + .command('preflight') + .description('check readiness for the swap test') + .action(run(preflight)) +program + .command('swap-test') + .description('run the mixed Omnipool + Uniswap v3 router swap') + .action(run(swapTest)) + +program.parseAsync(process.argv) diff --git a/scripts/uniswap-v3-lark/src/commands/deploy.ts b/scripts/uniswap-v3-lark/src/commands/deploy.ts new file mode 100644 index 0000000000..3aa3ef1c36 --- /dev/null +++ b/scripts/uniswap-v3-lark/src/commands/deploy.ts @@ -0,0 +1,43 @@ +import { spawn } from 'node:child_process' +import { existsSync, readFileSync } from 'node:fs' +import { resolve } from 'node:path' +import { Wallet } from 'ethers' +import { config, packageRoot } from '../config' +import { assetToEvmAddress } from '../core/assets' + +export async function deploy(): Promise { + if (!existsSync(config.deployCli)) { + throw new Error(`uniswap deploy CLI not found at ${config.deployCli} — set UNISWAP_DEPLOY_CLI`) + } + + const owner = new Wallet(config.evmPrivateKey).address + const statePath = resolve(packageRoot, 'state.json') + const weth9 = assetToEvmAddress(config.gasAssetId) + + const args = [ + config.deployCli, + '--private-key', config.evmPrivateKey, + '--json-rpc', config.rpcEvm, + '--weth9-address', weth9, + '--native-currency-label', 'WETH', + '--owner-address', owner, + '--state', statePath, + '--confirmations', '3', + ] + + console.log(`deploying uniswap v3 stack via ${config.deployCli}`) + console.log(`state -> ${statePath}`) + + await new Promise((resolvePromise, reject) => { + const child = spawn('node', args, { stdio: 'inherit' }) + child.on('error', reject) + child.on('exit', (code) => (code === 0 ? resolvePromise() : reject(new Error(`deploy exited with code ${code}`)))) + }) + + const state = JSON.parse(readFileSync(statePath, 'utf8')) as Record + console.log('\ndeploy complete — set these in .env:') + console.log(`UNISWAP_V3_FACTORY=${state['v3CoreFactoryAddress']}`) + console.log(`UNISWAP_V3_SWAP_ROUTER=${state['swapRouter02']}`) + console.log(`UNISWAP_V3_QUOTER=${state['quoterV2Address']}`) + console.log(`UNISWAP_V3_NPM=${state['nonfungibleTokenPositionManagerAddress']}`) +} diff --git a/scripts/uniswap-v3-lark/src/commands/fund.ts b/scripts/uniswap-v3-lark/src/commands/fund.ts new file mode 100644 index 0000000000..844f69c327 --- /dev/null +++ b/scripts/uniswap-v3-lark/src/commands/fund.ts @@ -0,0 +1,45 @@ +import { Wallet } from 'ethers' +import { config } from '../config' +import { truncatedAccountId } from '../core/assets' +import { connectApi, freeBalance, keyringFromSuri, submitRootReferendum } from '../core/substrate' + +export async function fund(): Promise { + const evmAddress = new Wallet(config.evmPrivateKey).address + const dest = truncatedAccountId(evmAddress) + console.log(`deployer EVM ${evmAddress}`) + console.log(`mapped substrate ${dest}`) + + const api = await connectApi(config.rpcWs) + try { + const signer = await keyringFromSuri(config.suri) + console.log(`gov signer ${signer.address}`) + + const verify = async (): Promise => { + const [gas, a, b] = await Promise.all([ + freeBalance(api, dest, config.gasAssetId), + freeBalance(api, dest, config.tokenA), + freeBalance(api, dest, config.tokenB), + ]) + return gas >= config.fundGasAmount && a >= config.fundAmountA && b >= config.fundAmountB + } + + if (await verify()) { + console.log('deployer already funded') + return + } + + const calls = [ + api.tx.currencies.updateBalance(dest, config.gasAssetId, config.fundGasAmount.toString()), + api.tx.currencies.updateBalance(dest, config.tokenA, config.fundAmountA.toString()), + api.tx.currencies.updateBalance(dest, config.tokenB, config.fundAmountB.toString()), + ] + await submitRootReferendum(api, signer, api.tx.utility.batchAll(calls), { + voteHdx: config.voteHdx, + enactAfter: config.enactAfter, + verify, + }) + console.log('deployer funded') + } finally { + await api.disconnect() + } +} diff --git a/scripts/uniswap-v3-lark/src/commands/preflight.ts b/scripts/uniswap-v3-lark/src/commands/preflight.ts new file mode 100644 index 0000000000..e2be6866da --- /dev/null +++ b/scripts/uniswap-v3-lark/src/commands/preflight.ts @@ -0,0 +1,66 @@ +import { Contract } from 'ethers' +import { config } from '../config' +import { FACTORY_ABI } from '../core/abis' +import { assetToEvmAddress, sortTokens } from '../core/assets' +import { evmProvider } from '../core/evm' +import { connectApi, freeBalance, keyringFromSuri } from '../core/substrate' + +const mark = (ready: boolean): string => (ready ? 'OK ' : 'XX ') + +export async function preflight(): Promise { + const api = await connectApi(config.rpcWs) + const call = api.call as any + const tx = api.tx as any + const events = api.events as any + + try { + const signer = await keyringFromSuri(config.suri) + + console.log('=== runtime metadata ===') + console.log(`${mark(!!call.uniswapV3Api?.pool)} api.call.uniswapV3Api.pool`) + console.log(`${mark(!!tx.parameters?.setUniswapV3Addresses)} tx.parameters.setUniswapV3Addresses`) + console.log(`${mark(!!tx.router?.sell)} tx.router.sell`) + console.log(`${mark(!!events.broadcast?.Swapped3)} events.broadcast.Swapped3`) + + console.log('=== parameters ===') + const factory = (await api.query.parameters.uniswapV3Factory()) as any + const router = (await api.query.parameters.uniswapV3SwapRouter()) as any + const quoter = (await api.query.parameters.uniswapV3Quoter()) as any + console.log(`factory: ${factory.isSome ? factory.unwrap().toHex() : 'NOT SET'}`) + console.log(`router : ${router.isSome ? router.unwrap().toHex() : 'NOT SET'}`) + console.log(`quoter : ${quoter.isSome ? quoter.unwrap().toHex() : 'NOT SET'}`) + + console.log('=== omnipool leg ===') + for (const id of [config.sellAssetIn, config.tokenA]) { + const state = (await api.query.omnipool.assets(id)) as any + console.log(`asset ${id}: ${state.isSome ? `tradable ${state.unwrap().tradable.toString()}` : 'NOT IN OMNIPOOL'}`) + } + + console.log('=== signer ===') + console.log(`${signer.address} HDX ${await freeBalance(api, signer.address, 0)}`) + + if (factory.isSome) { + console.log('=== runtime quote (QuoterV2 via staticcall) ===') + const pool = (await call.uniswapV3Api.pool(config.tokenA, config.quoteAsset, config.fee)) as any + console.log(`pool(${config.tokenA},${config.quoteAsset},${config.fee}): ${pool.isSome ? pool.unwrap().toHex() : 'NONE'}`) + const probe = config.amountA / 1000n + const out = (await call.uniswapV3Api.quoteSell( + config.tokenA, + config.quoteAsset, + config.fee, + probe.toString(), + )) as any + console.log(`quoteSell ${probe} ${config.tokenA}->${config.quoteAsset}: ${out.isSome ? out.unwrap().toString() : 'NONE'}`) + } + + if (config.factory) { + console.log('=== v3 pool (EVM) ===') + const provider = evmProvider(config.rpcEvm) + const factoryContract = new Contract(config.factory, FACTORY_ABI, provider) + const [t0, t1] = sortTokens(assetToEvmAddress(config.tokenA), assetToEvmAddress(config.tokenB)) + console.log(`getPool: ${await factoryContract.getPool(t0, t1, config.fee)}`) + } + } finally { + await api.disconnect() + } +} diff --git a/scripts/uniswap-v3-lark/src/commands/seed.ts b/scripts/uniswap-v3-lark/src/commands/seed.ts new file mode 100644 index 0000000000..a055c0ffb8 --- /dev/null +++ b/scripts/uniswap-v3-lark/src/commands/seed.ts @@ -0,0 +1,79 @@ +import { Contract, ZeroAddress } from 'ethers' +import { config, requireDeployed } from '../config' +import { ERC20_ABI, FACTORY_ABI, NPM_ABI, POOL_ABI } from '../core/abis' +import { assetToEvmAddress, sortTokens, truncatedAccountId } from '../core/assets' +import { SequentialTxRunner, evmProvider, evmWallet } from '../core/evm' +import { FEE_TICK_SPACING, fullRangeTicks, sqrtPriceX96FromAmounts } from '../core/math' + +export async function seed(): Promise { + const { factory: factoryAddress, npm: npmAddress } = requireDeployed(config) + + const provider = evmProvider(config.rpcEvm) + const wallet = evmWallet(config.evmPrivateKey, provider) + + const addrA = assetToEvmAddress(config.tokenA) + const addrB = assetToEvmAddress(config.tokenB) + const [token0, token1] = sortTokens(addrA, addrB) + const isA0 = token0 === addrA + const amount0 = isA0 ? config.amountA : config.amountB + const amount1 = isA0 ? config.amountB : config.amountA + + console.log(`deployer ${wallet.address} (mapped ${truncatedAccountId(wallet.address)})`) + console.log(`token0 ${token0} amount0 ${amount0}`) + console.log(`token1 ${token1} amount1 ${amount1}`) + console.log(`fee ${config.fee} spacing ${FEE_TICK_SPACING[config.fee]}`) + + const erc0 = new Contract(token0, ERC20_ABI, wallet) + const erc1 = new Contract(token1, ERC20_ABI, wallet) + const [bal0, bal1, gas] = await Promise.all([ + erc0.balanceOf(wallet.address) as Promise, + erc1.balanceOf(wallet.address) as Promise, + provider.getBalance(wallet.address), + ]) + console.log(`balance0 ${bal0} balance1 ${bal1} gas ${gas}`) + if (bal0 < amount0 || bal1 < amount1 || gas === 0n) { + throw new Error('insufficient deployer balance/gas — run `fund` first') + } + + const runner = new SequentialTxRunner(wallet, config.evmGasLimit) + await runner.init() + + const factory = new Contract(factoryAddress, FACTORY_ABI, wallet) + let pool: string = await factory.getPool(token0, token1, config.fee) + if (pool === ZeroAddress) { + console.log('creating pool...') + await runner.confirm('createPool', factory.createPool(token0, token1, config.fee, runner.next())) + pool = await factory.getPool(token0, token1, config.fee) + } + if (pool === ZeroAddress) throw new Error('pool creation failed') + console.log(`pool ${pool}`) + + const poolContract = new Contract(pool, POOL_ABI, wallet) + let sqrtNow = 0n + try { + sqrtNow = (await poolContract.slot0())[0] as bigint + } catch { + sqrtNow = 0n + } + if (sqrtNow === 0n) { + const sqrtPrice = sqrtPriceX96FromAmounts(amount0, amount1) + console.log(`initialize sqrtPriceX96 ${sqrtPrice}`) + await runner.confirm('initialize', poolContract.initialize(sqrtPrice, runner.next())) + } else { + console.log(`already initialized sqrtPriceX96 ${sqrtNow}`) + } + + console.log('approving NPM...') + await runner.confirm('approve0', erc0.approve(npmAddress, amount0, runner.next())) + await runner.confirm('approve1', erc1.approve(npmAddress, amount1, runner.next())) + + const [tickLower, tickUpper] = fullRangeTicks(config.fee) + const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600) + const params = [token0, token1, config.fee, tickLower, tickUpper, amount0, amount1, 0n, 0n, wallet.address, deadline] + const npm = new Contract(npmAddress, NPM_ABI, wallet) + console.log(`mint full-range [${tickLower}, ${tickUpper}]...`) + await runner.confirm('mint', npm.mint(params, runner.next())) + + const liquidity = (await poolContract.liquidity()) as bigint + console.log(`pool ${pool} liquidity ${liquidity}`) +} diff --git a/scripts/uniswap-v3-lark/src/commands/seedConcentrated.ts b/scripts/uniswap-v3-lark/src/commands/seedConcentrated.ts new file mode 100644 index 0000000000..5882a1a96b --- /dev/null +++ b/scripts/uniswap-v3-lark/src/commands/seedConcentrated.ts @@ -0,0 +1,120 @@ +import { Contract, ZeroAddress } from 'ethers' +import { config, requireDeployed } from '../config' +import { ERC20_ABI, FACTORY_ABI, NPM_ABI, POOL_ABI } from '../core/abis' +import { assetToEvmAddress, sortTokens, truncatedAccountId } from '../core/assets' +import { SequentialTxRunner, evmProvider, evmWallet } from '../core/evm' +import { FEE_TICK_SPACING, sqrtPriceX96FromAmounts } from '../core/math' + +const CONC_FEE = 500 + +const POSITIONS = [ + { tickLower: -200, tickUpper: 200, amount0: 3n * 10n ** 23n, amount1: 3n * 10n ** 23n }, + { tickLower: -2000, tickUpper: 2000, amount0: 1n * 10n ** 23n, amount1: 1n * 10n ** 23n }, +] + +const POOL_TICKS_ABI = [ + ...POOL_ABI, + 'function ticks(int24 tick) view returns (uint128 liquidityGross, int128 liquidityNet, uint256 feeGrowthOutside0X128, uint256 feeGrowthOutside1X128, int56 tickCumulativeOutside, uint160 secondsPerLiquidityOutsideX128, uint32 secondsOutside, bool initialized)', +] + +export async function seedConcentrated(): Promise { + const { factory: factoryAddress, npm: npmAddress } = requireDeployed(config) + const spacing = FEE_TICK_SPACING[CONC_FEE] + if (spacing === undefined) throw new Error(`unknown fee tier: ${CONC_FEE}`) + for (const p of POSITIONS) { + if (p.tickLower % spacing !== 0 || p.tickUpper % spacing !== 0) { + throw new Error(`tick not aligned to spacing ${spacing}: [${p.tickLower}, ${p.tickUpper}]`) + } + } + + const provider = evmProvider(config.rpcEvm) + const wallet = evmWallet(config.evmPrivateKey, provider) + + const addrA = assetToEvmAddress(config.tokenA) + const addrB = assetToEvmAddress(config.tokenB) + const [token0, token1] = sortTokens(addrA, addrB) + + const total0 = POSITIONS.reduce((a, p) => a + p.amount0, 0n) + const total1 = POSITIONS.reduce((a, p) => a + p.amount1, 0n) + + console.log(`deployer ${wallet.address} (mapped ${truncatedAccountId(wallet.address)})`) + console.log(`pair token0 ${token0} token1 ${token1} | fee ${CONC_FEE} spacing ${spacing}`) + console.log(`positions ${POSITIONS.map((p) => `[${p.tickLower},${p.tickUpper}]`).join(' ')}`) + + const erc0 = new Contract(token0, ERC20_ABI, wallet) + const erc1 = new Contract(token1, ERC20_ABI, wallet) + const [bal0, bal1, gas] = await Promise.all([ + erc0.balanceOf(wallet.address) as Promise, + erc1.balanceOf(wallet.address) as Promise, + provider.getBalance(wallet.address), + ]) + console.log(`balance0 ${bal0} balance1 ${bal1} gas ${gas}`) + if (bal0 < total0 || bal1 < total1 || gas === 0n) { + throw new Error('insufficient deployer balance/gas — run `fund` first') + } + + const runner = new SequentialTxRunner(wallet, config.evmGasLimit) + await runner.init() + + const factory = new Contract(factoryAddress, FACTORY_ABI, wallet) + let pool: string = await factory.getPool(token0, token1, CONC_FEE) + if (pool === ZeroAddress) { + console.log('creating pool...') + await runner.confirm('createPool', factory.createPool(token0, token1, CONC_FEE, runner.next())) + pool = await factory.getPool(token0, token1, CONC_FEE) + } + if (pool === ZeroAddress) throw new Error('pool creation failed') + console.log(`pool ${pool}`) + + const poolContract = new Contract(pool, POOL_TICKS_ABI, wallet) + let sqrtNow = 0n + try { + sqrtNow = (await poolContract.slot0())[0] as bigint + } catch { + sqrtNow = 0n + } + if (sqrtNow === 0n) { + const sqrtPrice = sqrtPriceX96FromAmounts(1n, 1n) + console.log(`initialize sqrtPriceX96 ${sqrtPrice}`) + await runner.confirm('initialize', poolContract.initialize(sqrtPrice, runner.next())) + } else { + console.log(`already initialized sqrtPriceX96 ${sqrtNow}`) + } + + console.log('approving NPM...') + await runner.confirm('approve0', erc0.approve(npmAddress, total0, runner.next())) + await runner.confirm('approve1', erc1.approve(npmAddress, total1, runner.next())) + + const npm = new Contract(npmAddress, NPM_ABI, wallet) + const deadline = BigInt(Math.floor(Date.now() / 1000) + 3600) + for (const p of POSITIONS) { + const params = [ + token0, + token1, + CONC_FEE, + p.tickLower, + p.tickUpper, + p.amount0, + p.amount1, + 0n, + 0n, + wallet.address, + deadline, + ] + console.log(`mint [${p.tickLower}, ${p.tickUpper}]...`) + await runner.confirm(`mint[${p.tickLower},${p.tickUpper}]`, npm.mint(params, runner.next())) + } + + const [slot0, liquidity] = await Promise.all([ + poolContract.slot0(), + poolContract.liquidity() as Promise, + ]) + console.log('\n=== seeded concentrated pool ===') + console.log(`pool ${pool} fee ${CONC_FEE}`) + console.log(`sqrtPriceX96 ${slot0[0]} tick ${slot0[1]}`) + console.log(`in-range liquidity ${liquidity}`) + for (const t of [-2000, -200, 200, 2000]) { + const info = await poolContract.ticks(t) + console.log(`tick ${t}: liquidityNet ${info[1]} initialized ${info[7]}`) + } +} diff --git a/scripts/uniswap-v3-lark/src/commands/setAddresses.ts b/scripts/uniswap-v3-lark/src/commands/setAddresses.ts new file mode 100644 index 0000000000..d846ab4615 --- /dev/null +++ b/scripts/uniswap-v3-lark/src/commands/setAddresses.ts @@ -0,0 +1,34 @@ +import { config, requireDeployed } from '../config' +import { connectApi, keyringFromSuri, submitRootReferendum } from '../core/substrate' + +export async function setAddresses(): Promise { + const { factory, swapRouter, quoter } = requireDeployed(config) + + const api = await connectApi(config.rpcWs) + try { + const signer = await keyringFromSuri(config.suri) + console.log(`factory ${factory}`) + console.log(`router ${swapRouter}`) + console.log(`quoter ${quoter}`) + + const verify = async (): Promise => { + const stored = (await api.query.parameters.uniswapV3Factory()) as any + return stored.isSome && stored.unwrap().toHex().toLowerCase() === factory.toLowerCase() + } + + if (await verify()) { + console.log('addresses already set') + return + } + + await submitRootReferendum( + api, + signer, + api.tx.parameters.setUniswapV3Addresses(factory, swapRouter, quoter), + { voteHdx: config.voteHdx, enactAfter: config.enactAfter, verify }, + ) + console.log('addresses set') + } finally { + await api.disconnect() + } +} diff --git a/scripts/uniswap-v3-lark/src/commands/swapTest.ts b/scripts/uniswap-v3-lark/src/commands/swapTest.ts new file mode 100644 index 0000000000..960ba37e4d --- /dev/null +++ b/scripts/uniswap-v3-lark/src/commands/swapTest.ts @@ -0,0 +1,94 @@ +import { config } from '../config' +import { connectApi, freeBalance, keyringFromSuri, signAndSend } from '../core/substrate' + +interface Candidate { + mid: number + fee: number + pool: string +} + +function pick(items: T[]): T { + const chosen = items[Math.floor(Math.random() * items.length)] + if (chosen === undefined) throw new Error('cannot pick from empty list') + return chosen +} + +export async function swapTest(): Promise { + const api = await connectApi(config.rpcWs) + const call = api.call as any + const events = api.events as any + + try { + const signer = await keyringFromSuri(config.suri) + console.log(`signer ${signer.address}`) + + const factory = (await api.query.parameters.uniswapV3Factory()) as any + if (factory.isNone) throw new Error('uniswap v3 addresses not set — run `set-addresses`') + console.log(`v3 factory ${factory.unwrap().toHex()}`) + + const keys = await api.query.omnipool.assets.keys() + const omnipoolAssets = keys.map((k) => Number((k.args[0] as any).toString())) + console.log(`omnipool assets: ${omnipoolAssets.join(', ')}`) + if (!omnipoolAssets.includes(config.sellAssetIn)) { + throw new Error(`SELL_ASSET_IN ${config.sellAssetIn} not in omnipool`) + } + + const candidates: Candidate[] = [] + for (const mid of omnipoolAssets) { + if (mid === config.quoteAsset || mid === config.sellAssetIn) continue + for (const fee of config.fees) { + const pool = (await call.uniswapV3Api.pool(mid, config.quoteAsset, fee)) as any + if (pool.isSome) { + const address = pool.unwrap().toHex() + candidates.push({ mid, fee, pool: address }) + console.log(` v3 pool: ${mid}/${config.quoteAsset} fee ${fee} -> ${address}`) + } + } + } + if (candidates.length === 0) throw new Error('no v3 pools discovered into the quote asset — run `seed`') + + const chosen = pick(candidates) + console.log( + `\nrandom path: ${config.sellAssetIn} --[Omnipool]--> ${chosen.mid} --[UniswapV3 ${chosen.fee}]--> ${config.quoteAsset}`, + ) + + const route = [ + { pool: 'Omnipool', assetIn: config.sellAssetIn, assetOut: chosen.mid }, + { pool: { UniswapV3: chosen.fee }, assetIn: chosen.mid, assetOut: config.quoteAsset }, + ] + + const before = await freeBalance(api, signer.address, config.quoteAsset) + const result = await signAndSend( + api, + api.tx.router.sell(config.sellAssetIn, config.quoteAsset, config.sellAmount.toString(), '0', route as any), + signer, + `router.sell ${config.sellAmount} of ${config.sellAssetIn} -> ${config.quoteAsset}`, + ) + const after = await freeBalance(api, signer.address, config.quoteAsset) + + let sawUniswap = false + let sawOmnipool = false + for (const { event } of result.events) { + if (events.broadcast?.Swapped3?.is(event)) { + const data = event.data as any + const fillerType = data.fillerType ?? data[2] + const filler = fillerType.type ?? fillerType.toString() + if (filler === 'UniswapV3') sawUniswap = true + if (filler === 'Omnipool') sawOmnipool = true + console.log(` Swapped3 filler ${filler}`) + } + } + + console.log(`\n${config.quoteAsset} balance: ${before} -> ${after} (delta ${after - before})`) + console.log(`v3 leg emitted: ${sawUniswap}`) + console.log(`omnipool leg emitted: ${sawOmnipool}`) + + if (after > before && sawUniswap) { + console.log('\nPASS: mixed swap routed through Uniswap v3') + return + } + throw new Error('expected positive output and a UniswapV3 Swapped3 event') + } finally { + await api.disconnect() + } +} diff --git a/scripts/uniswap-v3-lark/src/commands/whitelist.ts b/scripts/uniswap-v3-lark/src/commands/whitelist.ts new file mode 100644 index 0000000000..a2e9f99f22 --- /dev/null +++ b/scripts/uniswap-v3-lark/src/commands/whitelist.ts @@ -0,0 +1,29 @@ +import { Wallet } from 'ethers' +import { config } from '../config' +import { connectApi, keyringFromSuri, submitRootReferendum } from '../core/substrate' + +export async function whitelist(): Promise { + const evmAddress = new Wallet(config.evmPrivateKey).address + console.log(`deployer ${evmAddress}`) + + const api = await connectApi(config.rpcWs) + try { + const signer = await keyringFromSuri(config.suri) + const verify = async (): Promise => + ((await api.query.evmAccounts.contractDeployer(evmAddress)) as any).isSome + + if (await verify()) { + console.log('already whitelisted') + return + } + + await submitRootReferendum(api, signer, api.tx.evmAccounts.addContractDeployer(evmAddress), { + voteHdx: config.voteHdx, + enactAfter: config.enactAfter, + verify, + }) + console.log('deployer whitelisted') + } finally { + await api.disconnect() + } +} diff --git a/scripts/uniswap-v3-lark/src/config.ts b/scripts/uniswap-v3-lark/src/config.ts new file mode 100644 index 0000000000..adf599bf44 --- /dev/null +++ b/scripts/uniswap-v3-lark/src/config.ts @@ -0,0 +1,93 @@ +import { config as loadEnv } from 'dotenv' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import { z } from 'zod' + +export const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..') +loadEnv({ path: resolve(packageRoot, '.env') }) + +const ANVIL_KEY = '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80' + +const Raw = z.object({ + RPC_WS: z.string().url().default('wss://1.lark.hydration.cloud'), + RPC_EVM: z.string().url().default('https://1.lark.hydration.cloud'), + SURI: z.string().default('//Alice'), + EVM_PRIVATE_KEY: z + .string() + .regex(/^0x[0-9a-fA-F]{64}$/, 'EVM_PRIVATE_KEY must be 0x + 64 hex') + .default(ANVIL_KEY), + + UNISWAP_V3_FACTORY: z.string().default(''), + UNISWAP_V3_SWAP_ROUTER: z.string().default(''), + UNISWAP_V3_QUOTER: z.string().default(''), + UNISWAP_V3_NPM: z.string().default(''), + UNISWAP_DEPLOY_CLI: z.string().default(''), + + TOKEN_A: z.coerce.number().int().default(16), + TOKEN_B: z.coerce.number().int().default(9), + FEE: z.coerce.number().int().default(3000), + AMOUNT_A: z.string().default('1000000000000000000000000'), + AMOUNT_B: z.string().default('1000000000000000000000000'), + + GAS_ASSET_ID: z.coerce.number().int().default(20), + FUND_GAS_AMOUNT: z.string().default('100000000000000000000'), + FUND_AMOUNT_A: z.string().default('2000000000000000000000000'), + FUND_AMOUNT_B: z.string().default('2000000000000000000000000'), + + SELL_ASSET_IN: z.coerce.number().int().default(0), + QUOTE_ASSET: z.coerce.number().int().default(9), + SELL_AMOUNT: z.string().default('1000000000000000'), + FEES: z.string().default('500,3000,10000'), + + VOTE_HDX: z.coerce.number().int().default(3_000_000_000), + ENACT_AFTER: z.coerce.number().int().default(10), + EVM_GAS_LIMIT: z.string().default('15000000'), +}) + +const raw = Raw.parse(process.env) +const optional = (s: string): string | undefined => (s.trim() === '' ? undefined : s.trim()) + +export const config = { + rpcWs: raw.RPC_WS, + rpcEvm: raw.RPC_EVM, + suri: raw.SURI, + evmPrivateKey: raw.EVM_PRIVATE_KEY, + factory: optional(raw.UNISWAP_V3_FACTORY), + swapRouter: optional(raw.UNISWAP_V3_SWAP_ROUTER), + quoter: optional(raw.UNISWAP_V3_QUOTER), + npm: optional(raw.UNISWAP_V3_NPM), + deployCli: optional(raw.UNISWAP_DEPLOY_CLI) ?? resolve(packageRoot, '../../../uniswap-v3-deploy/dist/index.js'), + tokenA: raw.TOKEN_A, + tokenB: raw.TOKEN_B, + fee: raw.FEE, + amountA: BigInt(raw.AMOUNT_A), + amountB: BigInt(raw.AMOUNT_B), + gasAssetId: raw.GAS_ASSET_ID, + fundGasAmount: BigInt(raw.FUND_GAS_AMOUNT), + fundAmountA: BigInt(raw.FUND_AMOUNT_A), + fundAmountB: BigInt(raw.FUND_AMOUNT_B), + sellAssetIn: raw.SELL_ASSET_IN, + quoteAsset: raw.QUOTE_ASSET, + sellAmount: BigInt(raw.SELL_AMOUNT), + fees: raw.FEES.split(',').map((s) => Number(s.trim())), + voteHdx: BigInt(raw.VOTE_HDX), + enactAfter: raw.ENACT_AFTER, + evmGasLimit: BigInt(raw.EVM_GAS_LIMIT), +} as const + +export type Config = typeof config + +export interface DeployedAddresses { + factory: string + swapRouter: string + quoter: string + npm: string +} + +export function requireDeployed(c: Config): DeployedAddresses { + const missing = (['factory', 'swapRouter', 'quoter', 'npm'] as const).filter((k) => !c[k]) + if (missing.length > 0) { + throw new Error(`missing deployed addresses in .env: ${missing.join(', ')} — run \`deploy\` first`) + } + return { factory: c.factory!, swapRouter: c.swapRouter!, quoter: c.quoter!, npm: c.npm! } +} diff --git a/scripts/uniswap-v3-lark/src/core/abis.ts b/scripts/uniswap-v3-lark/src/core/abis.ts new file mode 100644 index 0000000000..3ca6de06f3 --- /dev/null +++ b/scripts/uniswap-v3-lark/src/core/abis.ts @@ -0,0 +1,22 @@ +export const ERC20_ABI = [ + 'function approve(address spender, uint256 amount) returns (bool)', + 'function allowance(address owner, address spender) view returns (uint256)', + 'function balanceOf(address account) view returns (uint256)', +] + +export const FACTORY_ABI = [ + 'function getPool(address tokenA, address tokenB, uint24 fee) view returns (address)', + 'function createPool(address tokenA, address tokenB, uint24 fee) returns (address)', +] + +export const POOL_ABI = [ + 'function initialize(uint160 sqrtPriceX96)', + 'function slot0() view returns (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint8 feeProtocol, bool unlocked)', + 'function liquidity() view returns (uint128)', + 'function token0() view returns (address)', + 'function token1() view returns (address)', +] + +export const NPM_ABI = [ + 'function mint((address token0, address token1, uint24 fee, int24 tickLower, int24 tickUpper, uint256 amount0Desired, uint256 amount1Desired, uint256 amount0Min, uint256 amount1Min, address recipient, uint256 deadline)) payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1)', +] diff --git a/scripts/uniswap-v3-lark/src/core/assets.ts b/scripts/uniswap-v3-lark/src/core/assets.ts new file mode 100644 index 0000000000..3ed689a448 --- /dev/null +++ b/scripts/uniswap-v3-lark/src/core/assets.ts @@ -0,0 +1,18 @@ +import { getAddress } from 'ethers' + +export function assetToEvmAddress(assetId: number): string { + if (!Number.isInteger(assetId) || assetId < 0) { + throw new Error(`invalid asset id: ${assetId}`) + } + const value = 0x0100000000n + BigInt(assetId) + return getAddress('0x' + value.toString(16).padStart(40, '0')) +} + +export function truncatedAccountId(evmAddress: string): string { + const body = evmAddress.toLowerCase().replace(/^0x/, '').padStart(40, '0') + return '0x45544800' + body + '0000000000000000' +} + +export function sortTokens(a: string, b: string): [string, string] { + return a.toLowerCase() < b.toLowerCase() ? [a, b] : [b, a] +} diff --git a/scripts/uniswap-v3-lark/src/core/evm.ts b/scripts/uniswap-v3-lark/src/core/evm.ts new file mode 100644 index 0000000000..ab33f312d5 --- /dev/null +++ b/scripts/uniswap-v3-lark/src/core/evm.ts @@ -0,0 +1,43 @@ +import { JsonRpcProvider, Wallet, type ContractTransactionResponse } from 'ethers' + +export function evmProvider(rpc: string): JsonRpcProvider { + return new JsonRpcProvider(rpc, undefined, { batchMaxCount: 1 }) +} + +export function evmWallet(privateKey: string, provider: JsonRpcProvider): Wallet { + return new Wallet(privateKey, provider) +} + +export interface TxOverrides { + gasLimit: bigint + nonce: number +} + +export class SequentialTxRunner { + private nonce = -1 + + constructor( + private readonly wallet: Wallet, + private readonly gasLimit: bigint, + ) {} + + async init(): Promise { + const provider = this.wallet.provider + if (!provider) throw new Error('wallet has no provider') + this.nonce = await provider.getTransactionCount(this.wallet.address) + } + + next(): TxOverrides { + if (this.nonce < 0) throw new Error('SequentialTxRunner.init() not called') + return { gasLimit: this.gasLimit, nonce: this.nonce++ } + } + + async confirm(label: string, sent: Promise): Promise { + const tx = await sent + const receipt = await tx.wait() + if (!receipt || receipt.status !== 1) { + throw new Error(`${label} reverted (tx ${tx.hash})`) + } + console.log(` ${label} tx ${receipt.hash} status ${receipt.status}`) + } +} diff --git a/scripts/uniswap-v3-lark/src/core/math.ts b/scripts/uniswap-v3-lark/src/core/math.ts new file mode 100644 index 0000000000..4ccb6a9a8d --- /dev/null +++ b/scripts/uniswap-v3-lark/src/core/math.ts @@ -0,0 +1,32 @@ +export const FEE_TICK_SPACING: Readonly> = { + 100: 1, + 500: 10, + 3000: 60, + 10000: 200, +} + +export const MIN_TICK = -887272 +export const MAX_TICK = 887272 + +export function isqrt(n: bigint): bigint { + if (n < 0n) throw new Error('isqrt of negative') + if (n < 2n) return n + let x = n + let y = (x + 1n) / 2n + while (y < x) { + x = y + y = (x + n / x) / 2n + } + return x +} + +export function sqrtPriceX96FromAmounts(amount0: bigint, amount1: bigint): bigint { + if (amount0 <= 0n) throw new Error('amount0 must be positive') + return isqrt((amount1 << 192n) / amount0) +} + +export function fullRangeTicks(fee: number): [number, number] { + const spacing = FEE_TICK_SPACING[fee] + if (spacing === undefined) throw new Error(`unknown fee tier: ${fee}`) + return [Math.ceil(MIN_TICK / spacing) * spacing, Math.floor(MAX_TICK / spacing) * spacing] +} diff --git a/scripts/uniswap-v3-lark/src/core/substrate.ts b/scripts/uniswap-v3-lark/src/core/substrate.ts new file mode 100644 index 0000000000..6ffb392408 --- /dev/null +++ b/scripts/uniswap-v3-lark/src/core/substrate.ts @@ -0,0 +1,146 @@ +import { ApiPromise, WsProvider } from '@polkadot/api' +import { Keyring } from '@polkadot/keyring' +import type { KeyringPair } from '@polkadot/keyring/types' +import type { SubmittableExtrinsic } from '@polkadot/api/types' +import type { ISubmittableResult } from '@polkadot/types/types' +import { cryptoWaitReady } from '@polkadot/util-crypto' + +const HDX_DECIMALS = 12n + +export async function connectApi(ws: string): Promise { + if (!ws) throw new Error('empty ws endpoint') + const api = await ApiPromise.create({ provider: new WsProvider(ws) }) + await api.isReady + return api +} + +export async function keyringFromSuri(suri: string): Promise { + await cryptoWaitReady() + return new Keyring({ type: 'sr25519', ss58Format: 63 }).addFromUri(suri) +} + +export async function freeBalance(api: ApiPromise, address: string, assetId: number): Promise { + if (assetId === 0) { + const account = (await api.query.system.account(address)) as any + return BigInt(account.data.free.toString()) + } + const account = (await api.query.tokens.accounts(address, assetId)) as any + return BigInt(account.free.toString()) +} + +export function decodeError(api: ApiPromise, error: any): string { + if (error.isModule) { + const meta = api.registry.findMetaError(error.asModule) + return `${meta.section}.${meta.name}: ${meta.docs.join(' ')}` + } + return error.toString() +} + +export function signAndSend( + api: ApiPromise, + tx: SubmittableExtrinsic<'promise'>, + signer: KeyringPair, + label: string, +): Promise { + console.log(`-> ${label}`) + return new Promise((resolve, reject) => { + tx.signAndSend(signer, (result) => { + const { status, dispatchError } = result + if (dispatchError) { + reject(new Error(decodeError(api, dispatchError))) + return + } + if (status.isInBlock) { + console.log(` in block ${status.asInBlock.toHex()}`) + resolve(result) + } + }).catch(reject) + }) +} + +export const sleep = (ms: number): Promise => new Promise((resolve) => setTimeout(resolve, ms)) + +export interface ReferendumOptions { + voteHdx: bigint + enactAfter: number + verify?: () => Promise +} + +export async function submitRootReferendum( + api: ApiPromise, + signer: KeyringPair, + inner: SubmittableExtrinsic<'promise'>, + opts: ReferendumOptions, +): Promise { + const callHex = inner.method.toHex() + const lenBytes = (callHex.length - 2) / 2 + + let proposal: Record + if (lenBytes <= 100) { + proposal = { Inline: callHex } + } else { + try { + await signAndSend(api, api.tx.preimage.notePreimage(callHex), signer, `Note preimage (${lenBytes} bytes)`) + } catch (e) { + if (!String(e).includes('AlreadyNoted')) throw e + console.log(' preimage already noted') + } + proposal = { Lookup: { hash: inner.method.hash.toHex(), len: lenBytes } } + } + + const submitted = await signAndSend( + api, + api.tx.referenda.submit({ system: 'Root' }, proposal as any, { After: opts.enactAfter }), + signer, + 'Submit Root referendum', + ) + + let refIndex: number | null = null + for (const { event } of submitted.events) { + if (event.section === 'referenda' && event.method === 'Submitted') { + const raw = event.data[0] + if (raw) refIndex = Number(raw.toString()) + break + } + } + if (refIndex === null) throw new Error('no referenda.Submitted event') + console.log(` referendum index ${refIndex}`) + + await signAndSend(api, api.tx.referenda.placeDecisionDeposit(refIndex), signer, 'Place decision deposit') + await signAndSend( + api, + api.tx.convictionVoting.vote(refIndex, { + Standard: { vote: { aye: true, conviction: 'None' }, balance: opts.voteHdx * 10n ** HDX_DECIMALS }, + }), + signer, + `Vote aye with ${opts.voteHdx} HDX`, + ) + + console.log(' waiting for approval...') + let approved = false + for (let i = 0; i < 80; i++) { + const info = (await api.query.referenda.referendumInfoFor(refIndex)).toHuman() as Record | null + if (info?.['Approved']) { + approved = true + break + } + if (info?.['Rejected'] || info?.['Cancelled'] || info?.['TimedOut'] || info?.['Killed']) { + throw new Error(`referendum ended without approval: ${JSON.stringify(info)}`) + } + await sleep(6000) + } + if (!approved) throw new Error('referendum not approved within timeout') + + if (opts.verify) { + console.log(' waiting for enactment...') + for (let i = 0; i < 40; i++) { + if (await opts.verify()) { + console.log(' effect confirmed') + return refIndex + } + await sleep(6000) + } + throw new Error('referendum approved but effect not observed within timeout') + } + return refIndex +} diff --git a/scripts/uniswap-v3-lark/tests/assets.test.ts b/scripts/uniswap-v3-lark/tests/assets.test.ts new file mode 100644 index 0000000000..7bb78a076a --- /dev/null +++ b/scripts/uniswap-v3-lark/tests/assets.test.ts @@ -0,0 +1,31 @@ +import { describe, expect, it } from 'vitest' +import { assetToEvmAddress, sortTokens, truncatedAccountId } from '../src/core/assets' + +describe('assetToEvmAddress', () => { + it('maps an asset id to its erc20-precompile address', () => { + expect(assetToEvmAddress(0)).toBe('0x0000000000000000000000000000000100000000') + expect(assetToEvmAddress(2)).toBe('0x0000000000000000000000000000000100000002') + expect(assetToEvmAddress(20)).toBe('0x0000000000000000000000000000000100000014') + }) + + it('throws on a negative id', () => { + expect(() => assetToEvmAddress(-1)).toThrow() + }) +}) + +describe('truncatedAccountId', () => { + it('prefixes ETH\\0 and right-pads the address with zeros', () => { + expect(truncatedAccountId('0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266')).toBe( + '0x45544800f39fd6e51aad88f6f4ce6ab8827279cfffb922660000000000000000', + ) + }) +}) + +describe('sortTokens', () => { + it('orders a pair ascending by address', () => { + const lo = '0x0000000000000000000000000000000100000009' + const hi = '0x0000000000000000000000000000000100000010' + expect(sortTokens(hi, lo)).toEqual([lo, hi]) + expect(sortTokens(lo, hi)).toEqual([lo, hi]) + }) +}) diff --git a/scripts/uniswap-v3-lark/tests/math.test.ts b/scripts/uniswap-v3-lark/tests/math.test.ts new file mode 100644 index 0000000000..8f02b281bb --- /dev/null +++ b/scripts/uniswap-v3-lark/tests/math.test.ts @@ -0,0 +1,44 @@ +import { describe, expect, it } from 'vitest' +import { fullRangeTicks, isqrt, sqrtPriceX96FromAmounts } from '../src/core/math' + +describe('isqrt', () => { + it('floors the integer square root', () => { + expect(isqrt(0n)).toBe(0n) + expect(isqrt(1n)).toBe(1n) + expect(isqrt(4n)).toBe(2n) + expect(isqrt(8n)).toBe(2n) + expect(isqrt(9n)).toBe(3n) + expect(isqrt(1n << 192n)).toBe(1n << 96n) + }) + + it('throws on negative input', () => { + expect(() => isqrt(-1n)).toThrow() + }) +}) + +describe('sqrtPriceX96FromAmounts', () => { + it('returns 2^96 for equal amounts (price 1)', () => { + expect(sqrtPriceX96FromAmounts(10n ** 24n, 10n ** 24n)).toBe(2n ** 96n) + }) + + it('returns 2^97 for 4:1 amounts (price 4)', () => { + expect(sqrtPriceX96FromAmounts(1n, 4n)).toBe(2n ** 97n) + }) + + it('throws on zero amount0', () => { + expect(() => sqrtPriceX96FromAmounts(0n, 1n)).toThrow() + }) +}) + +describe('fullRangeTicks', () => { + it('aligns the full range to the fee tier spacing', () => { + expect(fullRangeTicks(3000)).toEqual([-887220, 887220]) + expect(fullRangeTicks(500)).toEqual([-887270, 887270]) + expect(fullRangeTicks(10000)).toEqual([-887200, 887200]) + expect(fullRangeTicks(100)).toEqual([-887272, 887272]) + }) + + it('throws on an unknown fee tier', () => { + expect(() => fullRangeTicks(1234)).toThrow() + }) +}) diff --git a/scripts/uniswap-v3-lark/tsconfig.json b/scripts/uniswap-v3-lark/tsconfig.json new file mode 100644 index 0000000000..b28645e707 --- /dev/null +++ b/scripts/uniswap-v3-lark/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "lib": ["ES2023"], + "strict": true, + "noImplicitOverride": true, + "noFallthroughCasesInSwitch": true, + "esModuleInterop": true, + "skipLibCheck": true, + "resolveJsonModule": true, + "types": ["node"], + "outDir": "dist", + "rootDir": "." + }, + "include": ["src", "tests"] +} diff --git a/traits/Cargo.toml b/traits/Cargo.toml index bca6d7f613..2231c1c6a2 100644 --- a/traits/Cargo.toml +++ b/traits/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-traits" -version = "4.11.0" +version = "4.12.0" description = "Shared traits" authors = ["GalacticCouncil"] edition = "2021" diff --git a/traits/src/router.rs b/traits/src/router.rs index 44259e8f79..b03fb486af 100644 --- a/traits/src/router.rs +++ b/traits/src/router.rs @@ -87,6 +87,7 @@ pub enum PoolType { Omnipool, Aave, HSM, + UniswapV3(u32), } #[derive(Debug, PartialEq, Eq)] @@ -220,7 +221,7 @@ pub trait TradeExecution { } #[allow(clippy::redundant_clone)] //Needed as it complains about redundant clone, but clone is needed as Origin is moved and it is not copy type. -#[impl_trait_for_tuples::impl_for_tuples(1, 6)] +#[impl_trait_for_tuples::impl_for_tuples(1, 7)] impl TradeExecution for Tuple {