Skip to content

Commit

Permalink
Merge pull request #5 from api3dao/optimistic-oev
Browse files Browse the repository at this point in the history
Optimistic payOevBid
  • Loading branch information
matejos authored Oct 9, 2024
2 parents b9d8f49 + cc07276 commit 62a68c2
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 55 deletions.
78 changes: 37 additions & 41 deletions contracts/Compound3Liquidator.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { Ownable } from '@openzeppelin/contracts/access/Ownable.sol';
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { IERC20 } from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import { IComet } from './compound3/interfaces/IComet.sol';
import { IUniswapV3FlashCallback } from './uniswap/v3-core/contracts/interfaces/callback/IUniswapV3FlashCallback.sol';
import { IUniswapV3SwapCallback } from './uniswap/v3-core/contracts/interfaces/callback/IUniswapV3SwapCallback.sol';
import { IUniswapV3Pool } from './uniswap/v3-core/contracts/interfaces/IUniswapV3Pool.sol';
import { TickMath } from './uniswap/v3-core/contracts/libraries/TickMath.sol';
Expand All @@ -16,10 +15,11 @@ import { IWETH9 } from './uniswap/v3-periphery/contracts/interfaces/external/IWE
import { ISwapRouter02 } from './uniswap/swap-router-contracts/contracts/interfaces/ISwapRouter02.sol';
import { IV3SwapRouter } from './uniswap/swap-router-contracts/contracts/interfaces/IV3SwapRouter.sol';
import { IApi3ServerV1OevExtension } from './api3-contracts/api3-server-v1/interfaces/IApi3ServerV1OevExtension.sol';
import { IApi3ServerV1OevExtensionPayOevBidCallback } from './api3-contracts/api3-server-v1/interfaces/IApi3ServerV1OevExtensionPayOevBidCallback.sol';

event AbsorbFailed(address indexed borrower);

