diff --git a/.dockerignore b/.dockerignore index 9ae2930c10..6451e9896b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,8 @@ target/ !target/release/hydradx Dockerfile +.git +**/node_modules +launch-configs/fork/data +launch-configs/fork/state.json +launch-configs/fork/forked-chainspec.json diff --git a/Cargo.lock b/Cargo.lock index 8d2b3262f2..b203a07698 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5633,7 +5633,7 @@ dependencies = [ [[package]] name = "hydradx" -version = "15.0.2" +version = "15.1.5" dependencies = [ "async-trait", "clap", @@ -5663,6 +5663,7 @@ dependencies = [ "frame-support", "frame-system", "futures", + "futures-timer", "hex-literal", "hydra-dx-build-script-utils", "hydradx-runtime", @@ -5672,6 +5673,7 @@ dependencies = [ "kvdb-rocksdb", "liquidation-worker-support", "log", + "lru 0.12.5", "num_cpus", "pallet-claims", "pallet-currencies-rpc-runtime-api", @@ -5684,6 +5686,7 @@ dependencies = [ "polkadot-primitives", "polkadot-service", "primitives", + "rlp 0.6.1", "sc-basic-authorship", "sc-chain-spec", "sc-cli", @@ -5711,6 +5714,7 @@ dependencies = [ "sp-offchain", "sp-runtime", "sp-std", + "sp-storage", "sp-timestamp", "substrate-frame-rpc-system", "substrate-prometheus-endpoint", @@ -5779,8 +5783,10 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "422.0.0" +version = "425.0.0" dependencies = [ + "alloy-primitives 0.7.7", + "alloy-sol-types 0.7.7", "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", "cumulus-pallet-weight-reclaim", @@ -5794,6 +5800,7 @@ dependencies = [ "cumulus-primitives-utility", "ethabi", "ethereum", + "ethereum-types", "evm", "fp-evm", "fp-rpc", @@ -13869,7 +13876,7 @@ checksum = "48fd7bd8a6377e15ad9d42a8ec25371b94ddc67abe7c8b9127bec79bebaaae18" [[package]] name = "runtime-integration-tests" -version = "1.88.0" +version = "1.88.1" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", @@ -13881,6 +13888,7 @@ dependencies = [ "cumulus-test-relay-sproof-builder", "ethabi", "ethereum", + "ethereum-types", "fp-evm", "fp-rpc", "fp-self-contained", diff --git a/Dockerfile.with-builder b/Dockerfile.with-builder new file mode 100644 index 0000000000..d2ea6cd358 --- /dev/null +++ b/Dockerfile.with-builder @@ -0,0 +1,33 @@ +# Self-contained build of the synth-logs node: compiles from source in a builder +# stage, then ships just the binary. Builder and runtime share glibc (bookworm +# 2.36), so no host/base libc mismatch. +FROM rust:1.88-bookworm AS builder + +RUN apt-get update && apt-get install -y --no-install-recommends \ + clang protobuf-compiler cmake pkg-config libssl-dev git \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /build +COPY . . + +ENV CXXFLAGS="-include cstdint" +RUN cargo build --release --locked --config net.git-fetch-with-cli=true -p hydradx + +FROM debian:bookworm-slim +LABEL org.opencontainers.image.source = "https://github.com/galacticcouncil/hydration-node" + +RUN apt-get update && apt-get install -y --no-install-recommends ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +RUN useradd -m -u 1000 -U -s /bin/sh -d /hydra hydra && \ + mkdir -p /hydra/.local/share && \ + chown -R hydra:hydra /hydra + +COPY --from=builder /build/target/release/hydradx /usr/local/bin/hydradx + +USER hydra +EXPOSE 30333 9933 9944 9615 +VOLUME ["/hydra/.local/share"] + +ENTRYPOINT ["/usr/local/bin/hydradx"] +CMD ["--prometheus-external", "--", "--execution=wasm" ,"--telemetry-url", "wss://telemetry.hydradx.io:9000/submit/ 0"] diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 5f90671feb..6479350985 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runtime-integration-tests" -version = "1.88.0" +version = "1.88.1" description = "Integration tests" authors = ["GalacticCouncil"] edition = "2021" @@ -51,6 +51,7 @@ pallet-gigahdx = { workspace = true } pallet-gigahdx-rewards = { workspace = true } liquidation-worker-support = { workspace = true } pallet-broadcast = { workspace = true } +ethereum-types = { workspace = true } pallet-duster = { workspace = true } pallet-duster-rpc-runtime-api = { workspace = true } diff --git a/integration-tests/src/dca.rs b/integration-tests/src/dca.rs index 6571f9be31..f507062997 100644 --- a/integration-tests/src/dca.rs +++ b/integration-tests/src/dca.rs @@ -3723,14 +3723,12 @@ mod stableswap { assert_eq!( alice_stable_shares_after, 0, - "User should have 0 pool shares since DCA execution failed. Got {}", - alice_stable_shares_after + "User should have 0 pool shares since DCA execution failed. Got {alice_stable_shares_after}" ); assert_eq!( router_reserved_shares, 0, - "Router should have 0 reserved shares. Got {}", - router_reserved_shares + "Router should have 0 reserved shares. Got {router_reserved_shares}" ); TransactionOutcome::Commit(DispatchResult::Ok(())) @@ -5556,9 +5554,7 @@ mod extra_gas_erc20 { assert!( fee_with_extra > base_fee, - "Fee with extra gas ({}) should be > base fee ({})", - fee_with_extra, - base_fee + "Fee with extra gas ({fee_with_extra}) should be > base fee ({base_fee})" ); // The first attempt charged base fee (before extra gas was added) diff --git a/integration-tests/src/dispatcher.rs b/integration-tests/src/dispatcher.rs index 8ec103a6b5..41de5542a3 100644 --- a/integration-tests/src/dispatcher.rs +++ b/integration-tests/src/dispatcher.rs @@ -101,7 +101,7 @@ fn dispatch_as_aave_admin_can_modify_supply_cap_on_testnet() { RuntimeEvent::Dispatcher(pallet_dispatcher::Event::AaveManagerCallDispatched { result: Ok(..), .. }) => {} - _ => panic!("Unexpected event: {:?}", event), + _ => panic!("Unexpected event: {event:?}"), } }); } @@ -248,8 +248,7 @@ fn dispatch_with_extra_gas_should_pay_for_extra_gas_used_when_it_is_not_used() { let gas_eater_paid_fee = initial_alice_hdx_balance - alice_balance_final; assert_eq!( gas_eater_paid_fee, hydra_paid_fee, - "GasEater transfer should cost the same as HydraToken transfer: {:?} == {:?}", - gas_eater_paid_fee, hydra_paid_fee + "GasEater transfer should cost the same as HydraToken transfer: {gas_eater_paid_fee:?} == {hydra_paid_fee:?}" ); }); } @@ -363,17 +362,13 @@ fn dispatch_with_extra_gas_should_not_refund_extra_gas_correctly() { // Fee charged on tx submit should be higher in the second case assert!( fee_charge_2 > fee_charge_1, - "Fee charged on tx submit should be higher in the second case: {:?} > {:?}", - fee_charge_2, - fee_charge_1 + "Fee charged on tx submit should be higher in the second case: {fee_charge_2:?} > {fee_charge_1:?}" ); // the two tx fees should be the same because it should refund correctly the unused gas assert!( actual_fee_paid_1 < actual_fee_paid_2, - "Paid fee should be higher for the second tx: {:?} < {:?}", - actual_fee_paid_1, - actual_fee_paid_2 + "Paid fee should be higher for the second tx: {actual_fee_paid_1:?} < {actual_fee_paid_2:?}" ); } diff --git a/integration-tests/src/dynamic_fees.rs b/integration-tests/src/dynamic_fees.rs index 6dc9e5e1b1..82a408781a 100644 --- a/integration-tests/src/dynamic_fees.rs +++ b/integration-tests/src/dynamic_fees.rs @@ -834,10 +834,10 @@ fn fees_should_be_applied_correctly_when_min_equals_max_in_dynamic_config() { println!("DOT asset fee: 1% (applied on DOT being sold)"); println!("HDX protocol fee: 0.5% (applied on HDX being received)"); println!("---"); - println!("Amount received with zero fees: {}", amount_out_with_zero_fee); - println!("Amount received with fees: {}", amount_out_with_fee); - println!("Difference: {}", actual_difference); - println!("Actual percentage difference: {:.4}%", actual_percentage_value); + println!("Amount received with zero fees: {amount_out_with_zero_fee}"); + println!("Amount received with fees: {amount_out_with_fee}"); + println!("Difference: {actual_difference}"); + println!("Actual percentage difference: {actual_percentage_value:.4}%"); println!("====================================\n"); // Verify the fees were applied (should see some difference) @@ -850,16 +850,10 @@ fn fees_should_be_applied_correctly_when_min_equals_max_in_dynamic_config() { assert!( actual_percentage_value >= expected_percentage - tolerance, - "Actual fee percentage ({:.4}%) is lower than expected ({:.2}% - {:.2}%)", - actual_percentage_value, - expected_percentage, - tolerance + "Actual fee percentage ({actual_percentage_value:.4}%) is lower than expected ({expected_percentage:.2}% - {tolerance:.2}%)" ); assert!( actual_percentage_value <= expected_percentage + tolerance, - "Actual fee percentage ({:.4}%) is higher than expected ({:.2}% + {:.2}%)", - actual_percentage_value, - expected_percentage, - tolerance + "Actual fee percentage ({actual_percentage_value:.4}%) is higher than expected ({expected_percentage:.2}% + {tolerance:.2}%)" ); } diff --git a/integration-tests/src/evm.rs b/integration-tests/src/evm.rs index b8a5d5014e..9024ec41a7 100644 --- a/integration-tests/src/evm.rs +++ b/integration-tests/src/evm.rs @@ -350,7 +350,7 @@ mod account_conversion { ExitReason::Succeed(ExitSucceed::Stopped) ); - println!("{:?}", res); + println!("{res:?}"); }); } @@ -3833,9 +3833,7 @@ fn compare_fee_in_eth_between_evm_and_native_omnipool_calls() { let native_fee = new_alice_currency_balance - alice_currency_balance_pre_dispatch; assert!( evm_fee > native_fee, - "assertion failed evm_fee > native fee. Evm fee: {:?} Native fee: {:?}", - evm_fee, - native_fee + "assertion failed evm_fee > native fee. Evm fee: {evm_fee:?} Native fee: {native_fee:?}" ); let fee_difference = evm_fee - native_fee; @@ -3846,9 +3844,7 @@ fn compare_fee_in_eth_between_evm_and_native_omnipool_calls() { // EVM fees should be not higher than 20% assert!( relative_fee_difference < tolerated_fee_difference, - "relative_fee_difference: {:?} is bigger than tolerated {:?}", - relative_fee_difference, - tolerated_fee_difference + "relative_fee_difference: {relative_fee_difference:?} is bigger than tolerated {tolerated_fee_difference:?}" ); }) } @@ -4850,7 +4846,10 @@ impl PrecompileHandle for MockHandle { } fn log(&mut self, _: H160, _: Vec, _: Vec) -> Result<(), ExitError> { - unimplemented!() + // no-op: tests using this mock handle don't inspect emitted logs; + // the precompile (e.g. multicurrency) calls `handle.log(...)` to emit + // the ERC-20 Transfer event inline, which we accept silently here. + Ok(()) } fn code_address(&self) -> H160 { diff --git a/integration-tests/src/evm_permit.rs b/integration-tests/src/evm_permit.rs index 17be60d7bc..06459c6699 100644 --- a/integration-tests/src/evm_permit.rs +++ b/integration-tests/src/evm_permit.rs @@ -169,9 +169,7 @@ fn compare_fee_in_hdx_between_evm_and_native_omnipool_calls_when_permit_is_dispa // EVM fees should be not higher than 20% assert!( relative_fee_difference < tolerated_fee_difference, - "relative_fee_difference: {:?} is bigger than tolerated {:?}", - relative_fee_difference, - tolerated_fee_difference + "relative_fee_difference: {relative_fee_difference:?} is bigger than tolerated {tolerated_fee_difference:?}" ); }) } @@ -1195,13 +1193,11 @@ fn evm_permit_set_currency_dispatch_should_pay_evm_fee_in_insufficient_asset() { let payed_fee = initial_user_insufficient_balance - user_insufficient_asset_balance; assert!( payed_fee > 50_000_000, - "payed_fee: {:?} is less than 50_000_000", - payed_fee + "payed_fee: {payed_fee:?} is less than 50_000_000" ); assert!( payed_fee < 120_000_000, - "payed_fee: {:?} is more than 120_000_000", - payed_fee + "payed_fee: {payed_fee:?} is more than 120_000_000" ); TransactionOutcome::Commit(DispatchResult::Ok(())) diff --git a/integration-tests/src/global_withdraw_limit.rs b/integration-tests/src/global_withdraw_limit.rs index ab42622b5c..d70ce7195d 100644 --- a/integration-tests/src/global_withdraw_limit.rs +++ b/integration-tests/src/global_withdraw_limit.rs @@ -32,7 +32,7 @@ fn xcm_message_withdraw_deposit(token_location: Location, amount: Balance) -> Xc Xcm(vec![ WithdrawAsset(asset.clone().into()), BuyExecution { - fees: asset.into(), + fees: asset, weight_limit: Unlimited, }, DepositReserveAsset { @@ -54,7 +54,7 @@ fn set_dot_external_and_get_transfer_call() -> hydradx_runtime::RuntimeCall { let dot: Asset = Asset { id: cumulus_primitives_core::AssetId(DOT_ASSET_LOCATION.into()), - fun: Fungible(1 * UNITS), + fun: Fungible(UNITS), }; let bob_beneficiary = Location::new( @@ -195,7 +195,7 @@ fn xtokens_transfer_should_fail_when_lockdown_active_and_asset_is_egress() { let call = RuntimeCall::XTokens(orml_xtokens::Call::transfer { currency_id: HDX, - amount: 1 * UNITS, + amount: UNITS, dest: Box::new(bob_location.into_versioned()), dest_weight_limit: WeightLimit::Unlimited, }); @@ -238,7 +238,7 @@ fn on_charge_transaction_skips_global_withdraw_accounting_for_native_asset() { let call = RuntimeCall::System(frame_system::Call::remark { remark: vec![1, 2, 3] }); // Act - let fee_amount = 1 * UNITS; + let fee_amount = UNITS; let _ = ::OnChargeTransaction::withdraw_fee( &alice, &call, @@ -264,7 +264,7 @@ fn on_charge_transaction_skips_global_withdraw_accounting_for_native_asset() { Currencies::withdraw( HDX, &BOB.into(), - 1 * UNITS, + UNITS, frame_support::traits::ExistenceRequirement::AllowDeath ), pallet_circuit_breaker::Error::::WithdrawLockdownActive @@ -311,7 +311,7 @@ fn on_charge_transaction_skips_global_withdraw_accounting_for_external_asset() { let call = RuntimeCall::System(frame_system::Call::remark { remark: vec![1, 2, 3] }); // Act - let fee_amount = 1 * UNITS; + let fee_amount = UNITS; let _ = ::OnChargeTransaction::withdraw_fee( &alice, &call, @@ -337,7 +337,7 @@ fn on_charge_transaction_skips_global_withdraw_accounting_for_external_asset() { Currencies::withdraw( DOT, &ALICE.into(), - 1 * UNITS, + UNITS, frame_support::traits::ExistenceRequirement::AllowDeath ), pallet_circuit_breaker::Error::::WithdrawLockdownActive @@ -407,7 +407,7 @@ fn evm_on_charge_transaction_skips_global_withdraw_accounting() { Currencies::withdraw( WETH, &evm_account, - 1 * UNITS, + UNITS, frame_support::traits::ExistenceRequirement::AllowDeath ), pallet_circuit_breaker::Error::::WithdrawLockdownActive @@ -804,7 +804,7 @@ fn dot_external_limit_trigger_fails_then_decays_to_zero() { assert_ok!(AssetRegistry::set_location(DOT, DOT_ASSET_LOCATION)); let alice: AccountId = ALICE.into(); - let amount = 1 * UNITS; + let amount = UNITS; assert!( Currencies::free_balance(DOT, &alice) >= amount * 3, "Test requires Alice to have enough DOT" @@ -877,7 +877,7 @@ fn dot_external_lockdown_blocks_withdraw_but_regular_dot_transfer_still_works() let alice: AccountId = ALICE.into(); let bob: AccountId = BOB.into(); - let amount = 1 * UNITS; + let amount = UNITS; assert!(Currencies::free_balance(DOT, &alice) >= amount * 2); assert_eq!(CircuitBreaker::withdraw_limit_accumulator().0, 0); diff --git a/integration-tests/src/hsm.rs b/integration-tests/src/hsm.rs index 1935368aba..4fb75a5b72 100644 --- a/integration-tests/src/hsm.rs +++ b/integration-tests/src/hsm.rs @@ -2768,7 +2768,7 @@ fn arb_should_repeg_continuously_when_more_hollar_in_pool() { Some(state.fee), &state.pegs, ); - println!("Block: {:?}: spot: {:?}", block_idx, spot_price); + println!("Block: {block_idx:?}: spot: {spot_price:?}"); assert!(spot_price < last_spot_price); last_spot_price = spot_price; hydradx_run_to_next_block(); @@ -2955,7 +2955,7 @@ fn arb_should_repeg_continuously_when_more_hollar_in_pool_and_collateral_has_12_ Some(state.fee), &state.pegs, ); - println!("Block: {:?}: spot: {:?}", block_idx, spot_price); + println!("Block: {block_idx:?}: spot: {spot_price:?}"); assert!(spot_price < last_spot_price); last_spot_price = spot_price; hydradx_run_to_next_block(); diff --git a/integration-tests/src/liquidation.rs b/integration-tests/src/liquidation.rs index c3e6a727bd..0ada91f587 100644 --- a/integration-tests/src/liquidation.rs +++ b/integration-tests/src/liquidation.rs @@ -530,8 +530,7 @@ fn assert_health_factor_is_within_tolerance(health_factor: U256, target_health_f // HF uses 18 decimal places assert!( health_factor_diff < U256::from(10).pow(15.into()), - "HF diff: {:?}", - health_factor_diff + "HF diff: {health_factor_diff:?}" ); } diff --git a/integration-tests/src/omnipool_add_all_liquidity.rs b/integration-tests/src/omnipool_add_all_liquidity.rs index 2647c26055..d1b17a90a3 100644 --- a/integration-tests/src/omnipool_add_all_liquidity.rs +++ b/integration-tests/src/omnipool_add_all_liquidity.rs @@ -35,7 +35,7 @@ fn add_all_liquidity_should_work() { // circuit-breaker's per-block add-liquidity limit (5% of the pool's reserve). // The DOT pool reserve after add_token is ~87_719_298_250_000, so the limit is // ~4_385_964_912_500. We keep 1 * UNITS (1_000_000_000_000) for ALICE to deposit. - let keep_amount = 1 * UNITS; // 1 DOT — well within 5% of the pool + let keep_amount = UNITS; // 1 DOT — well within 5% of the pool let lp_balance = Currencies::free_balance(DOT, &lp); let transfer_away = lp_balance.saturating_sub(keep_amount); assert_ok!(Currencies::transfer( @@ -145,7 +145,7 @@ fn add_all_liquidity_position_matches_explicit_add_liquidity_with_limit() { // add-liquidity limit is 5%, so we stay well within it. let prepare_lp = || { let lp = AccountId::from(ALICE); - let keep = 1 * UNITS; + let keep = UNITS; let balance = Currencies::free_balance(DOT, &lp); let transfer_away = balance.saturating_sub(keep); if transfer_away > 0 { diff --git a/integration-tests/src/omnipool_fixed_fees.rs b/integration-tests/src/omnipool_fixed_fees.rs index 75394db8dd..f4dce62a58 100644 --- a/integration-tests/src/omnipool_fixed_fees.rs +++ b/integration-tests/src/omnipool_fixed_fees.rs @@ -76,14 +76,9 @@ fn omnipool_fixed_fees_should_override_dynamic_fees() { // With 50% fixed fee, the received amount should be approximately half of the original let expected_half = hdx_received_dynamic / 2; let tolerance = hdx_received_dynamic / 10; // 10% tolerance - let difference = if hdx_received_fixed > expected_half { - hdx_received_fixed - expected_half - } else { - expected_half - hdx_received_fixed - }; + let difference = hdx_received_fixed.abs_diff(expected_half); assert!(difference <= tolerance, - "HDX received with 50% fee ({}) should be approximately half of dynamic fee amount ({}), expected around {}, tolerance: {}", - hdx_received_fixed, hdx_received_dynamic, expected_half, tolerance); + "HDX received with 50% fee ({hdx_received_fixed}) should be approximately half of dynamic fee amount ({hdx_received_dynamic}), expected around {expected_half}, tolerance: {tolerance}"); let asset_state = hydradx_runtime::Omnipool::load_asset_state(ASSET_ID_TO_TEST).unwrap(); let current_fees = pallet_dynamic_fees::UpdateAndRetrieveFees::::get((ASSET_ID_TO_TEST, asset_state.reserve)); diff --git a/integration-tests/src/omnipool_slip_fees.rs b/integration-tests/src/omnipool_slip_fees.rs index 92987d58b5..b55d0e830b 100644 --- a/integration-tests/src/omnipool_slip_fees.rs +++ b/integration-tests/src/omnipool_slip_fees.rs @@ -72,9 +72,7 @@ fn sell_with_slip_fees_gives_less_output_than_without() { assert!( output_with_slip < output_no_slip, - "Slip fee should reduce sell output: no_slip={} with_slip={}", - output_no_slip, - output_with_slip + "Slip fee should reduce sell output: no_slip={output_no_slip} with_slip={output_with_slip}" ); } @@ -147,9 +145,7 @@ fn buy_with_slip_fees_costs_more_than_without() { assert!( cost_with_slip > cost_no_slip, - "Slip fee should increase buy cost: no_slip={} with_slip={}", - cost_no_slip, - cost_with_slip + "Slip fee should increase buy cost: no_slip={cost_no_slip} with_slip={cost_with_slip}" ); } @@ -206,9 +202,7 @@ fn sell_lrna_with_slip_fees_gives_less_output_than_without() { assert!( output_with_slip < output_no_slip, - "Slip fee should reduce LRNA sell output: no_slip={} with_slip={}", - output_no_slip, - output_with_slip + "Slip fee should reduce LRNA sell output: no_slip={output_no_slip} with_slip={output_with_slip}" ); } @@ -265,9 +259,7 @@ fn buy_with_lrna_with_slip_fees_costs_more_than_without() { assert!( cost_with_slip > cost_no_slip, - "Slip fee should increase LRNA buy cost: no_slip={} with_slip={}", - cost_no_slip, - cost_with_slip + "Slip fee should increase LRNA buy cost: no_slip={cost_no_slip} with_slip={cost_with_slip}" ); } @@ -340,18 +332,14 @@ fn slip_fee_deltas_are_cleared_across_blocks() { // Slip fees are active: first trade gives less than no-slip baseline assert!( first_output < output_no_slip, - "Slip fee should reduce output: no_slip={} with_slip={}", - output_no_slip, - first_output + "Slip fee should reduce output: no_slip={output_no_slip} with_slip={first_output}" ); // After delta clearing, output should be close to first trade (no accumulated penalty). // Pool state changed slightly from the first trade, but the slip fee restarts from zero. assert!( cleared_output >= first_output * 99 / 100, - "Cleared trade should not suffer accumulated slip penalty: first={} cleared={}", - first_output, - cleared_output + "Cleared trade should not suffer accumulated slip penalty: first={first_output} cleared={cleared_output}" ); } @@ -432,9 +420,7 @@ fn sequential_trades_accumulate_slip_within_block() { // Basic: second trade gets less output in both cases assert!( slip_second < slip_first, - "Second trade should get less output due to accumulated slip: first={} second={}", - slip_first, - slip_second + "Second trade should get less output due to accumulated slip: first={slip_first} second={slip_second}" ); // The drop between first and second trade should be LARGER with slip fees than without. @@ -444,9 +430,7 @@ fn sequential_trades_accumulate_slip_within_block() { let slip_drop = slip_first - slip_second; assert!( slip_drop > no_slip_drop, - "Slip fees should cause a larger drop between sequential trades: slip_drop={} no_slip_drop={}", - slip_drop, - no_slip_drop + "Slip fees should cause a larger drop between sequential trades: slip_drop={slip_drop} no_slip_drop={no_slip_drop}" ); } diff --git a/integration-tests/src/omnipool_slip_fees_xval.rs b/integration-tests/src/omnipool_slip_fees_xval.rs index 9aae067fc3..4d968467dd 100644 --- a/integration-tests/src/omnipool_slip_fees_xval.rs +++ b/integration-tests/src/omnipool_slip_fees_xval.rs @@ -23,19 +23,10 @@ fn enable_slip_fees() { /// Assert that `actual` is within `tolerance` of `expected`. fn assert_approx(actual: u128, expected: u128, tolerance: u128, label: &str) { - let diff = if actual >= expected { - actual - expected - } else { - expected - actual - }; + let diff = actual.abs_diff(expected); assert!( diff <= tolerance, - "{}: rust={} python={} diff={} tolerance={}", - label, - actual, - expected, - diff, - tolerance + "{label}: rust={actual} python={expected} diff={diff} tolerance={tolerance}" ); } @@ -64,9 +55,9 @@ fn dump_pool_state_and_fees() { let dai_reserve = Currencies::free_balance(DAI, &Omnipool::protocol_account()); println!("=== POOL STATE ==="); - println!("HDX reserve: {}", hdx_reserve); + println!("HDX reserve: {hdx_reserve}"); println!("HDX hub_reserve: {}", hdx_state.hub_reserve); - println!("DAI reserve: {}", dai_reserve); + println!("DAI reserve: {dai_reserve}"); println!("DAI hub_reserve: {}", dai_state.hub_reserve); let (hdx_asset_fee, hdx_protocol_fee) = @@ -74,8 +65,8 @@ fn dump_pool_state_and_fees() { let (dai_asset_fee, dai_protocol_fee) = pallet_dynamic_fees::UpdateAndRetrieveFees::::get((DAI, dai_state.reserve)); - println!("HDX asset_fee={:?} protocol_fee={:?}", hdx_asset_fee, hdx_protocol_fee); - println!("DAI asset_fee={:?} protocol_fee={:?}", dai_asset_fee, dai_protocol_fee); + println!("HDX asset_fee={hdx_asset_fee:?} protocol_fee={hdx_protocol_fee:?}"); + println!("DAI asset_fee={dai_asset_fee:?} protocol_fee={dai_protocol_fee:?}"); }); } @@ -168,9 +159,7 @@ fn xval_single_buy() { // Slip fees must increase cost vs baseline assert!( hdx_spent > cost_no_slip, - "Slip fee should increase buy cost: with_slip={} no_slip={}", - hdx_spent, - cost_no_slip + "Slip fee should increase buy cost: with_slip={hdx_spent} no_slip={cost_no_slip}" ); } @@ -340,9 +329,7 @@ fn xval_multiple_buys_same_direction() { let slip_total = trade1_cost + trade2_cost; assert!( slip_total > no_slip_total, - "Slip fees should increase total buy cost: with_slip={} no_slip={}", - slip_total, - no_slip_total + "Slip fees should increase total buy cost: with_slip={slip_total} no_slip={no_slip_total}" ); } @@ -432,15 +419,11 @@ fn xval_multiple_buys_opposite_direction() { // Slip fees must increase costs vs baseline assert!( trade1_cost > no_slip_trade1, - "Slip fee should increase trade1 cost: with_slip={} no_slip={}", - trade1_cost, - no_slip_trade1 + "Slip fee should increase trade1 cost: with_slip={trade1_cost} no_slip={no_slip_trade1}" ); assert!( trade2_cost > no_slip_trade2, - "Slip fee should increase trade2 cost: with_slip={} no_slip={}", - trade2_cost, - no_slip_trade2 + "Slip fee should increase trade2 cost: with_slip={trade2_cost} no_slip={no_slip_trade2}" ); } @@ -550,9 +533,7 @@ fn xval_mixed_trades() { // Slip fees must reduce trade 1 output vs no-slip baseline assert!( t1 < t1_no_slip, - "Slip fee should reduce trade1 output: no_slip={} with_slip={}", - t1_no_slip, - t1 + "Slip fee should reduce trade1 output: no_slip={t1_no_slip} with_slip={t1}" ); }); } @@ -735,9 +716,7 @@ fn xval_sell_lrna_for_dai_with_prior_delta() { let py_fresh: u128 = 2035714285714285714285; assert!( t2_dai < py_fresh, - "Prior delta should reduce sell_lrna output: {} < {}", - t2_dai, - py_fresh + "Prior delta should reduce sell_lrna output: {t2_dai} < {py_fresh}" ); }); } @@ -785,9 +764,7 @@ fn xval_buy_dai_with_lrna_after_prior_sell() { let py_fresh_cost: u128 = 4511278; assert!( t2_lrna_cost >= py_fresh_cost, - "Prior delta should increase buy_lrna cost: {} >= {}", - t2_lrna_cost, - py_fresh_cost + "Prior delta should increase buy_lrna cost: {t2_lrna_cost} >= {py_fresh_cost}" ); }); } diff --git a/integration-tests/src/oracle.rs b/integration-tests/src/oracle.rs index 6fd0ee2ee9..48e9f278d0 100644 --- a/integration-tests/src/oracle.rs +++ b/integration-tests/src/oracle.rs @@ -65,8 +65,7 @@ fn oracle_smoothing_period_matches_configuration() { let smoothing_from_period = smoothing_from_period(configured_length); assert_eq!( configured_smoothing, smoothing_from_period, - "Smoothing period for {:?} does not match configured length of {:?}", - supported_period, configured_length, + "Smoothing period for {supported_period:?} does not match configured length of {configured_length:?}", ); } } diff --git a/integration-tests/src/polkadot_test_net.rs b/integration-tests/src/polkadot_test_net.rs index 6c6fa97946..ac1c859e98 100644 --- a/integration-tests/src/polkadot_test_net.rs +++ b/integration-tests/src/polkadot_test_net.rs @@ -336,7 +336,7 @@ pub mod rococo { /// Helper function to generate a crypto pair from seed fn get_from_seed(seed: &str) -> ::Public { - TPublic::Pair::from_string(&format!("//{}", seed), None) + TPublic::Pair::from_string(&format!("//{seed}"), None) .expect("static values are valid; qed") .public() } @@ -396,7 +396,7 @@ pub mod rococo { AuthorityDiscoveryId, ) { ( - get_account_id_from_seed::(&format!("{}//stash", seed)), + get_account_id_from_seed::(&format!("{seed}//stash")), get_account_id_from_seed::(seed), get_from_seed::(seed), get_from_seed::(seed), @@ -463,7 +463,7 @@ type AccountPublic = ::Signer; /// Helper function to generate a crypto pair from seed fn get_from_seed(seed: &str) -> ::Public { - TPublic::Pair::from_string(&format!("//{}", seed), None) + TPublic::Pair::from_string(&format!("//{seed}"), None) .expect("static values are valid; qed") .public() } @@ -917,7 +917,7 @@ pub fn hydradx_run_to_next_block() { pub fn hydradx_run_to_block(to: BlockNumber) { let b = hydradx_runtime::System::block_number(); - assert!(b <= to, "the current block number {:?} is higher than expected.", b); + assert!(b <= to, "the current block number {b:?} is higher than expected."); if b < to { go_to_block(to); diff --git a/integration-tests/src/router.rs b/integration-tests/src/router.rs index fe7a6d2cf1..947a1aef5a 100644 --- a/integration-tests/src/router.rs +++ b/integration-tests/src/router.rs @@ -5320,11 +5320,7 @@ mod route_spot_price { .unwrap() .checked_mul_int(amount_to_sell) .unwrap(); - let difference = if expected_amount_out > calculated_amount_out { - expected_amount_out - calculated_amount_out - } else { - calculated_amount_out - expected_amount_out - }; + let difference = expected_amount_out.abs_diff(calculated_amount_out); let relative_difference = FixedU128::from_rational(difference, expected_amount_out); let tolerated_difference = FixedU128::from_rational(1, 100); // The difference of the amount out calculated with spot price should be less than 1% @@ -5391,11 +5387,7 @@ mod route_spot_price { .unwrap() .checked_mul_int(amount_to_sell) .unwrap(); - let difference = if expected_amount_out > calculated_amount_out { - expected_amount_out - calculated_amount_out - } else { - calculated_amount_out - expected_amount_out - }; + let difference = expected_amount_out.abs_diff(calculated_amount_out); let relative_difference = FixedU128::from_rational(difference, expected_amount_out); let tolerated_difference = FixedU128::from_rational(1, 100); // The difference of the amount out calculated with spot price should be less than 1% diff --git a/integration-tests/src/stableswap_curve_comparison.rs b/integration-tests/src/stableswap_curve_comparison.rs index 541eba7159..570ed11840 100644 --- a/integration-tests/src/stableswap_curve_comparison.rs +++ b/integration-tests/src/stableswap_curve_comparison.rs @@ -320,21 +320,10 @@ fn assert_parity(label: &str, hydra: u128, curve: u128, max_tolerance: u128, exp let diff = hydra.abs_diff(curve); assert!( diff <= max_tolerance, - "{}: diff {} exceeds tolerance {} (hydra={}, curve={})", - label, - diff, - max_tolerance, - hydra, - curve + "{label}: diff {diff} exceeds tolerance {max_tolerance} (hydra={hydra}, curve={curve})" ); if expect_hydra_gte { - assert!( - hydra >= curve, - "{}: expected hydra ({}) >= curve ({})", - label, - hydra, - curve - ); + assert!(hydra >= curve, "{label}: expected hydra ({hydra}) >= curve ({curve})"); } } @@ -345,14 +334,7 @@ fn assert_parity_with_fee(label: &str, hydra: u128, curve: u128, max_abs_toleran let tolerance = max_abs_tolerance.max(relative_tolerance); assert!( diff <= tolerance, - "{}: diff {} exceeds tolerance {} (abs={}, rel={}) (hydra={}, curve={})", - label, - diff, - tolerance, - max_abs_tolerance, - relative_tolerance, - hydra, - curve + "{label}: diff {diff} exceeds tolerance {tolerance} (abs={max_abs_tolerance}, rel={relative_tolerance}) (hydra={hydra}, curve={curve})" ); } @@ -763,7 +745,7 @@ fn run_shares_with_fee_comparison( let hydra_shares = hydra_calc_shares(old, new, amp, supply, fee); assert_parity_with_fee( - &format!("{} fee={:?}", label, fee), + &format!("{label} fee={fee:?}"), hydra_shares, curve_shares, MAX_SHARE_TOLERANCE, @@ -960,13 +942,13 @@ fn run_withdraw_with_fee_comparison( hydra_calc_withdraw_one_asset(balances, withdraw_shares, i, total_supply, amp, fee); assert_parity_with_fee( - &format!("{} withdraw amount", label), + &format!("{label} withdraw amount"), hydra_dy, curve_dy, MAX_SWAP_TOLERANCE, ); assert_parity_with_fee( - &format!("{} withdraw fee", label), + &format!("{label} withdraw fee"), hydra_fee_amount, curve_fee_amount, MAX_SWAP_TOLERANCE, diff --git a/integration-tests/src/utils/contracts.rs b/integration-tests/src/utils/contracts.rs index a14a2d4ac6..464965ca84 100644 --- a/integration-tests/src/utils/contracts.rs +++ b/integration-tests/src/utils/contracts.rs @@ -7,10 +7,7 @@ use sp_core::U256; use std::fs; pub fn get_contract_bytecode(name: &str) -> Vec { - let path = format!( - "../scripts/test-contracts/artifacts/contracts/{}.sol/{}.json", - name, name - ); + let path = format!("../scripts/test-contracts/artifacts/contracts/{name}.sol/{name}.json"); let str = fs::read_to_string(path).unwrap(); let json: serde_json::Value = serde_json::from_str(&str).unwrap(); let code = json.get("bytecode").unwrap().as_str().unwrap(); @@ -38,7 +35,7 @@ pub fn deploy_contract_code(code: Vec, deployer: EvmAddress) -> EvmAddress { let address = match info.clone().unwrap().exit_reason { ExitReason::Succeed(_) => info.unwrap().value, - reason => panic!("{:?}", reason), + reason => panic!("{reason:?}"), }; let deployed = hydradx_runtime::Runtime::account_code_at(address); diff --git a/launch-configs/fork/Dockerfile.with-builder b/launch-configs/fork/Dockerfile.with-builder new file mode 100644 index 0000000000..95bc83dd6d --- /dev/null +++ b/launch-configs/fork/Dockerfile.with-builder @@ -0,0 +1,68 @@ +# Fork image built against THIS repo's node (e.g. a PR build) instead of the +# released binary. Stage 1 compiles hydradx in bookworm so its glibc matches the +# node:18-bookworm runtime; scraper + relay binaries still come from releases. +# Build with the repo root as context: +# docker build -f launch-configs/fork/Dockerfile.with-builder -t galacticcouncil/fork:synth . +FROM rust:1.88-bookworm AS builder + +RUN apt-get update && apt-get install -y --no-install-recommends \ + clang libclang-dev protobuf-compiler cmake pkg-config libssl-dev git \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /build +COPY . . + +ENV CXXFLAGS="-include cstdint" +RUN cargo build --release --locked --config net.git-fetch-with-cli=true -p hydradx + +FROM node:18-bookworm + +RUN apt-get update && apt-get install -y \ + curl \ + wget \ + nodejs \ + npm \ + ca-certificates \ + && update-ca-certificates \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /zombienet/hydration-node/launch-configs/fork + +RUN wget -q https://github.com/paritytech/zombienet/releases/download/v1.3.128/zombienet-linux-x64 -O /usr/local/bin/zombienet && \ + chmod +x /usr/local/bin/zombienet +RUN zombienet version + +ARG POLKADOT_SDK_VERSION=latest + +RUN mkdir -p /zombienet/polkadot-sdk/target/release +RUN if [ "$POLKADOT_SDK_VERSION" = "latest" ]; then \ + DOWNLOAD_URL="https://github.com/paritytech/polkadot-sdk/releases/latest/download"; \ + else \ + DOWNLOAD_URL="https://github.com/paritytech/polkadot-sdk/releases/download/$POLKADOT_SDK_VERSION"; \ + fi && \ + for binary in polkadot polkadot-execute-worker polkadot-prepare-worker; do \ + wget -q $DOWNLOAD_URL/$binary \ + -O /zombienet/polkadot-sdk/target/release/$binary \ + && chmod +x /zombienet/polkadot-sdk/target/release/$binary \ + && /zombienet/polkadot-sdk/target/release/$binary --version; \ + done + +ARG HYDRATION_BIN_URL=https://github.com/galacticcouncil/hydration-node/releases/latest/download + +RUN mkdir -p /zombienet/hydration-node/target/release +# scraper is unchanged by this build, pull it from the latest release. +RUN wget -q $HYDRATION_BIN_URL/scraper -O /zombienet/hydration-node/target/release/scraper && \ + chmod +x /zombienet/hydration-node/target/release/scraper +# hydradx comes from the local builder stage (this PR). +COPY --from=builder /build/target/release/hydradx /zombienet/hydration-node/target/release/hydradx +RUN chmod +x /zombienet/hydration-node/target/release/hydradx && \ + /zombienet/hydration-node/target/release/hydradx --version + +COPY launch-configs/fork/ . + +RUN npm i + +VOLUME /zombienet/hydration-node/launch-configs/fork/data +EXPOSE 9988 + +CMD npm start diff --git a/launch-configs/fork/apply-upgrade.mjs b/launch-configs/fork/apply-upgrade.mjs new file mode 100644 index 0000000000..1326bd956a --- /dev/null +++ b/launch-configs/fork/apply-upgrade.mjs @@ -0,0 +1,52 @@ +import { ApiPromise, WsProvider, Keyring } from '@polkadot/api'; +import { hexToU8a } from '@polkadot/util'; +import fs from 'fs'; + +const WASM_PATH = process.env.WASM || + '../../target/release/wbuild/hydradx-runtime/hydradx_runtime.compact.compressed.wasm'; +const ENDPOINT = process.env.ENDPOINT || 'ws://127.0.0.1:9999'; + +const main = async () => { + const wasm = fs.readFileSync(WASM_PATH); + const api = await ApiPromise.create({ provider: new WsProvider(ENDPOINT) }); + const keyring = new Keyring({ type: 'sr25519' }); + const alice = keyring.addFromUri('//Alice'); + + console.log(`endpoint: ${ENDPOINT}`); + console.log(`wasm: ${WASM_PATH} (${wasm.length} bytes)`); + console.log(`spec_version pre: ${(await api.rpc.state.getRuntimeVersion()).specVersion.toString()}`); + const auth = await api.query.system.authorizedUpgrade(); + console.log(`authorized upgrade: ${auth.toString()}`); + + console.log('submitting system.applyAuthorizedUpgrade ...'); + await new Promise((resolve, reject) => { + api.tx.system + .applyAuthorizedUpgrade(`0x${wasm.toString('hex')}`) + .signAndSend(alice, ({ status, dispatchError }) => { + if (dispatchError) { + reject(new Error(dispatchError.toString())); + return; + } + console.log(`status: ${status.type}`); + if (status.isInBlock || status.isFinalized) resolve(); + }) + .catch(reject); + }); + + console.log('waiting for new spec version...'); + for (let i = 0; i < 60; i++) { + await new Promise((r) => setTimeout(r, 2000)); + const v = (await api.rpc.state.getRuntimeVersion()).specVersion.toNumber(); + process.stdout.write(`spec=${v} `); + if (v !== 412) { + console.log(`\nupgraded to spec_version ${v}`); + break; + } + } + await api.disconnect(); +}; + +main().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/launch-configs/fork/check-upgrade.mjs b/launch-configs/fork/check-upgrade.mjs new file mode 100644 index 0000000000..d1fa5c2f72 --- /dev/null +++ b/launch-configs/fork/check-upgrade.mjs @@ -0,0 +1,15 @@ +import { ApiPromise, WsProvider } from '@polkadot/api'; +const api = await ApiPromise.create({ provider: new WsProvider('ws://127.0.0.1:9999') }); + +console.log('parachainSystem storage items:'); +const sys = api.query.parachainSystem || {}; +for (const k of Object.keys(sys)) console.log(' ', k); + +const auth = await api.query.system.authorizedUpgrade(); +console.log('\nSystem.AuthorizedUpgrade:', auth.toHuman()); + +const head = await api.rpc.chain.getHeader(); +console.log('Best block:', head.number.toNumber()); +console.log('Spec version:', (await api.rpc.state.getRuntimeVersion()).specVersion.toNumber()); + +await api.disconnect(); diff --git a/launch-configs/fork/clean-fork.sh b/launch-configs/fork/clean-fork.sh new file mode 100755 index 0000000000..0b89c54fed --- /dev/null +++ b/launch-configs/fork/clean-fork.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Clear zombienet's per-run artifacts under data/ so a restart picks up the +# updated chainspec / freshly injected wasm. Keeps the source files +# (data/forked-chainspec.json, data/state.json, the *.yaml templates, +# zombie-wrapper.sh). +set -euo pipefail +cd "$(dirname "$0")" + +rm -rf \ + data/alice data/alice-1 \ + data/bob data/bob-1 \ + data/charlie data/dave \ + data/cfg data/2034 \ + data/temp data/temp-1 data/temp-collator \ + data/export-genesis-state data/zombie.json \ + data/local-2034-rococo-local*.json data/rococo-local*.json \ + data/logs data/namespace data/finished.txt \ + data/*.log + +echo "cleaned data/ — keeping forked-chainspec.json, state.json, *.yaml" diff --git a/launch-configs/fork/endow-alice.mjs b/launch-configs/fork/endow-alice.mjs new file mode 100644 index 0000000000..94cdb0627e --- /dev/null +++ b/launch-configs/fork/endow-alice.mjs @@ -0,0 +1,57 @@ +import fs from 'fs'; +import { TypeRegistry } from '@polkadot/types'; +import { xxhashAsHex, blake2AsHex } from '@polkadot/util-crypto'; +import { Keyring } from '@polkadot/keyring'; +import { hexToU8a, u8aToHex } from '@polkadot/util'; + +const CHAIN_SPEC = process.argv[2] || 'data/forked-chainspec.json'; +const ALICE_PUB = '0xd43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d'; + +// (asset_id, free_amount_decimal) +const ENDOWMENTS = [ + [2, 1_000_000_000_000_000_000_000n], // DAI (18 decimals): 1000 DAI + [5, 100_000_000_000_000_000_000n], // DOT (10 decimals scaled 10^20 internal): 1000 DOT + [10, 1_000_000_000_000n], // USDT (6 decimals): 1M USDT + [20, 1_000_000_000_000_000_000_000n], // WETH (18 decimals): 1000 WETH + [22, 1_000_000_000_000n], // USDC (6 decimals): 1M USDC +]; + +const tokensAccountsKey = (account, assetId) => { + const palletPrefix = xxhashAsHex('Tokens', 128).replace('0x', ''); + const itemPrefix = xxhashAsHex('Accounts', 128).replace('0x', ''); + const accBlake = blake2AsHex(hexToU8a(account), 128).replace('0x', ''); + const accHex = account.replace('0x', ''); + const idBytes = new Uint8Array(4); + new DataView(idBytes.buffer).setUint32(0, assetId, true); + const idHex = u8aToHex(idBytes).replace('0x', ''); + const idTwox = xxhashAsHex(idBytes, 64).replace('0x', ''); + return '0x' + palletPrefix + itemPrefix + accBlake + accHex + idTwox + idHex; +}; + +const encodeAccountData = (free) => { + const reg = new TypeRegistry(); + reg.register({ + AccountData: { + free: 'u128', + reserved: 'u128', + frozen: 'u128', + }, + }); + const data = reg.createType('AccountData', { free, reserved: 0, frozen: 0 }); + return u8aToHex(data.toU8a()); +}; + +const main = () => { + const spec = JSON.parse(fs.readFileSync(CHAIN_SPEC, 'utf8')); + const top = spec.genesis.raw.top; + for (const [assetId, free] of ENDOWMENTS) { + const key = tokensAccountsKey(ALICE_PUB, assetId); + const value = encodeAccountData(free); + top[key] = value; + console.log(`endowed alice asset=${assetId} free=${free}`); + } + fs.writeFileSync(CHAIN_SPEC, JSON.stringify(spec)); + console.log(`saved ${CHAIN_SPEC}`); +}; + +main(); diff --git a/launch-configs/fork/inject-wasm.mjs b/launch-configs/fork/inject-wasm.mjs new file mode 100644 index 0000000000..2202656ad5 --- /dev/null +++ b/launch-configs/fork/inject-wasm.mjs @@ -0,0 +1,33 @@ +// Replace the runtime wasm in a forked chainspec with a freshly-built local +// wasm. Faster than running a full preauth + applyAuthorizedUpgrade dance +// when iterating on the fork — the parachain comes up at the new spec from +// block 1 (no upgrade extrinsic required). +// +// Usage: +// node inject-wasm.mjs # uses defaults below +// WASM=/path/to.wasm SPEC=data/foo.json node inject-wasm.mjs +// +// Note: zombienet caches a derived spec in data/local-2034-rococo-local.json +// after the first launch — clear data/ before restart so the new wasm is +// picked up. (See `clean-fork.sh` if you have one, or rm -rf the data +// subdirs before `npm run zombie:init`.) + +import fs from 'fs'; + +const WASM_PATH = + process.env.WASM || + '../../target/release/wbuild/hydradx-runtime/hydradx_runtime.compact.compressed.wasm'; +const SPEC_PATH = process.env.SPEC || 'data/forked-chainspec.json'; +const CODE_KEY = '0x3a636f6465'; // sp_storage::well_known_keys::CODE = b":code" + +const wasm = fs.readFileSync(WASM_PATH); +const spec = JSON.parse(fs.readFileSync(SPEC_PATH, 'utf8')); + +const oldLen = (spec.genesis.raw.top[CODE_KEY] || '').length; +spec.genesis.raw.top[CODE_KEY] = '0x' + wasm.toString('hex'); + +fs.writeFileSync(SPEC_PATH, JSON.stringify(spec)); + +console.log(`wasm: ${WASM_PATH} (${wasm.length} bytes)`); +console.log(`spec: ${SPEC_PATH}`); +console.log(`:code: ${oldLen} -> ${spec.genesis.raw.top[CODE_KEY].length} hex chars`); diff --git a/launch-configs/fork/package-lock.json b/launch-configs/fork/package-lock.json index bd92f53a98..5a86284449 100644 --- a/launch-configs/fork/package-lock.json +++ b/launch-configs/fork/package-lock.json @@ -9,16 +9,26 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "@polkadot/api": "^16.5.6", "@polkadot/types": "^15.0.2", + "@polkadot/util-crypto": "^14.0.3", + "ethers": "^6.16.0", "npm-run-all": "^4.1.5" } }, + "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/@noble/curves": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.7.0.tgz", - "integrity": "sha512-UTMhXK9SeDhFJVrHeUJ5uZlI6ajXg10O6Ddocf9S6GjbSBVZsJo88HzKwXznNfGpMTRDyJkqMjNDPYgf0qFWnw==", + "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.6.0" + "@noble/hashes": "1.8.0" }, "engines": { "node": "^14.21.3 || >=16" @@ -27,110 +37,1837 @@ "url": "https://paulmillr.com/funding/" } }, - "node_modules/@noble/curves/node_modules/@noble/hashes": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.0.tgz", - "integrity": "sha512-YUULf0Uk4/mAA89w+k3+yUYh6NrEvxZa5T6SY3wlMvE2chHkxFUUIDI8/XW1QSC357iA5pSnqt7XEhvFOqmDyQ==", + "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/" + "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-augment/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/api-augment/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/api-augment/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/api-augment/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/api-augment/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/api-augment/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/api-augment/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/api-augment/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/api-augment/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/api-augment/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/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-base/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/api-base/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/api-base/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/api-base/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/api-base/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/api-base/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/api-base/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/api-base/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/api-base/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/api-base/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/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/api-derive/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/api-derive/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/api-derive/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/api-derive/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/api-derive/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/api-derive/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/api-derive/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/api-derive/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/api-derive/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/api-derive/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/api/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/api/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/api/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/api/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/api/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/api/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/api/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/api/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/api/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/api/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/keyring": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-13.2.3.tgz", + "integrity": "sha512-pgTo6DXNXub0wGD+MnVHYhKxf80Jl+QMOCb818ioGdXz++Uw4mTueFAwtB+N7TGo0HafhChUiNJDxFdlDkcAng==", + "dependencies": { + "@polkadot/util": "13.2.3", + "@polkadot/util-crypto": "13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.2.3", + "@polkadot/util-crypto": "13.2.3" + } + }, + "node_modules/@polkadot/keyring/node_modules/@polkadot/networks": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-13.2.3.tgz", + "integrity": "sha512-mG+zkXg/33AyPrkv2xBbAo3LBUwOwBn6qznBU/4jxiZPnVvCwMaxE7xHM22B5riItbNJ169FXv3wy0v6ZmkFbw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "13.2.3", + "@substrate/ss58-registry": "^1.51.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/keyring/node_modules/@polkadot/util-crypto": { + "version": "13.2.3", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.2.3.tgz", + "integrity": "sha512-5sbggmLbn5eiuVMyPROPlT5roHRqdKHOfSpioNbGvGIZ1qIWVoC1RfsK0NWJOVGDzy6DpQe0KYT/kgcU5Xsrzw==", + "license": "Apache-2.0", + "dependencies": { + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "13.2.3", + "@polkadot/util": "13.2.3", + "@polkadot/wasm-crypto": "^7.4.1", + "@polkadot/wasm-util": "^7.4.1", + "@polkadot/x-bigint": "13.2.3", + "@polkadot/x-randomvalues": "13.2.3", + "@scure/base": "^1.1.7", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.2.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/networks/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/networks/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/networks/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/networks/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/networks/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/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-augment/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/rpc-augment/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/rpc-augment/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/rpc-augment/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/rpc-augment/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/rpc-augment/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/rpc-augment/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/rpc-augment/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/rpc-augment/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/rpc-augment/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/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-core/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/rpc-core/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/rpc-core/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/rpc-core/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/rpc-core/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/rpc-core/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/rpc-core/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/rpc-core/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/rpc-core/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/rpc-core/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/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/rpc-provider/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/rpc-provider/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/rpc-provider/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/rpc-provider/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/rpc-provider/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/rpc-provider/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/rpc-provider/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/rpc-provider/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/rpc-provider/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/rpc-provider/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/types": { + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-15.0.2.tgz", + "integrity": "sha512-6gZBnuXU58hQAXWI2daED2OaQwFMxbQdkE8HVGMovMobEf0PxPfIqf+GdnVmWbe09EU9mv2gCWBcdnvHSRBlQg==", + "dependencies": { + "@polkadot/keyring": "^13.2.3", + "@polkadot/types-augment": "15.0.2", + "@polkadot/types-codec": "15.0.2", + "@polkadot/types-create": "15.0.2", + "@polkadot/util": "^13.2.3", + "@polkadot/util-crypto": "^13.2.3", + "rxjs": "^7.8.1", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-augment": { + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-15.0.2.tgz", + "integrity": "sha512-UiFJVEYML30+V9GdFAHPbA3s4MVQTL1CevsZMnX0+ApvlgEHJMZnVFfYF7jL2bl9BcUYM/zoxEAhj2MpqFFfxw==", + "dependencies": { + "@polkadot/types": "15.0.2", + "@polkadot/types-codec": "15.0.2", + "@polkadot/util": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-codec": { + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-15.0.2.tgz", + "integrity": "sha512-44Q40p1rl0t7Bl1QUamewqXNVPway9xgqByyifv6ODSGhtt+lFoarb3U4JzqRUuuK0PP57ePB0L8q81Totxeew==", + "dependencies": { + "@polkadot/util": "^13.2.3", + "@polkadot/x-bigint": "^13.2.3", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@polkadot/types-create": { + "version": "15.0.2", + "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-15.0.2.tgz", + "integrity": "sha512-YhpcqbH3oI87PkgrV6Fez9jWDqFIep0KcS1YWQcwc9gsBNnuour80t2AAK41/tqAYwOZi6tpJwIevnEhVkxFYA==", + "dependencies": { + "@polkadot/types-codec": "15.0.2", + "@polkadot/util": "^13.2.3", + "tslib": "^2.8.0" + }, + "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-known/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/types-known/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-known/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-known/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-known/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/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/types-known/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/types-known/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/types-known/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/types-known/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/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/types-support/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/types-support/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/types-support/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/types-support/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/types-support/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/types/node_modules/@polkadot/networks": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-13.5.9.tgz", + "integrity": "sha512-nmKUKJjiLgcih0MkdlJNMnhEYdwEml2rv/h59ll2+rAvpsVWMTLCb6Cq6q7UC44+8kiWK2UUJMkFU+3PFFxndA==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/util": "13.5.9", + "@substrate/ss58-registry": "^1.51.0", + "tslib": "^2.8.0" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@noble/hashes": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.6.1.tgz", - "integrity": "sha512-pq5D8h10hHBjyqX+cfBm0i8JUXJ0UhczFc4r74zbuT9XgewFo2E3J1cOaGtdZynILNmQ685YWGzGE1Zv6io50w==", - "engines": { - "node": "^14.21.3 || >=16" + "node_modules/@polkadot/types/node_modules/@polkadot/util": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/util/-/util-13.5.9.tgz", + "integrity": "sha512-pIK3XYXo7DKeFRkEBNYhf3GbCHg6dKQisSvdzZwuyzA6m7YxQq4DFw4IE464ve4Z7WsJFt3a6C9uII36hl9EWw==", + "license": "Apache-2.0", + "dependencies": { + "@polkadot/x-bigint": "13.5.9", + "@polkadot/x-global": "13.5.9", + "@polkadot/x-textdecoder": "13.5.9", + "@polkadot/x-textencoder": "13.5.9", + "@types/bn.js": "^5.1.6", + "bn.js": "^5.2.1", + "tslib": "^2.8.0" }, - "funding": { - "url": "https://paulmillr.com/funding/" + "engines": { + "node": ">=18" } }, - "node_modules/@polkadot/keyring": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/@polkadot/keyring/-/keyring-13.2.3.tgz", - "integrity": "sha512-pgTo6DXNXub0wGD+MnVHYhKxf80Jl+QMOCb818ioGdXz++Uw4mTueFAwtB+N7TGo0HafhChUiNJDxFdlDkcAng==", + "node_modules/@polkadot/types/node_modules/@polkadot/util-crypto": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.5.9.tgz", + "integrity": "sha512-foUesMhxkTk8CZ0/XEcfvHk6I0O+aICqqVJllhOpyp/ZVnrTBKBf59T6RpsXx2pCtBlMsLRvg/6Mw7RND1HqDg==", + "license": "Apache-2.0", "dependencies": { - "@polkadot/util": "13.2.3", - "@polkadot/util-crypto": "13.2.3", + "@noble/curves": "^1.3.0", + "@noble/hashes": "^1.3.3", + "@polkadot/networks": "13.5.9", + "@polkadot/util": "13.5.9", + "@polkadot/wasm-crypto": "^7.5.3", + "@polkadot/wasm-util": "^7.5.3", + "@polkadot/x-bigint": "13.5.9", + "@polkadot/x-randomvalues": "13.5.9", + "@scure/base": "^1.1.7", "tslib": "^2.8.0" }, "engines": { "node": ">=18" }, "peerDependencies": { - "@polkadot/util": "13.2.3", - "@polkadot/util-crypto": "13.2.3" + "@polkadot/util": "13.5.9" } }, - "node_modules/@polkadot/networks": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/@polkadot/networks/-/networks-13.2.3.tgz", - "integrity": "sha512-mG+zkXg/33AyPrkv2xBbAo3LBUwOwBn6qznBU/4jxiZPnVvCwMaxE7xHM22B5riItbNJ169FXv3wy0v6ZmkFbw==", + "node_modules/@polkadot/types/node_modules/@polkadot/x-bigint": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-bigint/-/x-bigint-13.5.9.tgz", + "integrity": "sha512-JVW6vw3e8fkcRyN9eoc6JIl63MRxNQCP/tuLdHWZts1tcAYao0hpWUzteqJY93AgvmQ91KPsC1Kf3iuuZCi74g==", + "license": "Apache-2.0", "dependencies": { - "@polkadot/util": "13.2.3", - "@substrate/ss58-registry": "^1.51.0", + "@polkadot/x-global": "13.5.9", "tslib": "^2.8.0" }, "engines": { "node": ">=18" } }, - "node_modules/@polkadot/types": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@polkadot/types/-/types-15.0.2.tgz", - "integrity": "sha512-6gZBnuXU58hQAXWI2daED2OaQwFMxbQdkE8HVGMovMobEf0PxPfIqf+GdnVmWbe09EU9mv2gCWBcdnvHSRBlQg==", + "node_modules/@polkadot/types/node_modules/@polkadot/x-global": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-13.5.9.tgz", + "integrity": "sha512-zSRWvELHd3Q+bFkkI1h2cWIqLo1ETm+MxkNXLec3lB56iyq/MjWBxfXnAFFYFayvlEVneo7CLHcp+YTFd9aVSA==", + "license": "Apache-2.0", "dependencies": { - "@polkadot/keyring": "^13.2.3", - "@polkadot/types-augment": "15.0.2", - "@polkadot/types-codec": "15.0.2", - "@polkadot/types-create": "15.0.2", - "@polkadot/util": "^13.2.3", - "@polkadot/util-crypto": "^13.2.3", - "rxjs": "^7.8.1", "tslib": "^2.8.0" }, "engines": { "node": ">=18" } }, - "node_modules/@polkadot/types-augment": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@polkadot/types-augment/-/types-augment-15.0.2.tgz", - "integrity": "sha512-UiFJVEYML30+V9GdFAHPbA3s4MVQTL1CevsZMnX0+ApvlgEHJMZnVFfYF7jL2bl9BcUYM/zoxEAhj2MpqFFfxw==", + "node_modules/@polkadot/types/node_modules/@polkadot/x-randomvalues": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-13.5.9.tgz", + "integrity": "sha512-Uuuz3oubf1JCCK97fsnVUnHvk4BGp/W91mQWJlgl5TIOUSSTIRr+lb5GurCfl4kgnQq53Zi5fJV+qR9YumbnZw==", + "license": "Apache-2.0", "dependencies": { - "@polkadot/types": "15.0.2", - "@polkadot/types-codec": "15.0.2", - "@polkadot/util": "^13.2.3", + "@polkadot/x-global": "13.5.9", "tslib": "^2.8.0" }, "engines": { "node": ">=18" + }, + "peerDependencies": { + "@polkadot/util": "13.5.9", + "@polkadot/wasm-util": "*" } }, - "node_modules/@polkadot/types-codec": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@polkadot/types-codec/-/types-codec-15.0.2.tgz", - "integrity": "sha512-44Q40p1rl0t7Bl1QUamewqXNVPway9xgqByyifv6ODSGhtt+lFoarb3U4JzqRUuuK0PP57ePB0L8q81Totxeew==", + "node_modules/@polkadot/types/node_modules/@polkadot/x-textdecoder": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-textdecoder/-/x-textdecoder-13.5.9.tgz", + "integrity": "sha512-W2HhVNUbC/tuFdzNMbnXAWsIHSg9SC9QWDNmFD3nXdSzlXNgL8NmuiwN2fkYvCQBtp/XSoy0gDLx0C+Fo19cfw==", + "license": "Apache-2.0", "dependencies": { - "@polkadot/util": "^13.2.3", - "@polkadot/x-bigint": "^13.2.3", + "@polkadot/x-global": "13.5.9", "tslib": "^2.8.0" }, "engines": { "node": ">=18" } }, - "node_modules/@polkadot/types-create": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/@polkadot/types-create/-/types-create-15.0.2.tgz", - "integrity": "sha512-YhpcqbH3oI87PkgrV6Fez9jWDqFIep0KcS1YWQcwc9gsBNnuour80t2AAK41/tqAYwOZi6tpJwIevnEhVkxFYA==", + "node_modules/@polkadot/types/node_modules/@polkadot/x-textencoder": { + "version": "13.5.9", + "resolved": "https://registry.npmjs.org/@polkadot/x-textencoder/-/x-textencoder-13.5.9.tgz", + "integrity": "sha512-SG0MHnLUgn1ZxFdm0KzMdTHJ47SfqFhdIPMcGA0Mg/jt2rwrfrP3jtEIJMsHfQpHvfsNPfv55XOMmoPWuQnP/Q==", + "license": "Apache-2.0", "dependencies": { - "@polkadot/types-codec": "15.0.2", - "@polkadot/util": "^13.2.3", + "@polkadot/x-global": "13.5.9", "tslib": "^2.8.0" }, "engines": { @@ -155,34 +1892,123 @@ } }, "node_modules/@polkadot/util-crypto": { - "version": "13.2.3", - "resolved": "https://registry.npmjs.org/@polkadot/util-crypto/-/util-crypto-13.2.3.tgz", - "integrity": "sha512-5sbggmLbn5eiuVMyPROPlT5roHRqdKHOfSpioNbGvGIZ1qIWVoC1RfsK0NWJOVGDzy6DpQe0KYT/kgcU5Xsrzw==", + "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": "13.2.3", - "@polkadot/util": "13.2.3", - "@polkadot/wasm-crypto": "^7.4.1", - "@polkadot/wasm-util": "^7.4.1", - "@polkadot/x-bigint": "13.2.3", - "@polkadot/x-randomvalues": "13.2.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": "13.2.3" + "@polkadot/util": "14.0.3" + } + }, + "node_modules/@polkadot/util-crypto/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/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/util-crypto/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/util-crypto/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/util-crypto/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/util-crypto/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/wasm-bridge": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-bridge/-/wasm-bridge-7.4.1.tgz", - "integrity": "sha512-tdkJaV453tezBxhF39r4oeG0A39sPKGDJmN81LYLf+Fihb7astzwju+u75BRmDrHZjZIv00un3razJEWCxze6g==", + "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.4.1", + "@polkadot/wasm-util": "7.5.4", "tslib": "^2.7.0" }, "engines": { @@ -194,15 +2020,16 @@ } }, "node_modules/@polkadot/wasm-crypto": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto/-/wasm-crypto-7.4.1.tgz", - "integrity": "sha512-kHN/kF7hYxm1y0WeFLWeWir6oTzvcFmR4N8fJJokR+ajYbdmrafPN+6iLgQVbhZnDdxyv9jWDuRRsDnBx8tPMQ==", - "dependencies": { - "@polkadot/wasm-bridge": "7.4.1", - "@polkadot/wasm-crypto-asmjs": "7.4.1", - "@polkadot/wasm-crypto-init": "7.4.1", - "@polkadot/wasm-crypto-wasm": "7.4.1", - "@polkadot/wasm-util": "7.4.1", + "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": { @@ -214,9 +2041,10 @@ } }, "node_modules/@polkadot/wasm-crypto-asmjs": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-7.4.1.tgz", - "integrity": "sha512-pwU8QXhUW7IberyHJIQr37IhbB6DPkCG5FhozCiNTq4vFBsFPjm9q8aZh7oX1QHQaiAZa2m2/VjIVE+FHGbvHQ==", + "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" }, @@ -228,14 +2056,15 @@ } }, "node_modules/@polkadot/wasm-crypto-init": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-init/-/wasm-crypto-init-7.4.1.tgz", - "integrity": "sha512-AVka33+f7MvXEEIGq5U0dhaA2SaXMXnxVCQyhJTaCnJ5bRDj0Xlm3ijwDEQUiaDql7EikbkkRtmlvs95eSUWYQ==", - "dependencies": { - "@polkadot/wasm-bridge": "7.4.1", - "@polkadot/wasm-crypto-asmjs": "7.4.1", - "@polkadot/wasm-crypto-wasm": "7.4.1", - "@polkadot/wasm-util": "7.4.1", + "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": { @@ -247,11 +2076,12 @@ } }, "node_modules/@polkadot/wasm-crypto-wasm": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-7.4.1.tgz", - "integrity": "sha512-PE1OAoupFR0ZOV2O8tr7D1FEUAwaggzxtfs3Aa5gr+yxlSOaWUKeqsOYe1KdrcjmZVV3iINEAXxgrbzCmiuONg==", + "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.4.1", + "@polkadot/wasm-util": "7.5.4", "tslib": "^2.7.0" }, "engines": { @@ -262,9 +2092,10 @@ } }, "node_modules/@polkadot/wasm-util": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/@polkadot/wasm-util/-/wasm-util-7.4.1.tgz", - "integrity": "sha512-RAcxNFf3zzpkr+LX/ItAsvj+QyM56TomJ0xjUMo4wKkHjwsxkz4dWJtx5knIgQz/OthqSDMR59VNEycQeNuXzA==", + "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" }, @@ -287,6 +2118,32 @@ "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-fetch/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-global": { "version": "13.2.3", "resolved": "https://registry.npmjs.org/@polkadot/x-global/-/x-global-13.2.3.tgz", @@ -302,6 +2159,7 @@ "version": "13.2.3", "resolved": "https://registry.npmjs.org/@polkadot/x-randomvalues/-/x-randomvalues-13.2.3.tgz", "integrity": "sha512-Zf0GTfLmVk+VzPUmcQSpXjjmFzMTjPhXoLuIoE7xIu73T+vQ+TX9j7DvorN6bIRsnZ9l1SyTZsSf/NTjNZKIZg==", + "license": "Apache-2.0", "dependencies": { "@polkadot/x-global": "13.2.3", "tslib": "^2.8.0" @@ -338,6 +2196,53 @@ "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/@polkadot/x-ws/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-ws/node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "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/@scure/base": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.1.tgz", @@ -346,10 +2251,71 @@ "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==" + "integrity": "sha512-TWDurLiPxndFgKjVavCniytBIw+t4ViOi7TYp9h/D0NMmkEc9klFTo+827eyEJ0lELpqO207Ey7uGxUa+BS1jQ==", + "license": "Apache-2.0" }, "node_modules/@types/bn.js": { "version": "5.1.6", @@ -367,6 +2333,12 @@ "undici-types": "~6.20.0" } }, + "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/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -536,6 +2508,15 @@ "node": ">=4.8" } }, + "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/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -584,6 +2565,23 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/define-data-property": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", @@ -761,6 +2759,108 @@ "node": ">=0.8.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/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/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/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -769,6 +2869,18 @@ "is-callable": "^1.1.3" } }, + "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/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -1288,6 +3400,12 @@ "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==" }, + "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/load-json-file": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", @@ -1329,11 +3447,78 @@ "node": "*" } }, + "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/nice-try": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==" }, + "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/normalize-package-data": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", @@ -1470,6 +3655,15 @@ "node": ">= 0.4" } }, + "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/read-pkg": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", @@ -1582,6 +3776,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/semver": { "version": "5.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", @@ -1718,6 +3919,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "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/spdx-correct": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", @@ -1952,6 +4163,15 @@ "spdx-expression-parse": "^3.0.0" } }, + "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/which": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", @@ -2042,6 +4262,27 @@ "funding": { "url": "https://github.com/sponsors/ljharb" } + }, + "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 + } + } } } -} \ No newline at end of file +} diff --git a/launch-configs/fork/package.json b/launch-configs/fork/package.json index 71140e923f..19d1f4e1b3 100644 --- a/launch-configs/fork/package.json +++ b/launch-configs/fork/package.json @@ -28,7 +28,10 @@ "author": "", "license": "ISC", "dependencies": { + "@polkadot/api": "^16.5.6", "@polkadot/types": "^15.0.2", + "@polkadot/util-crypto": "^14.0.3", + "ethers": "^6.16.0", "npm-run-all": "^4.1.5" } } diff --git a/launch-configs/fork/prepare-state-for-zombienet.js b/launch-configs/fork/prepare-state-for-zombienet.js index 8d28fd505a..f8b17fd621 100644 --- a/launch-configs/fork/prepare-state-for-zombienet.js +++ b/launch-configs/fork/prepare-state-for-zombienet.js @@ -9,7 +9,7 @@ const NEW_ID = process.env.CHAIN_ID || "local_testnet"; const NEW_RELAY_CHAIN = "rococo_local_testnet"; // Define replacement values -const AURA_AUTHORITIES_VALUE = "0x08be4f21c56d926b91f020b5071f14935cb93f001f1127c53d3eac6eed23ffea64dc4d79aad5a9d01a359995838830a80733a0bff7e4eb087bfc621ef1873fec49"; +const AURA_AUTHORITIES_VALUE = "0x08d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d8eaf04151687736326c9fea17e25fc5287613693c912909cb226aa4794f26a48"; const COUNCIL_AND_TECHNICAL_COMMITTEE_VALUE = "0x04d43593c715fdd31c61141abd04a99fd6822c8558854ccde39a5684e7a56da27d"; const SYSTEM_ACCOUNT_VALUE = "0x00000000000000000100000000000000ba31bc09df123864f700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; diff --git a/node/Cargo.toml b/node/Cargo.toml index 9845b0ca9e..3b3d6a579a 100644 --- a/node/Cargo.toml +++ b/node/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx" -version = "15.0.2" +version = "15.1.5" description = "Hydration node" authors = ["GalacticCouncil"] edition = "2021" @@ -26,6 +26,7 @@ serde = { workspace = true } serde_json = { workspace = true } clap = { workspace = true } futures = { workspace = true } +futures-timer = "3.0.3" async-trait = { workspace = true } hyperv14 = { package = "hyper", features = ["http2", "stream", "tcp"], version = "0.14.29", default-features = true } hyper-rustls = { version = "0.24.2", features = ["http1", "http2", "webpki-roots", "tokio-runtime"], default-features = false } @@ -35,6 +36,8 @@ num_cpus = { version = "1.13.1" } ethereum = { workspace = true } ethabi = { workspace = true } kvdb-rocksdb = "0.19.0" +lru = "0.12" +rlp = "0.6" # local dependencies hydradx-runtime = { workspace = true, features = ["std"] } @@ -66,6 +69,7 @@ sp-api = { workspace = true } sp-std = { workspace = true } sp-block-builder = { workspace = true } sp-blockchain = { workspace = true } +sp-storage = { workspace = true } sp-consensus = { workspace = true } sp-consensus-aura = { workspace = true } sp-core = { workspace = true } diff --git a/node/src/lib.rs b/node/src/lib.rs index 4331956082..503453a6fb 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -20,3 +20,4 @@ pub mod cli; pub mod liquidation_worker; pub mod rpc; pub mod service; +pub mod synthetic_logs; diff --git a/node/src/main.rs b/node/src/main.rs index c0efd1b715..babb4f145a 100644 --- a/node/src/main.rs +++ b/node/src/main.rs @@ -27,6 +27,7 @@ mod cli; mod command; mod liquidation_worker; mod rpc; +mod synthetic_logs; #[allow(clippy::result_large_err)] fn main() -> sc_cli::Result<()> { diff --git a/node/src/rpc.rs b/node/src/rpc.rs index cd94aaffdd..6c49e2acb0 100644 --- a/node/src/rpc.rs +++ b/node/src/rpc.rs @@ -186,6 +186,13 @@ where execute_gas_limit_multiplier, } = deps; + // Clones for the custom `eth_getLogs` (registered below) — `frontier_backend` + // and `block_data_cache` are moved into `EthFilter::new`. + let getlogs_client = client.clone(); + let getlogs_backend = frontier_backend.clone(); + let getlogs_cache = block_data_cache.clone(); + let getlogs_max = max_past_logs; + let mut signers = Vec::new(); if enable_dev_signer { signers.push(Box::new(EthDevSigner::new()) as Box); @@ -253,6 +260,20 @@ where .into_rpc(), )?; + // Replace fc-rpc's `eth_getLogs` with the synth-aware, canonical-hash filter: + // fc-rpc derives `log.blockHash` from `current_block().header`, so it must stay + // canonical (no bloom mutation) to round-trip via `eth_getBlockByHash`. + io.remove_method("eth_getLogs"); + io.register_async_method("eth_getLogs", move |params, _ctx, _ext| { + let client = getlogs_client.clone(); + let backend = getlogs_backend.clone(); + let cache = getlogs_cache.clone(); + async move { + let filter = params.one::()?; + crate::synthetic_logs::eth_filter::logs(client, backend, cache, getlogs_max, filter).await + } + })?; + io.merge( EthPubSub::new( pool, diff --git a/node/src/service.rs b/node/src/service.rs index aafe0ecfb9..2d7c5fdd4f 100644 --- a/node/src/service.rs +++ b/node/src/service.rs @@ -387,7 +387,18 @@ async fn start_node_impl( ); } - let overrides = Arc::new(crate::rpc::StorageOverrideHandler::new(client.clone())); + // Wrap the stock storage override so eth-rpc reads also surface synthetic + // logs (substrate transfers + swaps), translated client-side from each + // block's events read out of state — no on-chain synth txs, and works on + // any runtime version (no runtime-API dependency). + let overrides: Arc> = + Arc::new(crate::synthetic_logs::storage_override::SyntheticStorageOverride::< + ParachainClient, + ParachainBackend, + >::new( + Arc::new(crate::rpc::StorageOverrideHandler::new(client.clone())), + client.clone(), + )); let block_data_cache = Arc::new(fc_rpc::EthBlockDataCacheTask::new( task_manager.spawn_handle(), overrides.clone(), diff --git a/node/src/service/evm.rs b/node/src/service/evm.rs index 1934a0145c..a033af63ad 100644 --- a/node/src/service/evm.rs +++ b/node/src/service/evm.rs @@ -20,10 +20,11 @@ // http://www.apache.org/licenses/LICENSE-2.0 use crate::service::ParachainClient; +use crate::synthetic_logs::mapping_sync::SyntheticMappingSyncWorker; use cumulus_client_consensus_common::ParachainBlockImportMarker; use fc_consensus::Error; use fc_db::kv::Backend as FrontierBackend; -use fc_mapping_sync::{kv::MappingSyncWorker, SyncStrategy}; +use fc_mapping_sync::SyncStrategy; use fc_rpc::{EthTask, StorageOverride}; use fc_rpc_core::types::{FeeHistoryCache, FeeHistoryCacheLimit, FilterPool}; use fp_consensus::ensure_log; @@ -159,7 +160,7 @@ pub fn spawn_frontier_tasks( task_manager.spawn_essential_handle().spawn( "frontier-mapping-sync-worker", None, - MappingSyncWorker::new( + SyntheticMappingSyncWorker::new( client.import_notification_stream(), Duration::new(6, 0), client.clone(), diff --git a/node/src/synthetic_logs/eth_filter.rs b/node/src/synthetic_logs/eth_filter.rs new file mode 100644 index 0000000000..1ba4c2a946 --- /dev/null +++ b/node/src/synthetic_logs/eth_filter.rs @@ -0,0 +1,180 @@ +// This file is part of hydration-node. +// +// Copyright (C) 2020-2026 Intergalactic, Limited (GIB). +// SPDX-License-Identifier: Apache-2.0 + +//! Custom `eth_getLogs` for the synthetic-logs node-indexing variant. +//! +//! fc-rpc's filter both prefilters on, and derives `log.blockHash` from, +//! `current_block().header` — so OR-ing the synth bloom into that header (to keep +//! the prefilter from skipping synth-only blocks) would change the canonical eth +//! block hash and break `eth_getBlockByHash`/receipt round-trips. Instead the +//! override keeps the header canonical, and this filter builds the prefilter bloom +//! from the block's real+synth tx statuses, so synth blocks are matched while +//! `log.blockHash` stays canonical. + +use std::{ + sync::Arc, + time::{Duration, Instant}, +}; + +use ethereum::BlockV3 as EthereumBlock; +use fc_db::kv::Backend as FrontierBackend; +use fc_rpc::{frontier_backend_client, internal_err, EthBlockDataCacheTask}; +use fc_rpc_core::types::{Bytes, Filter, FilteredParams, Log}; +use fp_rpc::TransactionStatus; +use jsonrpsee::core::RpcResult; +use primitives::Block; +use sp_blockchain::HeaderBackend; +use sp_core::{hashing::keccak_256, H256, U256}; +use sp_runtime::traits::{NumberFor, One, UniqueSaturatedInto}; + +const MAX_DURATION: Duration = Duration::from_secs(10); + +/// `eth_getLogs` over the synth-aware override, preserving canonical block hashes. +pub async fn logs( + client: Arc, + backend: Arc>, + block_data_cache: Arc>, + max_past_logs: u32, + filter: Filter, +) -> RpcResult> +where + C: HeaderBackend + Send + Sync + 'static, +{ + if let Some(hash) = filter.block_hash { + let substrate_hash = + match frontier_backend_client::load_hash::(client.as_ref(), backend.as_ref(), hash).await? { + Some(h) => h, + None => return Err(fc_rpc::err(-32000, "unknown block", None)), + }; + let block = block_data_cache.current_block(substrate_hash).await; + let statuses = block_data_cache.current_transaction_statuses(substrate_hash).await; + return Ok(match (block, statuses) { + (Some(block), Some(statuses)) => filter_block_logs(&filter, block, statuses), + _ => Vec::new(), + }); + } + + let best = client.info().best_number; + let to = filter + .to_block + .and_then(|v| v.to_min_block_num()) + .map(|s| s.unique_saturated_into()) + .unwrap_or(best) + .min(best); + let from = filter + .from_block + .and_then(|v| v.to_min_block_num()) + .map(|s| s.unique_saturated_into()) + .unwrap_or(best); + + range_logs(client.as_ref(), &block_data_cache, max_past_logs, &filter, from, to).await +} + +async fn range_logs( + client: &C, + block_data_cache: &EthBlockDataCacheTask, + max_past_logs: u32, + filter: &Filter, + from: NumberFor, + to: NumberFor, +) -> RpcResult> +where + C: HeaderBackend, +{ + let mut ret = Vec::new(); + if from > to { + return Ok(ret); + } + let address_bloom = FilteredParams::address_bloom_filter(&filter.address); + let topics_bloom = FilteredParams::topics_bloom_filter(&filter.topics()); + let begin = Instant::now(); + let mut current = from; + + loop { + if let Some(substrate_hash) = client.hash(current).map_err(|e| internal_err(format!("{e:?}")))? { + let block = block_data_cache.current_block(substrate_hash).await; + let statuses = block_data_cache.current_transaction_statuses(substrate_hash).await; + if let (Some(block), Some(statuses)) = (block, statuses) { + // Synth-aware prefilter: the canonical header bloom only covers real + // txs, so OR in each (real + synth) tx status's bloom. + let mut bloom = block.header.logs_bloom; + for status in &statuses { + for (b, s) in bloom.0.iter_mut().zip(status.logs_bloom.0.iter()) { + *b |= *s; + } + } + if FilteredParams::address_in_bloom(bloom, &address_bloom) + && FilteredParams::topics_in_bloom(bloom, &topics_bloom) + { + ret.extend(filter_block_logs(filter, block, statuses)); + } + } + } + + if ret.len() as u32 > max_past_logs { + return Err(internal_err(format!( + "query returned more than {max_past_logs} results" + ))); + } + if begin.elapsed() > MAX_DURATION { + return Err(internal_err(format!( + "query timeout of {} seconds exceeded", + MAX_DURATION.as_secs() + ))); + } + if current == to { + break; + } + current = current.saturating_add(One::one()); + } + Ok(ret) +} + +// Mirror of fc-rpc's `filter_block_logs`, but `block` carries the canonical header +// (the override doesn't mutate it), so `block_hash` here equals the fc-db mapping +// key and round-trips via `eth_getBlockByHash`. Counters kept manual to match +// upstream (`block_log_index` spans both loops, so it isn't an `enumerate`). +#[allow(clippy::explicit_counter_loop)] +fn filter_block_logs(filter: &Filter, block: EthereumBlock, statuses: Vec) -> Vec { + let params = FilteredParams::new(filter.clone()); + let block_hash = H256::from(keccak_256(&rlp::encode(&block.header))); + let mut block_log_index: u32 = 0; + let mut logs = Vec::new(); + for status in statuses.iter() { + let mut transaction_log_index: u32 = 0; + let transaction_hash = status.transaction_hash; + for ethereum_log in &status.logs { + let mut log = Log { + address: ethereum_log.address, + topics: ethereum_log.topics.clone(), + data: Bytes(ethereum_log.data.clone()), + block_hash: None, + block_number: None, + transaction_hash: None, + transaction_index: None, + log_index: None, + transaction_log_index: None, + removed: false, + }; + let topics_match = filter.topics().is_empty() || params.filter_topics(&log.topics); + let address_match = filter + .address + .as_ref() + .is_none_or(|_| params.filter_address(&log.address)); + if topics_match && address_match { + log.block_hash = Some(block_hash); + log.block_number = Some(block.header.number); + log.transaction_hash = Some(transaction_hash); + log.transaction_index = Some(U256::from(status.transaction_index)); + log.log_index = Some(U256::from(block_log_index)); + log.transaction_log_index = Some(U256::from(transaction_log_index)); + logs.push(log); + } + transaction_log_index += 1; + block_log_index += 1; + } + } + logs +} diff --git a/node/src/synthetic_logs/mapping_sync.rs b/node/src/synthetic_logs/mapping_sync.rs new file mode 100644 index 0000000000..5e10db0740 --- /dev/null +++ b/node/src/synthetic_logs/mapping_sync.rs @@ -0,0 +1,467 @@ +// Adapted from Frontier's `fc-mapping-sync` (kv backend). +// Copyright (C) Parity Technologies (UK) Ltd. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// +// Vendored verbatim except for ONE change in `sync_block`: the commitment's +// `ethereum_transaction_hashes` is taken from the synth-aware `StorageOverride` +// (our `SyntheticStorageOverride`, which returns `[real ++ synth]` statuses), +// instead of only the consensus-digest (real) hashes. That indexes the +// synthetic-tx hashes in `fc-db` so `eth_getTransactionByHash` / +// `eth_getTransactionReceipt(synthHash)` resolve. Everything else (catch-up +// sync, reorg handling, notifications) is the upstream logic unchanged. + +#![allow(clippy::too_many_arguments)] + +use std::{collections::HashMap, pin::Pin, sync::Arc, time::Duration}; + +use futures::{ + prelude::*, + task::{Context, Poll}, +}; +use futures_timer::Delay; +use log::debug; +use sc_client_api::{ + backend::{Backend, StorageProvider}, + client::ImportNotifications, +}; +use sp_api::{ApiExt, ProvideRuntimeApi}; +use sp_blockchain::{Backend as _, HeaderBackend}; +use sp_consensus::SyncOracle; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT, UniqueSaturatedInto, Zero}; + +use fc_mapping_sync::{ + emit_block_notification, BlockNotificationContext, EthereumBlockNotification, EthereumBlockNotificationSinks, + ReorgInfo, SyncStrategy, +}; +use fc_rpc::StorageOverride; +use fp_consensus::{FindLogError, Hashes, Log, PostLog, PreLog}; +use fp_rpc::EthereumRuntimeRPCApi; + +pub fn sync_block>( + storage_override: Arc>, + backend: &fc_db::kv::Backend, + header: &Block::Header, +) -> Result<(), String> { + let substrate_block_hash = header.hash(); + let block_number: u64 = (*header.number()).unique_saturated_into(); + + // `[real ++ synth]` tx hashes via the synth-aware override (phase 3). Falls + // back to the consensus-digest (real-only) hashes if the override yields + // nothing for this block. + let augment = |fallback: Vec| -> Vec { + match storage_override.current_transaction_statuses(substrate_block_hash) { + Some(statuses) if !statuses.is_empty() => statuses.into_iter().map(|s| s.transaction_hash).collect(), + _ => fallback, + } + }; + + match fp_consensus::find_log(header.digest()) { + Ok(log) => { + let gen_from_hashes = |hashes: Hashes| -> fc_db::kv::MappingCommitment { + fc_db::kv::MappingCommitment { + block_hash: substrate_block_hash, + ethereum_block_hash: hashes.block_hash, + ethereum_transaction_hashes: augment(hashes.transaction_hashes), + } + }; + let gen_from_block = |block| -> fc_db::kv::MappingCommitment { + let hashes = Hashes::from_block(block); + gen_from_hashes(hashes) + }; + + match log { + Log::Pre(PreLog::Block(block)) => { + let mapping_commitment = gen_from_block(block); + backend.mapping().write_hashes(mapping_commitment, block_number) + } + Log::Post(post_log) => match post_log { + PostLog::Hashes(hashes) => { + let mapping_commitment = gen_from_hashes(hashes); + backend.mapping().write_hashes(mapping_commitment, block_number) + } + PostLog::Block(block) => { + let mapping_commitment = gen_from_block(block); + backend.mapping().write_hashes(mapping_commitment, block_number) + } + PostLog::BlockHash(expect_eth_block_hash) => { + let ethereum_block = storage_override.current_block(substrate_block_hash); + match ethereum_block { + Some(block) => { + let got_eth_block_hash = block.header.hash(); + if got_eth_block_hash != expect_eth_block_hash { + Err(format!( + "Ethereum block hash mismatch: \ + frontier consensus digest ({expect_eth_block_hash:?}), \ + db state ({got_eth_block_hash:?})" + )) + } else { + let mapping_commitment = gen_from_block(block); + backend.mapping().write_hashes(mapping_commitment, block_number) + } + } + None => backend.mapping().write_none(substrate_block_hash), + } + } + }, + } + } + Err(FindLogError::NotFound) => backend.mapping().write_none(substrate_block_hash), + Err(FindLogError::MultipleLogs) => Err("Multiple logs found".to_string()), + } +} + +pub fn sync_genesis_block( + client: &C, + backend: &fc_db::kv::Backend, + header: &Block::Header, +) -> Result<(), String> +where + C: HeaderBackend + ProvideRuntimeApi, + C::Api: EthereumRuntimeRPCApi, +{ + let substrate_block_hash = header.hash(); + let block_number: u64 = (*header.number()).unique_saturated_into(); + + if let Some(api_version) = client + .runtime_api() + .api_version::>(substrate_block_hash) + .map_err(|e| format!("{e:?}"))? + { + let block = if api_version > 1 { + client + .runtime_api() + .current_block(substrate_block_hash) + .map_err(|e| format!("{e:?}"))? + } else { + #[allow(deprecated)] + let legacy_block = client + .runtime_api() + .current_block_before_version_2(substrate_block_hash) + .map_err(|e| format!("{e:?}"))?; + legacy_block.map(|block| block.into()) + }; + let block_hash = block + .ok_or_else(|| "Ethereum genesis block not found".to_string())? + .header + .hash(); + let mapping_commitment = fc_db::kv::MappingCommitment:: { + block_hash: substrate_block_hash, + ethereum_block_hash: block_hash, + ethereum_transaction_hashes: Vec::new(), + }; + backend.mapping().write_hashes(mapping_commitment, block_number)?; + } else { + backend.mapping().write_none(substrate_block_hash)?; + }; + + Ok(()) +} + +pub fn sync_one_block( + client: &C, + substrate_backend: &BE, + storage_override: Arc>, + frontier_backend: &fc_db::kv::Backend, + sync_from: ::Number, + strategy: SyncStrategy, + sync_oracle: Arc, + pubsub_notification_sinks: Arc>>, + best_at_import: &mut HashMap>, +) -> Result +where + C: ProvideRuntimeApi, + C::Api: EthereumRuntimeRPCApi, + C: HeaderBackend + StorageProvider, + BE: Backend, +{ + let mut current_syncing_tips = frontier_backend.meta().current_syncing_tips()?; + + if current_syncing_tips.is_empty() { + let mut leaves = substrate_backend.blockchain().leaves().map_err(|e| format!("{e:?}"))?; + if leaves.is_empty() { + return Ok(false); + } + current_syncing_tips.append(&mut leaves); + } + + let best_hash = client.info().best_hash; + if SyncStrategy::Parachain == strategy && !frontier_backend.mapping().is_synced(&best_hash)? { + current_syncing_tips.push(best_hash); + } + + let mut operating_header = None; + while let Some(checking_tip) = current_syncing_tips.pop() { + if let Some(checking_header) = fetch_header( + substrate_backend.blockchain(), + frontier_backend, + checking_tip, + sync_from, + )? { + operating_header = Some(checking_header); + break; + } + } + let operating_header = match operating_header { + Some(operating_header) => operating_header, + None => { + frontier_backend + .meta() + .write_current_syncing_tips(current_syncing_tips)?; + return Ok(false); + } + }; + + if operating_header.number() == &Zero::zero() { + sync_genesis_block(client, frontier_backend, &operating_header)?; + frontier_backend + .meta() + .write_current_syncing_tips(current_syncing_tips)?; + } else { + if SyncStrategy::Parachain == strategy && operating_header.number() > &client.info().best_number { + return Ok(false); + } + sync_block(storage_override, frontier_backend, &operating_header)?; + + current_syncing_tips.push(*operating_header.parent_hash()); + frontier_backend + .meta() + .write_current_syncing_tips(current_syncing_tips)?; + } + let hash = operating_header.hash(); + let best_info = best_at_import.remove(&hash); + let is_new_best = best_info.is_some() || client.info().best_hash == hash; + let reorg_info = best_info.and_then(|info| info.reorg_info); + + if is_new_best { + let block_number: u64 = (*operating_header.number()).unique_saturated_into(); + frontier_backend + .mapping() + .set_latest_canonical_indexed_block(block_number)?; + } + + emit_block_notification( + pubsub_notification_sinks.as_ref(), + sync_oracle.as_ref(), + BlockNotificationContext { + hash, + is_new_best, + reorg_info, + }, + ); + + Ok(true) +} + +pub fn sync_blocks( + client: &C, + substrate_backend: &BE, + storage_override: Arc>, + frontier_backend: &fc_db::kv::Backend, + limit: usize, + sync_from: ::Number, + strategy: SyncStrategy, + sync_oracle: Arc, + pubsub_notification_sinks: Arc>>, + best_at_import: &mut HashMap>, +) -> Result +where + C: ProvideRuntimeApi, + C::Api: EthereumRuntimeRPCApi, + C: HeaderBackend + StorageProvider, + BE: Backend, +{ + let mut synced_any = false; + + for _ in 0..limit { + synced_any = synced_any + || sync_one_block( + client, + substrate_backend, + storage_override.clone(), + frontier_backend, + sync_from, + strategy, + sync_oracle.clone(), + pubsub_notification_sinks.clone(), + best_at_import, + )?; + } + + let finalized_number = client.info().finalized_number; + best_at_import.retain(|_, info| info.block_number > finalized_number); + + Ok(synced_any) +} + +pub fn fetch_header( + substrate_backend: &BE, + frontier_backend: &fc_db::kv::Backend, + checking_tip: Block::Hash, + sync_from: ::Number, +) -> Result, String> +where + C: HeaderBackend, + BE: HeaderBackend, +{ + if frontier_backend.mapping().is_synced(&checking_tip)? { + return Ok(None); + } + + match substrate_backend.header(checking_tip) { + Ok(Some(checking_header)) if checking_header.number() >= &sync_from => Ok(Some(checking_header)), + Ok(Some(_)) => Ok(None), + Ok(None) | Err(_) => Err("Header not found".to_string()), + } +} + +/// Information tracked at import time for a block that was `is_new_best`. +pub struct BestBlockInfo { + pub block_number: ::Number, + pub reorg_info: Option>>, +} + +pub struct SyntheticMappingSyncWorker { + import_notifications: ImportNotifications, + timeout: Duration, + inner_delay: Option, + + client: Arc, + substrate_backend: Arc, + storage_override: Arc>, + frontier_backend: Arc>, + + have_next: bool, + retry_times: usize, + sync_from: ::Number, + strategy: SyncStrategy, + + sync_oracle: Arc, + pubsub_notification_sinks: Arc>>, + + best_at_import: HashMap>, +} + +impl Unpin for SyntheticMappingSyncWorker {} + +impl SyntheticMappingSyncWorker { + pub fn new( + import_notifications: ImportNotifications, + timeout: Duration, + client: Arc, + substrate_backend: Arc, + storage_override: Arc>, + frontier_backend: Arc>, + retry_times: usize, + sync_from: ::Number, + strategy: SyncStrategy, + sync_oracle: Arc, + pubsub_notification_sinks: Arc>>, + ) -> Self { + Self { + import_notifications, + timeout, + inner_delay: None, + + client, + substrate_backend, + storage_override, + frontier_backend, + + have_next: true, + retry_times, + sync_from, + strategy, + + sync_oracle, + pubsub_notification_sinks, + best_at_import: HashMap::new(), + } + } +} + +impl Stream for SyntheticMappingSyncWorker +where + Block: BlockT, + C: ProvideRuntimeApi, + C::Api: EthereumRuntimeRPCApi, + C: HeaderBackend + StorageProvider, + BE: Backend, +{ + type Item = (); + + fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + let mut fire = false; + + loop { + match Stream::poll_next(Pin::new(&mut self.import_notifications), cx) { + Poll::Pending => break, + Poll::Ready(Some(notification)) => { + fire = true; + if notification.is_new_best { + let reorg_info = notification + .tree_route + .as_ref() + .map(|tree_route| Arc::new(ReorgInfo::from_tree_route(tree_route, notification.hash))); + self.best_at_import.insert( + notification.hash, + BestBlockInfo { + block_number: *notification.header.number(), + reorg_info, + }, + ); + } + } + Poll::Ready(None) => return Poll::Ready(None), + } + } + + let timeout = self.timeout; + let inner_delay = self.inner_delay.get_or_insert_with(|| Delay::new(timeout)); + + match Future::poll(Pin::new(inner_delay), cx) { + Poll::Pending => (), + Poll::Ready(()) => { + fire = true; + } + } + + if self.have_next { + fire = true; + } + + if fire { + self.inner_delay = None; + + let mut best_at_import = std::mem::take(&mut self.best_at_import); + + let result = sync_blocks( + self.client.as_ref(), + self.substrate_backend.as_ref(), + self.storage_override.clone(), + self.frontier_backend.as_ref(), + self.retry_times, + self.sync_from, + self.strategy, + self.sync_oracle.clone(), + self.pubsub_notification_sinks.clone(), + &mut best_at_import, + ); + + self.best_at_import = best_at_import; + + match result { + Ok(have_next) => { + self.have_next = have_next; + Poll::Ready(Some(())) + } + Err(e) => { + self.have_next = false; + debug!(target: "mapping-sync", "Syncing failed with error {e:?}, retrying."); + Poll::Ready(Some(())) + } + } + } else { + Poll::Pending + } + } +} diff --git a/node/src/synthetic_logs/mod.rs b/node/src/synthetic_logs/mod.rs new file mode 100644 index 0000000000..20fc3ae6a7 --- /dev/null +++ b/node/src/synthetic_logs/mod.rs @@ -0,0 +1,18 @@ +// This file is part of hydration-node. +// +// Copyright (C) 2020-2026 Intergalactic, Limited (GIB). +// SPDX-License-Identifier: Apache-2.0 + +//! Node-side synthetic-logs indexing: surface substrate token/trade activity as +//! EVM logs over eth json-rpc, entirely off-chain. +//! +//! - [`storage_override`]: appends synthetic txs/statuses/receipts to Frontier's +//! reads (header left canonical). +//! - [`eth_filter`]: custom `eth_getLogs` that surfaces synth logs without +//! corrupting canonical block hashes. +//! - [`mapping_sync`]: vendored mapping-sync worker that also indexes the +//! synthetic tx hashes so `eth_getTransactionByHash`/`*_receipt` resolve. + +pub mod eth_filter; +pub mod mapping_sync; +pub mod storage_override; diff --git a/node/src/synthetic_logs/storage_override.rs b/node/src/synthetic_logs/storage_override.rs new file mode 100644 index 0000000000..34794fdd5f --- /dev/null +++ b/node/src/synthetic_logs/storage_override.rs @@ -0,0 +1,168 @@ +// This file is part of hydration-node. +// +// Copyright (C) 2020-2026 Intergalactic, Limited (GIB). +// SPDX-License-Identifier: Apache-2.0 + +//! `StorageOverride` that augments Frontier's reads with synthetic ethereum txs, +//! produced client-side from a block's events via +//! `event_logs::synthetic_txs_from_records` (no runtime API, any runtime version). +//! +//! Real entries come first, synth appended in stable order, so a synth tx's index +//! resolves consistently across `current_transaction_statuses`/`current_receipts` +//! and `current_block.transactions` (fc-rpc indexes `block.transactions[index]`). +//! The block header is left canonical (hash unchanged); `eth_getLogs` discovery of +//! synth-only blocks is handled by the sibling `eth_filter` module. + +use std::{ + marker::PhantomData, + num::NonZeroUsize, + sync::{Arc, Mutex}, +}; + +use codec::Decode; +use fc_rpc::StorageOverride; +use fp_rpc::TransactionStatus; +use frame_system::EventRecord; +use hydradx_runtime::{evm::event_logs::synthetic_txs_from_records, RuntimeEvent}; +use lru::LruCache; +use pallet_ethereum::{Block as EthBlock, Receipt as EthReceipt, Transaction as EthTransaction}; +use primitives::Block; +use sc_client_api::{backend::Backend, StorageProvider}; +use sp_blockchain::HeaderBackend; +use sp_core::{hashing::twox_128, H160, H256, U256}; +use sp_runtime::traits::{Block as BlockT, Header as HeaderT, UniqueSaturatedInto}; +use sp_storage::StorageKey; + +type Hash = ::Hash; +type SynthTxs = Vec<(EthTransaction, TransactionStatus, EthReceipt)>; + +// `synthetic()` is invoked once each by `current_block`/`current_receipts`/ +// `current_transaction_statuses`, so a range `eth_getLogs` would re-read and +// re-translate a block's events 3× without this per-block cache. +const SYNTH_CACHE_CAP: usize = 256; + +pub struct SyntheticStorageOverride { + inner: Arc>, + client: Arc, + cache: Mutex>>, + _marker: PhantomData, +} + +impl SyntheticStorageOverride { + pub fn new(inner: Arc>, client: Arc) -> Self { + Self { + inner, + client, + cache: Mutex::new(LruCache::new( + NonZeroUsize::new(SYNTH_CACHE_CAP).expect("non-zero; qed"), + )), + _marker: PhantomData, + } + } +} + +fn storage_key(pallet: &[u8], item: &[u8]) -> StorageKey { + StorageKey([twox_128(pallet), twox_128(item)].concat()) +} + +impl SyntheticStorageOverride +where + C: StorageProvider + HeaderBackend + Send + Sync + 'static, + BE: Backend + Send + Sync + 'static, +{ + fn read_decode(&self, at: Hash, key: &StorageKey) -> Option { + let data = self.client.storage(at, key).ok().flatten()?; + Decode::decode(&mut &data.0[..]).ok() + } + + fn synthetic(&self, at: Hash) -> SynthTxs { + if let Some(hit) = self.cache.lock().expect("synth cache mutex; qed").get(&at) { + return (**hit).clone(); + } + let txs = Arc::new(self.compute_synthetic(at)); + self.cache.lock().expect("synth cache mutex; qed").put(at, txs.clone()); + (*txs).clone() + } + + fn compute_synthetic(&self, at: Hash) -> SynthTxs { + let records: Vec> = + match self.read_decode(at, &storage_key(b"System", b"Events")) { + Some(r) => r, + None => return Vec::new(), + }; + if records.is_empty() { + return Vec::new(); + } + let header = match self.client.header(at) { + Ok(Some(h)) => h, + _ => return Vec::new(), + }; + let parent_hash = *header.parent_hash(); + let block_number: u64 = (*header.number()).unique_saturated_into(); + let chain_id: u64 = self + .read_decode(at, &storage_key(b"EVMChainId", b"ChainId")) + .unwrap_or_default(); + let real_statuses = self.inner.current_transaction_statuses(at).unwrap_or_default(); + + synthetic_txs_from_records(&records, chain_id, parent_hash.as_ref(), block_number, &real_statuses) + } +} + +impl StorageOverride for SyntheticStorageOverride +where + C: StorageProvider + HeaderBackend + Send + Sync + 'static, + BE: Backend + Send + Sync + 'static, +{ + fn account_code_at(&self, at: Hash, address: H160) -> Option> { + self.inner.account_code_at(at, address) + } + + fn account_storage_at(&self, at: Hash, address: H160, index: U256) -> Option { + self.inner.account_storage_at(at, address, index) + } + + fn current_block(&self, at: Hash) -> Option { + let mut block = self.inner.current_block(at)?; + // Append synth txs so `eth_getTransactionByHash`/`*_receipt` can index them + // (fc-rpc does `block.transactions[index]`). The header is left UNTOUCHED so + // the canonical eth block hash is preserved — surfacing synth logs in + // `eth_getLogs` is handled by the `eth_filter` module, not by mutating the + // header bloom (which would change the block hash). + for (tx, _, _) in self.synthetic(at) { + block.transactions.push(tx); + } + Some(block) + } + + fn current_receipts(&self, at: Hash) -> Option> { + let synth = self.synthetic(at); + match self.inner.current_receipts(at) { + Some(mut real) => { + real.extend(synth.into_iter().map(|(_, _, r)| r)); + Some(real) + } + None if !synth.is_empty() => Some(synth.into_iter().map(|(_, _, r)| r).collect()), + None => None, + } + } + + fn current_transaction_statuses(&self, at: Hash) -> Option> { + let synth = self.synthetic(at); + match self.inner.current_transaction_statuses(at) { + Some(mut real) => { + real.extend(synth.into_iter().map(|(_, s, _)| s)); + Some(real) + } + None if !synth.is_empty() => Some(synth.into_iter().map(|(_, s, _)| s).collect()), + None => None, + } + } + + fn elasticity(&self, at: Hash) -> Option { + self.inner.elasticity(at) + } + + fn is_eip1559(&self, at: Hash) -> bool { + self.inner.is_eip1559(at) + } +} diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index 80088a8044..ada4203fce 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "422.0.0" +version = "425.0.0" authors = ["GalacticCouncil"] edition = "2021" license = "Apache 2.0" @@ -21,6 +21,8 @@ smallvec = { workspace = true } log = { workspace = true } num_enum = { workspace = true, default-features = false } evm = { workspace = true, features = ["with-codec"] } +alloy-primitives = { workspace = true } +alloy-sol-types = { workspace = true } # local dependencies primitives = { workspace = true } @@ -189,6 +191,7 @@ serde_json = { workspace = true, default-features = false, features = [ "alloc", ] } ethereum = { workspace = true } +ethereum-types = { workspace = true } [dev-dependencies] pretty_assertions = { workspace = true } diff --git a/runtime/hydradx/src/evm/event_logs.rs b/runtime/hydradx/src/evm/event_logs.rs new file mode 100644 index 0000000000..f2f6de7a10 --- /dev/null +++ b/runtime/hydradx/src/evm/event_logs.rs @@ -0,0 +1,619 @@ +// This file is part of hydration-node. +// +// Copyright (C) 2020-2026 Intergalactic, Limited (GIB). +// SPDX-License-Identifier: Apache-2.0 + +//! Pure event → synthetic-eth-tx translation for the node-indexing variant. +//! +//! `synthetic_txs_from_records` turns a block's `EventRecord`s into +//! `(Transaction, TransactionStatus, Receipt)` triples with no state reads, so +//! the node's client-side indexer can call it for any runtime version. Balance +//! movements map to erc20 `Transfer` (reserved/frozen go to per-owner sentinels +//! so aggregated transfers equal an account's transferable balance), plus +//! `Swapped3` → uniswap-v2 `Swap` and internal `pallet_evm::Log` (deduped vs +//! real eth txs). + +use super::synthetic_logs::{ + account_to_evm_address, assemble_synth_txs, asset_evm_address, build_erc20_transfer_log, build_uniswap_v2_swap_log, + frozen_address_of, reserved_address_of, Bucket, HookPhase, +}; +use crate::RuntimeEvent; +use frame_system::{EventRecord, Phase}; +use pallet_broadcast::types::{Asset, ExecutionType, TradeOperation}; +use pallet_ethereum::{Receipt, Transaction, TransactionStatus}; +use primitive_types::{H160, U256}; +use primitives::constants::chain::CORE_ASSET_ID; +use primitives::AccountId; +use sp_core::H256; +use sp_std::collections::btree_map::BTreeMap; +use sp_std::vec::Vec; + +const ZERO: H160 = H160([0u8; 20]); + +fn evm_addr(account: &AccountId) -> H160 { + account_to_evm_address(account.as_ref()) +} + +/// reserve repatriation → `Transfer(reserved_sentinel(from), to_or_its_reserved_sentinel)`. +fn repatriate( + asset: u32, + from_owner: H160, + to_owner: H160, + amount: u128, + to_reserved: bool, +) -> Vec<(H160, ethereum::Log)> { + let to = if to_reserved { + reserved_address_of(to_owner) + } else { + to_owner + }; + transfer(asset, reserved_address_of(from_owner), to, amount) +} + +fn to_reserved(status: &orml_traits::BalanceStatus) -> bool { + matches!(status, orml_traits::BalanceStatus::Reserved) +} + +// Pure mirror of `erc20_mapping::is_asset_address`: prefix `0x..01` (15 zero +// bytes then `1`), the range `asset_evm_address` produces. +fn is_asset_address(addr: H160) -> bool { + addr.0[..15] == [0u8; 15] && addr.0[15] == 1 +} + +/// One erc20 `Transfer` on `asset`'s evm address (empty for zero). Mint = `from` +/// zero, burn = `to` zero, reserve/lock = `to` the owner's sentinel. +fn transfer(asset: u32, from: H160, to: H160, amount: u128) -> Vec<(H160, ethereum::Log)> { + if amount == 0 { + return Vec::new(); + } + let addr = asset_evm_address(asset); + sp_std::vec![(addr, build_erc20_transfer_log(addr, from, to, U256::from(amount)))] +} + +/// Pure: the evm logs an indexer should see for one runtime event. +pub fn logs_from_event(event: &RuntimeEvent) -> Vec<(H160, ethereum::Log)> { + use orml_tokens::Event as Tokens; + use pallet_balances::Event as Balances; + match event { + // ---- orml-tokens (non-native assets) ---- + RuntimeEvent::Tokens(Tokens::Transfer { + currency_id, + from, + to, + amount, + }) => transfer(*currency_id, evm_addr(from), evm_addr(to), *amount), + RuntimeEvent::Tokens(Tokens::Deposited { + currency_id, + who, + amount, + }) => transfer(*currency_id, ZERO, evm_addr(who), *amount), + RuntimeEvent::Tokens(Tokens::Withdrawn { + currency_id, + who, + amount, + }) + | RuntimeEvent::Tokens(Tokens::DustLost { + currency_id, + who, + amount, + }) => transfer(*currency_id, evm_addr(who), ZERO, *amount), + RuntimeEvent::Tokens(Tokens::Slashed { + currency_id, + who, + free_amount, + reserved_amount, + }) => { + let owner = evm_addr(who); + let mut logs = transfer(*currency_id, owner, ZERO, *free_amount); + logs.extend(transfer( + *currency_id, + reserved_address_of(owner), + ZERO, + *reserved_amount, + )); + logs + } + RuntimeEvent::Tokens(Tokens::Reserved { + currency_id, + who, + amount, + }) => { + let owner = evm_addr(who); + transfer(*currency_id, owner, reserved_address_of(owner), *amount) + } + RuntimeEvent::Tokens(Tokens::Unreserved { + currency_id, + who, + amount, + }) => { + let owner = evm_addr(who); + transfer(*currency_id, reserved_address_of(owner), owner, *amount) + } + RuntimeEvent::Tokens(Tokens::ReserveRepatriated { + currency_id, + from, + to, + amount, + status, + }) => repatriate(*currency_id, evm_addr(from), evm_addr(to), *amount, to_reserved(status)), + RuntimeEvent::Tokens(Tokens::Locked { + currency_id, + who, + amount, + }) => { + let owner = evm_addr(who); + transfer(*currency_id, owner, frozen_address_of(owner), *amount) + } + RuntimeEvent::Tokens(Tokens::Unlocked { + currency_id, + who, + amount, + }) => { + let owner = evm_addr(who); + transfer(*currency_id, frozen_address_of(owner), owner, *amount) + } + + // ---- native HDX (pallet-balances) ---- + RuntimeEvent::Balances(Balances::Transfer { from, to, amount }) => { + transfer(CORE_ASSET_ID, evm_addr(from), evm_addr(to), *amount) + } + // Mint: `Deposit` (Currency api) and `Minted` (fungible api) are emitted by + // disjoint code paths — a given increase emits exactly one, so mapping both + // captures every increase once. + RuntimeEvent::Balances(Balances::Deposit { who, amount }) + | RuntimeEvent::Balances(Balances::Minted { who, amount }) => transfer(CORE_ASSET_ID, ZERO, evm_addr(who), *amount), + // Burn: `Withdraw`/`Burned`/`DustLost` are disjoint reductions of free balance. + RuntimeEvent::Balances(Balances::Withdraw { who, amount }) + | RuntimeEvent::Balances(Balances::Burned { who, amount }) + | RuntimeEvent::Balances(Balances::DustLost { account: who, amount }) => { + transfer(CORE_ASSET_ID, evm_addr(who), ZERO, *amount) + } + // Native slashing here only ever hits reserved balance (governance bonds via + // democracy/elections); the event can't distinguish, so burn the reserved sentinel. + RuntimeEvent::Balances(Balances::Slashed { who, amount }) => { + transfer(CORE_ASSET_ID, reserved_address_of(evm_addr(who)), ZERO, *amount) + } + RuntimeEvent::Balances(Balances::Reserved { who, amount }) => { + let owner = evm_addr(who); + transfer(CORE_ASSET_ID, owner, reserved_address_of(owner), *amount) + } + RuntimeEvent::Balances(Balances::Unreserved { who, amount }) => { + let owner = evm_addr(who); + transfer(CORE_ASSET_ID, reserved_address_of(owner), owner, *amount) + } + RuntimeEvent::Balances(Balances::ReserveRepatriated { + from, + to, + amount, + destination_status, + }) => repatriate( + CORE_ASSET_ID, + evm_addr(from), + evm_addr(to), + *amount, + matches!( + destination_status, + frame_support::traits::tokens::BalanceStatus::Reserved + ), + ), + // Lock + freeze both adjust `frozen`; each event carries the frozen delta. + RuntimeEvent::Balances(Balances::Locked { who, amount }) + | RuntimeEvent::Balances(Balances::Frozen { who, amount }) => { + let owner = evm_addr(who); + transfer(CORE_ASSET_ID, owner, frozen_address_of(owner), *amount) + } + RuntimeEvent::Balances(Balances::Unlocked { who, amount }) + | RuntimeEvent::Balances(Balances::Thawed { who, amount }) => { + let owner = evm_addr(who); + transfer(CORE_ASSET_ID, frozen_address_of(owner), owner, *amount) + } + + // ---- swaps ---- + RuntimeEvent::Broadcast(pallet_broadcast::Event::Swapped3 { + swapper, + filler, + operation, + inputs, + outputs, + .. + }) => swap_log(swapper, filler, operation, inputs, outputs) + .into_iter() + .collect(), + // EVM logs from internal `Executor::call` paths (hsm, dispatcher-driven, + // liquidations); deduped against real eth txs by the caller. + RuntimeEvent::EVM(pallet_evm::Event::Log { log }) => { + sp_std::vec![( + log.address, + ethereum::Log { + address: log.address, + topics: log.topics.clone(), + data: log.data.clone(), + } + )] + } + _ => Vec::new(), + } +} + +/// Pure: a trade → uniswap-v2 `Swap` log (emitter = pool's evm address), or +/// `None` for trades outside v1 scope (bilateral `ExactIn`/`ExactOut`). +fn swap_log( + swapper: &AccountId, + filler: &AccountId, + operation: &TradeOperation, + inputs: &[Asset], + outputs: &[Asset], +) -> Option<(H160, ethereum::Log)> { + if !matches!(operation, TradeOperation::ExactIn | TradeOperation::ExactOut) { + return None; + } + if inputs.len() != 1 || outputs.len() != 1 { + return None; + } + let in_asset = inputs[0].asset; + let in_amount = inputs[0].amount; + let out_asset = outputs[0].asset; + let out_amount = outputs[0].amount; + if in_amount == 0 && out_amount == 0 { + return None; + } + + let pool_address = evm_addr(filler); + if is_asset_address(pool_address) { + // would collide with the erc20 asset-address range; skip (~2^-128). + return None; + } + + let sender = evm_addr(swapper); + let input_is_token0 = in_asset <= out_asset; // token0 = lower asset id + let log = build_uniswap_v2_swap_log( + pool_address, + sender, + sender, + input_is_token0, + U256::from(in_amount), + U256::from(out_amount), + ); + Some((pool_address, log)) +} + +/// Bucket origin for hook-phase events. Only `Swapped3` carries its originating +/// context (`operation_stack`); others fall back to `None`. +fn event_origin_hint(event: &RuntimeEvent) -> Option { + match event { + RuntimeEvent::Broadcast(pallet_broadcast::Event::Swapped3 { operation_stack, .. }) => { + operation_stack.first().copied() + } + _ => None, + } +} + +type LogKey = (H160, Vec, Vec); + +fn log_key(address: H160, topics: &[H256], data: &[u8]) -> LogKey { + (address, topics.to_vec(), data.to_vec()) +} + +/// PURE: assemble a block's synthetic txs from its event records. Shared by the +/// runtime API and the node's client-side indexer. `real_statuses` are the +/// block's real eth-tx statuses — used both to index synth txs after them +/// (`base_tx_index`) and to dedup `EVM::Log` events that already appear in a +/// real tx's receipt. +pub fn synthetic_txs_from_records( + records: &[EventRecord], + chain_id: u64, + parent_hash: &[u8], + block_number: u64, + real_statuses: &[TransactionStatus], +) -> Vec<(Transaction, TransactionStatus, Receipt)> { + let base_tx_index = real_statuses.len() as u32; + + // An EVM log is also recorded in the receipt of the real eth tx in its extrinsic, + // so drop EVM::Log events matching that receipt (one-for-one); extra logs from a + // separate internal Executor::call survive. Real statuses follow Executed order. + let mut real_logs_by_ext: BTreeMap> = BTreeMap::new(); + let mut nth_eth_tx = 0usize; + for rec in records.iter() { + if matches!( + rec.event, + RuntimeEvent::Ethereum(pallet_ethereum::Event::Executed { .. }) + ) { + if let Phase::ApplyExtrinsic(i) = rec.phase { + if let Some(status) = real_statuses.get(nth_eth_tx) { + let ms = real_logs_by_ext.entry(i).or_default(); + for log in status.logs.iter() { + *ms.entry(log_key(log.address, &log.topics, &log.data)).or_insert(0) += 1; + } + } + } + nth_eth_tx += 1; + } + } + + let mut entries: Vec<(Bucket, H160, ethereum::Log)> = Vec::new(); + for rec in records.iter() { + if let RuntimeEvent::EVM(pallet_evm::Event::Log { log }) = &rec.event { + if let Phase::ApplyExtrinsic(i) = rec.phase { + if let Some(ms) = real_logs_by_ext.get_mut(&i) { + if let Some(count) = ms.get_mut(&log_key(log.address, &log.topics, &log.data)) { + if *count > 0 { + *count -= 1; + continue; + } + } + } + } + } + let bucket = match rec.phase { + Phase::ApplyExtrinsic(i) => Bucket::Extrinsic(i), + Phase::Initialization => Bucket::Hook { + phase: HookPhase::Initialization, + origin: event_origin_hint(&rec.event), + }, + Phase::Finalization => Bucket::Hook { + phase: HookPhase::Finalization, + origin: event_origin_hint(&rec.event), + }, + }; + for (emitter, log) in logs_from_event(&rec.event) { + entries.push((bucket, emitter, log)); + } + } + + if entries.is_empty() { + return Vec::new(); + } + assemble_synth_txs(entries, chain_id, parent_hash, block_number, base_tx_index) +} + +#[cfg(test)] +mod tests { + use super::*; + use orml_tokens::Event as Tokens; + use pallet_balances::Event as Balances; + use pretty_assertions::assert_eq; + + const HDX: u32 = CORE_ASSET_ID; + const DAI: u32 = 2; + + fn acc(n: u8) -> AccountId { + AccountId::from([n; 32]) + } + fn owner_of(n: u8) -> H160 { + H160([n; 20]) + } + fn from_of(log: ðereum::Log) -> H160 { + H160::from_slice(&log.topics[1].0[12..]) + } + fn to_of(log: ðereum::Log) -> H160 { + H160::from_slice(&log.topics[2].0[12..]) + } + fn amount_of(log: ðereum::Log) -> u128 { + U256::from_big_endian(&log.data).low_u128() + } + fn one(event: RuntimeEvent) -> (H160, ethereum::Log) { + let logs = logs_from_event(&event); + assert_eq!(logs.len(), 1, "expected exactly one log"); + logs.into_iter().next().unwrap() + } + + #[test] + fn token_transfer_should_map_to_erc20_transfer_on_asset_address() { + let (emitter, log) = one(RuntimeEvent::Tokens(Tokens::Transfer { + currency_id: DAI, + from: acc(1), + to: acc(2), + amount: 500, + })); + assert_eq!(emitter, asset_evm_address(DAI)); + assert_eq!(from_of(&log), owner_of(1)); + assert_eq!(to_of(&log), owner_of(2)); + assert_eq!(amount_of(&log), 500); + } + + #[test] + fn native_transfer_should_map_to_core_asset_address() { + let (emitter, _) = one(RuntimeEvent::Balances(Balances::Transfer { + from: acc(1), + to: acc(2), + amount: 7, + })); + assert_eq!(emitter, asset_evm_address(HDX)); + } + + #[test] + fn deposit_and_mint_should_map_to_transfer_from_zero() { + for ev in [ + RuntimeEvent::Tokens(Tokens::Deposited { + currency_id: DAI, + who: acc(1), + amount: 9, + }), + RuntimeEvent::Balances(Balances::Deposit { who: acc(1), amount: 9 }), + RuntimeEvent::Balances(Balances::Minted { who: acc(1), amount: 9 }), + ] { + let (_, log) = one(ev); + assert_eq!(from_of(&log), ZERO); + assert_eq!(to_of(&log), owner_of(1)); + } + } + + #[test] + fn withdraw_burn_dust_should_map_to_transfer_to_zero() { + for ev in [ + RuntimeEvent::Tokens(Tokens::Withdrawn { + currency_id: DAI, + who: acc(1), + amount: 3, + }), + RuntimeEvent::Balances(Balances::Withdraw { who: acc(1), amount: 3 }), + RuntimeEvent::Balances(Balances::Burned { who: acc(1), amount: 3 }), + RuntimeEvent::Balances(Balances::DustLost { + account: acc(1), + amount: 3, + }), + ] { + let (_, log) = one(ev); + assert_eq!(from_of(&log), owner_of(1)); + assert_eq!(to_of(&log), ZERO); + } + } + + #[test] + fn native_slash_should_burn_from_reserved_sentinel() { + // native slashing only ever hits reserved balance in this runtime. + let (_, log) = one(RuntimeEvent::Balances(Balances::Slashed { who: acc(1), amount: 3 })); + assert_eq!(from_of(&log), reserved_address_of(owner_of(1))); + assert_eq!(to_of(&log), ZERO); + } + + #[test] + fn reserve_should_move_to_reserved_sentinel_and_unreserve_back() { + let owner = owner_of(1); + let (_, r) = one(RuntimeEvent::Balances(Balances::Reserved { who: acc(1), amount: 4 })); + assert_eq!(from_of(&r), owner); + assert_eq!(to_of(&r), reserved_address_of(owner)); + let (_, u) = one(RuntimeEvent::Balances(Balances::Unreserved { who: acc(1), amount: 4 })); + assert_eq!(from_of(&u), reserved_address_of(owner)); + assert_eq!(to_of(&u), owner); + } + + #[test] + fn lock_should_use_frozen_sentinel_distinct_from_reserved() { + let owner = owner_of(1); + assert_ne!(frozen_address_of(owner), reserved_address_of(owner)); + let (_, l) = one(RuntimeEvent::Balances(Balances::Frozen { who: acc(1), amount: 6 })); + assert_eq!(from_of(&l), owner); + assert_eq!(to_of(&l), frozen_address_of(owner)); + let (_, t) = one(RuntimeEvent::Balances(Balances::Thawed { who: acc(1), amount: 6 })); + assert_eq!(from_of(&t), frozen_address_of(owner)); + assert_eq!(to_of(&t), owner); + } + + #[test] + fn token_slash_should_split_free_and_reserved() { + let owner = owner_of(1); + let logs = logs_from_event(&RuntimeEvent::Tokens(Tokens::Slashed { + currency_id: DAI, + who: acc(1), + free_amount: 10, + reserved_amount: 5, + })); + assert_eq!(logs.len(), 2); + assert_eq!(from_of(&logs[0].1), owner); + assert_eq!(amount_of(&logs[0].1), 10); + assert_eq!(from_of(&logs[1].1), reserved_address_of(owner)); + assert_eq!(amount_of(&logs[1].1), 5); + assert!(logs.iter().all(|(_, l)| to_of(l) == ZERO)); + } + + #[test] + fn zero_amount_should_emit_no_log() { + assert!(logs_from_event(&RuntimeEvent::Balances(Balances::Transfer { + from: acc(1), + to: acc(2), + amount: 0, + })) + .is_empty()); + } + + // the headline invariant: aggregating (incoming − outgoing) erc20 transfer + // amounts for an owner reconstructs its *transferable* balance. + #[test] + fn aggregated_transfers_should_reconstruct_transferable_balance() { + let owner = owner_of(1); + // free 100, then reserve 20 (free→reserved), then freeze 30 (lien on free). + // transferable = free(80) − frozen(30) = 50. + let events = [ + RuntimeEvent::Balances(Balances::Minted { + who: acc(1), + amount: 100, + }), + RuntimeEvent::Balances(Balances::Reserved { + who: acc(1), + amount: 20, + }), + RuntimeEvent::Balances(Balances::Frozen { + who: acc(1), + amount: 30, + }), + ]; + let net = |target: H160| -> i128 { + let mut bal = 0i128; + for ev in events.iter() { + for (_, log) in logs_from_event(ev) { + if to_of(&log) == target { + bal += amount_of(&log) as i128; + } + if from_of(&log) == target { + bal -= amount_of(&log) as i128; + } + } + } + bal + }; + assert_eq!(net(owner), 50, "owner aggregate must equal transferable balance"); + assert_eq!(net(reserved_address_of(owner)), 20, "reserved sentinel holds reserved"); + assert_eq!(net(frozen_address_of(owner)), 30, "frozen sentinel holds frozen"); + } + + // gap-2: an `EVM::Log` already in the real eth tx's receipt is deduped, but an + // extra log from a separate internal call in the same extrinsic survives. + #[test] + fn evm_log_dedup_should_drop_receipt_duplicates_but_keep_internal_logs() { + // the event carries `fp_evm::Log`; the receipt carries `ethereum::Log`. + let dup_addr = H160([9u8; 20]); + let internal_addr = H160([8u8; 20]); + let evm_log = |addr: H160, t: u8, d: u8| fp_evm::Log { + address: addr, + topics: sp_std::vec![H256([t; 32])], + data: sp_std::vec![d], + }; + let receipt_dup = ethereum::Log { + address: dup_addr, + topics: sp_std::vec![H256([1u8; 32])], + data: sp_std::vec![1], + }; + let rec = |event| EventRecord { + phase: Phase::ApplyExtrinsic(0), + event, + topics: sp_std::vec![], + }; + let records = sp_std::vec![ + rec(RuntimeEvent::Ethereum(pallet_ethereum::Event::Executed { + from: H160::zero(), + to: H160::zero(), + transaction_hash: H256::zero(), + exit_reason: fp_evm::ExitReason::Succeed(fp_evm::ExitSucceed::Returned), + extra_data: sp_std::vec![], + })), + rec(RuntimeEvent::EVM(pallet_evm::Event::Log { + log: evm_log(dup_addr, 1, 1), + })), + rec(RuntimeEvent::EVM(pallet_evm::Event::Log { + log: evm_log(internal_addr, 2, 4), + })), + ]; + let real = sp_std::vec![TransactionStatus { + transaction_hash: H256::zero(), + transaction_index: 0, + from: H160::zero(), + to: None, + contract_address: None, + logs: sp_std::vec![receipt_dup], + logs_bloom: Default::default(), + }]; + let out = synthetic_txs_from_records(&records, 1, &[0u8; 32], 1, &real); + let synth_logs: Vec = out.iter().flat_map(|(_, s, _)| s.logs.clone()).collect(); + assert!( + synth_logs.iter().any(|l| l.address == internal_addr), + "internal-call log must be synthesized" + ); + assert!( + !synth_logs.iter().any(|l| l.address == dup_addr), + "receipt-duplicate log must be dropped" + ); + // synth tx indices continue after the one real eth tx. + assert_eq!(out[0].1.transaction_index, 1); + } +} diff --git a/runtime/hydradx/src/evm/mod.rs b/runtime/hydradx/src/evm/mod.rs index ba6d3607f5..f3f5f475b2 100644 --- a/runtime/hydradx/src/evm/mod.rs +++ b/runtime/hydradx/src/evm/mod.rs @@ -55,6 +55,7 @@ use sp_core::{crypto::AccountId32, Get, U256}; pub mod aave_trade_executor; mod accounts_conversion; mod erc20_currency; +pub mod event_logs; pub mod evm_error_decoder; mod evm_fee; mod executor; @@ -62,6 +63,7 @@ mod gas_to_weight_mapping; pub mod permit; pub mod precompiles; mod runner; +pub mod synthetic_logs; use crate::circuit_breaker::IgnoreWithdrawFuse; pub use erc20_currency::Erc20Currency; diff --git a/runtime/hydradx/src/evm/precompiles/mod.rs b/runtime/hydradx/src/evm/precompiles/mod.rs index a36136ae43..fb7c2f65b0 100644 --- a/runtime/hydradx/src/evm/precompiles/mod.rs +++ b/runtime/hydradx/src/evm/precompiles/mod.rs @@ -234,6 +234,25 @@ pub fn is_precompile(address: H160) -> bool { address == DISPATCH_ADDR || address == LOCK_MANAGER || is_asset_address(address) || is_standard_precompile(address) } +/// emits ERC-20 `Approval(owner, spender, value)` inline at the precompile's address. +pub fn emit_approval_log( + handle: &mut impl PrecompileHandle, + owner: H160, + spender: H160, + amount: U256, +) -> EvmResult<()> { + use crate::evm::synthetic_logs::{encode_u256_be, h160_to_h256, APPROVAL_TOPIC}; + let topics = sp_std::vec![APPROVAL_TOPIC, h160_to_h256(owner), h160_to_h256(spender)]; + let data = encode_u256_be(amount).to_vec(); + let cost = costs::log_costs(topics.len(), data.len()).map_err(|_| PrecompileFailure::Error { + exit_status: ExitError::OutOfGas, + })?; + handle.record_cost(cost)?; + let address = handle.code_address(); + handle.log(address, topics, data)?; + Ok(()) +} + // This is a reimplementation of the upstream u64->H160 conversion // function, made `const` to make our precompile address `const`s a // bit cleaner. It can be removed when upstream has a const conversion diff --git a/runtime/hydradx/src/evm/precompiles/multicurrency.rs b/runtime/hydradx/src/evm/precompiles/multicurrency.rs index 06bfce14c2..89080b6455 100644 --- a/runtime/hydradx/src/evm/precompiles/multicurrency.rs +++ b/runtime/hydradx/src/evm/precompiles/multicurrency.rs @@ -39,7 +39,7 @@ use hydradx_traits::evm::{Erc20Encoding, InspectEvmAccounts}; use hydradx_traits::registry::Inspect as InspectRegistry; use orml_traits::{MultiCurrency as MultiCurrencyT, MultiCurrency}; use pallet_evm::{AddressMapping, ExitRevert, Precompile, PrecompileFailure, PrecompileHandle, PrecompileResult}; -use primitive_types::H160; +use primitive_types::{H160, U256}; use primitives::{AssetId, Balance}; use sp_runtime::traits::Dispatchable; use sp_std::marker::PhantomData; @@ -273,6 +273,8 @@ where pallet_evm_accounts::Pallet::::set_allowance(asset_id.into(), owner, spender, amount); + crate::evm::precompiles::emit_approval_log(handle, owner, spender, U256::from(amount))?; + Ok(succeed(EvmDataWriter::new().write(true).build())) } diff --git a/runtime/hydradx/src/evm/synthetic_logs.rs b/runtime/hydradx/src/evm/synthetic_logs.rs new file mode 100644 index 0000000000..0a3bdc83f2 --- /dev/null +++ b/runtime/hydradx/src/evm/synthetic_logs.rs @@ -0,0 +1,692 @@ +// This file is part of hydration-node. +// +// Copyright (C) 2020-2026 Intergalactic, Limited (GIB). +// SPDX-License-Identifier: Apache-2.0 + +//! Pure primitives for turning substrate token/trade events into synthetic +//! ethereum `Transaction`/`TransactionStatus`/`Receipt` records (one synth tx +//! per bucket: per-extrinsic, or per hook-phase + broadcast origin). The node +//! assembles and serves these off-chain over eth json-rpc by calling +//! `event_logs::synthetic_txs_from_records`; nothing here touches chain state. + +use codec::{Decode, Encode, MaxEncodedLen}; +use ethereum::EIP1559Transaction; +use ethereum_types::{Bloom, BloomInput, H160, H256, U256}; +use frame_support::pallet_prelude::RuntimeDebug; +use pallet_broadcast::types::ExecutionType; +use pallet_ethereum::{Receipt, Transaction, TransactionAction, TransactionStatus}; +use scale_info::TypeInfo; +use sp_std::collections::btree_map::BTreeMap; +use sp_std::prelude::*; + +/// `from`/`to` for synthetic txs. logs inside carry their own emitter address. +pub const SENTINEL_ADDRESS: H160 = H160([ + 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, + 0xef, +]); + +/// Constant fake `r`/`s`. Inside ECDSA range so the envelope decodes; never recovered. +pub const SYNTH_SIG_RS: H256 = H256([ + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +]); + +#[derive(Clone, Copy, Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug, PartialEq, Eq)] +pub enum HookPhase { + Initialization, + Finalization, +} + +#[derive(Clone, Copy, Encode, Decode, MaxEncodedLen, TypeInfo, RuntimeDebug, PartialEq, Eq)] +pub enum Bucket { + Extrinsic(u32), + Hook { + phase: HookPhase, + origin: Option, + }, +} + +/// Per-block domain-separation seed folded into each synth tx's `input`, so +/// envelope hashes are unique per block (frontier indexes txs by hash; without +/// this, `Extrinsic(2)` in block N and N+1 would hash identically). +fn block_domain(parent_hash: &[u8], block_number: u64) -> Vec { + let mut seed = Vec::with_capacity(18 + parent_hash.len() + 8); + seed.extend_from_slice(b"hydration-synth-v1"); + seed.extend_from_slice(parent_hash); + seed.extend_from_slice(&block_number.to_le_bytes()); + seed +} + +fn logs_bloom(logs: &[ethereum::Log]) -> Bloom { + let mut bloom = Bloom::default(); + for log in logs { + bloom.accrue(BloomInput::Raw(&log.address[..])); + for topic in log.topics.iter() { + bloom.accrue(BloomInput::Raw(&topic[..])); + } + } + bloom +} + +/// Assemble synthetic ethereum txs from bucketed logs. **Pure** — no storage, +/// no `T`. +/// +/// One synth tx per bucket, emitted in ascending `bucket_sort_key` order +/// (init hooks < extrinsics by index < finalize hooks), insertion order +/// preserved within a bucket. `base_tx_index` is the count of real eth txs +/// already in the block; synth tx indices continue from there. Grouping is +/// O(N log G) via the BTreeMap (vs an O(N*G) linear scan). +pub fn assemble_synth_txs( + entries: Vec<(Bucket, H160, ethereum::Log)>, + chain_id: u64, + parent_hash: &[u8], + block_number: u64, + base_tx_index: u32, +) -> Vec<(Transaction, TransactionStatus, Receipt)> { + let mut groups: BTreeMap<(u8, u64), (Bucket, Vec)> = BTreeMap::new(); + for (bucket, _emitter, log) in entries { + groups + .entry(bucket_sort_key(&bucket)) + .or_insert_with(|| (bucket, Vec::new())) + .1 + .push(log); + } + + let input = block_domain(parent_hash, block_number); + let signature = ethereum::eip2930::TransactionSignature::new(false, SYNTH_SIG_RS, SYNTH_SIG_RS) + .expect("synthetic signature constants are within valid ECDSA range; qed"); + + let mut out = Vec::with_capacity(groups.len()); + for (group_index, (_key, (bucket, logs))) in groups.into_iter().enumerate() { + let group_index = group_index as u32; + // `value = group_index` keeps hashes distinct within a block; `input` + // (block domain) keeps them distinct across blocks. + let transaction = Transaction::EIP1559(EIP1559Transaction { + chain_id, + nonce: U256::from(bucket_nonce(bucket)), + max_priority_fee_per_gas: U256::zero(), + max_fee_per_gas: U256::zero(), + gas_limit: U256::zero(), + action: TransactionAction::Call(SENTINEL_ADDRESS), + value: U256::from(group_index), + input: input.clone(), + access_list: Vec::new(), + signature: signature.clone(), + }); + let transaction_hash = transaction.hash(); + let bloom = logs_bloom(&logs); + let status = TransactionStatus { + transaction_hash, + transaction_index: base_tx_index + group_index, + from: SENTINEL_ADDRESS, + to: Some(SENTINEL_ADDRESS), + contract_address: None, + logs: logs.clone(), + logs_bloom: bloom, + }; + let receipt = Receipt::EIP1559(ethereum::EIP658ReceiptData { + status_code: 1, + used_gas: U256::zero(), + logs_bloom: bloom, + logs, + }); + out.push((transaction, status, receipt)); + } + out +} + +/// keccak256("Transfer(address,address,uint256)") +pub const TRANSFER_TOPIC: H256 = H256([ + 0xdd, 0xf2, 0x52, 0xad, 0x1b, 0xe2, 0xc8, 0x9b, 0x69, 0xc2, 0xb0, 0x68, 0xfc, 0x37, 0x8d, 0xaa, 0x95, 0x2b, 0xa7, + 0xf1, 0x63, 0xc4, 0xa1, 0x16, 0x28, 0xf5, 0x5a, 0x4d, 0xf5, 0x23, 0xb3, 0xef, +]); + +/// keccak256("Swap(address,uint256,uint256,uint256,uint256,address)") (uniswap v2) +pub const SWAP_TOPIC: H256 = H256([ + 0xd7, 0x8a, 0xd9, 0x5f, 0xa4, 0x6c, 0x99, 0x4b, 0x65, 0x51, 0xd0, 0xda, 0x85, 0xfc, 0x27, 0x5f, 0xe6, 0x13, 0xce, + 0x37, 0x65, 0x7f, 0xb8, 0xd5, 0xe3, 0xd1, 0x30, 0x84, 0x01, 0x59, 0xd8, 0x22, +]); + +/// keccak256("Approval(address,address,uint256)") +pub const APPROVAL_TOPIC: H256 = H256([ + 0x8c, 0x5b, 0xe1, 0xe5, 0xeb, 0xec, 0x7d, 0x5b, 0xd1, 0x4f, 0x71, 0x42, 0x7d, 0x1e, 0x84, 0xf3, 0xdd, 0x03, 0x14, + 0xc0, 0xf7, 0xb2, 0x29, 0x1e, 0x5b, 0x20, 0x0a, 0xc8, 0xc7, 0xc3, 0xb9, 0x25, +]); + +pub fn h160_to_h256(addr: H160) -> H256 { + let mut bytes = [0u8; 32]; + bytes[12..].copy_from_slice(&addr.0); + H256(bytes) +} + +/// Pure mirror of `pallet_evm_accounts::evm_address`: an EVM-derived account is +/// `b"ETH\0" ++ <20-byte h160> ++ [0u8; 8]`; otherwise truncate to 20 bytes. No +/// state read — so it works client-side and against any runtime version. +pub fn account_to_evm_address(account: &[u8]) -> H160 { + if account.len() >= 32 && &account[0..4] == b"ETH\0" && account[24..32] == [0u8; 8] { + H160::from_slice(&account[4..24]) + } else { + H160::from_slice(&account[..20]) + } +} + +/// Pure mirror of `HydraErc20Mapping::encode_evm_address`: `0x..01 ++ asset_id` +/// (big-endian in the last 4 bytes). Correct for the registry assets that emit +/// orml/balances events; bound real-ERC20 assets transact as real contracts and +/// surface via real EVM logs, so they never reach this path. +pub fn asset_evm_address(asset_id: u32) -> H160 { + let mut bytes = [0u8; 20]; + bytes[15] = 1; + bytes[16..20].copy_from_slice(&asset_id.to_be_bytes()); + H160(bytes) +} + +/// per-owner sentinel for reserved balance: `Transfer(owner, reserved_address_of(owner))` +/// on reserve, inverse on unreserve. derivation: xor first byte with `0xEE` +/// (reversible). collision with asset-prefix range `0x000…01` is `2^-128`. +pub fn reserved_address_of(owner: H160) -> H160 { + let mut bytes = owner.0; + bytes[0] ^= 0xEE; + H160(bytes) +} + +/// per-owner sentinel for frozen (locked) balance, kept DISTINCT from +/// [`reserved_address_of`] (xor `0xDD` vs `0xEE`) so locks and reserves never +/// alias. moving the frozen delta to this sentinel makes the owner's aggregated +/// transfer balance equal its *transferable* balance (free minus frozen): a lock +/// freezes free in place, so we mirror that as `Transfer(owner, frozen_address_of(owner))`. +pub fn frozen_address_of(owner: H160) -> H160 { + let mut bytes = owner.0; + bytes[0] ^= 0xDD; + H160(bytes) +} + +pub fn encode_u256_be(value: U256) -> [u8; 32] { + value.to_big_endian() +} + +/// 4 × u256 = 128 bytes — abi shape of uniswap v2 `Swap` non-indexed fields. +pub fn encode_uint256_quad(a: U256, b: U256, c: U256, d: U256) -> Vec { + let mut data = Vec::with_capacity(128); + data.extend_from_slice(&encode_u256_be(a)); + data.extend_from_slice(&encode_u256_be(b)); + data.extend_from_slice(&encode_u256_be(c)); + data.extend_from_slice(&encode_u256_be(d)); + data +} + +// --- pure log-shape builders ------------------------------------------------ +// Callers resolve addresses; these just encode the evm-log shape. + +/// ERC-20 `Transfer(from, to, value)` log emitted from `token`'s address. +pub fn build_erc20_transfer_log(token: H160, from: H160, to: H160, amount: U256) -> ethereum::Log { + ethereum::Log { + address: token, + topics: vec![TRANSFER_TOPIC, h160_to_h256(from), h160_to_h256(to)], + data: encode_u256_be(amount).to_vec(), + } +} + +/// Uniswap-v2 `Swap(sender, a0In, a1In, a0Out, a1Out, to)` log from `pool`. +/// +/// `input_is_token0` is whether the trade's input asset sorts as token0 of the +/// pair (token0 = the lower asset id); it selects which `amountN{In,Out}` slots +/// the in/out amounts land in. +pub fn build_uniswap_v2_swap_log( + pool: H160, + sender: H160, + recipient: H160, + input_is_token0: bool, + in_amount: U256, + out_amount: U256, +) -> ethereum::Log { + let (a0_in, a1_in, a0_out, a1_out) = if input_is_token0 { + (in_amount, U256::zero(), U256::zero(), out_amount) + } else { + (U256::zero(), in_amount, out_amount, U256::zero()) + }; + ethereum::Log { + address: pool, + topics: vec![SWAP_TOPIC, h160_to_h256(sender), h160_to_h256(recipient)], + data: encode_uint256_quad(a0_in, a1_in, a0_out, a1_out), + } +} + +// 0=init hooks, 1=extrinsics (by index), 2=finalize hooks — preserves wall-clock order. +fn bucket_sort_key(bucket: &Bucket) -> (u8, u64) { + match bucket { + Bucket::Hook { + phase: HookPhase::Initialization, + origin: None, + } => (0, 0), + Bucket::Hook { + phase: HookPhase::Initialization, + origin: Some(_), + } => (0, bucket_nonce(*bucket)), + Bucket::Extrinsic(i) => (1, *i as u64), + Bucket::Hook { + phase: HookPhase::Finalization, + origin: None, + } => (2, 0), + Bucket::Hook { + phase: HookPhase::Finalization, + origin: Some(_), + } => (2, bucket_nonce(*bucket)), + } +} + +/// `nonce` field on the synth tx. lets indexers reverse a synth tx to its origin. +/// +/// layout: +/// Extrinsic(i) → i (low) +/// Hook { Init, None } → MAX - 3 +/// Hook { Init, Some(t) } → 0xDCA0… | tag(t) +/// Hook { Final, None } → MAX - 2 +/// Hook { Final, Some(t) } → 0xF1A1… | tag(t) +pub fn bucket_nonce(bucket: Bucket) -> u64 { + match bucket { + Bucket::Extrinsic(i) => i as u64, + Bucket::Hook { + phase: HookPhase::Initialization, + origin: None, + } => u64::MAX - 3, + Bucket::Hook { + phase: HookPhase::Initialization, + origin: Some(o), + } => 0xDCA0_0000_0000_0000u64 | origin_tag(&o), + Bucket::Hook { + phase: HookPhase::Finalization, + origin: None, + } => u64::MAX - 2, + Bucket::Hook { + phase: HookPhase::Finalization, + origin: Some(o), + } => 0xF1A1_0000_0000_0000u64 | origin_tag(&o), + } +} + +fn origin_tag(origin: &ExecutionType) -> u64 { + match origin { + ExecutionType::Router(id) => 0x0100_0000_0000 | (*id as u64), + ExecutionType::DCA(schedule_id, _) => 0x0200_0000_0000 | (*schedule_id as u64), + ExecutionType::Batch(id) => 0x0300_0000_0000 | (*id as u64), + ExecutionType::Omnipool(id) => 0x0400_0000_0000 | (*id as u64), + ExecutionType::XcmExchange(id) => 0x0500_0000_0000 | (*id as u64), + ExecutionType::Xcm(_, id) => 0x0600_0000_0000 | (*id as u64), + } +} + +#[cfg(test)] +mod tests { + use super::*; + use ethereum_types::{H160, U256}; + + #[test] + fn h160_to_h256_left_pads_with_zeros() { + let addr = H160([ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, + 0xbb, 0xcc, + ]); + let topic = h160_to_h256(addr); + // First 12 bytes zero, last 20 bytes are the address. + assert_eq!(&topic.0[..12], &[0u8; 12]); + assert_eq!(&topic.0[12..], &addr.0[..]); + } + + #[test] + fn encode_u256_be_round_trip() { + let value = U256::from(123_456_789u64); + let encoded = encode_u256_be(value); + assert_eq!(encoded.len(), 32); + assert_eq!(U256::from_big_endian(&encoded), value); + } + + #[test] + fn encode_uint256_quad_is_128_bytes() { + let data = encode_uint256_quad(U256::from(1u64), U256::from(2u64), U256::from(3u64), U256::from(4u64)); + assert_eq!(data.len(), 128); + assert_eq!(U256::from_big_endian(&data[0..32]), U256::from(1u64)); + assert_eq!(U256::from_big_endian(&data[32..64]), U256::from(2u64)); + assert_eq!(U256::from_big_endian(&data[64..96]), U256::from(3u64)); + assert_eq!(U256::from_big_endian(&data[96..128]), U256::from(4u64)); + } + + #[test] + fn build_erc20_transfer_log_matches_erc20_transfer_shape() { + let token = H160::repeat_byte(0xAB); + let from = H160::repeat_byte(0x11); + let to = H160::repeat_byte(0x22); + let log = build_erc20_transfer_log(token, from, to, U256::from(1_000u64)); + + assert_eq!(log.address, token); + assert_eq!(log.topics.len(), 3); + assert_eq!(log.topics[0], TRANSFER_TOPIC); + assert_eq!(log.topics[1], h160_to_h256(from)); + assert_eq!(log.topics[2], h160_to_h256(to)); + // non-indexed `value` is a single left-padded uint256 + assert_eq!(log.data.len(), 32); + assert_eq!(U256::from_big_endian(&log.data), U256::from(1_000u64)); + } + + #[test] + fn build_uniswap_v2_swap_log_maps_amounts_by_token0_side() { + let pool = H160::repeat_byte(0x77); + let sender = H160::repeat_byte(0x33); + let recipient = H160::repeat_byte(0x44); + let (in_amt, out_amt) = (U256::from(500u64), U256::from(900u64)); + + // input is token0 → (a0In, a1In, a0Out, a1Out) = (in, 0, 0, out) + let log0 = build_uniswap_v2_swap_log(pool, sender, recipient, true, in_amt, out_amt); + assert_eq!(log0.address, pool); + assert_eq!( + log0.topics, + vec![SWAP_TOPIC, h160_to_h256(sender), h160_to_h256(recipient)] + ); + assert_eq!(log0.data.len(), 128); + assert_eq!(U256::from_big_endian(&log0.data[0..32]), in_amt); // amount0In + assert_eq!(U256::from_big_endian(&log0.data[32..64]), U256::zero()); // amount1In + assert_eq!(U256::from_big_endian(&log0.data[64..96]), U256::zero()); // amount0Out + assert_eq!(U256::from_big_endian(&log0.data[96..128]), out_amt); // amount1Out + + // input is token1 → mirrored + let log1 = build_uniswap_v2_swap_log(pool, sender, recipient, false, in_amt, out_amt); + assert_eq!(U256::from_big_endian(&log1.data[0..32]), U256::zero()); // amount0In + assert_eq!(U256::from_big_endian(&log1.data[32..64]), in_amt); // amount1In + assert_eq!(U256::from_big_endian(&log1.data[64..96]), out_amt); // amount0Out + assert_eq!(U256::from_big_endian(&log1.data[96..128]), U256::zero()); // amount1Out + } + + #[test] + fn synth_signature_is_in_valid_range() { + // Confirms our constant signature passes the ECDSA range check; the + // flusher panics with a message if this regresses. + let sig = ethereum::eip2930::TransactionSignature::new(false, SYNTH_SIG_RS, SYNTH_SIG_RS); + assert!(sig.is_some(), "synthetic signature constants must satisfy ECDSA range"); + } + + #[test] + fn known_topic_constants_match_expected_keccak256() { + // ERC-20 Transfer(address,address,uint256) + let expected_transfer = [ + 0xdd, 0xf2, 0x52, 0xad, 0x1b, 0xe2, 0xc8, 0x9b, 0x69, 0xc2, 0xb0, 0x68, 0xfc, 0x37, 0x8d, 0xaa, 0x95, 0x2b, + 0xa7, 0xf1, 0x63, 0xc4, 0xa1, 0x16, 0x28, 0xf5, 0x5a, 0x4d, 0xf5, 0x23, 0xb3, 0xef, + ]; + assert_eq!(TRANSFER_TOPIC.0, expected_transfer); + + // Uniswap V2 Swap(address,uint256,uint256,uint256,uint256,address) + let expected_swap = [ + 0xd7, 0x8a, 0xd9, 0x5f, 0xa4, 0x6c, 0x99, 0x4b, 0x65, 0x51, 0xd0, 0xda, 0x85, 0xfc, 0x27, 0x5f, 0xe6, 0x13, + 0xce, 0x37, 0x65, 0x7f, 0xb8, 0xd5, 0xe3, 0xd1, 0x30, 0x84, 0x01, 0x59, 0xd8, 0x22, + ]; + assert_eq!(SWAP_TOPIC.0, expected_swap); + + // ERC-20 Approval(address,address,uint256) + let expected_approval = [ + 0x8c, 0x5b, 0xe1, 0xe5, 0xeb, 0xec, 0x7d, 0x5b, 0xd1, 0x4f, 0x71, 0x42, 0x7d, 0x1e, 0x84, 0xf3, 0xdd, 0x03, + 0x14, 0xc0, 0xf7, 0xb2, 0x29, 0x1e, 0x5b, 0x20, 0x0a, 0xc8, 0xc7, 0xc3, 0xb9, 0x25, + ]; + assert_eq!(APPROVAL_TOPIC.0, expected_approval); + } + + #[test] + fn reserved_address_of_is_reversible() { + let owner = H160([ + 0x12, 0x34, 0x56, 0x78, 0x9a, 0xbc, 0xde, 0xf0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xaa, + 0xbb, 0xcc, + ]); + let sentinel = reserved_address_of(owner); + assert_ne!(owner, sentinel); + assert_eq!(reserved_address_of(sentinel), owner, "xor with 0xEE is its own inverse"); + } + + // Two synth txs sharing the same bucket-nonce class but different group_index + // must have distinct canonical envelope hashes — frontier indexes by hash, so + // a collision would mean one synth tx shadows the other in eth_getTransactionByHash. + #[test] + fn synth_envelope_hash_is_unique_per_group_index() { + use super::Transaction; + use ethereum::{eip2930::TransactionSignature, EIP1559Transaction, TransactionAction}; + + let signature = TransactionSignature::new(false, SYNTH_SIG_RS, SYNTH_SIG_RS).expect("synth sig in range"); + let mk = |group_index: u32, nonce: u64| { + Transaction::EIP1559(EIP1559Transaction { + chain_id: 222_222, + nonce: U256::from(nonce), + max_priority_fee_per_gas: U256::zero(), + max_fee_per_gas: U256::zero(), + gas_limit: U256::zero(), + action: TransactionAction::Call(SENTINEL_ADDRESS), + value: U256::from(group_index), + input: Vec::new(), + access_list: Vec::new(), + signature: signature.clone(), + }) + }; + + // same nonce (same bucket-nonce class) but different group_index → distinct hashes + let nonce = u64::MAX - 3; // Hook { Init, None } + assert_ne!(mk(0, nonce).hash(), mk(1, nonce).hash()); + assert_ne!(mk(0, nonce).hash(), mk(2, nonce).hash()); + assert_ne!(mk(1, nonce).hash(), mk(2, nonce).hash()); + + // determinism: same (group_index, nonce) → same hash + assert_eq!(mk(7, 42).hash(), mk(7, 42).hash()); + } + + // The same bucket + group_index recurs every block (nonce is bucket-derived, + // group_index resets per block), so without per-block entropy the envelope + // hash would collide across blocks and frontier could only resolve one of + // them. The flusher folds parent_hash + block number into `input`; assert that + // distinguishes otherwise-identical envelopes. + #[test] + fn synth_envelope_hash_is_unique_per_block() { + use super::Transaction; + use ethereum::{eip2930::TransactionSignature, EIP1559Transaction, TransactionAction}; + + let signature = TransactionSignature::new(false, SYNTH_SIG_RS, SYNTH_SIG_RS).expect("synth sig in range"); + let mk = |block_domain: &[u8]| { + Transaction::EIP1559(EIP1559Transaction { + chain_id: 222_222, + nonce: U256::from(u64::MAX - 3), // same bucket + max_priority_fee_per_gas: U256::zero(), + max_fee_per_gas: U256::zero(), + gas_limit: U256::zero(), + action: TransactionAction::Call(SENTINEL_ADDRESS), + value: U256::zero(), // same group_index + input: block_domain.to_vec(), + access_list: Vec::new(), + signature: signature.clone(), + }) + }; + + let block_n = b"hydration-synth-v1\x00block-n-parent-hash\x00\x64"; + let block_n1 = b"hydration-synth-v1\x00block-n1-parent-hash\x00\x65"; + assert_ne!( + mk(block_n).hash(), + mk(block_n1).hash(), + "same bucket+group_index in different blocks must hash differently" + ); + // determinism within a block + assert_eq!(mk(block_n).hash(), mk(block_n).hash()); + } + + // Bucket grouping must (a) collapse repeated (bucket, log) entries from the + // same bucket into one group, and (b) sort init < extrinsic < finalize. + #[test] + fn flush_bucket_grouping_and_sort_order() { + use pallet_broadcast::types::ExecutionType; + + // Bare grouping helper that mirrors `flush`'s grouping step (without + // driving the full pallet runtime). We test the visible invariants: + // 1. preserves insertion order within a bucket + // 2. produces one group per distinct bucket + let entries: Vec<(Bucket, ethereum::Log)> = vec![ + (Bucket::Extrinsic(2), log(1)), + ( + Bucket::Hook { + phase: HookPhase::Initialization, + origin: None, + }, + log(2), + ), + (Bucket::Extrinsic(2), log(3)), + ( + Bucket::Hook { + phase: HookPhase::Finalization, + origin: None, + }, + log(4), + ), + (Bucket::Extrinsic(0), log(5)), + ( + Bucket::Hook { + phase: HookPhase::Initialization, + origin: Some(ExecutionType::DCA(7, 1)), + }, + log(6), + ), + ]; + + let mut groups: Vec<(Bucket, Vec)> = Vec::new(); + for (bucket, log) in entries { + match groups.iter_mut().find(|(b, _)| *b == bucket) { + Some((_, logs)) => logs.push(log), + None => groups.push((bucket, vec![log])), + } + } + groups.sort_by(|a, b| bucket_sort_key(&a.0).cmp(&bucket_sort_key(&b.0))); + + // 5 distinct buckets, in order: Init/None < Init/DCA < Extrinsic(0) < Extrinsic(2) < Final/None + let order: Vec = groups.iter().map(|(b, _)| *b).collect(); + assert!(matches!( + order[0], + Bucket::Hook { + phase: HookPhase::Initialization, + origin: None + } + )); + assert!(matches!( + order[1], + Bucket::Hook { + phase: HookPhase::Initialization, + origin: Some(ExecutionType::DCA(_, _)) + } + )); + assert!(matches!(order[2], Bucket::Extrinsic(0))); + assert!(matches!(order[3], Bucket::Extrinsic(2))); + assert!(matches!( + order[4], + Bucket::Hook { + phase: HookPhase::Finalization, + origin: None + } + )); + + // Extrinsic(2) holds both its logs in insertion order. + let ext2_logs = &groups + .iter() + .find(|(b, _)| matches!(b, Bucket::Extrinsic(2))) + .unwrap() + .1; + assert_eq!(ext2_logs.len(), 2); + assert_eq!(ext2_logs[0].address.0[19], 1); + assert_eq!(ext2_logs[1].address.0[19], 3); + } + + #[test] + fn bucket_nonce_layout_is_distinct_per_class() { + use pallet_broadcast::types::ExecutionType; + let cases = [ + Bucket::Extrinsic(0), + Bucket::Extrinsic(7), + Bucket::Hook { + phase: HookPhase::Initialization, + origin: None, + }, + Bucket::Hook { + phase: HookPhase::Initialization, + origin: Some(ExecutionType::DCA(123, 1)), + }, + Bucket::Hook { + phase: HookPhase::Finalization, + origin: None, + }, + Bucket::Hook { + phase: HookPhase::Finalization, + origin: Some(ExecutionType::Router(99)), + }, + ]; + let nonces: Vec = cases.iter().map(|b| bucket_nonce(*b)).collect(); + let unique: std::collections::BTreeSet<_> = nonces.iter().collect(); + assert_eq!( + unique.len(), + cases.len(), + "every bucket class must produce a distinct nonce" + ); + } + + // The pure assembly shared by the on-chain flusher and the (future) node + // runtime API: one synth tx per bucket, sorted, indices continuing from the + // real-eth-tx base, insertion order preserved within a bucket. + #[test] + fn assemble_synth_txs_groups_indexes_and_orders() { + let entries = vec![ + (Bucket::Extrinsic(2), H160::repeat_byte(0xAA), log(1)), + (Bucket::Extrinsic(0), H160::repeat_byte(0xBB), log(2)), + (Bucket::Extrinsic(2), H160::repeat_byte(0xCC), log(3)), + ]; + let txs = assemble_synth_txs(entries, 222_222, &[0x42u8; 32], 100, 5); + + // two distinct buckets → two synth txs, sorted Extrinsic(0) < Extrinsic(2) + assert_eq!(txs.len(), 2); + let (_, s0, r0) = &txs[0]; + let (_, s1, _) = &txs[1]; + + // indices continue from base_tx_index (5) + assert_eq!(s0.transaction_index, 5); + assert_eq!(s1.transaction_index, 6); + + // Extrinsic(0) holds its single log; Extrinsic(2) keeps insertion order (1 then 3) + assert_eq!(s0.logs.len(), 1); + assert_eq!(s0.logs[0].address.0[19], 2); + assert_eq!(s1.logs.len(), 2); + assert_eq!(s1.logs[0].address.0[19], 1); + assert_eq!(s1.logs[1].address.0[19], 3); + + // distinct hashes; sentinel from/to; receipt mirrors the status + assert_ne!(s0.transaction_hash, s1.transaction_hash); + assert_eq!(s0.from, SENTINEL_ADDRESS); + assert_eq!(s0.to, Some(SENTINEL_ADDRESS)); + match r0 { + super::Receipt::EIP1559(d) => { + assert_eq!(d.status_code, 1); + assert_eq!(d.logs.len(), 1); + } + _ => panic!("expected EIP1559 receipt"), + } + } + + #[test] + fn assemble_synth_txs_hash_differs_across_blocks() { + let mk = |parent: &[u8], bn: u64| { + let entries = vec![(Bucket::Extrinsic(0), H160::repeat_byte(0x01), log(1))]; + assemble_synth_txs(entries, 222_222, parent, bn, 0)[0] + .1 + .transaction_hash + }; + // same bucket + base index, different block identity → different synth-tx hash + assert_ne!(mk(&[0x11u8; 32], 100), mk(&[0x22u8; 32], 101)); + // determinism + assert_eq!(mk(&[0x11u8; 32], 100), mk(&[0x11u8; 32], 100)); + } + + fn log(tag: u8) -> ethereum::Log { + let mut addr = [0u8; 20]; + addr[19] = tag; + ethereum::Log { + address: H160(addr), + topics: vec![TRANSFER_TOPIC], + data: Vec::new(), + } + } +} diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index caaa7e33e0..a4cb5c9f4f 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: 422, + spec_version: 425, impl_version: 0, apis: RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/scripts/test-contracts/artifacts/contracts/GasEatingToken.sol/GasEatingToken.json b/scripts/test-contracts/artifacts/contracts/GasEatingToken.sol/GasEatingToken.json new file mode 100644 index 0000000000..194c93ded2 --- /dev/null +++ b/scripts/test-contracts/artifacts/contracts/GasEatingToken.sol/GasEatingToken.json @@ -0,0 +1,324 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "GasEatingToken", + "sourceName": "contracts/GasEatingToken.sol", + "abi": [ + { + "inputs": [], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "allowance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientAllowance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "balance", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "needed", + "type": "uint256" + } + ], + "name": "ERC20InsufficientBalance", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "approver", + "type": "address" + } + ], + "name": "ERC20InvalidApprover", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "receiver", + "type": "address" + } + ], + "name": "ERC20InvalidReceiver", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + } + ], + "name": "ERC20InvalidSender", + "type": "error" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "ERC20InvalidSpender", + "type": "error" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "decimals", + "outputs": [ + { + "internalType": "uint8", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "symbol", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "internalType": "address", + "name": "recipient", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60806040523480156200001157600080fd5b506040518060400160405280600881526020017f47617345617465720000000000000000000000000000000000000000000000008152506040518060400160405280600581526020017f454154455200000000000000000000000000000000000000000000000000000081525081600390816200008f91906200062a565b508060049081620000a191906200062a565b505050620000e433620000b9620000ea60201b60201c565b600a620000c79190620008a1565b633b9aca00620000d89190620008f2565b620000f360201b60201c565b62000a45565b60006012905090565b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603620001685760006040517fec442f050000000000000000000000000000000000000000000000000000000081526004016200015f919062000982565b60405180910390fd5b6200017c600083836200018060201b60201c565b5050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff1603620001d6578060026000828254620001c991906200099f565b92505081905550620002ac565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490508181101562000265578381836040517fe450d38c0000000000000000000000000000000000000000000000000000000081526004016200025c93929190620009eb565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff1603620002f7578060026000828254039250508190555062000344565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051620003a3919062000a28565b60405180910390a3505050565b600081519050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b600060028204905060018216806200043257607f821691505b602082108103620004485762000447620003ea565b5b50919050565b60008190508160005260206000209050919050565b60006020601f8301049050919050565b600082821b905092915050565b600060088302620004b27fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8262000473565b620004be868362000473565b95508019841693508086168417925050509392505050565b6000819050919050565b6000819050919050565b60006200050b62000505620004ff84620004d6565b620004e0565b620004d6565b9050919050565b6000819050919050565b6200052783620004ea565b6200053f620005368262000512565b84845462000480565b825550505050565b600090565b6200055662000547565b620005638184846200051c565b505050565b5b818110156200058b576200057f6000826200054c565b60018101905062000569565b5050565b601f821115620005da57620005a4816200044e565b620005af8462000463565b81016020851015620005bf578190505b620005d7620005ce8562000463565b83018262000568565b50505b505050565b600082821c905092915050565b6000620005ff60001984600802620005df565b1980831691505092915050565b60006200061a8383620005ec565b9150826002028217905092915050565b6200063582620003b0565b67ffffffffffffffff811115620006515762000650620003bb565b5b6200065d825462000419565b6200066a8282856200058f565b600060209050601f831160018114620006a257600084156200068d578287015190505b6200069985826200060c565b86555062000709565b601f198416620006b2866200044e565b60005b82811015620006dc57848901518255600182019150602085019450602081019050620006b5565b86831015620006fc5784890151620006f8601f891682620005ec565b8355505b6001600288020188555050505b505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60008160011c9050919050565b6000808291508390505b60018511156200079f5780860481111562000777576200077662000711565b5b6001851615620007875780820291505b8081029050620007978562000740565b945062000757565b94509492505050565b600082620007ba57600190506200088d565b81620007ca57600090506200088d565b8160018114620007e35760028114620007ee5762000824565b60019150506200088d565b60ff84111562000803576200080262000711565b5b8360020a9150848211156200081d576200081c62000711565b5b506200088d565b5060208310610133831016604e8410600b84101617156200085e5782820a90508381111562000858576200085762000711565b5b6200088d565b6200086d84848460016200074d565b9250905081840481111562000887576200088662000711565b5b81810290505b9392505050565b600060ff82169050919050565b6000620008ae82620004d6565b9150620008bb8362000894565b9250620008ea7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8484620007a8565b905092915050565b6000620008ff82620004d6565b91506200090c83620004d6565b92508282026200091c81620004d6565b9150828204841483151762000936576200093562000711565b5b5092915050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006200096a826200093d565b9050919050565b6200097c816200095d565b82525050565b600060208201905062000999600083018462000971565b92915050565b6000620009ac82620004d6565b9150620009b983620004d6565b9250828201905080821115620009d457620009d362000711565b5b92915050565b620009e581620004d6565b82525050565b600060608201905062000a02600083018662000971565b62000a116020830185620009da565b62000a206040830184620009da565b949350505050565b600060208201905062000a3f6000830184620009da565b92915050565b610f658062000a556000396000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c8063313ce56711610066578063313ce5671461013457806370a082311461015257806395d89b4114610182578063a9059cbb146101a0578063dd62ed3e146101d057610093565b806306fdde0314610098578063095ea7b3146100b657806318160ddd146100e657806323b872dd14610104575b600080fd5b6100a0610200565b6040516100ad9190610b3d565b60405180910390f35b6100d060048036038101906100cb9190610bf8565b610292565b6040516100dd9190610c53565b60405180910390f35b6100ee6102b5565b6040516100fb9190610c7d565b60405180910390f35b61011e60048036038101906101199190610c98565b6102bf565b60405161012b9190610c53565b60405180910390f35b61013c61030a565b6040516101499190610d07565b60405180910390f35b61016c60048036038101906101679190610d22565b610313565b6040516101799190610c7d565b60405180910390f35b61018a61035b565b6040516101979190610b3d565b60405180910390f35b6101ba60048036038101906101b59190610bf8565b6103ed565b6040516101c79190610c53565b60405180910390f35b6101ea60048036038101906101e59190610d4f565b610436565b6040516101f79190610c7d565b60405180910390f35b60606003805461020f90610dbe565b80601f016020809104026020016040519081016040528092919081815260200182805461023b90610dbe565b80156102885780601f1061025d57610100808354040283529160200191610288565b820191906000526020600020905b81548152906001019060200180831161026b57829003601f168201915b5050505050905090565b60008061029d6104bd565b90506102aa8185856104c5565b600191505092915050565b6000600254905090565b60006205cc6060005a905060005b825a836102da9190610e1e565b10156102f35780806102eb90610e52565b9150506102cd565b6102fe8787876104d7565b93505050509392505050565b60006012905090565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60606004805461036a90610dbe565b80601f016020809104026020016040519081016040528092919081815260200182805461039690610dbe565b80156103e35780601f106103b8576101008083540402835291602001916103e3565b820191906000526020600020905b8154815290600101906020018083116103c657829003601f168201915b5050505050905090565b60006205cc6060005a905060005b825a836104089190610e1e565b101561042157808061041990610e52565b9150506103fb565b61042b8686610506565b935050505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b6104d28383836001610529565b505050565b6000806104e26104bd565b90506104ef858285610700565b6104fa858585610794565b60019150509392505050565b6000806105116104bd565b905061051e818585610794565b600191505092915050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff160361059b5760006040517fe602df050000000000000000000000000000000000000000000000000000000081526004016105929190610ea9565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361060d5760006040517f94280d620000000000000000000000000000000000000000000000000000000081526004016106049190610ea9565b60405180910390fd5b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555080156106fa578273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516106f19190610c7d565b60405180910390a35b50505050565b600061070c8484610436565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461078e578181101561077e578281836040517ffb8f41b200000000000000000000000000000000000000000000000000000000815260040161077593929190610ec4565b60405180910390fd5b61078d84848484036000610529565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036108065760006040517f96c6fd1e0000000000000000000000000000000000000000000000000000000081526004016107fd9190610ea9565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036108785760006040517fec442f0500000000000000000000000000000000000000000000000000000000815260040161086f9190610ea9565b60405180910390fd5b610883838383610888565b505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036108da5780600260008282546108ce9190610efb565b925050819055506109ad565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610966578381836040517fe450d38c00000000000000000000000000000000000000000000000000000000815260040161095d93929190610ec4565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036109f65780600260008282540392505081905550610a43565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610aa09190610c7d565b60405180910390a3505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610ae7578082015181840152602081019050610acc565b60008484015250505050565b6000601f19601f8301169050919050565b6000610b0f82610aad565b610b198185610ab8565b9350610b29818560208601610ac9565b610b3281610af3565b840191505092915050565b60006020820190508181036000830152610b578184610b04565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610b8f82610b64565b9050919050565b610b9f81610b84565b8114610baa57600080fd5b50565b600081359050610bbc81610b96565b92915050565b6000819050919050565b610bd581610bc2565b8114610be057600080fd5b50565b600081359050610bf281610bcc565b92915050565b60008060408385031215610c0f57610c0e610b5f565b5b6000610c1d85828601610bad565b9250506020610c2e85828601610be3565b9150509250929050565b60008115159050919050565b610c4d81610c38565b82525050565b6000602082019050610c686000830184610c44565b92915050565b610c7781610bc2565b82525050565b6000602082019050610c926000830184610c6e565b92915050565b600080600060608486031215610cb157610cb0610b5f565b5b6000610cbf86828701610bad565b9350506020610cd086828701610bad565b9250506040610ce186828701610be3565b9150509250925092565b600060ff82169050919050565b610d0181610ceb565b82525050565b6000602082019050610d1c6000830184610cf8565b92915050565b600060208284031215610d3857610d37610b5f565b5b6000610d4684828501610bad565b91505092915050565b60008060408385031215610d6657610d65610b5f565b5b6000610d7485828601610bad565b9250506020610d8585828601610bad565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610dd657607f821691505b602082108103610de957610de8610d8f565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610e2982610bc2565b9150610e3483610bc2565b9250828203905081811115610e4c57610e4b610def565b5b92915050565b6000610e5d82610bc2565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610e8f57610e8e610def565b5b600182019050919050565b610ea381610b84565b82525050565b6000602082019050610ebe6000830184610e9a565b92915050565b6000606082019050610ed96000830186610e9a565b610ee66020830185610c6e565b610ef36040830184610c6e565b949350505050565b6000610f0682610bc2565b9150610f1183610bc2565b9250828201905080821115610f2957610f28610def565b5b9291505056fea26469706673582212203bafddc7b544e22a6f9cb5cc7ac40e036ecc256d79e905ac7755e127f009825464736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100935760003560e01c8063313ce56711610066578063313ce5671461013457806370a082311461015257806395d89b4114610182578063a9059cbb146101a0578063dd62ed3e146101d057610093565b806306fdde0314610098578063095ea7b3146100b657806318160ddd146100e657806323b872dd14610104575b600080fd5b6100a0610200565b6040516100ad9190610b3d565b60405180910390f35b6100d060048036038101906100cb9190610bf8565b610292565b6040516100dd9190610c53565b60405180910390f35b6100ee6102b5565b6040516100fb9190610c7d565b60405180910390f35b61011e60048036038101906101199190610c98565b6102bf565b60405161012b9190610c53565b60405180910390f35b61013c61030a565b6040516101499190610d07565b60405180910390f35b61016c60048036038101906101679190610d22565b610313565b6040516101799190610c7d565b60405180910390f35b61018a61035b565b6040516101979190610b3d565b60405180910390f35b6101ba60048036038101906101b59190610bf8565b6103ed565b6040516101c79190610c53565b60405180910390f35b6101ea60048036038101906101e59190610d4f565b610436565b6040516101f79190610c7d565b60405180910390f35b60606003805461020f90610dbe565b80601f016020809104026020016040519081016040528092919081815260200182805461023b90610dbe565b80156102885780601f1061025d57610100808354040283529160200191610288565b820191906000526020600020905b81548152906001019060200180831161026b57829003601f168201915b5050505050905090565b60008061029d6104bd565b90506102aa8185856104c5565b600191505092915050565b6000600254905090565b60006205cc6060005a905060005b825a836102da9190610e1e565b10156102f35780806102eb90610e52565b9150506102cd565b6102fe8787876104d7565b93505050509392505050565b60006012905090565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60606004805461036a90610dbe565b80601f016020809104026020016040519081016040528092919081815260200182805461039690610dbe565b80156103e35780601f106103b8576101008083540402835291602001916103e3565b820191906000526020600020905b8154815290600101906020018083116103c657829003601f168201915b5050505050905090565b60006205cc6060005a905060005b825a836104089190610e1e565b101561042157808061041990610e52565b9150506103fb565b61042b8686610506565b935050505092915050565b6000600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905092915050565b600033905090565b6104d28383836001610529565b505050565b6000806104e26104bd565b90506104ef858285610700565b6104fa858585610794565b60019150509392505050565b6000806105116104bd565b905061051e818585610794565b600191505092915050565b600073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff160361059b5760006040517fe602df050000000000000000000000000000000000000000000000000000000081526004016105929190610ea9565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff160361060d5760006040517f94280d620000000000000000000000000000000000000000000000000000000081526004016106049190610ea9565b60405180910390fd5b81600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208190555080156106fa578273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040516106f19190610c7d565b60405180910390a35b50505050565b600061070c8484610436565b90507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff811461078e578181101561077e578281836040517ffb8f41b200000000000000000000000000000000000000000000000000000000815260040161077593929190610ec4565b60405180910390fd5b61078d84848484036000610529565b5b50505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036108065760006040517f96c6fd1e0000000000000000000000000000000000000000000000000000000081526004016107fd9190610ea9565b60405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036108785760006040517fec442f0500000000000000000000000000000000000000000000000000000000815260040161086f9190610ea9565b60405180910390fd5b610883838383610888565b505050565b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff16036108da5780600260008282546108ce9190610efb565b925050819055506109ad565b60008060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905081811015610966578381836040517fe450d38c00000000000000000000000000000000000000000000000000000000815260040161095d93929190610ec4565b60405180910390fd5b8181036000808673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550505b600073ffffffffffffffffffffffffffffffffffffffff168273ffffffffffffffffffffffffffffffffffffffff16036109f65780600260008282540392505081905550610a43565b806000808473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055505b8173ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef83604051610aa09190610c7d565b60405180910390a3505050565b600081519050919050565b600082825260208201905092915050565b60005b83811015610ae7578082015181840152602081019050610acc565b60008484015250505050565b6000601f19601f8301169050919050565b6000610b0f82610aad565b610b198185610ab8565b9350610b29818560208601610ac9565b610b3281610af3565b840191505092915050565b60006020820190508181036000830152610b578184610b04565b905092915050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000610b8f82610b64565b9050919050565b610b9f81610b84565b8114610baa57600080fd5b50565b600081359050610bbc81610b96565b92915050565b6000819050919050565b610bd581610bc2565b8114610be057600080fd5b50565b600081359050610bf281610bcc565b92915050565b60008060408385031215610c0f57610c0e610b5f565b5b6000610c1d85828601610bad565b9250506020610c2e85828601610be3565b9150509250929050565b60008115159050919050565b610c4d81610c38565b82525050565b6000602082019050610c686000830184610c44565b92915050565b610c7781610bc2565b82525050565b6000602082019050610c926000830184610c6e565b92915050565b600080600060608486031215610cb157610cb0610b5f565b5b6000610cbf86828701610bad565b9350506020610cd086828701610bad565b9250506040610ce186828701610be3565b9150509250925092565b600060ff82169050919050565b610d0181610ceb565b82525050565b6000602082019050610d1c6000830184610cf8565b92915050565b600060208284031215610d3857610d37610b5f565b5b6000610d4684828501610bad565b91505092915050565b60008060408385031215610d6657610d65610b5f565b5b6000610d7485828601610bad565b9250506020610d8585828601610bad565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b60006002820490506001821680610dd657607f821691505b602082108103610de957610de8610d8f565b5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000610e2982610bc2565b9150610e3483610bc2565b9250828203905081811115610e4c57610e4b610def565b5b92915050565b6000610e5d82610bc2565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610e8f57610e8e610def565b5b600182019050919050565b610ea381610b84565b82525050565b6000602082019050610ebe6000830184610e9a565b92915050565b6000606082019050610ed96000830186610e9a565b610ee66020830185610c6e565b610ef36040830184610c6e565b949350505050565b6000610f0682610bc2565b9150610f1183610bc2565b9250828201905080821115610f2957610f28610def565b5b9291505056fea26469706673582212203bafddc7b544e22a6f9cb5cc7ac40e036ecc256d79e905ac7755e127f009825464736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/scripts/test-contracts/artifacts/contracts/LogOrderProbe.sol/IERC20.json b/scripts/test-contracts/artifacts/contracts/LogOrderProbe.sol/IERC20.json new file mode 100644 index 0000000000..66919cc8e0 --- /dev/null +++ b/scripts/test-contracts/artifacts/contracts/LogOrderProbe.sol/IERC20.json @@ -0,0 +1,35 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "IERC20", + "sourceName": "contracts/LogOrderProbe.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x", + "deployedBytecode": "0x", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/scripts/test-contracts/artifacts/contracts/LogOrderProbe.sol/LogOrderProbe.json b/scripts/test-contracts/artifacts/contracts/LogOrderProbe.sol/LogOrderProbe.json new file mode 100644 index 0000000000..e027268945 --- /dev/null +++ b/scripts/test-contracts/artifacts/contracts/LogOrderProbe.sol/LogOrderProbe.json @@ -0,0 +1,47 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "LogOrderProbe", + "sourceName": "contracts/LogOrderProbe.sol", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "idx", + "type": "uint256" + } + ], + "name": "Marker", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amount", + "type": "uint256" + } + ], + "name": "exercise", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b506103c4806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c8063430c930414610030575b600080fd5b61004a600480360381019061004591906101d9565b61004c565b005b7f83264c98256454386201e4c55918ea57058c5c0052e60bd0b0f9a8fd2f3c1b24600060405161007c9190610271565b60405180910390a18273ffffffffffffffffffffffffffffffffffffffff1663a9059cbb83836040518363ffffffff1660e01b81526004016100bf9291906102aa565b6020604051808303816000875af11580156100de573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610102919061030b565b507f83264c98256454386201e4c55918ea57058c5c0052e60bd0b0f9a8fd2f3c1b2460016040516101339190610373565b60405180910390a1505050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061017082610145565b9050919050565b61018081610165565b811461018b57600080fd5b50565b60008135905061019d81610177565b92915050565b6000819050919050565b6101b6816101a3565b81146101c157600080fd5b50565b6000813590506101d3816101ad565b92915050565b6000806000606084860312156101f2576101f1610140565b5b60006102008682870161018e565b93505060206102118682870161018e565b9250506040610222868287016101c4565b9150509250925092565b6000819050919050565b6000819050919050565b600061025b6102566102518461022c565b610236565b6101a3565b9050919050565b61026b81610240565b82525050565b60006020820190506102866000830184610262565b92915050565b61029581610165565b82525050565b6102a4816101a3565b82525050565b60006040820190506102bf600083018561028c565b6102cc602083018461029b565b9392505050565b60008115159050919050565b6102e8816102d3565b81146102f357600080fd5b50565b600081519050610305816102df565b92915050565b60006020828403121561032157610320610140565b5b600061032f848285016102f6565b91505092915050565b6000819050919050565b600061035d61035861035384610338565b610236565b6101a3565b9050919050565b61036d81610342565b82525050565b60006020820190506103886000830184610364565b9291505056fea2646970667358221220737d96a602a1437aee784f9959dfb12b28e1ed46dc9f2f6dda81d7bb84adf3b164736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063430c930414610030575b600080fd5b61004a600480360381019061004591906101d9565b61004c565b005b7f83264c98256454386201e4c55918ea57058c5c0052e60bd0b0f9a8fd2f3c1b24600060405161007c9190610271565b60405180910390a18273ffffffffffffffffffffffffffffffffffffffff1663a9059cbb83836040518363ffffffff1660e01b81526004016100bf9291906102aa565b6020604051808303816000875af11580156100de573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610102919061030b565b507f83264c98256454386201e4c55918ea57058c5c0052e60bd0b0f9a8fd2f3c1b2460016040516101339190610373565b60405180910390a1505050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061017082610145565b9050919050565b61018081610165565b811461018b57600080fd5b50565b60008135905061019d81610177565b92915050565b6000819050919050565b6101b6816101a3565b81146101c157600080fd5b50565b6000813590506101d3816101ad565b92915050565b6000806000606084860312156101f2576101f1610140565b5b60006102008682870161018e565b93505060206102118682870161018e565b9250506040610222868287016101c4565b9150509250925092565b6000819050919050565b6000819050919050565b600061025b6102566102518461022c565b610236565b6101a3565b9050919050565b61026b81610240565b82525050565b60006020820190506102866000830184610262565b92915050565b61029581610165565b82525050565b6102a4816101a3565b82525050565b60006040820190506102bf600083018561028c565b6102cc602083018461029b565b9392505050565b60008115159050919050565b6102e8816102d3565b81146102f357600080fd5b50565b600081519050610305816102df565b92915050565b60006020828403121561032157610320610140565b5b600061032f848285016102f6565b91505092915050565b6000819050919050565b600061035d61035861035384610338565b610236565b6101a3565b9050919050565b61036d81610342565b82525050565b60006020820190506103886000830184610364565b9291505056fea2646970667358221220737d96a602a1437aee784f9959dfb12b28e1ed46dc9f2f6dda81d7bb84adf3b164736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/scripts/test-contracts/artifacts/contracts/RevertingDispatcher.sol/IDispatcher.json b/scripts/test-contracts/artifacts/contracts/RevertingDispatcher.sol/IDispatcher.json new file mode 100644 index 0000000000..b7120d49e0 --- /dev/null +++ b/scripts/test-contracts/artifacts/contracts/RevertingDispatcher.sol/IDispatcher.json @@ -0,0 +1,30 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "IDispatcher", + "sourceName": "contracts/RevertingDispatcher.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "bytes", + "name": "", + "type": "bytes" + } + ], + "name": "dispatch", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x", + "deployedBytecode": "0x", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/scripts/test-contracts/artifacts/contracts/RevertingDispatcher.sol/RevertingDispatcher.json b/scripts/test-contracts/artifacts/contracts/RevertingDispatcher.sol/RevertingDispatcher.json new file mode 100644 index 0000000000..fea037c8fd --- /dev/null +++ b/scripts/test-contracts/artifacts/contracts/RevertingDispatcher.sol/RevertingDispatcher.json @@ -0,0 +1,42 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "RevertingDispatcher", + "sourceName": "contracts/RevertingDispatcher.sol", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "idx", + "type": "uint256" + } + ], + "name": "Marker", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "dispatcher", + "type": "address" + }, + { + "internalType": "bytes", + "name": "sub_call_data", + "type": "bytes" + } + ], + "name": "try_dispatch_then_revert", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x608060405234801561001057600080fd5b50610530806100206000396000f3fe608060405234801561001057600080fd5b506004361061002b5760003560e01c80634351d80014610030575b600080fd5b61004a60048036038101906100459190610273565b61004c565b005b7f83264c98256454386201e4c55918ea57058c5c0052e60bd0b0f9a8fd2f3c1b24600060405161007c9190610322565b60405180910390a160008373ffffffffffffffffffffffffffffffffffffffff1683836040516100ad92919061037c565b6000604051808303816000865af19150503d80600081146100ea576040519150601f19603f3d011682016040523d82523d6000602084013e6100ef565b606091505b5050905080610133576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161012a906103f2565b60405180910390fd5b7f83264c98256454386201e4c55918ea57058c5c0052e60bd0b0f9a8fd2f3c1b246001604051610163919061044d565b60405180910390a16040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161019d906104da565b60405180910390fd5b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101db826101b0565b9050919050565b6101eb816101d0565b81146101f657600080fd5b50565b600081359050610208816101e2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126102335761023261020e565b5b8235905067ffffffffffffffff8111156102505761024f610213565b5b60208301915083600182028301111561026c5761026b610218565b5b9250929050565b60008060006040848603121561028c5761028b6101a6565b5b600061029a868287016101f9565b935050602084013567ffffffffffffffff8111156102bb576102ba6101ab565b5b6102c78682870161021d565b92509250509250925092565b6000819050919050565b6000819050919050565b6000819050919050565b600061030c610307610302846102d3565b6102e7565b6102dd565b9050919050565b61031c816102f1565b82525050565b60006020820190506103376000830184610313565b92915050565b600081905092915050565b82818337600083830152505050565b6000610363838561033d565b9350610370838584610348565b82840190509392505050565b6000610389828486610357565b91508190509392505050565b600082825260208201905092915050565b7f6469737061746368206661696c65640000000000000000000000000000000000600082015250565b60006103dc600f83610395565b91506103e7826103a6565b602082019050919050565b6000602082019050818103600083015261040b816103cf565b9050919050565b6000819050919050565b600061043761043261042d84610412565b6102e7565b6102dd565b9050919050565b6104478161041c565b82525050565b6000602082019050610462600083018461043e565b92915050565b7f696e74656e74696f6e616c20726576657274206166746572206469737061746360008201527f6800000000000000000000000000000000000000000000000000000000000000602082015250565b60006104c4602183610395565b91506104cf82610468565b604082019050919050565b600060208201905081810360008301526104f3816104b7565b905091905056fea264697066735822122006a2182eb335756d2c37f9d3e077a3a7856fa3899cb4f90410c9cca0029aee4464736f6c63430008180033", + "deployedBytecode": "0x608060405234801561001057600080fd5b506004361061002b5760003560e01c80634351d80014610030575b600080fd5b61004a60048036038101906100459190610273565b61004c565b005b7f83264c98256454386201e4c55918ea57058c5c0052e60bd0b0f9a8fd2f3c1b24600060405161007c9190610322565b60405180910390a160008373ffffffffffffffffffffffffffffffffffffffff1683836040516100ad92919061037c565b6000604051808303816000865af19150503d80600081146100ea576040519150601f19603f3d011682016040523d82523d6000602084013e6100ef565b606091505b5050905080610133576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161012a906103f2565b60405180910390fd5b7f83264c98256454386201e4c55918ea57058c5c0052e60bd0b0f9a8fd2f3c1b246001604051610163919061044d565b60405180910390a16040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161019d906104da565b60405180910390fd5b600080fd5b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006101db826101b0565b9050919050565b6101eb816101d0565b81146101f657600080fd5b50565b600081359050610208816101e2565b92915050565b600080fd5b600080fd5b600080fd5b60008083601f8401126102335761023261020e565b5b8235905067ffffffffffffffff8111156102505761024f610213565b5b60208301915083600182028301111561026c5761026b610218565b5b9250929050565b60008060006040848603121561028c5761028b6101a6565b5b600061029a868287016101f9565b935050602084013567ffffffffffffffff8111156102bb576102ba6101ab565b5b6102c78682870161021d565b92509250509250925092565b6000819050919050565b6000819050919050565b6000819050919050565b600061030c610307610302846102d3565b6102e7565b6102dd565b9050919050565b61031c816102f1565b82525050565b60006020820190506103376000830184610313565b92915050565b600081905092915050565b82818337600083830152505050565b6000610363838561033d565b9350610370838584610348565b82840190509392505050565b6000610389828486610357565b91508190509392505050565b600082825260208201905092915050565b7f6469737061746368206661696c65640000000000000000000000000000000000600082015250565b60006103dc600f83610395565b91506103e7826103a6565b602082019050919050565b6000602082019050818103600083015261040b816103cf565b9050919050565b6000819050919050565b600061043761043261042d84610412565b6102e7565b6102dd565b9050919050565b6104478161041c565b82525050565b6000602082019050610462600083018461043e565b92915050565b7f696e74656e74696f6e616c20726576657274206166746572206469737061746360008201527f6800000000000000000000000000000000000000000000000000000000000000602082015250565b60006104c4602183610395565b91506104cf82610468565b604082019050919050565b600060208201905081810360008301526104f3816104b7565b905091905056fea264697066735822122006a2182eb335756d2c37f9d3e077a3a7856fa3899cb4f90410c9cca0029aee4464736f6c63430008180033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/scripts/test-contracts/contracts/LogOrderProbe.sol b/scripts/test-contracts/contracts/LogOrderProbe.sol new file mode 100644 index 0000000000..b1eede4aa6 --- /dev/null +++ b/scripts/test-contracts/contracts/LogOrderProbe.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +interface IERC20 { + function transfer(address to, uint256 amount) external returns (bool); +} + +/// Probe contract used to verify EVM log ordering. +/// +/// `exercise(token, to, amount)` emits Marker(0), then calls +/// token.transfer(to, amount) (which goes through the multicurrency +/// precompile and triggers the substrate-side hook), then emits Marker(1). +/// +/// In the resulting receipt logs we expect log_index ordering: +/// [Marker(0), Transfer(from=this, to=to, value=amount), Marker(1)] +/// +/// — proving that substrate-hook-emitted logs land at the precompile's +/// call site (via the per-precompile drain) and are NOT bunched at the +/// end of the EVM frame's logs. +contract LogOrderProbe { + event Marker(uint256 idx); + + function exercise(address token, address to, uint256 amount) external { + emit Marker(0); + IERC20(token).transfer(to, amount); + emit Marker(1); + } +} diff --git a/scripts/test-contracts/contracts/RevertingDispatcher.sol b/scripts/test-contracts/contracts/RevertingDispatcher.sol new file mode 100644 index 0000000000..8662cfd4c8 --- /dev/null +++ b/scripts/test-contracts/contracts/RevertingDispatcher.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.24; + +interface IDispatcher { + function dispatch(bytes calldata) external returns (bool); +} + +/// Probe for the "revert with substrate state changes pending" path. +/// +/// `try_dispatch_then_revert(target, data)` calls `target.call(data)` (assumed +/// to be the dispatcher precompile) and then unconditionally reverts. +/// The dispatch's substrate state changes get rolled back by pallet_evm's +/// `with_storage_layer` wrapping; the dispatcher's inline-emitted logs get +/// dropped by frontier when the outer frame reverts (substate logs are +/// cleared on revert). This contract verifies that NO Transfer logs leak +/// into the resulting eth-tx receipt or the synthetic-logs buffer when the +/// outer EVM frame reverts. +contract RevertingDispatcher { + event Marker(uint256 idx); + + function try_dispatch_then_revert(address dispatcher, bytes calldata sub_call_data) external { + emit Marker(0); + (bool ok, ) = dispatcher.call(sub_call_data); + require(ok, "dispatch failed"); + emit Marker(1); + revert("intentional revert after dispatch"); + } +}