Fixed Term Bond tokens can be minted with non-rounded expiry
Fixed Term Tellers intend to mint tokens that expire once per day, to consolidate liquidity and create a uniform experience. However, this rounding is not enforced on the external `deploy()` function, which allows for tokens expiring at unexpected times.\\nIn `BondFixedTermTeller.sol`, new tokenIds are deployed through the `_handlePayout()` function. The function calculates the expiry (rounded down to the nearest day), uses this expiry to create a tokenId, and — if that tokenId doesn't yet exist — deploys it.\\n```\\n// rest of code\\nexpiry = ((vesting_ + uint48(block.timestamp)) / uint48(1 days)) * uint48(1 days);\\n\\n// Fixed-term user payout information is handled in BondTeller.\\n// Teller mints ERC-1155 bond tokens for user.\\nuint256 tokenId = getTokenId(payoutToken_, expiry);\\n\\n// Create new bond token if it doesn't exist yet\\nif (!tokenMetadata[tokenId].active) {\\n    _deploy(tokenId, payoutToken_, expiry);\\n}\\n// rest of code\\n```\\n\\nThis successfully consolidates all liquidity into one daily tokenId, which expires (as expected) at the time included in the tokenId.\\nHowever, if the `deploy()` function is called directly, no such rounding occurs:\\n```\\nfunction deploy(ERC20 underlying_, uint48 expiry_)\\n    external\\n    override\\n    nonReentrant\\n    returns (uint256)\\n{\\n    uint256 tokenId = getTokenId(underlying_, expiry_);\\n    // Only creates token if it does not exist\\n    if (!tokenMetadata[tokenId].active) {\\n        _deploy(tokenId, underlying_, expiry_);\\n    }\\n    return tokenId;\\n}\\n```\\n\\nThis creates a mismatch between the tokenId time and the real expiry time, as tokenId is calculated by rounding the expiry down to the nearest day:\\n```\\nuint256 tokenId = uint256(\\n    keccak256(abi.encodePacked(underlying_, expiry_ / uint48(1 days)))\\n);\\n```\\n\\n... while the `_deploy()` function saves the original expiry:\\n```\\ntokenMetadata[tokenId_] = TokenMetadata(\\n    true,\\n    underlying_,\\n    uint8(underlying_.decimals()),\\n    expiry_,\\n    0\\n);\\n```\\n
Include the same rounding process in `deploy()` as is included in _handlePayout():\\n```\\nfunction deploy(ERC20 underlying_, uint48 expiry_)\\n        external\\n        override\\n        nonReentrant\\n        returns (uint256)\\n    {\\n        expiry = ((vesting_ + uint48(block.timestamp)) / uint48(1 days)) * uint48(1 days);\\n        uint256 tokenId = getTokenId(underlying_, expiry_);\\n        // rest of code\\n```\\n
The `deploy()` function causes a number of issues:\\nTokens can be deployed that don't expire at the expected daily time, which may cause issues with your front end or break user's expectations\\nTokens can expire at times that don't align with the time included in the tokenId\\nMalicious users can pre-deploy tokens at future timestamps to "take over" the token for a given day and lock it at a later time stamp, which then "locks in" that expiry time and can't be changed by the protocol
```\\n// rest of code\\nexpiry = ((vesting_ + uint48(block.timestamp)) / uint48(1 days)) * uint48(1 days);\\n\\n// Fixed-term user payout information is handled in BondTeller.\\n// Teller mints ERC-1155 bond tokens for user.\\nuint256 tokenId = getTokenId(payoutToken_, expiry);\\n\\n// Create new bond token if it doesn't exist yet\\nif (!tokenMetadata[tokenId].active) {\\n    _deploy(tokenId, payoutToken_, expiry);\\n}\\n// rest of code\\n```\\n