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

Take portion #250

Merged
merged 7 commits into from
Aug 2, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134171
134175
Original file line number Diff line number Diff line change
@@ -1 +1 @@
126966
126970
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_Bytecode.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
6942
7157
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
120501
120513
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
119696
119708
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
128568
128580
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
135398
135410
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn2Hops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
186897
186917
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
178832
178852
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn3Hops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
238421
238449
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
230380
230408
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactInputSingle.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134171
134175
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
119274
119278
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
118447
118451
Original file line number Diff line number Diff line change
@@ -1 +1 @@
126298
126310
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
120531
120543
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
129403
129415
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134204
134216
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut2Hops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
186306
186326
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
183201
183221
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut3Hops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
238448
238476
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
235367
235395
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
229600
229628
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOutputSingle.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
132975
132979
Original file line number Diff line number Diff line change
@@ -1 +1 @@
125069
125073
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
119360
119364
5 changes: 5 additions & 0 deletions src/V4Router.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol";

import {PathKey, PathKeyLib} from "./libraries/PathKey.sol";
import {CalldataDecoder} from "./libraries/CalldataDecoder.sol";
import {BipsLibrary} from "./libraries/BipsLibrary.sol";
import {IV4Router} from "./interfaces/IV4Router.sol";
import {BaseActionsRouter} from "./base/BaseActionsRouter.sol";
import {DeltaResolver} from "./base/DeltaResolver.sol";
Expand All @@ -23,6 +24,7 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver {
using SafeCastTemp for *;
using PathKeyLib for PathKey;
using CalldataDecoder for bytes;
using BipsLibrary for uint256;

constructor(IPoolManager _poolManager) BaseActionsRouter(_poolManager) {}

Expand Down Expand Up @@ -60,6 +62,9 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver {
// TODO should _take have a minAmountOut added slippage check?
// TODO recipient mapping
_take(currency, recipient, amount);
} else if (action == Actions.TAKE_PORTION) {
(Currency currency, address recipient, uint256 bips) = params.decodeCurrencyAddressAndUint256();
_take(currency, recipient, _getFullTakeAmount(currency).calculatePortion(bips));
} else {
revert UnsupportedAction(action);
}
Expand Down
17 changes: 17 additions & 0 deletions src/libraries/BipsLibrary.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.19;

/// @title For calculating a percentage of an amount, using bips
library BipsLibrary {
uint256 internal constant BIPS_BASE = 10_000;

/// @notice emitted when an invalid percentage is provided
error InvalidBips();

/// @param amount The total amount to calculate a percentage of
/// @param bips The percentage to calculate, in bips
function calculatePortion(uint256 amount, uint256 bips) internal pure returns (uint256) {
if (bips > BIPS_BASE) revert InvalidBips();
return (amount * bips) / BIPS_BASE;
}
}
13 changes: 13 additions & 0 deletions src/libraries/CalldataDecoder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,19 @@ library CalldataDecoder {
}
}

/// @dev equivalent to: abi.decode(params, (Currency, address, uint256)) in calldata
function decodeCurrencyAddressAndUint256(bytes calldata params)
internal
pure
returns (Currency currency, address _address, uint256 amount)
{
assembly ("memory-safe") {
gretzke marked this conversation as resolved.
Show resolved Hide resolved
currency := calldataload(params.offset)
_address := calldataload(add(params.offset, 0x20))
amount := calldataload(add(params.offset, 0x40))
}
}

/// @dev equivalent to: abi.decode(params, (Currency, uint256)) in calldata
function decodeCurrencyAndUint256(bytes calldata params)
internal
Expand Down
20 changes: 20 additions & 0 deletions test/libraries/BipsLibrary.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import "forge-std/Test.sol";
import "forge-std/StdError.sol";
import {BipsLibrary} from "../../src/libraries/BipsLibrary.sol";

