zDAO-Token - Revoking vesting tokens right before cliff period expiration might be delayed/front-runned
The owner of `TokenVesting` contract has the right to revoke the vesting of tokens for any `beneficiary`. By doing so, the amount of tokens that are already vested and weren't released yet are being transferred to the `beneficiary`, and the rest are being transferred to the owner. The `beneficiary` is expected to receive zero tokens in case the revocation transaction was executed before the cliff period is over. Although unlikely, the `beneficiary` may front run this revocation transaction by delaying the revocation (and) or inserting a release transaction right before that, thus withdrawing the vested amount.\\n```\\nfunction release(address beneficiary) public {\\n  uint256 unreleased = getReleasableAmount(beneficiary);\\n  require(unreleased > 0, "Nothing to release");\\n\\n  TokenAward storage award = getTokenAwardStorage(beneficiary);\\n  award.released += unreleased;\\n\\n  targetToken.safeTransfer(beneficiary, unreleased);\\n\\n  emit Released(beneficiary, unreleased);\\n}\\n\\n/\\*\\*\\n \\* @notice Allows the owner to revoke the vesting. Tokens already vested\\n \\* are transfered to the beneficiary, the rest are returned to the owner.\\n \\* @param beneficiary Who the tokens are being released to\\n \\*/\\nfunction revoke(address beneficiary) public onlyOwner {\\n  TokenAward storage award = getTokenAwardStorage(beneficiary);\\n\\n  require(award.revocable, "Cannot be revoked");\\n  require(!award.revoked, "Already revoked");\\n\\n  // Figure out how many tokens were owed up until revocation\\n  uint256 unreleased = getReleasableAmount(beneficiary);\\n  award.released += unreleased;\\n\\n  uint256 refund = award.amount - award.released;\\n\\n  // Mark award as revoked\\n  award.revoked = true;\\n  award.amount = award.released;\\n\\n  // Transfer owed vested tokens to beneficiary\\n  targetToken.safeTransfer(beneficiary, unreleased);\\n  // Transfer unvested tokens to owner (revoked amount)\\n  targetToken.safeTransfer(owner(), refund);\\n\\n  emit Released(beneficiary, unreleased);\\n  emit Revoked(beneficiary, refund);\\n}\\n```\\n
The issue described above is possible, but very unlikely. However, the `TokenVesting` owner should be aware of that, and make sure not to revoke vested tokens closely to cliff period ending.
```\\nfunction release(address beneficiary) public {\\n  uint256 unreleased = getReleasableAmount(beneficiary);\\n  require(unreleased > 0, "Nothing to release");\\n\\n  TokenAward storage award = getTokenAwardStorage(beneficiary);\\n  award.released += unreleased;\\n\\n  targetToken.safeTransfer(beneficiary, unreleased);\\n\\n  emit Released(beneficiary, unreleased);\\n}\\n\\n/\\*\\*\\n \\* @notice Allows the owner to revoke the vesting. Tokens already vested\\n \\* are transfered to the beneficiary, the rest are returned to the owner.\\n \\* @param beneficiary Who the tokens are being released to\\n \\*/\\nfunction revoke(address beneficiary) public onlyOwner {\\n  TokenAward storage award = getTokenAwardStorage(beneficiary);\\n\\n  require(award.revocable, "Cannot be revoked");\\n  require(!award.revoked, "Already revoked");\\n\\n  // Figure out how many tokens were owed up until revocation\\n  uint256 unreleased = getReleasableAmount(beneficiary);\\n  award.released += unreleased;\\n\\n  uint256 refund = award.amount - award.released;\\n\\n  // Mark award as revoked\\n  award.revoked = true;\\n  award.amount = award.released;\\n\\n  // Transfer owed vested tokens to beneficiary\\n  targetToken.safeTransfer(beneficiary, unreleased);\\n  // Transfer unvested tokens to owner (revoked amount)\\n  targetToken.safeTransfer(owner(), refund);\\n\\n  emit Released(beneficiary, unreleased);\\n  emit Revoked(beneficiary, refund);\\n}\\n```\\n