On benchmarks one can often see whitelisted_caller() being used to test extrinsics and I wonder, how do they bypass the need for fee, and even pay for extrinsics like transfer_keep_alive for benchmarks.

Here's some code :

// Give some multiple of the existential deposit
let balance = existential_deposit.saturating_mul(ED_MULTIPLIER.into());
let _ = <Balances<T, I> as Currency<_>>::make_free_balance_be(&caller, balance);

As you can see, caller which is the whitelisted account, is being given some balance, but he's also the caller of the function, which means, either he's paying no fee, or, has some coin from genesis. Or is it something entirely unrelated like the mock runtime not having the TransactionPayment pallet installed? I doubt it's the latter.

This also brings me to a general question about writing benchmarks, how should we go about benchmarking functions which requires the caller to lock or spend a substantial sum? Do we first mint coin into their wallet using the above technique make_free_balance_be or is there a better way?

1 Answer 1


Your assumption was nearly correct as you can see here. https://github.com/paritytech/substrate/blob/c4b83faeda9d8ee96d93a528f1bceded42b91945/frame/benchmarking/src/v1.rs#L1053

For benchmarking functions which requires the caller to lock or spend a substantial sum don't use whitelisted_caller() function. Instead use something like below where we create ALICE/BOB account and call make_free_balance_be. And then in create_nft benchmark function we are using one of these accounts instead of whitelisted_caller(). Then in verify you can check the necessary state checks you want to.

pub fn prepare_benchmarks<T: Config>() -> BenchmarkData {
let alice: T::AccountId = get_account::<T>("ALICE");
let bob: T::AccountId = get_account::<T>("BOB");

// Give them enough caps.
T::Currency::make_free_balance_be(&alice, BalanceOf::<T>::max_value());
T::Currency::make_free_balance_be(&bob, BalanceOf::<T>::max_value());

let nft_offchain_data =
    BoundedVec::try_from(vec![1; T::NFTOffchainDataLimit::get() as usize]).unwrap();
let collection_offchain_data =
    BoundedVec::try_from(vec![1; T::CollectionOffchainDataLimit::get() as usize]).unwrap();

// Create default NFT and collection.

BenchmarkData {
    nft_id: NFT::<T>::next_nft_id() - 1,
    collection_id: NFT::<T>::next_collection_id() - 1,

benchmarks! {
    create_nft {
        let s in 0 .. T::CollectionSizeLimit::get() - 1;
        let benchmark_data = prepare_benchmarks::<T>();
        let alice: T::AccountId = get_account::<T>("ALICE");
        let nft_offchain_data: BoundedVec<u8, T::NFTOffchainDataLimit> = BoundedVec::try_from(vec![1; T::NFTOffchainDataLimit::get() as usize]).unwrap();
        // Fill the collection.
        NFT::<T>::create_filled_collection(alice.clone(), benchmark_data.collection_id, 0, s).unwrap();
    }: _(origin::<T>("ALICE"), nft_offchain_data, PERCENT_100, Some(benchmark_data.collection_id), false)
    verify {
        // Get The NFT id.
        let nft_id = NFT::<T>::next_nft_id() - 1;
        // Get The NFT.
        let nft = NFT::<T>::nfts(nft_id).unwrap();
        assert_eq!(nft.owner, alice);
        assert_eq!(NFT::<T>::collections(benchmark_data.collection_id).unwrap().nfts.contains(&nft_id), true);
        assert_eq!(nft.collection_id, Some(benchmark_data.collection_id));
  • Can you elaborate on what's meant by whitelisting the "transactional layer"? Commented Jul 30, 2023 at 4:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.