Omniscia Boson Protocol Audit
TokenTransferAuthorizationLib Code Style Findings
TokenTransferAuthorizationLib Code Style Findings
TTA-01C: BPIP-12 Encoding Mismatch of EIP-3009
| Type | Severity | Location |
|---|---|---|
| Standard Conformity | ![]() | TokenTransferAuthorizationLib.sol:L213-L216 |
Description:
The BPIP-12 document highlights that the EIP-3009 encoding path should have the from, to, and value variables encoded preceding the currently decoded variables which is inefficient.
Example:
212function _consumeERC3009(address _token, address _from, address _to, uint256 _amount, bytes memory _data) private {213 (uint256 validAfter, uint256 validBefore, bytes32 nonce, uint8 v, bytes32 r, bytes32 s) = abi.decode(214 _data,215 (uint256, uint256, bytes32, uint8, bytes32, bytes32)216 );217 IERC3009(_token).receiveWithAuthorization(_from, _to, _amount, validAfter, validBefore, nonce, v, r, s);218}Recommendation:
We advise the BPIP-12 definition to be updated to match the implementation.
Alleviation (8fccc2716047809e4bc7786c95d346e5c61b2896):
The BPIP-12 definition was updated to match the decoding implementation, addressing this exhibit.
TTA-02C: BPIP-12 Strategy Support Mismatch
| Type | Severity | Location |
|---|---|---|
| Standard Conformity | ![]() | TokenTransferAuthorizationLib.sol:L198 |
Description:
The BPIP-12 definition has not been updated to outline the newly introduced DAI-like permit support as part of the TTA-02M remediation efforts.
Example:
196if (197 strategy == BosonTypes.TokenTransferAuthorizationStrategy.EIP2612 ||198 strategy == BosonTypes.TokenTransferAuthorizationStrategy.DAIPermit199) {Recommendation:
We advise the BPIP-12 to be updated, reflecting the latest state of the code.
Alleviation (aa4ed0861e282bd766a66dbb98c0883e81ec8db9):
The BPIP-12 standard was updated to properly illustrate DAI-like approval strategy support, addressing this exhibit in full.
TTA-03C: Inefficient Calldata-Memory Copy
| Type | Severity | Location |
|---|---|---|
| Gas Optimization | ![]() | TokenTransferAuthorizationLib.sol:L51, L62 |
Description:
The TokenTransferAuthorizationLib::loadQueue function will load the full _packed array into memory before processing each entry inefficiently.
Example:
50function loadQueue(bytes calldata _packed) internal {51 bytes[] memory queue = abi.decode(_packed, (bytes[]));52
53 uint256 length = queue.length;54 bytes32 lenSlot = LEN_SLOT;55 bytes32 headSlot = HEAD_SLOT;56 assembly {57 tstore(lenSlot, length)58 tstore(headSlot, 0)59 }60
61 for (uint256 i = 0; i < length; ++i) {62 _storeEntry(i, queue[i]);63 }64}Recommendation:
We advise each entry to be read directly from calldata, optimizing the code's gas cost.
Alleviation (8fccc2716047809e4bc7786c95d346e5c61b2896):
Queue loading was optimized to use a calldata pointer for the packed data, addressing this exhibit.
TTA-04C: Inefficient Iterator Increments
| Type | Severity | Location |
|---|---|---|
| Gas Optimization | ![]() | TokenTransferAuthorizationLib.sol: • I-1: L92, L93, L97 • I-2: L273, L274, L277 |
Description:
The w iterator on both code instances is inefficiently offset by 1 within the for loop body.
Example:
92for (uint256 w = 0; w < numWords; ++w) {93 bytes32 slot = bytes32(uint256(base) + 1 + w);94 bytes32 word;95 assembly {96 word := tload(slot)97 mstore(add(entry, mul(32, add(w, 1))), word)98 }99}Recommendation:
We advise this 1 offset to be assimilated into the loop itself (i.e. uint256 w = 0; w <= numWords;...), optimizing each iteration's gas cost.
Alleviation (8fccc2716047809e4bc7786c95d346e5c61b2896):
The first instance of this exhibit was properly optimized whilst the second instance has retained its original structure due to the revisions required by calldata pointer usage which no longer require a 1 offset in the assembly block.
TTA-05C: Inefficient TLOAD Operations
| Type | Severity | Location |
|---|---|---|
| Gas Optimization | ![]() | TokenTransferAuthorizationLib.sol:L181, L183 |
Description:
The TokenTransferAuthorizationLib::consumeForTransfer function invokes its TokenTransferAuthorizationLib::hasQueue function and proceeds to invoke TokenTransferAuthorizationLib::popNext right after it, resulting in the LEN_SLOT being read from transient storage twice redundantly.
Example:
181if (!hasQueue()) return false;182
183bytes memory entry = popNext();Recommendation:
We advise the data entry to be persisted across invocations, optimizing the code's gas cost and reducing the function's TLOAD calls by one.
Alleviation (8fccc2716047809e4bc7786c95d346e5c61b2896):
The code has been optimized to efficiently fetch the length once and pass it in as an argument, optimizing the code.
TTA-06C: Modifier-Style Usage Support
| Type | Severity | Location |
|---|---|---|
| Standard Conformity | ![]() | TokenTransferAuthorizationLib.sol:L44-L64, L132-L139 |
Description:
The TokenTransferAuthorizationLib structure indicates that every TokenTransferAuthorizationLib::loadQueue function call must be accompanied by a TokenTransferAuthorizationLib::clearQueue function call after the queue has been processed yet the contract trusts callers to use the library responsibly.
Example:
114/**115 * @notice Resets the queue's bookkeeping so a subsequent call in the same116 * transaction sees no loaded queue. The metatransaction entry117 * point that loaded the queue must call this at the end of its118 * successful path; otherwise leftover entries (e.g. when the119 * inner call consumed fewer entries than the queue carried) would120 * persist in transient storage and be popped by an unrelated121 * protocol call later in the same transaction.122 *123 * @dev Zeroing `LEN_SLOT` is sufficient: every read path is gated on124 * `len > 0` (`hasQueue`) or `head < len` (`popNext` / `discardNext`),125 * so a zero length makes the queue effectively absent. We also reset126 * `HEAD_SLOT` for a clean slate — costs one extra `tstore` and keeps127 * a follow-up `loadQueue` call in this same tx from inheriting a128 * stale head pointer. Per-entry word slots are not cleared; they are129 * unreachable while `len == 0` and a future `loadQueue` overwrites130 * them as needed.131 */132function clearQueue() internal {133 bytes32 lenSlot = LEN_SLOT;134 bytes32 headSlot = HEAD_SLOT;135 assembly {136 tstore(lenSlot, 0)137 tstore(headSlot, 0)138 }139}Recommendation:
We advise a modifier to be introduced instead that loads the queue at the beginning and clears it at the end, reducing the possibility of misuse by integrators of the library.
Alleviation (8fccc2716047809e4bc7786c95d346e5c61b2896):
A new TokenTransferAuthorizationBase contract was introduced that exposes a modifier via its TokenTransferAuthorizationBase::withTokenAuthorization signature, implementing the ease-of-use modifier as we advised.
