Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement VIPs for MakerPSM -- PRO-115 #46

Merged
merged 13 commits into from
Dec 27, 2023
2 changes: 1 addition & 1 deletion .forge-snapshots/settler_uniswapV2_DAI-WETH.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
136655
136638
2 changes: 1 addition & 1 deletion .forge-snapshots/settler_uniswapV2_USDC-WETH.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
160506
160489
2 changes: 1 addition & 1 deletion .forge-snapshots/settler_uniswapV2_USDT-WETH.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
153015
142580
1 change: 1 addition & 0 deletions .forge-snapshots/settler_uniswapV2_multihop_DAI-WETH.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
189118
1 change: 1 addition & 0 deletions .forge-snapshots/settler_uniswapV2_multihop_USDC-WETH.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
212990
1 change: 1 addition & 0 deletions .forge-snapshots/settler_uniswapV2_multihop_USDT-WETH.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
195056
2 changes: 1 addition & 1 deletion .forge-snapshots/settler_zeroExOtc_DAI-WETH.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
145780
172912
2 changes: 1 addition & 1 deletion .forge-snapshots/settler_zeroExOtc_USDC-WETH.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
174977
202822
2 changes: 1 addition & 1 deletion .forge-snapshots/settler_zeroExOtc_USDT-WETH.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
160683
188244
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
180742
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
212708
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
197858
35 changes: 32 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@ Note: The following is more akin to `gasLimit` than it is `gasUsed`, this is due
| ------- | ------- | --------- | ------ | ------ |
| 0x V4 | 0x V4 | USDC/WETH | 112785 | 0.00% |
| Settler | Settler | USDC/WETH | 113732 | 0.84% |
| Settler | 0x V4 | USDC/WETH | 174977 | 55.14% |
| Settler | 0x V4 | USDC/WETH | 202822 | 79.83% |
| | | | | |
| 0x V4 | 0x V4 | DAI/WETH | 93311 | 0.00% |
| Settler | Settler | DAI/WETH | 94258 | 1.01% |
| Settler | 0x V4 | DAI/WETH | 145780 | 56.23% |
| Settler | 0x V4 | DAI/WETH | 172912 | 85.31% |
| | | | | |
| 0x V4 | 0x V4 | USDT/WETH | 104423 | 0.00% |
| Settler | Settler | USDT/WETH | 105370 | 0.91% |
| Settler | 0x V4 | USDT/WETH | 160683 | 53.88% |
| Settler | 0x V4 | USDT/WETH | 188244 | 80.27% |
Comment on lines -77 to +85
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

previous versions of this comparison weren't accurate because they failed to actually transfer the buy token to the taker. this PR rectifies that oversight in the unit test, massively increasing the gas consumed

| | | | | |