contract PositionConfigTest is Test {
using BipsLibrary for uint256;

function test_fuzz_calculatePortion(uint256 amount, uint256 bips) public {
amount = bound(amount, 0, uint256(type(uint128).max));
if (bips > 10000) {
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
vm.expectRevert(BipsLibrary.InvalidBips.selector);
amount.calculatePortion(bips);
} else {
assertEq(amount.calculatePortion(bips), amount * bips / 10000);
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
}
}
}
58 changes: 58 additions & 0 deletions test/router/Payments.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,14 @@ import {IV4Router} from "../../src/interfaces/IV4Router.sol";
import {RoutingTestHelpers} from "../shared/RoutingTestHelpers.sol";
import {Plan, Planner} from "../shared/Planner.sol";
import {Actions} from "../../src/libraries/Actions.sol";
import {BipsLibrary} from "../../src/libraries/BipsLibrary.sol";

contract PaymentsTests is RoutingTestHelpers, GasSnapshot {
using CurrencyLibrary for Currency;
using Planner for Plan;

address bob = makeAddr("BOB");

function setUp() public {
setupRouterCurrenciesAndPoolsWithLiquidity();
plan = Planner.init();
Expand Down Expand Up @@ -81,4 +84,59 @@ contract PaymentsTests is RoutingTestHelpers, GasSnapshot {
assertEq(inputBalanceBefore, inputBalanceAfter);
assertEq(outputBalanceAfter - outputBalanceBefore, expectedAmountOut);
}

function test_settle_takePortion_takeAll() public {
uint256 amountIn = 1 ether;
uint256 expectedAmountOut = 992054607780215625;
IV4Router.ExactInputSingleParams memory params =
IV4Router.ExactInputSingleParams(key0, true, uint128(amountIn), 0, 0, bytes(""));

plan = plan.add(Actions.SWAP_EXACT_IN_SINGLE, abi.encode(params));
plan = plan.add(Actions.SETTLE_ALL, abi.encode(key0.currency0));
// take 15 bips to Bob
plan = plan.add(Actions.TAKE_PORTION, abi.encode(key0.currency1, bob, 15));
plan = plan.add(Actions.TAKE_ALL, abi.encode(key0.currency1, address(this)));

uint256 inputBalanceBefore = key0.currency0.balanceOfSelf();
uint256 outputBalanceBefore = key0.currency1.balanceOfSelf();
uint256 bobBalanceBefore = key0.currency1.balanceOf(bob);

// router is empty before
assertEq(currency0.balanceOf(address(router)), 0);
assertEq(currency1.balanceOf(address(router)), 0);

bytes memory data = plan.encode();
router.executeActions(data);

uint256 inputBalanceAfter = key0.currency0.balanceOfSelf();
uint256 outputBalanceAfter = key0.currency1.balanceOfSelf();
uint256 bobBalanceAfter = key0.currency1.balanceOf(bob);

uint256 expectedFee = expectedAmountOut * 15 / 10000;
hensha256 marked this conversation as resolved.
Show resolved Hide resolved

// router is empty
assertEq(currency0.balanceOf(address(router)), 0);
assertEq(currency1.balanceOf(address(router)), 0);
// Bob got expectedFee, and the caller got the rest of the output
assertEq(inputBalanceBefore - inputBalanceAfter, amountIn);
assertEq(outputBalanceAfter - outputBalanceBefore, expectedAmountOut - expectedFee);
assertEq(bobBalanceAfter - bobBalanceBefore, expectedFee);
}

function test_settle_takePortion_reverts() public {
uint256 amountIn = 1 ether;
IV4Router.ExactInputSingleParams memory params =
IV4Router.ExactInputSingleParams(key0, true, uint128(amountIn), 0, 0, bytes(""));

plan = plan.add(Actions.SWAP_EXACT_IN_SINGLE, abi.encode(params));
plan = plan.add(Actions.SETTLE_ALL, abi.encode(key0.currency0));
// bips is larger than maximum bips
plan = plan.add(Actions.TAKE_PORTION, abi.encode(key0.currency1, bob, 10001));
hensha256 marked this conversation as resolved.
Show resolved Hide resolved
plan = plan.add(Actions.TAKE_ALL, abi.encode(key0.currency1, address(this)));

bytes memory data = plan.encode();

vm.expectRevert(BipsLibrary.InvalidBips.selector);
router.executeActions(data);
}
}
Loading