contract Compound3Liquidator is Ownable, IUniswapV3SwapCallback, IUniswapV3FlashCallback {
contract Compound3Liquidator is Ownable, IUniswapV3SwapCallback, IApi3ServerV1OevExtensionPayOevBidCallback {
address public profitReceiver;
uint24 public constant DEFAULT_POOL_FEE = 500; // 0.05%
uint256 public constant DAPP_ID = 1;
Expand All @@ -29,7 +29,7 @@ contract Compound3Liquidator is Ownable, IUniswapV3SwapCallback, IUniswapV3Flash
address public constant WSTETH = 0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452;
address public constant SWAP_ROUTER = 0x2626664c2603336E57B271c5C0b26F421741e481;
address public constant UNISWAP_FACTORY = 0x33128a8fC17869897dcE68Ed026d694621f6FDfD;
address public constant API3_SERVER_V1_OEV_EXTENSION = 0xF930D1E37098128326F8731a476347f0840337cA;
address public constant API3_SERVER_V1_OEV_EXTENSION = 0x6a6F4b90ac94Df292fAe521b24b94cE8E58EB91e;

mapping(address => mapping(address => uint24)) public uniswapPoolFees;

Expand All @@ -45,16 +45,15 @@ contract Compound3Liquidator is Ownable, IUniswapV3SwapCallback, IUniswapV3Flash
}

struct PayBidAndUpdateFeedsAndLiquidateParams {
LiquidateParams liquidateParams;
uint32 signedDataTimestampCutoff;
bytes signature;
uint256 bidAmount;
bytes[][] signedDataArray;
PayOevBidCallbackData payOevBidCallbackData;
}

struct FlashCallbackData {
PoolAddress.PoolKey poolKey;
PayBidAndUpdateFeedsAndLiquidateParams params;
struct PayOevBidCallbackData {
LiquidateParams liquidateParams;
bytes[][] signedDataArray;
}

struct SwapCallbackData {
Expand Down Expand Up @@ -140,50 +139,43 @@ contract Compound3Liquidator is Ownable, IUniswapV3SwapCallback, IUniswapV3Flash

/// @notice This serves for simulating profit from liquidations via staticcall
function liquidate(LiquidateParams calldata params) external returns (uint256, uint256) {
uint256 wethBalanceBefore = weth.balanceOf(address(this));
uint256 ethBalanceBefore = address(this).balance;

_liquidate(params);

return _withdrawWethAndReturnProfit(wethBalanceBefore);
_withdrawWeth();

return _transferProfit(ethBalanceBefore);
}

/// @notice Pays OEV bid, updates OEV data feeds, performs liquidations accounts and withdraws ETH to `profitReceiver`
function payBidAndUpdateFeedsAndLiquidate(
PayBidAndUpdateFeedsAndLiquidateParams calldata params
) external returns (uint256, uint256) {
uint256 wethBalanceBefore = weth.balanceOf(address(this));
uint256 ethBalanceBefore = address(this).balance;

PoolAddress.PoolKey memory poolKey = _getFlashSwapPoolKey(USDC, WETH);
IUniswapV3Pool pool = _getFlashSwapPool(poolKey);

pool.flash(
address(this),
params.bidAmount, // token0 = WETH
0, // token1 = USDC
abi.encode(FlashCallbackData({ poolKey: poolKey, params: params }))
oevExtension.payOevBid(
DAPP_ID,
params.signedDataTimestampCutoff,
params.signature,
params.bidAmount,
abi.encode(params.payOevBidCallbackData)
);

return _withdrawWethAndReturnProfit(wethBalanceBefore);
return _transferProfit(ethBalanceBefore);
}

/// @notice Callback for flash loans through Uniswap V3
function uniswapV3FlashCallback(uint256 fee0, uint256 fee1, bytes calldata _data) external override {
FlashCallbackData memory data = abi.decode(_data, (FlashCallbackData));
CallbackValidation.verifyCallback(UNISWAP_FACTORY, data.poolKey);

weth.withdraw(data.params.bidAmount);
/// @notice Callback triggered by calling `payOevBid` on the OEV server extension. Updates data feeds,
/// liquidates accounts, and pays back the payment amount owed for the OEV bid.
function api3ServerV1OevExtensionPayOevBidCallback(uint256 amountOwed, bytes calldata _data) external override {
require(msg.sender == API3_SERVER_V1_OEV_EXTENSION, 'Unauthorized');

oevExtension.payOevBid{ value: data.params.bidAmount }(
DAPP_ID,
data.params.signedDataTimestampCutoff,
data.params.signature
);
PayOevBidCallbackData memory data = abi.decode(_data, (PayOevBidCallbackData));
_updateDataFeeds(data.signedDataArray);
_liquidate(data.liquidateParams);

_updateDataFeeds(data.params.signedDataArray);

_liquidate(data.params.liquidateParams);

weth.transfer(msg.sender, data.params.bidAmount + fee0);
_withdrawWeth();
API3_SERVER_V1_OEV_EXTENSION.call{ value: amountOwed }('');
}

/// @notice Callback for flash swaps through Uniswap V3
Expand Down Expand Up @@ -277,11 +269,15 @@ contract Compound3Liquidator is Ownable, IUniswapV3SwapCallback, IUniswapV3Flash
}
}

function _withdrawWethAndReturnProfit(uint256 wethBalanceBefore) internal returns (uint256, uint256) {
uint256 wethBalanceAfter = weth.balanceOf(address(this));
if (wethBalanceAfter > 0) {
weth.withdraw(wethBalanceAfter);
function _withdrawWeth() internal {
uint256 wethBalance = weth.balanceOf(address(this));
if (wethBalance > 0) {
weth.withdraw(wethBalance);
}
}

function _transferProfit(uint256 ethBalanceBefore) internal returns (uint256, uint256) {
uint256 ethBalanceAfter = address(this).balance;

uint8 numberOfAssets = comet.numAssets();
IComet.AssetInfo memory wethAsset;
Expand All @@ -292,7 +288,7 @@ contract Compound3Liquidator is Ownable, IUniswapV3SwapCallback, IUniswapV3Flash
}
}

uint256 profit = wethBalanceAfter - wethBalanceBefore;
uint256 profit = ethBalanceAfter - ethBalanceBefore;
uint256 profitUsd = (profit * comet.getPrice(wethAsset.priceFeed)) / wethAsset.scale;
profitReceiver.call{ value: profit }('');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,10 @@ interface IApi3ServerV1OevExtension is
function payOevBid(
uint256 dappId,
uint32 signedDataTimestampCutoff,
bytes calldata signature
) external payable;
bytes calldata signature,
uint256 bidAmount,
bytes calldata data
) external;

function updateDappOevDataFeed(
uint256 dappId,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @title Callback for IApi3ServerV1OevExtension#payOevBid
/// @notice Any contract that calls IApi3ServerV1OevExtension#payOevBid must implement this interface
interface IApi3ServerV1OevExtensionPayOevBidCallback {
/// @notice Called to `msg.sender` after granting the privilege to execute updates for the dApp from IApi3ServerV1OevExtension#payOevBid.
/// @dev In the implementation you must repay the server the tokens owed for the payment of the OEV bid.
/// The implementation is responsible to check that the caller of this method is the correct Api3ServerV1OevExtension.
/// @param amountOwed The amount of tokens owed to the server for the payment of the OEV bid
/// @param data Any data passed through by the caller via the IApi3ServerV1OevExtension#payOevBid call
function api3ServerV1OevExtensionPayOevBidCallback(
uint256 amountOwed,
bytes calldata data
) external;
}
12 changes: 6 additions & 6 deletions src/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,13 @@ import {
} from '../typechain-types';

export const baseContractAddresses = {
api3OevCbethEthProxy: '0x7583f6435cAD95bcF30C2dD7fDbfD3c5Ab58Ce4C',
api3OevEthUsdProxy: '0x86313242dBfedD9C52733a0Ed384E917424A7436',
api3OevWstethStethProxy: '0x3739c04CfE9d4750Bb40fc46904d592f3ed8EdEf',
api3OevStethUsdProxy: '0x93d2D4Aae8143E2a067a54C8138Dc8054Ad79910',
api3OevUsdcUsdProxy: '0x773f1a8E77Bd9e91a84bD80Bf35e67e4989D5C4C',
api3OevCbethEthProxy: '0x5bbeEE12b8779E1809f52441a9c2de6a3eD3dEA5',
api3OevEthUsdProxy: '0x06314AbEEA3f6A308741b1Df209f55edB58354AB',
api3OevWstethStethProxy: '0x63e3509F3Dc9f055441369A9d54B04D6FeE4adaf',
api3OevStethUsdProxy: '0x75e5A34dad31D1DB19dBeC6fFB82EbBee5e0b9ab',
api3OevUsdcUsdProxy: '0x6FBea86770975081D935456FAFfAB88524a0d1EF',
api3ServerV1: '0x709944a48cAf83535e43471680fDA4905FB3920a',
api3ServerV1OevExtension: '0xF930D1E37098128326F8731a476347f0840337cA',
api3ServerV1OevExtension: '0x6a6F4b90ac94Df292fAe521b24b94cE8E58EB91e',
multicall3: '0xcA11bde05977b3631167028862bE2a173976CA11',
} as const;

Expand Down
2 changes: 1 addition & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@ export const OEV_AWARD_BLOCK_RANGE = 500;

export const POSITIONS_CLOSE_TO_LIQUIDATION_LOG_SIZE = 10;

export const COMET_ADDRESS = '0xa193bcE4554663FECde688D5921dF38D4D41AA96';
export const COMET_ADDRESS = '0x15729c94243dc9140EaEEb37259A45D0b3550192';
13 changes: 8 additions & 5 deletions src/lib/oev-liquidation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,14 +267,17 @@ export const liquidatePositions = async (

// Prepare liquidation calldata.
const liquidationParams: Compound3Liquidator.PayBidAndUpdateFeedsAndLiquidateParamsStruct = {
liquidateParams: {
liquidatableAccounts: liquidatablePositions.map(({ position }) => position),
maxAmountsToPurchase: [MaxUint256, MaxUint256, MaxUint256],
liquidationThreshold: 0n,
payOevBidCallbackData: {
signedDataArray,
liquidateParams: {
liquidatableAccounts: liquidatablePositions.map(({ position }) => position),
maxAmountsToPurchase: [MaxUint256, MaxUint256, MaxUint256],
liquidationThreshold: 0n,
},
},
bidAmount,
signature,
signedDataArray,

signedDataTimestampCutoff,
};

Expand Down

0 comments on commit 62a68c2

Please sign in to comment.