From bb64836886efb48b72596ef9c306c306aee5440d Mon Sep 17 00:00:00 2001 From: defiQUG Date: Thu, 7 May 2026 05:25:53 -0700 Subject: [PATCH] Add Engine X proof vaults --- .../flash/DBISEngineXMaintainedProofVault.sol | 227 +++++++++++++++++ contracts/flash/DBISEngineXProofVault.sol | 234 ++++++++++++++++++ 2 files changed, 461 insertions(+) create mode 100644 contracts/flash/DBISEngineXMaintainedProofVault.sol create mode 100644 contracts/flash/DBISEngineXProofVault.sol diff --git a/contracts/flash/DBISEngineXMaintainedProofVault.sol b/contracts/flash/DBISEngineXMaintainedProofVault.sol new file mode 100644 index 0000000..1fa2a0e --- /dev/null +++ b/contracts/flash/DBISEngineXMaintainedProofVault.sol @@ -0,0 +1,227 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +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 Mainnet proof vault for Engine X with automatic cWUSDC surplus neutralization. +/// @dev Surplus cWUSDC is removed from pool reserves and transferred to a neutral receiver. +contract DBISEngineXMaintainedProofVault is Ownable { + using SafeERC20 for IERC20; + + IERC20 public immutable cWUSDC; + IERC20 public immutable usdc; + IERC20 public immutable xaut; + + uint256 public poolCwusdcReserve; + uint256 public poolUsdcReserve; + uint256 public lenderUsdcAvailable; + uint256 public totalNeutralizedCwusdc; + + uint256 public immutable xautUsdPrice6; + uint256 public immutable ltvBps; + uint256 public immutable maxRoundTripLossBps; + uint256 public immutable maxSurplusDust; + + address public surplusReceiver; + + event PoolSeeded(uint256 cwusdcAmount, uint256 usdcAmount); + event LenderFunded(uint256 usdcAmount); + event SurplusReceiverUpdated(address indexed receiver); + event SurplusNeutralized(bytes32 indexed proofId, uint256 indexed step, address indexed receiver, uint256 amount); + event ProofStep( + bytes32 indexed proofId, + uint256 indexed step, + uint256 debtUsdc, + uint256 collateralXaut, + uint256 cwusdcIn, + uint256 cwusdcOut, + uint256 cwusdcLoss, + uint256 neutralizedCwusdc, + uint256 poolCwusdcReserveAfter, + uint256 poolUsdcReserveAfter + ); + event ProofClosed( + bytes32 indexed proofId, + uint256 loops, + uint256 totalDebtUsdc, + uint256 totalCwusdcIn, + uint256 totalNeutralizedCwusdcAmount + ); + event OwnerWithdraw(address indexed token, address indexed to, uint256 amount); + + constructor( + address cWUSDC_, + address usdc_, + address xaut_, + address owner_, + address surplusReceiver_, + uint256 xautUsdPrice6_, + uint256 ltvBps_, + uint256 maxRoundTripLossBps_, + uint256 maxSurplusDust_ + ) Ownable(owner_) { + require(cWUSDC_ != address(0) && usdc_ != address(0) && xaut_ != address(0), "zero token"); + require(owner_ != address(0), "zero owner"); + require(surplusReceiver_ != address(0), "zero receiver"); + require(xautUsdPrice6_ > 0, "zero price"); + require(ltvBps_ > 0 && ltvBps_ < 10_000, "bad ltv"); + require(maxRoundTripLossBps_ > 0 && maxRoundTripLossBps_ < 10_000, "bad loss"); + cWUSDC = IERC20(cWUSDC_); + usdc = IERC20(usdc_); + xaut = IERC20(xaut_); + surplusReceiver = surplusReceiver_; + xautUsdPrice6 = xautUsdPrice6_; + ltvBps = ltvBps_; + maxRoundTripLossBps = maxRoundTripLossBps_; + maxSurplusDust = maxSurplusDust_; + } + + function seedPool(uint256 cwusdcAmount, uint256 usdcAmount) external onlyOwner { + require(cwusdcAmount > 0 && usdcAmount > 0, "zero seed"); + cWUSDC.safeTransferFrom(msg.sender, address(this), cwusdcAmount); + usdc.safeTransferFrom(msg.sender, address(this), usdcAmount); + poolCwusdcReserve += cwusdcAmount; + poolUsdcReserve += usdcAmount; + emit PoolSeeded(cwusdcAmount, usdcAmount); + } + + function fundLender(uint256 usdcAmount) external onlyOwner { + require(usdcAmount > 0, "zero fund"); + usdc.safeTransferFrom(msg.sender, address(this), usdcAmount); + lenderUsdcAvailable += usdcAmount; + emit LenderFunded(usdcAmount); + } + + function setSurplusReceiver(address receiver) external onlyOwner { + require(receiver != address(0), "zero receiver"); + surplusReceiver = receiver; + emit SurplusReceiverUpdated(receiver); + } + + function previewCwusdcInForExactUsdc(uint256 usdcOut) public view returns (uint256) { + return _getAmountIn(usdcOut, poolCwusdcReserve, poolUsdcReserve); + } + + function previewCwusdcOutForExactUsdcIn(uint256 usdcIn) public view returns (uint256) { + return _getAmountOut(usdcIn, poolUsdcReserve, poolCwusdcReserve); + } + + function currentSurplusCwusdc() public view returns (uint256) { + return poolCwusdcReserve > poolUsdcReserve ? poolCwusdcReserve - poolUsdcReserve : 0; + } + + function minimumXautCollateral(uint256 debtUsdc) public view returns (uint256) { + uint256 numerator = debtUsdc * 1e6 * 10_000; + uint256 denominator = xautUsdPrice6 * ltvBps; + return (numerator + denominator - 1) / denominator; + } + + function runProof(bytes32 proofId, uint256 debtUsdcPerLoop, uint256 loops) external { + _runProofFor(msg.sender, proofId, debtUsdcPerLoop, loops); + } + + function runProofBatch(bytes32[] calldata proofIds, uint256[] calldata debtsUsdcPerLoop, uint256[] calldata loops_) + external + { + require(proofIds.length == debtsUsdcPerLoop.length && proofIds.length == loops_.length, "length mismatch"); + for (uint256 i = 0; i < proofIds.length; i++) { + _runProofFor(msg.sender, proofIds[i], debtsUsdcPerLoop[i], loops_[i]); + } + } + + function _runProofFor(address account, bytes32 proofId, uint256 debtUsdcPerLoop, uint256 loops) internal { + require(proofId != bytes32(0), "zero proof"); + require(debtUsdcPerLoop > 0, "zero debt"); + require(loops > 0 && loops <= 32, "bad loops"); + + uint256 collateralPerLoop = minimumXautCollateral(debtUsdcPerLoop); + uint256 totalCollateral = collateralPerLoop * loops; + uint256 totalDebt; + uint256 totalCwusdcIn; + uint256 neutralizedInProof; + + xaut.safeTransferFrom(account, address(this), totalCollateral); + + for (uint256 i = 0; i < loops; i++) { + require(lenderUsdcAvailable >= debtUsdcPerLoop, "insufficient lender usdc"); + + uint256 poolUsdcBefore = poolUsdcReserve; + lenderUsdcAvailable -= debtUsdcPerLoop; + uint256 cwusdcIn = _getAmountIn(debtUsdcPerLoop, poolCwusdcReserve, poolUsdcReserve); + cWUSDC.safeTransferFrom(account, address(this), cwusdcIn); + + poolCwusdcReserve += cwusdcIn; + poolUsdcReserve -= debtUsdcPerLoop; + lenderUsdcAvailable += debtUsdcPerLoop; + + uint256 cwusdcOut = _getAmountOut(debtUsdcPerLoop, poolUsdcReserve, poolCwusdcReserve); + poolUsdcReserve += debtUsdcPerLoop; + poolCwusdcReserve -= cwusdcOut; + cWUSDC.safeTransfer(account, cwusdcOut); + + uint256 cwusdcLoss = cwusdcIn - cwusdcOut; + require(cwusdcLoss * 10_000 <= cwusdcIn * maxRoundTripLossBps, "roundtrip loss too high"); + require(poolUsdcReserve == poolUsdcBefore, "usdc reserve drift"); + + uint256 neutralized = _neutralizeSurplus(proofId, i + 1); + neutralizedInProof += neutralized; + + totalDebt += debtUsdcPerLoop; + totalCwusdcIn += cwusdcIn; + + emit ProofStep( + proofId, + i + 1, + debtUsdcPerLoop, + collateralPerLoop, + cwusdcIn, + cwusdcOut, + cwusdcLoss, + neutralized, + poolCwusdcReserve, + poolUsdcReserve + ); + } + + xaut.safeTransfer(account, totalCollateral); + emit ProofClosed(proofId, loops, totalDebt, totalCwusdcIn, neutralizedInProof); + } + + function _neutralizeSurplus(bytes32 proofId, uint256 step) internal returns (uint256 neutralized) { + uint256 surplus = currentSurplusCwusdc(); + if (surplus <= maxSurplusDust) { + return 0; + } + + neutralized = surplus - maxSurplusDust; + poolCwusdcReserve -= neutralized; + totalNeutralizedCwusdc += neutralized; + cWUSDC.safeTransfer(surplusReceiver, neutralized); + emit SurplusNeutralized(proofId, step, surplusReceiver, neutralized); + } + + function withdraw(address token, address to, uint256 amount) external onlyOwner { + require(to != address(0), "zero to"); + IERC20(token).safeTransfer(to, amount); + emit OwnerWithdraw(token, to, amount); + } + + function _getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) internal pure returns (uint256) { + require(amountOut > 0, "insufficient output"); + require(reserveIn > 0 && reserveOut > amountOut, "insufficient liquidity"); + uint256 numerator = reserveIn * amountOut * 1000; + uint256 denominator = (reserveOut - amountOut) * 997; + return (numerator / denominator) + 1; + } + + function _getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) internal pure returns (uint256) { + require(amountIn > 0, "insufficient input"); + require(reserveIn > 0 && reserveOut > 0, "insufficient liquidity"); + uint256 amountInWithFee = amountIn * 997; + uint256 numerator = amountInWithFee * reserveOut; + uint256 denominator = reserveIn * 1000 + amountInWithFee; + return numerator / denominator; + } +} diff --git a/contracts/flash/DBISEngineXProofVault.sol b/contracts/flash/DBISEngineXProofVault.sol new file mode 100644 index 0000000..e6b0c0c --- /dev/null +++ b/contracts/flash/DBISEngineXProofVault.sol @@ -0,0 +1,234 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +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 Mainnet proof-of-concept vault for the Engine X XAU/USD secured loop. +/// @dev This is intentionally small and deterministic: it is not a production lender. +contract DBISEngineXProofVault is Ownable { + using SafeERC20 for IERC20; + + IERC20 public immutable cWUSDC; + IERC20 public immutable usdc; + IERC20 public immutable xaut; + + uint256 public poolCwusdcReserve; + uint256 public poolUsdcReserve; + uint256 public lenderUsdcAvailable; + + uint256 public immutable xautUsdPrice6; + uint256 public immutable ltvBps; + uint256 public immutable maxRoundTripLossBps; + + event PoolSeeded(uint256 cwusdcAmount, uint256 usdcAmount); + event LenderFunded(uint256 usdcAmount); + event ProofStep( + bytes32 indexed proofId, + uint256 indexed step, + uint256 debtUsdc, + uint256 collateralXaut, + uint256 cwusdcIn, + uint256 cwusdcOut, + uint256 cwusdcLoss, + uint256 poolCwusdcReserveAfter, + uint256 poolUsdcReserveAfter + ); + event ProofClosed(bytes32 indexed proofId, uint256 loops, uint256 totalDebtUsdc, uint256 totalCwusdcIn); + event OwnerWithdraw(address indexed token, address indexed to, uint256 amount); + + constructor( + address cWUSDC_, + address usdc_, + address xaut_, + address owner_, + uint256 xautUsdPrice6_, + uint256 ltvBps_, + uint256 maxRoundTripLossBps_ + ) Ownable(owner_) { + require(cWUSDC_ != address(0) && usdc_ != address(0) && xaut_ != address(0), "zero token"); + require(owner_ != address(0), "zero owner"); + require(xautUsdPrice6_ > 0, "zero price"); + require(ltvBps_ > 0 && ltvBps_ < 10_000, "bad ltv"); + require(maxRoundTripLossBps_ > 0 && maxRoundTripLossBps_ < 10_000, "bad loss"); + cWUSDC = IERC20(cWUSDC_); + usdc = IERC20(usdc_); + xaut = IERC20(xaut_); + xautUsdPrice6 = xautUsdPrice6_; + ltvBps = ltvBps_; + maxRoundTripLossBps = maxRoundTripLossBps_; + } + + function seedPool(uint256 cwusdcAmount, uint256 usdcAmount) external onlyOwner { + require(cwusdcAmount > 0 && usdcAmount > 0, "zero seed"); + cWUSDC.safeTransferFrom(msg.sender, address(this), cwusdcAmount); + usdc.safeTransferFrom(msg.sender, address(this), usdcAmount); + poolCwusdcReserve += cwusdcAmount; + poolUsdcReserve += usdcAmount; + emit PoolSeeded(cwusdcAmount, usdcAmount); + } + + function fundLender(uint256 usdcAmount) external onlyOwner { + require(usdcAmount > 0, "zero fund"); + usdc.safeTransferFrom(msg.sender, address(this), usdcAmount); + lenderUsdcAvailable += usdcAmount; + emit LenderFunded(usdcAmount); + } + + function previewCwusdcInForExactUsdc(uint256 usdcOut) public view returns (uint256) { + return _getAmountIn(usdcOut, poolCwusdcReserve, poolUsdcReserve); + } + + function previewCwusdcOutForExactUsdcIn(uint256 usdcIn) public view returns (uint256) { + return _getAmountOut(usdcIn, poolUsdcReserve, poolCwusdcReserve); + } + + function minimumXautCollateral(uint256 debtUsdc) public view returns (uint256) { + uint256 numerator = debtUsdc * 1e6 * 10_000; + uint256 denominator = xautUsdPrice6 * ltvBps; + return (numerator + denominator - 1) / denominator; + } + + function runProof(bytes32 proofId, uint256 debtUsdcPerLoop, uint256 loops) external { + require(proofId != bytes32(0), "zero proof"); + require(debtUsdcPerLoop > 0, "zero debt"); + require(loops > 0 && loops <= 32, "bad loops"); + + uint256 collateralPerLoop = minimumXautCollateral(debtUsdcPerLoop); + uint256 totalCollateral = collateralPerLoop * loops; + uint256 totalDebt; + uint256 totalCwusdcIn; + + xaut.safeTransferFrom(msg.sender, address(this), totalCollateral); + + for (uint256 i = 0; i < loops; i++) { + require(lenderUsdcAvailable >= debtUsdcPerLoop, "insufficient lender usdc"); + + uint256 poolCwusdcBefore = poolCwusdcReserve; + uint256 poolUsdcBefore = poolUsdcReserve; + lenderUsdcAvailable -= debtUsdcPerLoop; + uint256 cwusdcIn = _getAmountIn(debtUsdcPerLoop, poolCwusdcReserve, poolUsdcReserve); + cWUSDC.safeTransferFrom(msg.sender, address(this), cwusdcIn); + + poolCwusdcReserve += cwusdcIn; + poolUsdcReserve -= debtUsdcPerLoop; + lenderUsdcAvailable += debtUsdcPerLoop; + + uint256 cwusdcOut = _getAmountOut(debtUsdcPerLoop, poolUsdcReserve, poolCwusdcReserve); + poolUsdcReserve += debtUsdcPerLoop; + poolCwusdcReserve -= cwusdcOut; + cWUSDC.safeTransfer(msg.sender, cwusdcOut); + + uint256 cwusdcLoss = cwusdcIn - cwusdcOut; + require(cwusdcLoss * 10_000 <= cwusdcIn * maxRoundTripLossBps, "roundtrip loss too high"); + require(poolUsdcReserve == poolUsdcBefore, "usdc reserve drift"); + require(poolCwusdcReserve >= poolCwusdcBefore, "cw reserve drift"); + + totalDebt += debtUsdcPerLoop; + totalCwusdcIn += cwusdcIn; + + emit ProofStep( + proofId, + i + 1, + debtUsdcPerLoop, + collateralPerLoop, + cwusdcIn, + cwusdcOut, + cwusdcLoss, + poolCwusdcReserve, + poolUsdcReserve + ); + } + + xaut.safeTransfer(msg.sender, totalCollateral); + emit ProofClosed(proofId, loops, totalDebt, totalCwusdcIn); + } + + function runProofBatch(bytes32[] calldata proofIds, uint256[] calldata debtsUsdcPerLoop, uint256[] calldata loops_) + external + { + require(proofIds.length == debtsUsdcPerLoop.length && proofIds.length == loops_.length, "length mismatch"); + for (uint256 i = 0; i < proofIds.length; i++) { + _runProofFor(msg.sender, proofIds[i], debtsUsdcPerLoop[i], loops_[i]); + } + } + + function _runProofFor(address account, bytes32 proofId, uint256 debtUsdcPerLoop, uint256 loops) internal { + require(proofId != bytes32(0), "zero proof"); + require(debtUsdcPerLoop > 0, "zero debt"); + require(loops > 0 && loops <= 32, "bad loops"); + + uint256 collateralPerLoop = minimumXautCollateral(debtUsdcPerLoop); + uint256 totalCollateral = collateralPerLoop * loops; + uint256 totalDebt; + uint256 totalCwusdcIn; + + xaut.safeTransferFrom(account, address(this), totalCollateral); + + for (uint256 i = 0; i < loops; i++) { + require(lenderUsdcAvailable >= debtUsdcPerLoop, "insufficient lender usdc"); + + uint256 poolCwusdcBefore = poolCwusdcReserve; + uint256 poolUsdcBefore = poolUsdcReserve; + lenderUsdcAvailable -= debtUsdcPerLoop; + uint256 cwusdcIn = _getAmountIn(debtUsdcPerLoop, poolCwusdcReserve, poolUsdcReserve); + cWUSDC.safeTransferFrom(account, address(this), cwusdcIn); + + poolCwusdcReserve += cwusdcIn; + poolUsdcReserve -= debtUsdcPerLoop; + lenderUsdcAvailable += debtUsdcPerLoop; + + uint256 cwusdcOut = _getAmountOut(debtUsdcPerLoop, poolUsdcReserve, poolCwusdcReserve); + poolUsdcReserve += debtUsdcPerLoop; + poolCwusdcReserve -= cwusdcOut; + cWUSDC.safeTransfer(account, cwusdcOut); + + uint256 cwusdcLoss = cwusdcIn - cwusdcOut; + require(cwusdcLoss * 10_000 <= cwusdcIn * maxRoundTripLossBps, "roundtrip loss too high"); + require(poolUsdcReserve == poolUsdcBefore, "usdc reserve drift"); + require(poolCwusdcReserve >= poolCwusdcBefore, "cw reserve drift"); + + totalDebt += debtUsdcPerLoop; + totalCwusdcIn += cwusdcIn; + + emit ProofStep( + proofId, + i + 1, + debtUsdcPerLoop, + collateralPerLoop, + cwusdcIn, + cwusdcOut, + cwusdcLoss, + poolCwusdcReserve, + poolUsdcReserve + ); + } + + xaut.safeTransfer(account, totalCollateral); + emit ProofClosed(proofId, loops, totalDebt, totalCwusdcIn); + } + + function withdraw(address token, address to, uint256 amount) external onlyOwner { + require(to != address(0), "zero to"); + IERC20(token).safeTransfer(to, amount); + emit OwnerWithdraw(token, to, amount); + } + + function _getAmountIn(uint256 amountOut, uint256 reserveIn, uint256 reserveOut) internal pure returns (uint256) { + require(amountOut > 0, "insufficient output"); + require(reserveIn > 0 && reserveOut > amountOut, "insufficient liquidity"); + uint256 numerator = reserveIn * amountOut * 1000; + uint256 denominator = (reserveOut - amountOut) * 997; + return (numerator / denominator) + 1; + } + + function _getAmountOut(uint256 amountIn, uint256 reserveIn, uint256 reserveOut) internal pure returns (uint256) { + require(amountIn > 0, "insufficient input"); + require(reserveIn > 0 && reserveOut > 0, "insufficient liquidity"); + uint256 amountInWithFee = amountIn * 997; + uint256 numerator = amountInWithFee * reserveOut; + uint256 denominator = reserveIn * 1000 + amountInWithFee; + return numerator / denominator; + } +}