Skip to content

Commit

Permalink
Merge pull request #82 from flood-protocol/tests-and-documentation
Browse files Browse the repository at this point in the history
Tests
  • Loading branch information
fulminmaxi authored Dec 11, 2023
2 parents 62c2878 + 480abae commit a3c996f
Show file tree
Hide file tree
Showing 18 changed files with 1,395 additions and 729 deletions.
31 changes: 17 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
{
"name": "flood-contracts",
"version": "1.0.0",
"description": "Flood contracts are the smart contracts that power Flood, the optimal routing protocol.",
"homepage": "https://flood.bid",
"bugs": "https://github.com/flood-protocol/flood-contracts/issues",
"license": "MIT",
"files": [
"src/**/*"
],
"repository": {
"type": "git",
"url": "https://github.com/flood-protocol/flood-contracts.git"
}
}
"name": "flood-contracts",
"version": "1.0.0",
"description": "Flood contracts are the smart contracts that power Flood, the optimal routing protocol.",
"homepage": "https://flood.bid",
"bugs": "https://github.com/flood-protocol/flood-contracts/issues",
"license": "MIT",
"files": [
"src/**/*"
],
"repository": {
"type": "git",
"url": "https://github.com/flood-protocol/flood-contracts.git"
},
"dependencies": {
"ethers": "^6.9.0"
}
}
2 changes: 1 addition & 1 deletion src/OnChainOrders.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ abstract contract OnChainOrders is IOnChainOrders {

function etchOrder(IFloodPlain.SignedOrder calldata signedOrder) external {
bytes32 orderHash = signedOrder.order.hash();
emit IOnChainOrders.OrderEtched(orderHash, signedOrder);
emit OrderEtched(orderHash, signedOrder);
}
}
24 changes: 9 additions & 15 deletions src/Zone.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ import {Pausable} from "@openzeppelin/utils/Pausable.sol";

