// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import {IERC3156FlashBorrower} from "@openzeppelin/contracts/interfaces/IERC3156FlashBorrower.sol"; import {IERC3156FlashLender} from "@openzeppelin/contracts/interfaces/IERC3156FlashLender.sol"; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol"; /// @notice Minimal Engine X ERC-3156 borrower for proving same-transaction USDC working capital. /// @dev Prefund this contract with at least the flash fee before calling `runFlashProof`. contract DBISEngineXFlashProofBorrower is IERC3156FlashBorrower, Ownable { using SafeERC20 for IERC20; bytes32 private constant _RETURN_VALUE = keccak256("ERC3156FlashBorrower.onFlashLoan"); IERC3156FlashLender public immutable lender; IERC20 public immutable usdc; mapping(bytes32 => bool) public usedProofIds; event EngineXFlashProof( bytes32 indexed proofId, address indexed initiator, address indexed lender, address token, uint256 amount, uint256 fee, bytes32 iso20022DocumentHash, bytes32 auditEnvelopeHash, bytes32 pegProofHash ); constructor(address lender_, address usdc_, address owner_) Ownable(owner_) { require(lender_ != address(0) && usdc_ != address(0), "zero address"); lender = IERC3156FlashLender(lender_); usdc = IERC20(usdc_); } function runFlashProof( uint256 amount, bytes32 proofId, bytes32 iso20022DocumentHash, bytes32 auditEnvelopeHash, bytes32 pegProofHash ) external onlyOwner returns (bool) { require(proofId != bytes32(0), "zero proof"); require(!usedProofIds[proofId], "proof used"); require(iso20022DocumentHash != bytes32(0), "zero iso hash"); require(auditEnvelopeHash != bytes32(0), "zero audit hash"); require(pegProofHash != bytes32(0), "zero peg hash"); bytes memory data = abi.encode(msg.sender, proofId, iso20022DocumentHash, auditEnvelopeHash, pegProofHash); return lender.flashLoan(this, address(usdc), amount, data); } function onFlashLoan(address initiator, address token, uint256 amount, uint256 fee, bytes calldata data) external override returns (bytes32) { require(msg.sender == address(lender), "bad lender"); require(initiator == address(this), "bad initiator"); require(token == address(usdc), "bad token"); ( address operator, bytes32 proofId, bytes32 iso20022DocumentHash, bytes32 auditEnvelopeHash, bytes32 pegProofHash ) = abi.decode(data, (address, bytes32, bytes32, bytes32, bytes32)); require(!usedProofIds[proofId], "proof used"); usedProofIds[proofId] = true; usdc.forceApprove(msg.sender, amount + fee); emit EngineXFlashProof( proofId, operator, msg.sender, token, amount, fee, iso20022DocumentHash, auditEnvelopeHash, pegProofHash ); return _RETURN_VALUE; } function withdraw(address token, address to, uint256 amount) external onlyOwner { require(to != address(0), "zero to"); IERC20(token).safeTransfer(to, amount); } }