- 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
73 lines
2.6 KiB
Solidity
73 lines
2.6 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 IDODOStyleSwapExactIn {
|
|
function swapExactIn(address pool, address tokenIn, uint256 amountIn, uint256 minAmountOut)
|
|
external
|
|
returns (uint256 amountOut);
|
|
}
|
|
|
|
/**
|
|
* @title SwapFlashWorkflowBorrower
|
|
* @notice ERC-3156 borrower: flash `token` → swap to `midToken` → swap back to `token` → repay `amount + fee`.
|
|
* @dev `data` must be `abi.encode(SwapFlashParams)`. The caller chooses `integration` — use only trusted DODO/PMM routers.
|
|
* `pool` is typically the same for both legs (e.g. cUSDT/USDT round-trip). Set mins from off-chain quotes.
|
|
*/
|
|
contract SwapFlashWorkflowBorrower is IERC3156FlashBorrower {
|
|
using SafeERC20 for IERC20;
|
|
|
|
bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan");
|
|
|
|
address public immutable trustedLender;
|
|
|
|
struct SwapFlashParams {
|
|
address integration;
|
|
address pool;
|
|
address midToken;
|
|
uint256 minOutFirst;
|
|
uint256 minOutSecond;
|
|
}
|
|
|
|
error UntrustedLender();
|
|
error BadParams();
|
|
error InsufficientToRepay();
|
|
|
|
constructor(address trustedLender_) {
|
|
trustedLender = trustedLender_;
|
|
}
|
|
|
|
function onFlashLoan(
|
|
address,
|
|
address token,
|
|
uint256 amount,
|
|
uint256 fee,
|
|
bytes calldata data
|
|
) external override returns (bytes32) {
|
|
if (msg.sender != trustedLender) revert UntrustedLender();
|
|
SwapFlashParams memory p = abi.decode(data, (SwapFlashParams));
|
|
if (p.integration == address(0) || p.pool == address(0) || p.midToken == address(0)) revert BadParams();
|
|
if (p.midToken == token) revert BadParams();
|
|
|
|
IERC20 borrowed = IERC20(token);
|
|
IERC20 mid = IERC20(p.midToken);
|
|
|
|
borrowed.forceApprove(p.integration, amount);
|
|
IDODOStyleSwapExactIn(p.integration).swapExactIn(p.pool, token, amount, p.minOutFirst);
|
|
|
|
uint256 midBal = mid.balanceOf(address(this));
|
|
mid.forceApprove(p.integration, midBal);
|
|
IDODOStyleSwapExactIn(p.integration).swapExactIn(p.pool, p.midToken, midBal, p.minOutSecond);
|
|
|
|
uint256 need = amount + fee;
|
|
if (borrowed.balanceOf(address(this)) < need) revert InsufficientToRepay();
|
|
borrowed.safeTransfer(msg.sender, need);
|
|
|
|
return _RETURN_VALUE;
|
|
}
|
|
}
|