Skip to content
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
41 changes: 21 additions & 20 deletions src/lifi/LiquidLaneLifiExecutor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,11 @@ contract LiquidLaneLifiExecutor is Initializable, OwnableUpgradeable, EIP712Upgr
}

/// @inheritdoc ILiquidLaneLifiExecutor
function finaliseWithCurrentTimestamp(IInputSettler.StandardOrder calldata order, FillRoute[] calldata routes)
external
onlyCaller
{
function finaliseWithCurrentTimestamp(
IInputSettler.StandardOrder calldata order,
FillRoute[] calldata routes,
DiscountRoute[] calldata discountRoutes
) external onlyCaller {
bytes32 executorId = bytes32(uint256(uint160(address(this))));
IInputSettler.SolveParams[] memory solveParams = new IInputSettler.SolveParams[](1);
solveParams[0] = IInputSettler.SolveParams({timestamp: uint32(block.timestamp), solver: executorId});
Expand All @@ -88,7 +89,8 @@ contract LiquidLaneLifiExecutor is Initializable, OwnableUpgradeable, EIP712Upgr
orderId: IInputSettler(INPUT_SETTLER).orderIdentifier(order),
output: order.outputs[0],
fillDeadline: order.fillDeadline,
routes: routes
routes: routes,
discountRoutes: discountRoutes
})
)
);
Expand All @@ -106,23 +108,22 @@ contract LiquidLaneLifiExecutor is Initializable, OwnableUpgradeable, EIP712Upgr
// forge-lint: disable-next-line(unsafe-typecast)
address tokenIn = address(uint160(inputs[0][0]));
uint256 routesLength = fillCall.routes.length;
for (uint256 i; i < routesLength; ++i) {
IERC20(tokenIn).safeTransfer(fillCall.routes[i].adapter, fillCall.routes[i].amountIn);
}

for (uint256 i; i < routesLength; ++i) {
FillRoute memory route = fillCall.routes[i];
if (route.discount.discountId == bytes32(0)) {
ILiquidLaneAdapter(route.adapter)
.swap(
ILiquidLaneAdapter.Swap({
recipient: address(this), tokenIn: tokenIn, amountIn: route.amountIn, amountOut: route.amountOut
})
);
} else {
ILiquidLaneAdapter(route.adapter)
.swap(route.discount.discountSwap, route.discount.protocolSignature, address(this), route.amountIn);
}
IERC20(tokenIn).safeTransfer(route.adapter, route.amountIn);
ILiquidLaneAdapter(route.adapter)
.swap(
ILiquidLaneAdapter.Swap({
recipient: address(this), tokenIn: tokenIn, amountIn: route.amountIn, amountOut: route.amountOut
})
);
}
uint256 discountRoutesLength = fillCall.discountRoutes.length;
for (uint256 i; i < discountRoutesLength; ++i) {
DiscountRoute memory route = fillCall.discountRoutes[i];
IERC20(tokenIn).safeTransfer(route.adapter, route.amountIn);
ILiquidLaneAdapter(route.adapter)
.swap(route.discountSwap, route.protocolSignature, address(this), route.amountIn);
}

