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 all 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 @@
134122
134126
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134260
134264
Original file line number Diff line number Diff line change
@@ -1 +1 @@
126917
126921
Original file line number Diff line number Diff line change
@@ -1 +1 @@
127055
127059
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_Bytecode.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
7119
7342
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn1Hop_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
120452
120464
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn1Hop_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
119647
119659
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn1Hop_oneForZero.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
128519
128531
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn1Hop_zeroForOne.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
135349
135361
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn2Hops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
186848
186868
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn2Hops_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
178783
178803
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn3Hops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
238372
238400
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactIn3Hops_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
230331
230359
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactInputSingle.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134122
134126
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactInputSingle_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
119225
119229
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactInputSingle_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
118398
118402
Original file line number Diff line number Diff line change
@@ -1 +1 @@
126249
126261
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut1Hop_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
120482
120494
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut1Hop_oneForZero.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
129354
129366
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut1Hop_zeroForOne.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
134155
134167
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut2Hops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
186257
186277
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut2Hops_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
183152
183172
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut3Hops.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
238399
238427
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut3Hops_nativeIn.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
235318
235346
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOut3Hops_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
229551
229579
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOutputSingle.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
132926
132930
Original file line number Diff line number Diff line change
@@ -1 +1 @@
125020
125024
2 changes: 1 addition & 1 deletion .forge-snapshots/V4Router_ExactOutputSingle_nativeOut.snap
Original file line number Diff line number Diff line change
@@ -1 +1 @@
119311
119315
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 @@ -59,6 +61,9 @@ abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver {

// TODO should _take have a minAmountOut added slippage check?
_take(currency, _map(recipient), amount);
} else if (action == Actions.TAKE_PORTION) {
(Currency currency, address recipient, uint256 bips) = params.decodeCurrencyAddressAndUint256();
_take(currency, _map(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 > BipsLibrary.BIPS_BASE) {
vm.expectRevert(BipsLibrary.InvalidBips.selector);
amount.calculatePortion(bips);
} else {
assertEq(amount.calculatePortion(bips), amount * bips / BipsLibrary.BIPS_BASE);
}
}
}
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 / BipsLibrary.BIPS_BASE;

// 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, BipsLibrary.BIPS_BASE + 1));
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