#47094 [SC-Insight] Missing Event Emission in `AgentVault` and `CollateralPoolToken` Factory Contracts
Submitted on Jun 8th 2025 at 22:27:50 UTC by @blackgrease for Audit Comp | Flare | FAssets
Report ID: #47094
Report Type: Smart Contract
Report severity: Insight
Target: https://github.com/flare-foundation/fassets/blob/main/contracts/assetManager/implementation/AgentVaultFactory.sol
Impacts:
Description
Note: Due to the issue being prevalent in two contracts but maintaining the same fix - and considering this is an Insight report - the two occurrences have been merged into one report.
Severity > Insight: Code Enhancements and Optimization and/or Best Practice
Summary
The two factory contracts, CollateralPoolTokenFactory and AgentVaultFactory, do not emit events upon successful creation of new contract instances. This omission reduces on-chain transparency and makes it difficult for off-chain systems to track deployed contracts. It further breaks the best practive of events being emitted on important state changes.
Description
Both CollateralPoolTokenFactory and AgentVaultFactory are responsible for deploying new proxy contract instances (CollateralPoolToken and AgentVault, respectively) using the ERC1967Proxy pattern.
However, neither factory contract emits an event upon creation of a new contract. This breaks a smart contract development best practice where a contract should emit an event on important state changes; such as a new contract being created, important state variable change and such. This helps to notify off-chain infrastructure (e.g indexers, explorers, front-ends) about new deployments.
Affected Areas
CollateralPoolTokenFactory
function create(IICollateralPool _pool, string memory _systemSuffix, string memory _agentSuffix)
external override
returns (address)
{
string memory tokenName = string.concat(TOKEN_NAME_PREFIX, _systemSuffix, "-", _agentSuffix);
string memory tokenSymbol = string.concat(TOKEN_SYMBOL_PREFIX, _systemSuffix, "-", _agentSuffix);
ERC1967Proxy proxy = new ERC1967Proxy(implementation, new bytes(0));
CollateralPoolToken poolToken = CollateralPoolToken(address(proxy));
poolToken.initialize(address(_pool), tokenName, tokenSymbol);
return address(poolToken); //@audit-insight: no event emission
}AgentVaultFactory
function create(IIAssetManager _assetManager) external returns (IIAgentVault) {
ERC1967Proxy proxy = new ERC1967Proxy(implementation, new bytes(0));
AgentVault agentVault = AgentVault(payable(address(proxy)));
agentVault.initialize(_assetManager);
return agentVault; //@audit-insight: no event emission
}Impact
This issue is an Insight under Code Optimizations and Enhancements and Best Practices. The results of the missing event emission are:
Reduced Transparency: Users, auditors, and developers are unable to verify contract deployments easily.
Poor Developer and User Experience: Front-ends cannot reliably fetch and display newly created contract instances for users without resorting to inefficient on-chain scans.
Security Concerns: The absence of event logs makes it harder to audit and detect malicious or accidental deployments.
Mitigation
The recommend mitigation is to include event emission each time a new instance of either AgentVault or CollateralPoolToken factory is created.
In
CollateralPoolTokenFactory
//Event Declaration
event CollateralPoolTokenCreated(address indexed poolToken, string tokenName, string tokenSymbol);
function create(IICollateralPool _pool, string memory _systemSuffix, string memory _agentSuffix)
external override
returns (address)
{
string memory tokenName = string.concat(TOKEN_NAME_PREFIX, _systemSuffix, "-", _agentSuffix);
string memory tokenSymbol = string.concat(TOKEN_SYMBOL_PREFIX, _systemSuffix, "-", _agentSuffix);
ERC1967Proxy proxy = new ERC1967Proxy(implementation, new bytes(0));
CollateralPoolToken poolToken = CollateralPoolToken(address(proxy));
poolToken.initialize(address(_pool), tokenName, tokenSymbol);
emit CollateralPoolTokenCreated(address(poolToken), tokenName, tokenSymbol); //Event implemented
return address(poolToken);
}
In
AgentVaultFactory
//Event Declaration
event AgentVaultCreated(address indexed vault, address indexed assetManager);
function create(IIAssetManager _assetManager) external returns (IIAgentVault) {
ERC1967Proxy proxy = new ERC1967Proxy(implementation, new bytes(0));
AgentVault agentVault = AgentVault(payable(address(proxy)));
agentVault.initialize(_assetManager);
emit AgentVaultCreated(address(agentVault), address(_assetManager)); //Event implemented
return agentVault;
}
Proof of Concept
Proof-of-concept
CollateralPoolTokenFactory
The function
createis called on deployed instance ofCollateralPoolFactoryThe contract
CollateralPoolTokenis created.The caller gets returned a address value but no event is emitted.
No external indexers, observers or off-chain systems are aware that a new
CollateralPoolTokenhas been created.Additional step: Caller has to manually make this information public for the system to be aware.
AgentVaultFactory
The function
createis called on deployed instance ofAgentVaultFactoryThe contract
AgentVaultis createdThe caller gets returned a address value but no event is emitted
No external indexers, observers or off-chain systems are aware that a new
AgentVaulthas been createdAdditional step: Caller has to manually make this information public aware.
Was this helpful?