Skip to content
Closed
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ This directory contains the core RFQ settlement contracts used by the Symbiotic

- `Reactor.sol` validates the signed order, pulls approved input from the swapper directly into factory-registered LiquidLane adapters, and enforces output delivery.
- `Executor.sol` is an example role-gated execution surface that calls the Reactor, performs adapter swaps, runs any post-swap execution payload, and approves output transfers back to the Reactor.
- `LiquidLaneUniswapXExecutor.sol` fills ERC-20 UniswapX orders through its Reactor callback using owner-managed callers, matching the RFQ executor access model. The executor contract remains the Reactor-facing filler, while callers select LiquidLane routes. Routes may consume less than a Dutch order's resolved input; the positive difference remains in the executor as filler surplus.

> [!NOTE]
>
Expand All @@ -15,6 +16,7 @@ This directory contains the core RFQ settlement contracts used by the Symbiotic

- [Reactor.sol](src/Reactor.sol)
- [Executor.sol](src/Executor.sol)
- [LiquidLaneUniswapXExecutor.sol](src/uniswapx/LiquidLaneUniswapXExecutor.sol)

## Flow

Expand Down
34 changes: 34 additions & 0 deletions script/DeployLifiExecutor.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {DeployLifiExecutorBaseScript} from "./deploy/base/DeployLifiExecutorBase.s.sol";

// forge script script/DeployLifiExecutor.s.sol:DeployLifiExecutorScript --rpc-url=RPC --broadcast

contract DeployLifiExecutorScript is DeployLifiExecutorBaseScript {
// Configurations - UPDATE THESE BEFORE DEPLOYMENT

// LI.FI OIF InputSettlerEscrow this executor finalises orders through.
address public constant INPUT_SETTLER = 0x000025c3226C00B2Cdc200005a1600509f4e00C0;
// LI.FI OIF OutputSettler this executor fills and attests outputs through.
address public constant OUTPUT_SETTLER = 0x0000000000eC36B683C2E6AC89e9A75989C22a2e;
// Executor owner. Defaults to the sender when left zero.
address public constant ADMIN = 0x0000000000000000000000000000000000000000;
// Owner of the proxy's ProxyAdmin, authorized to upgrade. Defaults to the sender when left zero.
address public constant PROXY_ADMIN_OWNER = 0x0000000000000000000000000000000000000000;
// Initial caller allowed to invoke finalise and register with LI.FI. Defaults to the sender when left zero.
address public constant CALLER = 0x0000000000000000000000000000000000000000;

function run() public returns (DeploymentData memory data) {
address owner = _scriptOwner();
data = runBase(
DeployParams({
inputSettler: INPUT_SETTLER,
outputSettler: OUTPUT_SETTLER,
admin: ADMIN == address(0) ? owner : ADMIN,
proxyAdminOwner: PROXY_ADMIN_OWNER == address(0) ? owner : PROXY_ADMIN_OWNER,
caller: CALLER == address(0) ? owner : CALLER
})
);
}
}
94 changes: 94 additions & 0 deletions script/deploy/base/DeployLifiExecutorBase.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.28;

import {Script, console2} from "forge-std/Script.sol";

import {TransparentUpgradeableProxy} from "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";

import {LiquidLaneLifiExecutor} from "../../../src/lifi/LiquidLaneLifiExecutor.sol";

contract DeployLifiExecutorBaseScript is Script {
struct DeployParams {
address inputSettler;
address outputSettler;
address admin;
address proxyAdminOwner;
address caller;
}

struct DeploymentData {
LiquidLaneLifiExecutor executor;
address implementation;
address inputSettler;
address outputSettler;
address admin;
address proxyAdminOwner;
address caller;
}

function runBase(DeployParams memory params) public virtual returns (DeploymentData memory data) {
_validateParams(params);

address[] memory callers = new address[](1);
callers[0] = params.caller;

_startBroadcast();
LiquidLaneLifiExecutor implementation = new LiquidLaneLifiExecutor(params.inputSettler, params.outputSettler);
TransparentUpgradeableProxy proxy = new TransparentUpgradeableProxy(
address(implementation),
params.proxyAdminOwner,
abi.encodeCall(LiquidLaneLifiExecutor.initialize, (params.admin, callers))
);
_stopBroadcast();

data.executor = LiquidLaneLifiExecutor(address(proxy));
data.implementation = address(implementation);
data.inputSettler = params.inputSettler;
data.outputSettler = params.outputSettler;
data.admin = params.admin;
data.proxyAdminOwner = params.proxyAdminOwner;
data.caller = params.caller;

_validateDeployment(data);
_logDeployment(data);
}

function _startBroadcast() internal virtual {
vm.startBroadcast();
}

function _stopBroadcast() internal virtual {
vm.stopBroadcast();
}

function _scriptOwner() internal view virtual returns (address owner_) {
(,, address origin) = vm.readCallers();
owner_ = origin == address(0) ? msg.sender : origin;
}

function _validateParams(DeployParams memory params) internal pure {
require(params.inputSettler != address(0), "invalid input settler");
require(params.outputSettler != address(0), "invalid output settler");
require(params.admin != address(0), "invalid admin");
require(params.proxyAdminOwner != address(0), "invalid proxy admin owner");
require(params.caller != address(0), "invalid caller");
}

function _validateDeployment(DeploymentData memory data) internal view {
assert(data.executor.owner() == data.admin);
assert(data.executor.INPUT_SETTLER() == data.inputSettler);
assert(data.executor.OUTPUT_SETTLER() == data.outputSettler);
assert(data.executor.isCaller(data.caller));
}

function _logDeployment(DeploymentData memory data) internal view {
console2.log("Deployed LI.FI Executor");
console2.log(" executor: ", address(data.executor));
console2.log(" implementation: ", data.implementation);
console2.log(" inputSettler: ", data.inputSettler);
console2.log(" outputSettler: ", data.outputSettler);
console2.log(" admin: ", data.admin);
console2.log(" proxyAdminOwner: ", data.proxyAdminOwner);
console2.log(" caller: ", data.caller);
}
}
Loading