- CCIP/trustless bridge contracts, GRU tokens, DEX/PMM tests, reserve vault. - Token-aggregation service routes, planner, chain config, relay env templates. - Config snapshots and multi-chain deployment markdown updates. - gitignore services/btc-intake/dist/ (tsc output); do not track dist. Run forge build && forge test before deploy (large solc graph). Made-with: Cursor
102 lines
3.7 KiB
Solidity
102 lines
3.7 KiB
Solidity
// SPDX-License-Identifier: MIT
|
|
pragma solidity ^0.8.20;
|
|
|
|
import {IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol";
|
|
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
|
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
|
|
|
|
/// @dev Matches `DODOPMMIntegration.swapExactIn` surface (any registered pool).
|
|
interface IDODOQuotePushSwapExactIn {
|
|
function swapExactIn(address pool, address tokenIn, uint256 amountIn, uint256 minAmountOut)
|
|
external
|
|
returns (uint256 amountOut);
|
|
}
|
|
|
|
/// @dev Minimal external unwind interface for converting PMM base back into flash-borrowed quote.
|
|
interface IExternalUnwinder {
|
|
function unwind(address tokenIn, address tokenOut, uint256 amountIn, uint256 minAmountOut, bytes calldata data)
|
|
external
|
|
returns (uint256 amountOut);
|
|
}
|
|
|
|
/**
|
|
* @title QuotePushFlashWorkflowBorrower
|
|
* @notice ERC-3156 borrower for a quote-push loop:
|
|
* flash `quoteToken` -> buy `baseToken` from a DODO-style PMM -> unwind externally back into `quoteToken`
|
|
* -> repay `amount + fee`, leaving any quote surplus on this contract.
|
|
* @dev `data` must be `abi.encode(QuotePushParams)`. The caller is responsible for choosing trusted integrations,
|
|
* setting conservative minimums, and sweeping any retained surplus from this contract after execution.
|
|
*/
|
|
contract QuotePushFlashWorkflowBorrower is IERC3156FlashBorrower {
|
|
using SafeERC20 for IERC20;
|
|
|
|
bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");
|
|
|
|
address public immutable trustedLender;
|
|
|
|
struct QuotePushParams {
|
|
address integration;
|
|
address pool;
|
|
address baseToken;
|
|
address externalUnwinder;
|
|
uint256 minOutPmm;
|
|
uint256 minOutUnwind;
|
|
bytes unwindData;
|
|
}
|
|
|
|
error UntrustedLender();
|
|
error BadParams();
|
|
error InsufficientToRepay();
|
|
|
|
event QuotePushExecuted(
|
|
address indexed quoteToken,
|
|
address indexed baseToken,
|
|
uint256 borrowedAmount,
|
|
uint256 fee,
|
|
uint256 baseOut,
|
|
uint256 unwindOut,
|
|
uint256 surplus
|
|
);
|
|
|
|
constructor(address trustedLender_) {
|
|
trustedLender = trustedLender_;
|
|
}
|
|
|
|
function onFlashLoan(
|
|
address,
|
|
address quoteToken,
|
|
uint256 amount,
|
|
uint256 fee,
|
|
bytes calldata data
|
|
) external override returns (bytes32) {
|
|
if (msg.sender != trustedLender) revert UntrustedLender();
|
|
QuotePushParams memory p = abi.decode(data, (QuotePushParams));
|
|
if (
|
|
p.integration == address(0) || p.pool == address(0) || p.baseToken == address(0)
|
|
|| p.externalUnwinder == address(0)
|
|
) revert BadParams();
|
|
if (p.baseToken == quoteToken) revert BadParams();
|
|
|
|
IERC20 borrowed = IERC20(quoteToken);
|
|
IERC20 base = IERC20(p.baseToken);
|
|
|
|
borrowed.forceApprove(p.integration, amount);
|
|
uint256 baseOut = IDODOQuotePushSwapExactIn(p.integration).swapExactIn(p.pool, quoteToken, amount, p.minOutPmm);
|
|
|
|
uint256 baseBal = base.balanceOf(address(this));
|
|
base.forceApprove(p.externalUnwinder, baseBal);
|
|
uint256 unwindOut =
|
|
IExternalUnwinder(p.externalUnwinder).unwind(p.baseToken, quoteToken, baseBal, p.minOutUnwind, p.unwindData);
|
|
|
|
uint256 need = amount + fee;
|
|
uint256 quoteBal = borrowed.balanceOf(address(this));
|
|
if (quoteBal < need) revert InsufficientToRepay();
|
|
|
|
uint256 surplus = quoteBal - need;
|
|
borrowed.safeTransfer(msg.sender, need);
|
|
emit QuotePushExecuted(quoteToken, p.baseToken, amount, fee, baseOut, unwindOut, surplus);
|
|
|
|
return _RETURN_VALUE;
|
|
}
|
|
}
|