synapsecns/sanguine

View on GitHub
packages/contracts-core/test/suite/libs/stack/Tips.t.sol

Summary

Maintainability
Test Coverage
// SPDX-License-Identifier: MIT
pragma solidity 0.8.17;

import {SynapseLibraryTest, MemViewLib} from "../../../utils/SynapseLibraryTest.t.sol";
import {Tips, TipsHarness} from "../../../harnesses/libs/stack/TipsHarness.t.sol";

import {TipsOverflow, TipsValueTooLow} from "../../../../contracts/libs/Errors.sol";
import {TIPS_MULTIPLIER, TIPS_LENGTH} from "../../../../contracts/libs/Constants.sol";

import {RawTips} from "../../../utils/libs/SynapseStructs.t.sol";

// solhint-disable func-name-mixedcase
contract TipsLibraryTest is SynapseLibraryTest {
    using MemViewLib for bytes;

    TipsHarness internal libHarness;

    function setUp() public {
        libHarness = new TipsHarness();
    }

    function test_encodeTips(RawTips memory rt) public {
        uint256 totalTips = uint256(rt.summitTip) + rt.attestationTip + rt.executionTip + rt.deliveryTip;
        vm.assume(totalTips <= type(uint64).max);
        // Test encoding
        uint256 encodedTips = libHarness.encodeTips(rt.summitTip, rt.attestationTip, rt.executionTip, rt.deliveryTip);
        uint256 expected = uint256(rt.summitTip) * 2 ** 192 + uint256(rt.attestationTip) * 2 ** 128
            + uint256(rt.executionTip) * 2 ** 64 + uint256(rt.deliveryTip);
        assertEq(encodedTips, expected, "!encodeTips");
        assertEq(libHarness.wrapPadded(encodedTips), expected, "!wrapPadded");
        // Test getters
        assertEq(libHarness.summitTip(encodedTips), rt.summitTip, "!summitTip");
        assertEq(libHarness.attestationTip(encodedTips), rt.attestationTip, "!attestationTip");
        assertEq(libHarness.executionTip(encodedTips), rt.executionTip, "!executionTip");
        assertEq(libHarness.deliveryTip(encodedTips), rt.deliveryTip, "!deliveryTip");
        assertEq(libHarness.value(encodedTips), totalTips * TIPS_MULTIPLIER, "!totalTips");
        // Test hashing
        assertEq(libHarness.leaf(encodedTips), keccak256(abi.encode(expected)), "!leaf");
    }

    function test_encodeTips256(uint96 summitTip, uint96 attestationTip, uint96 executionTip, uint96 deliveryTip)
        public
    {
        uint256 encodedTips256 = libHarness.encodeTips256(summitTip, attestationTip, executionTip, deliveryTip);
        uint256 encodedTips = libHarness.encodeTips(
            uint64(summitTip / TIPS_MULTIPLIER),
            uint64(attestationTip / TIPS_MULTIPLIER),
            uint64(executionTip / TIPS_MULTIPLIER),
            uint64(deliveryTip / TIPS_MULTIPLIER)
        );
        assertEq(encodedTips256, encodedTips, "!encodeTips256");
    }

    function test_matchTips(RawTips memory rt, uint256 newValue) public {
        uint256 totalTips = uint256(rt.summitTip) + rt.attestationTip + rt.executionTip + rt.deliveryTip;
        vm.assume(totalTips <= type(uint64).max);
        uint256 maxNewTotalTips = uint256(rt.summitTip) + rt.attestationTip + rt.executionTip + type(uint64).max;
        newValue = bound(newValue, totalTips * TIPS_MULTIPLIER, maxNewTotalTips * TIPS_MULTIPLIER);
        Tips newTips = libHarness.matchValue(rt.castToTips(), newValue);
        // Should not exceed newValue
        assertLe(newTips.value(), newValue);
        // Increasing the delivery tip by one should exceed newValue
        if (newTips.deliveryTip() < type(uint64).max) {
            newTips = Tips.wrap(Tips.unwrap(newTips) + 1);
            assertGt(newTips.value(), newValue);
        }
    }

    function test_matchTips_revert_newValueTooLow(RawTips memory rt, uint256 newValue) public {
        uint256 totalTips = uint256(rt.summitTip) + rt.attestationTip + rt.executionTip + rt.deliveryTip;
        vm.assume(totalTips != 0 && totalTips <= type(uint64).max);
        newValue = bound(newValue, 0, totalTips * TIPS_MULTIPLIER - 1);
        vm.expectRevert(TipsValueTooLow.selector);
        libHarness.matchValue(rt.castToTips(), newValue);
    }

    function test_matchTips_revert_tipsOverflow(RawTips memory rt, uint256 newValue) public {
        uint256 totalTips = uint256(rt.summitTip) + rt.attestationTip + rt.executionTip + rt.deliveryTip;
        vm.assume(totalTips <= type(uint64).max);
        uint256 overflowTotalTips = uint256(rt.summitTip) + rt.attestationTip + rt.executionTip + 1 << 64;
        newValue = bound(newValue, overflowTotalTips * TIPS_MULTIPLIER, type(uint256).max);
        vm.expectRevert(TipsOverflow.selector);
        libHarness.matchValue(rt.castToTips(), newValue);
    }

    function test_emptyTips() public {
        test_encodeTips(RawTips(0, 0, 0, 0));
        assertEq(libHarness.emptyTips(), 0, "!emptyTips");
    }

    function test_tipsLength(RawTips memory rt) public {
        assertEq(
            abi.encodePacked(libHarness.encodeTips(rt.summitTip, rt.attestationTip, rt.executionTip, rt.deliveryTip))
                .length,
            TIPS_LENGTH
        );
    }
}