Gas griefing of users depending on the length of created atoms and triples and on gas prices of the deployed chain
Description
The batchCreateAtom() and batchCreateTriple() functions allow creating large (up to block.gaslimit if user funds match) amount of atoms and triples. Depending on atom cost and which chain the contracts are deployed, a single cost of atom getAtomCost() or triple getTripleCost() may cost less then the gas price of a large batch call.
Note that anyone can front-run single createAtom() and createTriple() calls, however it's non-significant since the attacker essentially creates a free atom or triple for the initial creator.
EthMultiVault.sol - _createAtom()
function _createAtom(bytes calldata atomUri, uint256 value) internal returns (uint256, uint256) {
if (atomUri.length > generalConfig.atomUriMaxLength) {
revert Errors.MultiVault_AtomUriTooLong();
}
uint256 atomCost = getAtomCost();
// check if atom already exists based on hash
bytes32 hash = keccak256(atomUri);
if (atomsByHash[hash] != 0) {
revert Errors.MultiVault_AtomExists(atomUri);
}
// calculate user deposit amount
uint256 userDeposit = value - atomCost;
// create a new atom vault
uint256 id = _createVault();
// calculate protocol deposit fee
uint256 protocolDepositFee = protocolFeeAmount(userDeposit, id);
// calculate user deposit after protocol fees
uint256 userDepositAfterProtocolFees = userDeposit - protocolDepositFee;
// deposit user funds into vault and mint shares for the user and shares for the zero address
_depositOnVaultCreation(
id,
msg.sender, // receiver
userDepositAfterProtocolFees
);
// get atom wallet address for the corresponding atom
address atomWallet = computeAtomWalletAddr(id);
// deposit atomWalletInitialDepositAmount amount of assets and mint the shares for the atom wallet
_depositOnVaultCreation(
id,
atomWallet, // receiver
atomConfig.atomWalletInitialDepositAmount
);
// map the new vault ID to the atom data
atoms[id] = atomUri;
// map the resultant atom hash to the new vault ID
atomsByHash[hash] = id;
emit AtomCreated(msg.sender, atomWallet, atomUri, id);
return (id, protocolDepositFee);
}
Proof of Concept
User calls batchCreateAtom() with 10 atoms on ETH mainnet
Attacker monitors mempool and front-runs their a createAtom() call with the last atomUri in the batch call to maximalize griefing
User's TX executes and fails at the last iteration of the _createAtom() calls which requires him to pay for the entire execution up to that point
Attacker can repeat with all atom creations (he has to spend funds for this, but the gas-griefing may be larger)
Recommendation
Consider to check if atoms or triples provided already exist in the start of batchCreateAtom() and batchCreateTriple() functions. This will likely disincentivize attackers because the victim only has to pay a minimal gas cost since execution reverts at the start of the function, not including the large gas costs of multiple atom and triple creations.
Github username: @0xfuje Twitter username: 0xfuje Submission hash (on-chain): 0x29f4b8880245c61f2acd22d93c9da5a02745d7cf7d76acdbac3a210c1cb03b7a Severity: medium
Description:
Impact
Gas griefing of users depending on the length of created atoms and triples and on gas prices of the deployed chain
Description
The
batchCreateAtom()
andbatchCreateTriple()
functions allow creating large (up toblock.gaslimit
if user funds match) amount of atoms and triples. Depending on atom cost and which chain the contracts are deployed, a single cost of atomgetAtomCost()
or triplegetTripleCost()
may cost less then the gas price of a largebatch
call.Note that anyone can front-run single
createAtom()
andcreateTriple()
calls, however it's non-significant since the attacker essentially creates a free atom or triple for the initial creator.EthMultiVault.sol
-_createAtom()
Proof of Concept
batchCreateAtom()
with 10 atoms onETH
mainnetcreateAtom()
call with the lastatomUri
in the batch call to maximalize griefing_createAtom()
calls which requires him to pay for the entire execution up to that pointRecommendation
Consider to check if atoms or triples provided already exist in the start of
batchCreateAtom()
andbatchCreateTriple()
functions. This will likely disincentivize attackers because the victim only has to pay a minimal gas cost since execution reverts at the start of the function, not including the large gas costs of multiple atom and triple creations.