Skip to content

Commit

Permalink
use state vars instead of mapping
Browse files Browse the repository at this point in the history
  • Loading branch information
0xfuturistic committed Aug 31, 2024
1 parent 51e7db6 commit 366541f
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 25 deletions.
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# 🥪🦄 Sandwich-Resistant Uniswap V2
# 🥪🦄 Sandwich-Mitigating Uniswap V2

<img src="unicorn.png" width="46%">

> **Background:** Matheus V. X. Ferreira and David C. Parkes. _Credible Decentralized Exchange Design via Verifiable Sequencing Rules._ URL: https://arxiv.org/pdf/2209.15569.
Uniswap V2 is minimally modified to implement a verifiable sequencing rule that mitigates sandwich attacks, the Greedy Sequencing Rule (GSR).
Uniswap V2 is minimally modified to enforce a verifiable sequencing rule that makes sandwich attacks unprofitable. This approach preserves atomic composability and requires no additional infrastructure or off-chain computation.

## The Greedy Sequencing Rule (GSR)

Expand Down Expand Up @@ -46,42 +46,43 @@ This implementation modifies Uniswap V2's smart contracts to enforce the GSR rul
The key changes are in [`UniswapV2Pair`](src/UniswapV2Pair.sol)'s swap function, adding to it only 16 lines of code (uncommented). [`SwapType`](#swaptype-enum) and [`SequencingRuleInfo`](#sequencingruleinfo-struct) are defined in the [Appendix](#appendix). If a swap violates the GSR, the transaction reverts.

```solidity
SequencingRuleInfo public sequencingRuleInfo;
uint256 private lastSequencedBlock;
uint112 private blockPriceStart;
uint8 private blockTailSwapType;
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
// ... existing swap logic ...
SequencingRuleInfo storage sequencingRuleInfo = blockSequencingRuleInfo[block.number];
// compute the current price with 1e6 decimals (1e18 can easily overflow)
uint112 price = (_reserve1 * 1e6) / _reserve0;
// check if the sequencing rule info has been initialized for this block
if (sequencingRuleInfo.priceStart == 0) {
if (block.number != lastSequencedBlock) {
// if not, initialize it with the current price as the start price
sequencingRuleInfo.priceStart = price;
lastSequencedBlock = block.number;
blockPriceStart = price;
} else {
// Determine if this is a buy or sell swap
uint8 swapType = amount1Out > 0 ? 1 : 2; // 1 for buy, 2 for sell
if (sequencingRuleInfo.tailSwapType != 0) {
if (blockTailSwapType != 0) {
// We've entered the "tail" of the ordering (Definition 5.2).
// In the tail, all remaining swaps must be of the same type (Lemma 5.1).
// This occurs when we've run out of either buy or sell orders.
// The tailSwapType represents the type of swaps in the tail.
require(swapType == sequencingRuleInfo.tailSwapType, "UniswapV2: VIOLATES_GSR");
require(swapType == blockTailSwapType, "UniswapV2: VIOLATES_GSR");
} else {
// Determine the required swap type based on current reserves
// This implements the core logic of the Greedy Sequencing Rule
uint8 swapTypeExpected = price < sequencingRuleInfo.priceStart ? 1 : 2;
uint8 swapTypeExpected = price < blockPriceStart ? 1 : 2;
if (swapType != swapTypeExpected) {
// If the swap type doesn't match the required type, we've run out of one type of order
// This means we're entering the tail of the ordering
// The tail swap type is set to the current swap type
// All subsequent swaps must be of this type
sequencingRuleInfo.tailSwapType = swapType;
blockTailSwapType = swapType;
}
}
}
Expand Down
25 changes: 11 additions & 14 deletions src/UniswapV2Pair.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,6 @@ contract UniswapV2Pair is UniswapV2ERC20 {
using SafeMath for uint256;
using UQ112x112 for uint224;

struct SequencingRuleInfo {
uint112 priceStart;
uint8 tailSwapType; // 0 for none, 1 for buy, 2 for sell
}

uint256 public constant MINIMUM_LIQUIDITY = 10 ** 3;
bytes4 private constant SELECTOR = bytes4(keccak256(bytes("transfer(address,uint256)")));

Expand All @@ -35,7 +30,9 @@ contract UniswapV2Pair is UniswapV2ERC20 {

uint256 private unlocked = 1;

mapping(uint256 blockNumber => SequencingRuleInfo) public blockSequencingRuleInfo;
uint256 public lastSequencedBlock;
uint112 public blockPriceStart;
uint8 public blockTailSwapType; // 0 for none, 1 for buy, 2 for sell

modifier lock() {
require(unlocked == 1, "UniswapV2: LOCKED");
Expand Down Expand Up @@ -203,18 +200,18 @@ contract UniswapV2Pair is UniswapV2ERC20 {
/// SANDWICH ATTACK MITIGATION LOGIC ///
////////////////////////////////////////////////////////////////////////////////////////////////

SequencingRuleInfo storage sequencingRuleInfo = blockSequencingRuleInfo[block.number];
uint112 price = (_reserve1 * 1e6) / _reserve0;
if (sequencingRuleInfo.priceStart == 0) {
sequencingRuleInfo.priceStart = price;
if (block.number != lastSequencedBlock) {
lastSequencedBlock = block.number;
blockPriceStart = price;
} else {
uint8 swapType = amount1Out > 0 ? 1 : 2;
if (sequencingRuleInfo.tailSwapType != 0) {
require(swapType == sequencingRuleInfo.tailSwapType, "UniswapV2: VIOLATES_GSR");
uint8 swapType = amount1Out > 0 ? 1 : 2; // 1 for buy, 2 for sell
if (blockTailSwapType != 0) {
require(swapType == blockTailSwapType, "UniswapV2: VIOLATES_GSR");
} else {
uint8 swapTypeExpected = price < sequencingRuleInfo.priceStart ? 1 : 2;
uint8 swapTypeExpected = price < blockPriceStart ? 1 : 2;
if (swapType != swapTypeExpected) {
sequencingRuleInfo.tailSwapType = swapType;
blockTailSwapType = swapType;
}
}
}
Expand Down

0 comments on commit 366541f

Please sign in to comment.