Skip to content

Instantly share code, notes, and snippets.

@sambacha
Created May 31, 2024 16:08
Show Gist options
  • Save sambacha/e91e3a0f8a24436c98ea0e062018097c to your computer and use it in GitHub Desktop.
Save sambacha/e91e3a0f8a24436c98ea0e062018097c to your computer and use it in GitHub Desktop.
Solidity BigNumber Assertions
/// SPDX-License-Identifier: GPL-2.0
pragma solidity ^0.8.9;
import "forge-std/Test.sol";
/// @title BigNumberAssertions
contract BigNumberAssertions is Test {
uint256 constant fullScale = 10**18;
/**
* @dev Asserts that two uint256 values are within a certain variance of each other.
* @param actual The actual value received
* @param expected The expected value
* @param variance The allowable variance
* @param reason An optional reason for the assertion
*/
function assertBNClose(
uint256 actual,
uint256 expected,
uint256 variance,
string memory reason
) public {
uint256 actualDelta = actual > expected ? actual - expected : expected - actual;
string memory str = bytes(reason).length > 0 ? string(abi.encodePacked("\n\tReason: ", reason, "\n\t", uint2str(actual), " vs ", uint2str(expected))) : "";
assertTrue(actual >= expected - variance, string(abi.encodePacked("Number is too small to be close (Delta between actual and expected is ", uint2str(actualDelta), ", but variance was only ", uint2str(variance), str)));
assertTrue(actual <= expected + variance, string(abi.encodePacked("Number is too large to be close (Delta between actual and expected is ", uint2str(actualDelta), ", but variance was only ", uint2str(variance), str)));
}
/**
* @dev Asserts that two uint256 values are within a certain percentage of each other.
* @param a The first value
* @param b The second value
* @param variance The allowable variance as a percentage
* @param reason An optional reason for the assertion
*/
function assertBNClosePercent(
uint256 a,
uint256 b,
uint256 variance,
string memory reason
) public {
if (a == b) return;
uint256 diff = (a > b ? a - b : b - a) * 2 * fullScale / (a + b);
string memory str = bytes(reason).length > 0 ? string(abi.encodePacked("\n\tReason: ", reason, "\n\t", uint2str(a), " vs ", uint2str(b))) : "";
assertTrue(diff <= variance, string(abi.encodePacked("Numbers exceed variance limit (Difference between actual and expected is ", uint2str(diff), ", but variance was only ", uint2str(variance), str)));
}
/**
* @dev Asserts that one uint256 value is greater than or equal to another, but not exceeding a certain percentage increase.
* @param actual The actual value received
* @param equator The expected value
* @param maxPercentIncrease The maximum allowable percentage increase
* @param mustBeGreater If true, actual must be strictly greater than equator
*/
function assertBNSlightlyGTPercent(
uint256 actual,
uint256 equator,
uint256 maxPercentIncrease,
bool mustBeGreater
) public {
uint256 maxIncreaseUnits = equator * maxPercentIncrease / fullScale;
assertTrue(mustBeGreater ? actual > equator : actual >= equator, "Actual value should be greater than the expected value");
assertTrue(actual <= equator + maxIncreaseUnits, string(abi.encodePacked("Actual value should not exceed ", uint2str(maxPercentIncrease), "% greater than expected")));
}
/**
* @dev Find a contract event by address and event name.
* @param receipt The transaction receipt to search
* @param address The address of the contract
* @param eventName The name of the event
*/
function findContractEvent(
ContractReceipt memory receipt,
address contractAddress,
string memory eventName
) public pure returns (Event memory) {
for (uint i = 0; i < receipt.events.length; i++) {
if (keccak256(abi.encodePacked(receipt.events[i].address)) == keccak256(abi.encodePacked(contractAddress)) &&
keccak256(abi.encodePacked(receipt.events[i].event)) == keccak256(abi.encodePacked(eventName))) {
return receipt.events[i];
}
}
revert("Event not found");
}
//! FIXME:
/**
* @dev Custom matcher for any value.
*/
function anyValue() public pure returns (bool) {
return true;
}
function uint2str(uint256 _i) internal pure returns (string memory _uintAsString) {
if (_i == 0) {
return "0";
}
uint256 j = _i;
uint256 len;
while (j != 0) {
len++;
j /= 10;
}
bytes memory bstr = new bytes(len);
uint256 k = len;
while (_i != 0) {
k = k - 1;
uint8 temp = (48 + uint8(_i - _i / 10 * 10));
bytes1 b1 = bytes1(temp);
bstr[k] = b1;
_i /= 10;
}
return string(bstr);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment