Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MEVTime Oracle - Partial Function Example #51

Merged
merged 5 commits into from
Jul 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Compiler files
artifacts/
cache/
out/

Expand Down
5 changes: 5 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,10 @@ libs = ['lib']
evm_version = "cancun"
solc = "0.8.23"

ffi = true
ast = true
build_info = true
extra_output = ["storageLayout"]

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
auto_detect_solc = true
4 changes: 0 additions & 4 deletions test/LimitOrder.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ contract LimitOrderTest is Test, LimitOrderExampleLib {
}

function testLimitOrder() external {
// Disable running this test on local -- should run as fork test environment.
if (block.number == 1) {
return;
}
uint256 laminatorSequenceNumber;

vm.startPrank(pusher);
Expand Down
55 changes: 55 additions & 0 deletions test/PartialFunctionTest.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: GPL-3.0

pragma solidity 0.8.23;

import "forge-std/Test.sol";
import "src/timetravel/CallBreaker.sol";
import "test/examples/MEVOracle/PartialFunctionContract.sol";
import "test/solve-lib/MEVOracle/PartialFunctionExample.sol";

contract PartialFunctionTest is Test, PartialFunctionExampleLib {
address deployer;
address pusher;
address filler;

function setUp() public {
deployer = address(100);
pusher = address(200);
filler = address(300);

// give the pusher some eth
vm.deal(pusher, 100 ether);

// start deployer land
vm.startPrank(deployer);
deployerLand(pusher, 8, 11); // passing 8 as divisor and 11 as init value
vm.stopPrank();

// Label operations in the run function.
vm.label(pusher, "pusher");
vm.label(address(this), "deployer");
vm.label(filler, "filler");
}

function testPartialFunction() external {
uint256 laminatorSequenceNumber;

vm.startPrank(pusher);
laminatorSequenceNumber = userLand();
vm.stopPrank();

// go forward in time
vm.roll(block.number + 1);

vm.startPrank(filler);
solverLand(laminatorSequenceNumber, filler);
vm.stopPrank();

assertFalse(callbreaker.isPortalOpen());

(bool init, bool exec,) = LaminatedProxy(pusherLaminated).viewDeferredCall(laminatorSequenceNumber);

assertTrue(init);
assertTrue(exec);
}
}
55 changes: 55 additions & 0 deletions test/examples/MEVOracle/PartialFunctionContract.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;

import "src/timetravel/CallBreaker.sol";
import "src/timetravel/SmarterContract.sol";

contract PartialFunctionContract is SmarterContract {
address public callbreakerAddress;

uint256 public initValue;
uint256 public divisor;

/**
* @notice This is a basic example of performing a computation with a partial function application
* At solvetime, the solver can provide an additional value via. associatedData, and the contract
* can use that to perform the computation
* Alternatively, the contract can fetch values from other oracles AT SOLVETIME.
* This pattern may be able to be generalized to any function that can be partially applied.
*/
constructor(address _callbreaker, uint256 _divisor) SmarterContract(_callbreaker) {
callbreakerAddress = _callbreaker;
divisor = _divisor;
}

function setInitValue(uint256 _initValue) external {
initValue = _initValue;
}

/**
* @notice solve at MEVTime, get the correct arg from the data store
* Users would enforce invariants on what the correct arg should be
*/
function solve() external {
// Get a hint index (hintdex) from the solver, likely computed off-chain, where the correct object is.
bytes32 hintKey = keccak256(abi.encodePacked("solvedValue"));
bytes memory hintBytes = CallBreaker(payable(callbreakerAddress)).fetchFromAssociatedDataStore(hintKey);

uint256 returnedvalue = abi.decode(hintBytes, (uint256));
initValue += returnedvalue;

// check is solution works
CallObject memory callObj = CallObject({
amount: 0,
addr: address(this),
gas: 1000000,
callvalue: abi.encodeWithSignature("verifySolution()")
});

assertFutureCallTo(callObj, 1);
}

function verifySolution() external {
require(initValue % divisor == 0, "Invalid Solution Provided");
}
}
2 changes: 1 addition & 1 deletion test/examples/PnP.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ contract PnP {
_callbreakerAddress = callbreakerLocation;
// populate addrlist with a hash chain to look "random"
addrlist.push(hash(input));
for (uint256 i = 1; i < 100000; i++) {
for (uint256 i = 1; i < 1000; i++) {
addrlist.push(hash(addrlist[i - 1]));
}
}
Expand Down
94 changes: 94 additions & 0 deletions test/solve-lib/MEVOracle/PartialFunctionExample.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.23;

import "forge-std/Vm.sol";
import "forge-std/console.sol";

import "src/lamination/Laminator.sol";
import "src/timetravel/CallBreaker.sol";
import "src/timetravel/SmarterContract.sol";
import "test/examples/MEVOracle/PartialFunctionContract.sol";

contract PartialFunctionExampleLib {
address payable public pusherLaminated;
PartialFunctionContract public partialFunctionContract;
Laminator public laminator;
CallBreaker public callbreaker;
uint256 _tipWei = 33;
uint256 hashChainInitConst = 1;

function deployerLand(address pusher, uint256 divisor, uint256 initValue) public {
// Initializing contracts
callbreaker = new CallBreaker();
laminator = new Laminator(address(callbreaker));
pusherLaminated = payable(laminator.computeProxyAddress(pusher));
partialFunctionContract = new PartialFunctionContract(address(callbreaker), divisor);
partialFunctionContract.setInitValue(initValue);
}

function userLand() public returns (uint256) {
// send proxy some eth
pusherLaminated.transfer(1 ether);

// Userland operations
CallObject[] memory pusherCallObjs = new CallObject[](2);
pusherCallObjs[0] = CallObject({
amount: 0,
addr: address(partialFunctionContract),
gas: 1000000,
callvalue: abi.encodeWithSignature("solve()")
});

pusherCallObjs[1] = CallObject({amount: _tipWei, addr: address(callbreaker), gas: 10000000, callvalue: ""});

return laminator.pushToProxy(abi.encode(pusherCallObjs), 1);
}

function solverLand(uint256 laminatorSequenceNumber, address filler) public {
uint256 value = partialFunctionContract.initValue();
uint256 divisor = partialFunctionContract.divisor();
uint256 solution = divisor - (value % divisor);
CallObject[] memory callObjs = new CallObject[](2);
ReturnObject[] memory returnObjs = new ReturnObject[](2);

callObjs[0] = CallObject({
amount: 0,
addr: pusherLaminated,
gas: 10000000,
callvalue: abi.encodeWithSignature("pull(uint256)", laminatorSequenceNumber)
});

callObjs[1] = CallObject({
amount: 0,
addr: address(partialFunctionContract),
gas: 1000000,
callvalue: abi.encodeWithSignature("verifySolution()")
});

ReturnObject[] memory returnObjsFromPull = new ReturnObject[](2);
returnObjsFromPull[0] = ReturnObject({returnvalue: ""});
returnObjsFromPull[0] = ReturnObject({returnvalue: ""});

returnObjs[0] = ReturnObject({returnvalue: abi.encode(abi.encode(returnObjsFromPull))});
returnObjs[1] = ReturnObject({returnvalue: ""});

bytes32[] memory keys = new bytes32[](3);
keys[0] = keccak256(abi.encodePacked("tipYourBartender"));
keys[1] = keccak256(abi.encodePacked("pullIndex"));
keys[2] = keccak256(abi.encodePacked("solvedValue"));
bytes[] memory values = new bytes[](3);
values[0] = abi.encodePacked(filler);
values[1] = abi.encode(laminatorSequenceNumber);
values[2] = abi.encode(solution);
bytes memory encodedData = abi.encode(keys, values);

bytes32[] memory hintdicesKeys = new bytes32[](2);
hintdicesKeys[0] = keccak256(abi.encode(callObjs[0]));
hintdicesKeys[0] = keccak256(abi.encode(callObjs[1]));
uint256[] memory hintindicesVals = new uint256[](2);
hintindicesVals[0] = 0;
hintindicesVals[0] = 1;
bytes memory hintdices = abi.encode(hintdicesKeys, hintindicesVals);
callbreaker.verify(abi.encode(callObjs), abi.encode(returnObjs), encodedData, hintdices);
}
}