#42480 [BC-Medium] Unable to deposit the gas fee into the `governed_gas_pool` when using `deposit_from_fungible_store`
Submitted on Mar 24th 2025 at 08:38:55 UTC by @p_laksmana for Attackathon | Movement Labs
Report ID: #42480
Report Type: Blockchain/DLT
Report severity: Medium
Target: https://github.com/immunefi-team/attackathon-movement-aptos-core/tree/main
Impacts:
Direct loss of funds
Description
Brief/Intro
When deposit the gas fee to the governed_gas_pool
using deposit_from_fungible_store
, the transaction will fail because fungible_asset::deposit_internal
checks that the governed_gas_pool_store_address
must have a FungibleStore
.
Vulnerability Details
The function governed_gas_pool::deposit_gas_fee_v2
is called when governed_gas_pool_enabled
is enabled in this code https://github.com/immunefi-team/attackathon-movement-aptos-core/blob/627b4f9e0b63c33746fa5dae6cd672cbee3d8631/aptos-move/framework/aptos-framework/sources/transaction_validation.move#L324.
Then look at this:
public(friend) fun deposit_gas_fee_v2(gas_payer: address, gas_fee: u64) acquires GovernedGasPool {
if (features::operations_default_to_fa_apt_store_enabled()) {
=> deposit_from_fungible_store(gas_payer, gas_fee);
} else {
deposit_from<AptosCoin>(gas_payer, gas_fee);
};
}
fun deposit_from_fungible_store(account: address, amount: u64) acquires GovernedGasPool {
if (amount > 0){
// compute the governed gas pool store address
let governed_gas_pool_address = governed_gas_pool_address();
let governed_gas_pool_store_address = primary_fungible_store_address(governed_gas_pool_address);
// compute the account store address
let account_store_address = primary_fungible_store_address(account);
=> fungible_asset::deposit_internal(
governed_gas_pool_store_address,
fungible_asset::withdraw_internal(
account_store_address,
amount
)
);
}
}
When operations_default_to_fa_apt_store_enabled
is enabled in deposit_gas_fee_v2
, it triggers the deposit_from_fungible_store
function.
Unfortunately, deposit_from_fungible_store
calls fungible_asset::deposit_internal
, and within fungible_asset::deposit_internal
, there is a verification that the governed_gas_pool_store_address
must have a FungibleStore
:
public(friend) fun deposit_internal(store_addr: address, fa: FungibleAsset) acquires FungibleStore, ConcurrentFungibleBalance {
let FungibleAsset { metadata, amount } = fa;
if (amount == 0) return;
=> assert!(exists<FungibleStore>(store_addr), error::not_found(EFUNGIBLE_STORE_EXISTENCE));
let store = borrow_global_mut<FungibleStore>(store_addr);
assert!(metadata == store.metadata, error::invalid_argument(EFUNGIBLE_ASSET_AND_STORE_MISMATCH));
if (store.balance == 0 && concurrent_fungible_balance_exists_inline(store_addr)) {
let balance_resource = borrow_global_mut<ConcurrentFungibleBalance>(store_addr);
aggregator_v2::add(&mut balance_resource.balance, amount);
} else {
store.balance = store.balance + amount;
};
event::emit(Deposit { store: store_addr, amount });
}
As a result, when attempting to pay the fee using deposit_from_fungible_store
the transaction will fail.
Impact Details
Depositing the gas fee to the governed_gas_pool
using deposit_from_fungible_store
will fail.
Recommendation
When using deposit_from_fungible_store
to deposit the gas fee into the governed_gas_pool
, ensure that the governed_gas_pool_store_address
has a <FungibleStore>
. This can be done by fungible_asset::create_store
before calling fungible_asset::deposit_internal
."
Proof of Concept
Proof of Concept
When depositing the gas fee to the
governed_gas_pool
withdeposit_gas_fee_v2
,And
features::operations_default_to_fa_apt_store_enabled()
is enabled,As a result, the transaction fails in the
fungible_asset::deposit_internal
function.
Was this helpful?