Skip to content

Commit c2df4cf

Browse files
committed
permit batch test
1 parent d8faa9e commit c2df4cf

1 file changed

Lines changed: 141 additions & 0 deletions

File tree

integration-tests/src/evm_permit.rs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2875,6 +2875,147 @@ mod sponsored_paymaster {
28752875
)
28762876
}
28772877

2878+
fn build_permit_at_current_nonce(
2879+
inner_call: &RuntimeCall,
2880+
gas_limit: u64,
2881+
) -> (H160, Vec<u8>, u64, U256, u8, H256, H256) {
2882+
let from = alith_evm_address();
2883+
let secret_key = SecretKey::parse(&alith_secret_key()).unwrap();
2884+
let data = inner_call.encode();
2885+
let deadline = U256::from(1_000_000_000_000u128);
2886+
let nonce = pallet_evm_precompile_call_permit::NoncesStorage::get(from);
2887+
2888+
let permit = pallet_evm_precompile_call_permit::CallPermitPrecompile::<Runtime>::generate_permit(
2889+
CALLPERMIT,
2890+
from,
2891+
DISPATCH_ADDR,
2892+
U256::from(0),
2893+
data.clone(),
2894+
gas_limit,
2895+
nonce,
2896+
deadline,
2897+
);
2898+
let message = Message::parse(&permit);
2899+
let (rs, v) = sign(&message, &secret_key);
2900+
(
2901+
from,
2902+
data,
2903+
gas_limit,
2904+
deadline,
2905+
v.serialize(),
2906+
H256::from(rs.r.b32()),
2907+
H256::from(rs.s.b32()),
2908+
)
2909+
}
2910+
2911+
fn permit_batch_of_evm_calls(
2912+
paymaster: &AccountId,
2913+
target: H160,
2914+
legs: usize,
2915+
outer_gas_limit: u64,
2916+
leg_gas_limit: u64,
2917+
) -> frame_support::dispatch::DispatchResultWithPostInfo {
2918+
let leg = RuntimeCall::Dispatcher(pallet_dispatcher::Call::dispatch_evm_call {
2919+
call: Box::new(RuntimeCall::EVM(pallet_evm::Call::call {
2920+
source: alith_evm_address(),
2921+
target,
2922+
input: vec![0x06, 0xfd, 0xde, 0x03],
2923+
value: U256::zero(),
2924+
gas_limit: leg_gas_limit,
2925+
max_fee_per_gas: crate::evm::gas_price(),
2926+
max_priority_fee_per_gas: None,
2927+
nonce: None,
2928+
access_list: vec![],
2929+
authorization_list: vec![],
2930+
})),
2931+
});
2932+
2933+
let batch = RuntimeCall::Utility(pallet_utility::Call::batch_all { calls: vec![leg; legs] });
2934+
2935+
let (from, data, gas_limit, deadline, v, r, s) = build_permit_at_current_nonce(&batch, outer_gas_limit);
2936+
2937+
MultiTransactionPayment::dispatch_permit(
2938+
RuntimeOrigin::signed(paymaster.clone()),
2939+
from,
2940+
DISPATCH_ADDR,
2941+
U256::from(0),
2942+
data,
2943+
gas_limit,
2944+
deadline,
2945+
v,
2946+
r,
2947+
s,
2948+
)
2949+
}
2950+
2951+
#[test]
2952+
fn dispatch_permit_should_execute_every_leg_when_batching_evm_calls_under_one_permit() {
2953+
TestNet::reset();
2954+
2955+
Hydra::execute_with(|| {
2956+
init_omnipool_with_oracle_for_block_10();
2957+
2958+
let contract = crate::utils::contracts::deploy_contract("HydraToken", crate::contracts::deployer());
2959+
let paymaster = paymaster_account();
2960+
assert_ok!(Balances::mint_into(&paymaster, 100_000 * UNITS));
2961+
2962+
let user_acc = MockAccount::new(alith_truncated_account());
2963+
let initial_user_weth = user_acc.balance(WETH);
2964+
let initial_paymaster_hdx = Currencies::free_balance(HDX, &paymaster);
2965+
2966+
assert_ok!(permit_batch_of_evm_calls(&paymaster, contract, 14, 10_000_000, 100_000));
2967+
2968+
let batch_completed = frame_system::Pallet::<Runtime>::events().iter().any(|record| {
2969+
matches!(
2970+
&record.event,
2971+
RuntimeEvent::Utility(pallet_utility::Event::BatchCompleted)
2972+
)
2973+
});
2974+
assert!(
2975+
batch_completed,
2976+
"batch_all emits BatchCompleted only when EVERY leg succeeded"
2977+
);
2978+
2979+
assert_eq!(
2980+
user_acc.balance(WETH),
2981+
initial_user_weth,
2982+
"signer MUST NOT pay for any leg"
2983+
);
2984+
assert!(
2985+
Currencies::free_balance(HDX, &paymaster) < initial_paymaster_hdx,
2986+
"paymaster MUST bear the cost of the whole batch"
2987+
);
2988+
});
2989+
}
2990+
2991+
#[test]
2992+
fn dispatch_permit_should_fail_when_gas_limit_exceeds_what_the_paymaster_can_cover() {
2993+
TestNet::reset();
2994+
2995+
Hydra::execute_with(|| {
2996+
init_omnipool_with_oracle_for_block_10();
2997+
2998+
let contract = crate::utils::contracts::deploy_contract("HydraToken", crate::contracts::deployer());
2999+
let paymaster = paymaster_account();
3000+
3001+
let affordable = permit_batch_of_evm_calls(&paymaster, contract, 14, 10_000_000, 100_000);
3002+
assert_ok!(affordable);
3003+
3004+
let too_expensive = permit_batch_of_evm_calls(&paymaster, contract, 14, 40_000_000, 100_000)
3005+
.expect_err("gas budget above the paymaster's balance must be rejected");
3006+
3007+
assert_eq!(
3008+
too_expensive.error,
3009+
pallet_transaction_multi_payment::Error::<Runtime>::EvmPermitRunnerError.into(),
3010+
"the binding constraint is gas_limit x gas_price vs fee-payer balance, not the block gas limit"
3011+
);
3012+
assert!(
3013+
<Runtime as pallet_evm::Config>::BlockGasLimit::get() > U256::from(40_000_000u64),
3014+
"40M is well under the 60M block ceiling — this failure is affordability, not the block limit"
3015+
);
3016+
});
3017+
}
3018+
28783019
fn submit_signed_dispatch_permit_omni_sell(
28793020
paymaster: AccountId,
28803021
sell_amount: Balance,

0 commit comments

Comments
 (0)