// The output settler resolves the context-dependent amount it is owed and pulls it,
Expand Down
40 changes: 22 additions & 18 deletions src/lifi/interfaces/ILiquidLaneLifiExecutor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -26,44 +26,45 @@ interface ILiquidLaneLifiExecutor is IInputCallback, IERC1271 {
/* STRUCTS */

/**
* @notice Optional private-discount authorization for one route.
* @param discountId Backend discount identifier; zero selects the direct swap path.
* @param discountSwap Reusable signer policy plus the fresh protocol deadline.
* @param protocolSignature Fresh protocol cosign verified by the LiquidLane adapter.
* @notice One atomic direct-swap LiquidLane redemption leg.
* @param adapter LiquidLane adapter selected by the solver.
* @param amountIn Order-input amount routed to the adapter.
* @param amountOut Output amount requested from the adapter.
*/
struct FillDiscount {
bytes32 discountId;
ILiquidLaneAdapter.DiscountSwap discountSwap;
bytes protocolSignature;
struct FillRoute {
address adapter;
uint256 amountIn;
uint256 amountOut;
}

/**
* @notice One atomic LiquidLane redemption leg.
* @notice One atomic discount-backed LiquidLane redemption leg.
* @param adapter LiquidLane adapter selected by the solver.
* @param amountIn Order-input amount routed to the adapter.
* @param amountOut Output amount requested from the adapter on the direct swap path;
* unused for discount routes, where the signed discount terms set the output.
* @param discount Optional private-discount authorization; zero id means direct swap.
* @param discountSwap Reusable signer policy plus the fresh protocol deadline.
* @param protocolSignature Fresh protocol cosign verified by the LiquidLane adapter.
*/
struct FillRoute {
struct DiscountRoute {
address adapter;
uint256 amountIn;
uint256 amountOut;
FillDiscount discount;
ILiquidLaneAdapter.DiscountSwap discountSwap;
bytes protocolSignature;
}

/**
* @notice Callback payload constructed by `finaliseWithCurrentTimestamp` from the order itself.
* @param orderId OIF order id.
* @param output Single output to fill and attest.
* @param fillDeadline Fill deadline carried by the order.
* @param routes LiquidLane legs selected by the solver.
* @param routes Direct-swap LiquidLane legs selected by the solver.
* @param discountRoutes Discount-backed LiquidLane legs selected by the solver.
*/
struct FillCall {
bytes32 orderId;
MandateOutput output;
uint32 fillDeadline;
FillRoute[] routes;
DiscountRoute[] discountRoutes;
}

/* FUNCTIONS */
Expand All @@ -72,8 +73,11 @@ interface ILiquidLaneLifiExecutor is IInputCallback, IERC1271 {
function OUTPUT_SETTLER() external view returns (address outputSettler);
function callers(uint256 index) external view returns (address caller);
function initialize(address owner, address[] calldata initCallers) external;
function finaliseWithCurrentTimestamp(IInputSettler.StandardOrder calldata order, FillRoute[] calldata routes)
external;
function finaliseWithCurrentTimestamp(
IInputSettler.StandardOrder calldata order,
FillRoute[] calldata routes,
DiscountRoute[] calldata discountRoutes
) external;
function isCaller(address caller) external view returns (bool allowed);
function lifiRegistrationDigest(bytes32 messageHash) external view returns (bytes32 digest);
function setCallers(address[] calldata newCallers) external;
Expand Down
22 changes: 2 additions & 20 deletions test/lifi/LifiExecutorFork.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ contract LifiExecutorForkTest is Test {
bytes32 orderId = IInputSettlerEscrowLike(INPUT_SETTLER).orderIdentifier(order);

vm.prank(owner);
executor.finaliseWithCurrentTimestamp(order, _routes(order));
executor.finaliseWithCurrentTimestamp(order, _routes(order), new ILiquidLaneLifiExecutor.DiscountRoute[](0));

assertEq(IInputSettlerEscrowLike(INPUT_SETTLER).orderStatus(orderId), 2, "claimed");
assertEq(outputToken.balanceOf(recipient), 9 ether, "recipient output");
Expand Down Expand Up @@ -119,25 +119,7 @@ contract LifiExecutorForkTest is Test {
{
routes = new ILiquidLaneLifiExecutor.FillRoute[](1);
routes[0] = ILiquidLaneLifiExecutor.FillRoute({
adapter: address(adapter),
amountIn: order.inputs[0][1],
amountOut: 10 ether,
discount: ILiquidLaneLifiExecutor.FillDiscount({
discountId: bytes32(0),
discountSwap: ILiquidLaneAdapter.DiscountSwap({
discount: ILiquidLaneAdapter.Discount({
tokenToRedeem: address(0),
discount: 0,
signer: address(0),
protocol: address(0),
nonce: 0,
deadline: 0
}),
signerSignature: "",
protocolDeadline: 0
}),
protocolSignature: ""
})
adapter: address(adapter), amountIn: order.inputs[0][1], amountOut: 10 ether
});
}

Expand Down
Loading
Loading