Potential damages due to incorrect implementation of the ````ZIP```` algorithm
`WooracleV2_2.fallback()` is used to post zipped token price and state data to the contract for sake of gas saving. However, the first 4 bytes of zipped data are not reserved to distinguish the `ZIP` call and other normal call's function selector. This would cause `ZIP` calls to be accidentally interpreted as any other functions in the contract, result in unintended exceptions and potential damages.\\nAccording solidity's official doc, there are two forms of `fallback()` function `with` or `without` parameter\\n```\\nfallback () external [payable];\\nfallback (bytes calldata _input) external [payable] returns (bytes memory _output);\\n```\\n\\nIf the version with parameters is used, _input will contain the full data sent to the contract (equal to msg.data)\\nAs the `_input` data is equal to `msg.data`, the solidity compiler would firstly check if first 4 bytes matches any normal function selectors, and would only execute `fallback(_input)` while no matching. Therefore, in zipped data, the first 4 bytes must be set to some reserved function selector, such as `0x00000000`, with no collision to normal function selectors. And the real zipped data then starts from 5th byte.\\nThe following coded PoC shows cases that the zipped data is accidentally interpreted as:\\nfunction renounceOwnership(); function setStaleDuration(uint256); function postPrice(address,uint128); function syncTS(uint256);\\n```\\n// SPDX-License-Identifier: MIT\\npragma solidity ^0.8.0;\\n\\nimport {Test} from "../../lib/forge-std/src/Test.sol";\\nimport {console2} from "../../lib/forge-std/src/console2.sol";\\nimport {WooracleV2_2} from "../../contracts/wooracle/WooracleV2_2.sol";\\n\\ncontract WooracleZipBugTest is Test {\\n    WooracleV2_2 public oracle;\\n\\n    function setUp() public {\\n        oracle = new WooracleV2_2();\\n    }\\n\\n    function testNormalCase() public {\\n        /* reference:\\n            File: test\\typescript\\wooraclev2_zip_inherit.test.ts\\n            97:     function _encode_woo_price() {\\n            op = 0\\n            len = 1\\n            (base, p)\\n            base: 6, woo token\\n            price: 0.23020\\n            23020000 (decimal = 8)\\n        */\\n        uint8 base = 6;\\n        bytes memory zip = _makeZipData({\\n            op: 0,\\n            length: 1,\\n            leadingBytesOfBody: abi.encodePacked(base, uint32((2302 << 5) + 4))\\n        });\\n        (bool success, ) = address(oracle).call(zip);\\n        assertEq(success, true);\\n        address wooAddr = oracle.getBase(6);\\n        (uint256 price, bool feasible) = oracle.price(wooAddr);\\n        assertEq(price, 23020000);\\n        assertTrue(feasible);\\n    }\\n\\n    function testCollisionWithRenounceOwnership() public {\\n        // selector of "renounceOwnership()": "0x715018a6"\\n        bytes memory zip = _makeZipData({\\n            op: 1,\\n            length: 0x31,\\n            leadingBytesOfBody: abi.encodePacked(hex"5018a6")\\n        });\\n        assertEq(oracle.owner(), address(this));\\n        (bool success, ) = address(oracle).call(zip);\\n        assertEq(success, true);\\n        assertEq(oracle.owner(), address(0));\\n    }\\n\\n    function testCollisionWithSetStaleDuration() public {\\n        // selector of "setStaleDuration(uint256)": "0x99235fd4"\\n        bytes memory zip = _makeZipData({\\n            op: 2,\\n            length: 0x19,\\n            leadingBytesOfBody: abi.encodePacked(hex"235fd4")\\n        });\\n        assertEq(oracle.staleDuration(), 120); // default: 2 mins\\n        (bool success, ) = address(oracle).call(zip);\\n        assertEq(success, true);\\n        uint256 expectedStaleDuration;\\n        assembly {\\n            expectedStaleDuration := mload(add(zip, 36))\\n        }\\n        assertEq(oracle.staleDuration(), expectedStaleDuration);\\n        assertTrue(expectedStaleDuration != 120);\\n    }\\n\\n    function testCollisionWithPostPrice() public {\\n        // selector of "postPrice(address,uint128)": "0xd5bade07"\\n        bytes memory addressAndPrice = abi.encode(address(0x1111), uint256(100));\\n        bytes memory zip = _makeZipData({\\n            op: 3,\\n            length: 0x15,\\n            leadingBytesOfBody: abi.encodePacked(hex"bade07", addressAndPrice)\\n        });\\n        (bool success, ) = address(oracle).call(zip);\\n        assertEq(success, true);\\n        (uint256 price, bool feasible) = oracle.price(address(0x1111));\\n        assertEq(price, 100);\\n        assertTrue(feasible);\\n    }\\n\\n    function testCollisionWithSyncTS() public {\\n        // selector of "syncTS(uint256)": "4f1f1999"\\n        uint256 timestamp = 12345678;\\n        bytes memory zip = _makeZipData({\\n            op: 1,\\n            length: 0xf,\\n            leadingBytesOfBody: abi.encodePacked(hex"1f1999", timestamp)\\n        });\\n        (bool success, ) = address(oracle).call(zip);\\n        assertEq(success, true);\\n        assertEq(oracle.timestamp(), timestamp);\\n    }\\n\\n    function _makeZipData(\\n        uint8 op,\\n        uint8 length,\\n        bytes memory leadingBytesOfBody\\n    ) internal returns (bytes memory result) {\\n        assertTrue(length < 2 ** 6);\\n        assertTrue(op < 4);\\n        bytes1 head = bytes1(uint8((op << 6) + (length & 0x3F)));\\n        uint256 sizeOfItem = op == 0 || op == 2 ? 5 : 13;\\n        uint256 sizeOfHead = 1;\\n        uint256 sizeOfBody = sizeOfItem * length;\\n        assertTrue(sizeOfBody >= leadingBytesOfBody.length);\\n        result = bytes.concat(head, leadingBytesOfBody, _makePseudoRandomBytes(sizeOfBody - leadingBytesOfBody.length));\\n        assertEq(result.length, sizeOfHead + sizeOfBody);\\n    }\\n\\n    function _makePseudoRandomBytes(uint256 length) internal returns (bytes memory result) {\\n        uint256 words = (length + 31) / 32;\\n        result = new bytes(words * 32);\\n        for (uint256 i; i < words; ++i) {\\n            bytes32 rand = keccak256(abi.encode(block.timestamp + i));\\n            assembly {\\n                mstore(add(add(result, 32), mul(i, 32)), rand)\\n            }\\n        }\\n\\n        assembly {\\n            mstore(result, length) // change to required length\\n        }\\n        assertEq(length, result.length);\\n    }\\n}\\n```\\n\\nAnd the logs:\\n```\\n2024-03-woofi-swap\\WooPoolV2> forge test --match-contract WooracleZipBugTest -vv\\n[⠢] Compiling// rest of codeNo files changed, compilation skipped\\n[⠆] Compiling// rest of code\\n\\nRunning 5 tests for test/foundry/WooracleZipBug.t.sol:WooracleZipBugTest\\n[PASS] testCollisionWithPostPrice() (gas: 48643)\\n[PASS] testCollisionWithRenounceOwnership() (gas: 21301)\\n[PASS] testCollisionWithSetStaleDuration() (gas: 18289)\\n[PASS] testCollisionWithSyncTS() (gas: 35302)\\n[PASS] testNormalCase() (gas: 48027)\\nTest result: ok. 5 passed; 0 failed; 0 skipped; finished in 2.13ms\\n\\nRan 1 test suites: 5 tests passed, 0 failed, 0 skipped (5 total tests)\\n```\\n
```\\ndiff // Remove the line below\\n// Remove the line below\\ngit a/WooPoolV2/contracts/wooracle/WooracleV2_2.sol b/WooPoolV2/contracts/wooracle/WooracleV2_2.sol\\nindex 9e66c63..4a9138f 100644\\n// Remove the line below\\n// Remove the line below\\n// Remove the line below\\n a/WooPoolV2/contracts/wooracle/WooracleV2_2.sol\\n// Add the line below\\n// Add the line below\\n// Add the line below\\n b/WooPoolV2/contracts/wooracle/WooracleV2_2.sol\\n@@ // Remove the line below\\n416,9 // Add the line below\\n416,10 @@ contract WooracleV2_2 is Ownable, IWooracleV2 {\\n         */\\n\\n         uint256 x = _input.length;\\n// Remove the line below\\n        require(x > 0, "WooracleV2_2: !calldata");\\n// Add the line below\\n        require(x > 4, "WooracleV2_2: !calldata");\\n// Add the line below\\n        require(bytes4(_input[0:4]) == bytes4(hex"00000000"));\\n\\n// Remove the line below\\n        uint8 firstByte = uint8(bytes1(_input[0]));\\n// Add the line below\\n        uint8 firstByte = uint8(bytes1(_input[5]));\\n         uint8 op = firstByte  6; // 11000000\\n         uint8 len = firstByte & 0x3F; // 00111111\\n\\n@@ // Remove the line below\\n428,12 // Add the line below\\n429,12 @@ contract WooracleV2_2 is Ownable, IWooracleV2 {\\n             uint128 p;\\n\\n             for (uint256 i = 0; i < len; // Add the line below\\n// Add the line below\\ni) {\\n// Remove the line below\\n                base = getBase(uint8(bytes1(_input[1 // Add the line below\\n i * 5:1 // Add the line below\\n i * 5 // Add the line below\\n 1])));\\n// Remove the line below\\n                p = _decodePrice(uint32(bytes4(_input[1 // Add the line below\\n i * 5 // Add the line below\\n 1:1 // Add the line below\\n i * 5 // Add the line below\\n 5])));\\n// Add the line below\\n                base = getBase(uint8(bytes1(_input[5 // Add the line below\\n i * 5:5 // Add the line below\\n i * 5 // Add the line below\\n 1])));\\n// Add the line below\\n                p = _decodePrice(uint32(bytes4(_input[5 // Add the line below\\n i * 5 // Add the line below\\n 1:5 // Add the line below\\n i * 5 // Add the line below\\n 5])));\\n                 infos[base].price = p;\\n             }\\n\\n// Remove the line below\\n            timestamp = (op == 0) ? block.timestamp : uint256(uint32(bytes4(_input[1 // Add the line below\\n len * 5:1 // Add the line below\\n len * 5 // Add the line below\\n 4])));\\n// Add the line below\\n            timestamp = (op == 0) ? block.timestamp : uint256(uint32(bytes4(_input[5 // Add the line below\\n len * 5:5 // Add the line below\\n len * 5 // Add the line below\\n 4])));\\n         } else if (op == 1 || op == 3) {\\n             // post states list\\n             address base;\\n@@ // Remove the line below\\n442,14 // Add the line below\\n443,14 @@ contract WooracleV2_2 is Ownable, IWooracleV2 {\\n             uint64 k;\\n\\n             for (uint256 i = 0; i < len; // Add the line below\\n// Add the line below\\ni) {\\n// Remove the line below\\n                base = getBase(uint8(bytes1(_input[1 // Add the line below\\n i * 9:1 // Add the line below\\n i * 9 // Add the line below\\n 1])));\\n// Remove the line below\\n                p = _decodePrice(uint32(bytes4(_input[1 // Add the line below\\n i * 9 // Add the line below\\n 1:1 // Add the line below\\n i * 9 // Add the line below\\n 5])));\\n// Remove the line below\\n                s = _decodeKS(uint16(bytes2(_input[1 // Add the line below\\n i * 9 // Add the line below\\n 5:1 // Add the line below\\n i * 9 // Add the line below\\n 7])));\\n// Remove the line below\\n                k = _decodeKS(uint16(bytes2(_input[1 // Add the line below\\n i * 9 // Add the line below\\n 7:1 // Add the line below\\n i * 9 // Add the line below\\n 9])));\\n// Add the line below\\n                base = getBase(uint8(bytes1(_input[5 // Add the line below\\n i * 9:5 // Add the line below\\n i * 9 // Add the line below\\n 1])));\\n// Add the line below\\n                p = _decodePrice(uint32(bytes4(_input[5 // Add the line below\\n i * 9 // Add the line below\\n 1:5 // Add the line below\\n i * 9 // Add the line below\\n 5])));\\n// Add the line below\\n                s = _decodeKS(uint16(bytes2(_input[5 // Add the line below\\n i * 9 // Add the line below\\n 5:5 // Add the line below\\n i * 9 // Add the line below\\n 7])));\\n// Add the line below\\n                k = _decodeKS(uint16(bytes2(_input[5 // Add the line below\\n i * 9 // Add the line below\\n 7:5 // Add the line below\\n i * 9 // Add the line below\\n 9])));\\n                 _setState(base, p, s, k);\\n             }\\n\\n// Remove the line below\\n            timestamp = (op == 1) ? block.timestamp : uint256(uint32(bytes4(_input[1 // Add the line below\\n len * 9:1 // Add the line below\\n len * 9 // Add the line below\\n 4])));\\n// Add the line below\\n            timestamp = (op == 1) ? block.timestamp : uint256(uint32(bytes4(_input[5 // Add the line below\\n len * 9:5 // Add the line below\\n len * 9 // Add the line below\\n 4])));\\n         } else {\\n             revert("WooracleV2_2: !op");\\n         }\\n```\\n
This bug would result in unintended exceptions and potential damages such as:\\nCollision with normal price post functions might cause users' trades executed on incorrect price and suffer losses.\\nCollision with any view function might cause price post to fail silently and hold on trade processing until next submission, and users' trades might be executed on a delayed inexact price.\\nCollision with `setStaleDuration()` might cause price freshness check to break down.
```\\nfallback () external [payable];\\nfallback (bytes calldata _input) external [payable] returns (bytes memory _output);\\n```\\n