contract Zone is IAuthZone, AccessControlDefaultAdminRules, Pausable {
bytes32 public constant FULFILLER_ROLE = keccak256("FULFILLER_ROLE");
mapping(address => AuthFilter) public filters;
mapping(address => AuthFilter) private _filters;

FeeInfo private _fee;

event FeeUpdated(FeeInfo indexed newFee);
event FulfillerUpdated(address indexed fulfiller, bool indexed valid);
event FilterUpdated(address indexed actor, AuthFilter filter);

error FeeTooHigh();

constructor(address admin) AccessControlDefaultAdminRules(1 hours, admin) {}

function pause() external onlyRole(DEFAULT_ADMIN_ROLE) {
Expand All @@ -27,34 +29,26 @@ contract Zone is IAuthZone, AccessControlDefaultAdminRules, Pausable {
}

function setFee(FeeInfo calldata newFee) external onlyRole(DEFAULT_ADMIN_ROLE) {
if (newFee.bps > 500) revert FeeTooHigh();
_fee.bps = newFee.bps;
_fee.recipient = newFee.recipient;
emit FeeUpdated(newFee);
}

function setAuthorizationFilter(address actor, AuthFilter calldata filter) external onlyRole(DEFAULT_ADMIN_ROLE) {
filters[actor] = filter;
_filters[actor] = filter;
emit FilterUpdated(actor, filter);
}

function fee(IFloodPlain.Order calldata, address) external view returns (FeeInfo memory) {
function fee(IFloodPlain.Order calldata, address /* fulfiller */) external view returns (FeeInfo memory) {
return _fee;
}

function validate(IFloodPlain.Order calldata, /* order */ address fulfiller)
external
view
whenNotPaused
returns (bool)
{
if (!hasRole(FULFILLER_ROLE, fulfiller)) {
return false;
}

return true;
function validate(IFloodPlain.Order calldata, /* order */ address fulfiller) external view returns (bool) {
return hasRole(FULFILLER_ROLE, fulfiller) && !paused();
}

function authorizationFilter(address actor) external view returns (AuthFilter memory) {
return filters[actor];
return _filters[actor];
}
}
4 changes: 2 additions & 2 deletions src/libraries/Hooks.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
pragma solidity ^0.8.22;

import {IFloodPlain} from "../interfaces/IFloodPlain.sol";

Expand All @@ -14,7 +14,7 @@ library Hooks {
assembly ("memory-safe") {
extension := shl(32, calldataload(data.offset))
}
require(extension != SELECTOR_EXTENSION);
require(extension != SELECTOR_EXTENSION, "MALICIOUS_CALL");

assembly ("memory-safe") {
let fmp := mload(0x40)
Expand Down
125 changes: 125 additions & 0 deletions test/EncodedCalls.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "./utils/FloodPlainTestShared.sol";

import {EncodedCalls} from "src/EncodedCalls.sol";
import {IEncodedCalls} from "src/interfaces/IEncodedCalls.sol";

import {LEB128} from "src/libraries/LEB128.sol";
import {LEB128Encode} from "./utils/LEB128Encode.sol";

contract EncodedCallsContract is EncodedCalls {
uint256 public val;
bytes1 public constant FALLBACK_SELECTOR = 0x00;

function changeVal(uint256 a) external {
val = a;
}
}

contract MockDecoder {
fallback(bytes calldata data) external returns (bytes memory) {
uint256 ptr;
assembly {
ptr := data.offset
}
(uint256 a, uint256 newPtr) = LEB128.rawDecodeUint(ptr);
require(data.length >= newPtr - ptr, "DECODING_ERROR");
return abi.encodeWithSelector(EncodedCallsContract.changeVal.selector, a);
}
}

contract EncodedCallsTest is LEB128Encode, FloodPlainTestShared {
EncodedCallsContract encodedCalls;
MockDecoder decoder;

function setUp() public override {
encodedCalls = new EncodedCallsContract();
decoder = new MockDecoder();
super.setUp();
}

function test_addDecoder() public {
// Add a decoder.
uint256 decoderId = encodedCalls.addDecoder(address(0x69));
assertEq(decoderId, 0);
assertEq(encodedCalls.decoders(0), address(0x69));

decoderId = encodedCalls.addDecoder(address(0x70));
assertEq(decoderId, 1);
assertEq(encodedCalls.decoders(1), address(0x70));

decoderId = encodedCalls.addDecoder(address(0x71));
assertEq(decoderId, 2);
assertEq(encodedCalls.decoders(2), address(0x71));

decoderId = encodedCalls.addDecoder(address(0x72));
assertEq(decoderId, 3);
assertEq(encodedCalls.decoders(3), address(0x72));
}

function test_useDecoder(uint8 decoders, uint256 a) public {
assertEq(encodedCalls.val(), 0);

for (uint256 i; i < decoders; ++i) {
encodedCalls.addDecoder(address(0x69));
}
uint256 decoderId = encodedCalls.addDecoder(address(decoder));
bytes memory callData = abi.encodePacked(hex"00", _encode(decoderId), _encode(a));
(bool success, bytes memory data) = address(encodedCalls).call(callData);

assertTrue(success);
assertEq(data.length, 0);

assertEq(encodedCalls.val(), a);
}

function test_invalidDecoderId() public {
encodedCalls.addDecoder(address(decoder));
bytes memory callData = abi.encodePacked(hex"00", _encode(69), _encode(69));
(bool success, bytes memory data) = address(encodedCalls).call(callData);
assertFalse(success);
assertEq(data, abi.encodeWithSignature("Panic(uint256)", 0x32));
}

function test_emptyCalldata() public {
encodedCalls.addDecoder(address(decoder));
(bool success, bytes memory data) = address(encodedCalls).call("");
assertFalse(success);
assertEq(data, hex"");
}

function test_decoderError() public {
uint256 decoderId = encodedCalls.addDecoder(address(decoder));
bytes memory callData = abi.encodePacked(hex"00", _encode(decoderId));
(bool success, bytes memory data) = address(encodedCalls).call(callData);
assertFalse(success);
assertEq(data, abi.encodeWithSignature("Error(string)", "DECODING_ERROR"));
}

function test_methodError() public {
uint256 decoderId = encodedCalls.addDecoder(address(decoder));
bytes memory callData = abi.encodePacked(hex"00", _encode(decoderId));
(bool success, bytes memory data) = address(encodedCalls).call(callData);
assertFalse(success);
assertEq(data, abi.encodeWithSignature("Error(string)", "DECODING_ERROR"));
}

function test_NoSelectorClash() public {
string[] memory inputs = new string[](3);
inputs[0] = "sh";
inputs[1] = "test/utils/selector_check.sh";
inputs[2] = vm.toString(uint8(book.FALLBACK_SELECTOR()));

bytes memory res = vm.ffi(inputs);

// False in raw hexcode. Means does not match any existing selector.
assertEq(res.length, 5);
assertEq(uint8(res[0]), uint8(0x66));
assertEq(uint8(res[1]), uint8(0x61));
assertEq(uint8(res[2]), uint8(0x6c));
assertEq(uint8(res[3]), uint8(0x73));
assertEq(uint8(res[4]), uint8(0x65));
}
}
Loading

0 comments on commit a3c996f

Please sign in to comment.