Assessment reports>Bond Protocol>Threat Models>_handlePayout

Function _handlePayout( address recipient, uint256 payout_, ERC20 payoutToken_, uint48 vesting_) internal

INTERNAL FUNCTION

  1. Intended behavior.

    • This function is supposed to handle the payout of payoutToken_ to the user if vesting time is zero or to mint the corresponding number of Bond tokens with the possibility of withdrawal after a certain amount of time.

    • It should transfer payoutToken_ to recipient_ directly if there is no vesting time

    • It should mint Bond tokens to recipient_ if vesting time isn’t zero

    • If there isn’t Bond token for current payoutToken_ and expiry time, it should be created before mint

  2. Negative behavior.

    • It shouldn't mint and transfer zero value of tokens.

    • It shouldn't mint and transfer to zero address.

    • It shouldn't transfer tokens if there are not enough of them on the contract balance. (The safeTransfer call will be reverted)

    • it’s not supposed to mint if vesting is ==0 or transfer payout if vesting is !=0.

  3. Preconditions.

    • There is enough payoutToken_ for transfer to recipient

    • Assumes that the token that is supposed to be minted exists. Otherwise, it tries creating it.

    • Assumes that getTokenId works well and there are no issues with how it retrieves the id.

    • Assumes that the tokenId is unique and no multiple payoutToken_, expiry pairs can exist.

  4. Postconditions.

    • if vesting time is zero payoutToken_.balanceOf(recipient_) <= balanceBefore+payout and payoutToken_.balanceOf(BondFixedTermTeller) >= balanceBefore-payout and recipient_ shouldn’t get additional Bond tokens

    • if vesting time isn’t zero BondFixedTermTeller.balanceOf(recipient) ≤ balanceBefore+payout and shouldn't get additional payoutTokens

    • if the bondToken wasn’t deployed beforehand, now it should be.

  5. Inputs.

    Since the function is internal, conclusions were made based on the analysis of the calling function.

    • address recipient_ - controlled

    • uint256 payout_ - it is partially controlled because it is calculated based on the controlled amount value. but this function should not care whether this value is calculated correctly.

    • ERC20 payoutToken_ - partially controlled, the caller selects any market to which the payoutToken address is linked, that is, the caller can select any address from those already linked to the markets.

    • uint48 vesting_ - it is not controlled because it is a market setting.

  6. Examine all function calls the function makes.

    a. Call to _mintToken(recipient, tokenId, payout_); calls mint(to, tokenId_, amount_, bytes("")) calls ERC1155TokenReceiver(to).onERC1155Received If the calling contract is a contract, then it will be called.

    • What is controllable? (callee, params, return value): recipient - controlled, and if recipient is the contract address, it will be called by mint hook ^ see above; tokenId - partially controlled, calculated based on payoutToken_ address and expiry value; they assume it’s unique and no multiple payoutToken_, expiry pairs can exist; payout_ - partially controlled, calculated based on amount_

    • If return value controllable, how is it used and how can it go wrong? There is no return value.

    • What happens if it reverts or tries to reenter? this function is called only at the end of the purchase function, and all important functions include purchase have nonReentrant protection. it looks like it can't be used, but it's better to keep this possibility in mind; it will be reverted in the following situations; if address recipient == address(0); if ERC1155TokenReceiver(to).onERC1155Received will return wrong selector or reject the call inside

    b. Call to uint256 tokenId = getTokenId(payoutToken_, expiry)

    • What is controllable? (callee, params, return value): ERC20 payoutToken_ - partially controlled, the caller selects any market to which the payoutToken address is linked, that is, the caller can select any address from those already linked to the markets.; expiry - it is not controlled

    • If return value controllable, how is it used and how can it go wrong? if there were possible collisions between real and fake tokens, then it would be possible to deposit dummy tokens and withdraw real ones; if there is any way to bypass the encoding and calculation, it would be possible to create an additional payoutToken, expiry_ pair that would have the same tokenId

    • What happens if it reverts or tries to reenter? No problems

    c. Call to _deploy(tokenId, payoutToken_, expiry)

    • What is controllable? (callee, params, return value): uint256 tokenId - it is not controlled; ERC20 payoutToken_ - partially controlled, the caller selects any market to which the payoutToken address is linked, that is, the caller can select any address from those already linked to the markets; expiry - it is not controlled

    • If return value controllable, how is it used and how can it go wrong? there is no return value

    • What happens if it reverts or tries to reenter? No problem

    d. Call to payoutToken_.safeTransfer(recipient_, payout_)

    • What is controllable? (callee, params, return value): payoutToken* - partially controlled, the caller can select any address from those already linked to the markets; payout* - controlled

    • If return value controllable, how is it used and how can it go wrong? there is no return value

    • What happens if it reverts or tries to reenter? will be reverted if the current contract doesn't have enough tokens

Zellic © 2024Back to top ↑