| Curve | DEX | Pair | Gas | % |
Expand Down Expand Up @@ -501,3 +501,32 @@ TokenPermissions(address token,uint256 amount)
```

Where `actions` is added and contains the encoded actions the to perform.

## Anticipated AMM support

| AMM | support? | notes |
| :------------| :------: | :----- |
| Balancer | ✅ | |
| Balancer V2 | ✅ | |
| Bancor | ✅ | |
| Bancor V3 | ✅ | |
| Component | ✅ | Shell clone |
| CryptoCom | ✅ | UniV2 clone |
| Curve | ✅ | |
| Curve V2 | ✅ | |
| DODO | ❌ | Requires a helper contract in a specific direction to calculate the correct sell amount. One can only buy asset A and sell asset B |
| DODO V2 | ❌ | Uses a transfer + sync flow |
| KyberDMM | ✅ | |
| KyberElastic | ✅ | |
| Lido | ✅ | |
| MakerPsm | ✅ | Has VIP |
| mStable | ✅ | |
| Saddle | ✅ | Curve clone |
| Shell | ✅ | |
| ShibaSwap | ✅ | UniV2 clone |
| SushiSwap | ✅ | UniV2 clone |
| Synapse | ✅ | Curve clone |
| Synthetix | ❌ | |
| Uniswap | ✅ | |
| Uniswap V2 | ✅ | Has VIP |
| Uniswap V3 | ✅ | Has VIP |
10 changes: 3 additions & 7 deletions src/ISettlerActions.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
pragma solidity ^0.8.21;

import {ISignatureTransfer} from "permit2/src/interfaces/ISignatureTransfer.sol";
import {IZeroEx} from "./core/ZeroEx.sol";

interface ISettlerActions {
/// @dev Transfer funds from msg.sender Permit2.
Expand Down Expand Up @@ -62,6 +61,9 @@ interface ISettlerActions {
bytes memory sig
) external;

function MAKER_PSM_SELL_GEM(address recipient, uint256 bips, address psm, address gemToken) external;
function MAKER_PSM_BUY_GEM(address recipient, uint256 bips, address psm, address gemToken) external;

/// @dev Trades against UniswapV3 using user funds via Permit2 for funding. Metatransaction variant. Signature is over all actions.
function METATXN_UNISWAPV3_PERMIT2_SWAP_EXACT_IN(
address recipient,
Expand All @@ -76,12 +78,6 @@ interface ISettlerActions {

function POSITIVE_SLIPPAGE(address token, address recipient, uint256 expectedAmount) external;

// @dev Fill a 0x V4 OTC order using the 0x Exchange Proxy contract
// Pre-req: Funded
// Post-req: Payout
function ZERO_EX_OTC(IZeroEx.OtcOrder memory order, IZeroEx.Signature memory signature, uint256 sellAmount)
external;

/// @dev Trades against a basic AMM which follows the approval, transferFrom(msg.sender) interaction
// Pre-req: Funded
// Post-req: Payout
Expand Down
23 changes: 14 additions & 9 deletions src/Settler.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {Basic} from "./core/Basic.sol";
import {OtcOrderSettlement} from "./core/OtcOrderSettlement.sol";
import {UniswapV3} from "./core/UniswapV3.sol";
import {UniswapV2} from "./core/UniswapV2.sol";
import {IZeroEx, ZeroEx} from "./core/ZeroEx.sol";
import {IPSM, MakerPSM} from "./core/MakerPSM.sol";

import {SafeTransferLib} from "./utils/SafeTransferLib.sol";
import {UnsafeMath} from "./utils/UnsafeMath.sol";
Expand Down Expand Up @@ -71,7 +71,7 @@ library CalldataDecoder {
}
}

contract Settler is Permit2Payment, Basic, OtcOrderSettlement, UniswapV3, UniswapV2, ZeroEx, FreeMemory {
contract Settler is Permit2Payment, Basic, OtcOrderSettlement, UniswapV3, UniswapV2, MakerPSM, FreeMemory {
using SafeTransferLib for ERC20;
using SafeTransferLib for address payable;
using UnsafeMath for uint256;
Expand All @@ -94,13 +94,13 @@ contract Settler is Permit2Payment, Basic, OtcOrderSettlement, UniswapV3, Uniswa

receive() external payable {}

constructor(address permit2, address zeroEx, address uniFactory, bytes32 poolInitCodeHash, address feeRecipient)
constructor(address permit2, address uniFactory, bytes32 poolInitCodeHash, address dai, address feeRecipient)
Permit2Payment(permit2, feeRecipient)
Basic()
OtcOrderSettlement()
UniswapV3(uniFactory, poolInitCodeHash)
UniswapV2()
ZeroEx(zeroEx)
MakerPSM(dai)
{
assert(ACTIONS_AND_SLIPPAGE_TYPEHASH == keccak256(bytes(ACTIONS_AND_SLIPPAGE_TYPE)));
}
Expand Down Expand Up @@ -339,6 +339,16 @@ contract Settler is Permit2Payment, Basic, OtcOrderSettlement, UniswapV3, Uniswa
abi.decode(data, (address, uint256, uint256, bytes));

sellToUniswapV2(path, bips, amountOutMin, recipient);
} else if (action == ISettlerActions.MAKER_PSM_SELL_GEM.selector) {
(address recipient, uint256 bips, IPSM psm, ERC20 gemToken) =
abi.decode(data, (address, uint256, IPSM, ERC20));

makerPsmSellGem(recipient, bips, psm, gemToken);
} else if (action == ISettlerActions.MAKER_PSM_BUY_GEM.selector) {
(address recipient, uint256 bips, IPSM psm, ERC20 gemToken) =
abi.decode(data, (address, uint256, IPSM, ERC20));

makerPsmBuyGem(recipient, bips, psm, gemToken);
} else if (action == ISettlerActions.BASIC_SELL.selector) {
(address pool, ERC20 sellToken, uint256 proportion, uint256 offset, bytes memory _data) =
abi.decode(data, (address, ERC20, uint256, uint256, bytes));
Expand All @@ -361,11 +371,6 @@ contract Settler is Permit2Payment, Basic, OtcOrderSettlement, UniswapV3, Uniswa
}
}
}
} else if (action == ISettlerActions.ZERO_EX_OTC.selector) {
(IZeroEx.OtcOrder memory order, IZeroEx.Signature memory signature, uint256 sellAmount) =
abi.decode(data, (IZeroEx.OtcOrder, IZeroEx.Signature, uint256));

sellTokenForTokenToZeroExOTC(order, signature, sellAmount);
} else {
revert ActionInvalid({i: i, action: action, data: data});
}
Expand Down
62 changes: 62 additions & 0 deletions src/core/MakerPSM.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

import {ERC20} from "solmate/src/tokens/ERC20.sol";

import {SafeTransferLib} from "../utils/SafeTransferLib.sol";
import {UnsafeMath} from "../utils/UnsafeMath.sol";

interface IPSM {
/// @dev Get the fee for selling DAI to USDC in PSM
/// @return tout toll out [wad]
function tout() external view returns (uint256);

/// @dev Get the address of the underlying vault powering PSM
/// @return address of gemJoin contract
function gemJoin() external view returns (address);

/// @dev Sell USDC for DAI
/// @param usr The address of the account trading USDC for DAI.
/// @param gemAmt The amount of USDC to sell in USDC base units
function sellGem(address usr, uint256 gemAmt) external;

/// @dev Buy USDC for DAI
/// @param usr The address of the account trading DAI for USDC
/// @param gemAmt The amount of USDC to buy in USDC base units
function buyGem(address usr, uint256 gemAmt) external;
}

abstract contract MakerPSM {
using UnsafeMath for uint256;
using SafeTransferLib for ERC20;

// Maker units https://github.com/makerdao/dss/blob/master/DEVELOPING.md
// wad: fixed point decimal with 18 decimals (for basic quantities, e.g. balances)
uint256 internal constant WAD = 10 ** 18;

ERC20 internal immutable DAI;

constructor(address dai) {
DAI = ERC20(dai);
}

function makerPsmSellGem(address recipient, uint256 bips, IPSM psm, ERC20 gemToken) internal {
// phantom overflow can't happen here because PSM prohibits gemToken with decimals > 18
uint256 sellAmount = (gemToken.balanceOf(address(this)) * bips).unsafeDiv(10_000);
gemToken.safeApproveIfBelow(psm.gemJoin(), sellAmount);
psm.sellGem(recipient, sellAmount);
}

function makerPsmBuyGem(address recipient, uint256 bips, IPSM psm, ERC20 gemToken) internal {
// phantom overflow can't happen here because DAI has decimals = 18
uint256 sellAmount = (DAI.balanceOf(address(this)) * bips).unsafeDiv(10_000);
unchecked {
uint256 feeDivisor = psm.tout() + WAD; // eg. 1.001 * 10 ** 18 with 0.1% fee [tout is in wad];
// overflow can't happen at all because DAI is reasonable and PSM prohibits gemToken with decimals > 18
uint256 buyAmount = (sellAmount * 10 ** uint256(gemToken.decimals())).unsafeDiv(feeDivisor);

DAI.safeApproveIfBelow(address(psm), sellAmount);
psm.buyGem(recipient, buyAmount);
}
}
}
Loading