hats-finance / Intuition-0x538dbadc50cc87b281cd655f1edbc6ebda02a66a

The smart contracts of the Intuition protocol v1.
https://intuition.systems
Other
0 stars 1 forks source link

Anyone can front-run batch creations to gas-grief users #5

Closed 0xfuje closed 2 weeks ago

0xfuje commented 2 weeks ago

Severity

Medium

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() 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

  1. User calls batchCreateAtom() with 10 atoms on ETH mainnet
  2. Attacker monitors mempool and front-runs their a createAtom() call with the last atomUri in the batch call to maximalize griefing
  3. 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
  4. 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.