Skip to content
This repository was archived by the owner on Oct 24, 2025. It is now read-only.
Open
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
8 changes: 5 additions & 3 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,15 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: bullfrogsec/bullfrog@1831f79cce8ad602eef14d2163873f27081ebfb3 # v0.8.4

- name: Check out Git repository
uses: actions/checkout@v2
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0

- name: Set up node
uses: actions/setup-node@v1
uses: actions/setup-node@f1f314fca9dfce2769ece7d933488f076716723e # v1.4.6
with:
node-version: 12.x
node-version: 18.x
registry-url: https://registry.npmjs.org

- name: Install dependencies
Expand Down
37 changes: 19 additions & 18 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,37 @@ jobs:
runs-on:
group: npm-deploy
steps:
- name: Load Secrets
uses: 1password/load-secrets-action@581a835fb51b8e7ec56b71cf2ffddd7e68bb25e0
with:
# Export loaded secrets as environment variables
export-env: true
env:
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN }}
NPM_TOKEN: op://npm-deploy/npm-runner-token/secret
- uses: bullfrogsec/bullfrog@1831f79cce8ad602eef14d2163873f27081ebfb3 # v0.8.4

- name: Checkout
uses: actions/checkout@v4
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
with:
submodules: "true"
fetch-depth: 2

- name: Setup Node
uses: actions/setup-node@v4
uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
Comment thread
dgilmanuni marked this conversation as resolved.
with:
node-version: 18
node-version: 20.x
registry-url: 'https://registry.npmjs.org'
# Defaults to the user or organization that owns the workflow file
scope: '@uniswap'

- name: Setup CI
run: npm ci
- id: yarn-cache
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT

- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: yarn-${{ hashFiles('**/yarn.lock') }}
restore-keys: |
yarn-

- name: Install dependencies
run: yarn install --frozen-lockfile

- name: Build
run: npm run build
- name: Compile
run: yarn compile

- name: Publish
run: npm publish
env:
NODE_AUTH_TOKEN: ${{ env.NPM_TOKEN }}
run: yarn publish
12 changes: 7 additions & 5 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,18 @@ jobs:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v1
- uses: actions/setup-node@v1
- uses: bullfrogsec/bullfrog@1831f79cce8ad602eef14d2163873f27081ebfb3 # v0.8.4

- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
- uses: actions/setup-node@1e60f620b9541d16bece96c5465dc8ee9832be0b # v4.0.3
Comment thread
dgilmanuni marked this conversation as resolved.
with:
node-version: 12.x
node-version: 18.x
registry-url: https://registry.npmjs.org

- id: yarn-cache
run: echo "::set-output name=dir::$(yarn cache dir)"
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT

- uses: actions/cache@v1
- uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0
Comment thread
dgilmanuni marked this conversation as resolved.
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: yarn-${{ hashFiles('**/yarn.lock') }}
Expand Down
1 change: 0 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ contract MyContract {
// router.exactInput(...);
}
}

```

## Tests
Expand Down
10 changes: 6 additions & 4 deletions contracts/V2SwapRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,15 @@ abstract contract V2SwapRouter is IV2SwapRouter, ImmutableState, PeripheryPaymen
// scope to avoid stack too deep errors
{
(uint256 reserve0, uint256 reserve1, ) = pair.getReserves();
(uint256 reserveInput, uint256 reserveOutput) =
input == token0 ? (reserve0, reserve1) : (reserve1, reserve0);
(uint256 reserveInput, uint256 reserveOutput) = input == token0
? (reserve0, reserve1)
: (reserve1, reserve0);
amountInput = IERC20(input).balanceOf(address(pair)).sub(reserveInput);
amountOutput = UniswapV2Library.getAmountOut(amountInput, reserveInput, reserveOutput);
}
(uint256 amount0Out, uint256 amount1Out) =
input == token0 ? (uint256(0), amountOutput) : (amountOutput, uint256(0));
(uint256 amount0Out, uint256 amount1Out) = input == token0
? (uint256(0), amountOutput)
: (amountOutput, uint256(0));
address to = i < path.length - 2 ? UniswapV2Library.pairFor(factoryV2, output, path[i + 2]) : _to;
pair.swap(amount0Out, amount1Out, to, new bytes(0));
}
Expand Down
75 changes: 29 additions & 46 deletions contracts/V3SwapRouter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,7 @@ abstract contract V3SwapRouter is IV3SwapRouter, PeripheryPaymentsWithFeeExtende
uint256 private amountInCached = DEFAULT_AMOUNT_IN_CACHED;

/// @dev Returns the pool for the given token pair and fee. The pool contract may or may not exist.
function getPool(
address tokenA,
address tokenB,
uint24 fee
) private view returns (IUniswapV3Pool) {
function getPool(address tokenA, address tokenB, uint24 fee) private view returns (IUniswapV3Pool) {
return IUniswapV3Pool(PoolAddress.computeAddress(factory, PoolAddress.getPoolKey(tokenA, tokenB, fee)));
}

Expand All @@ -43,20 +39,15 @@ abstract contract V3SwapRouter is IV3SwapRouter, PeripheryPaymentsWithFeeExtende
}

/// @inheritdoc IUniswapV3SwapCallback
function uniswapV3SwapCallback(
int256 amount0Delta,
int256 amount1Delta,
bytes calldata _data
) external override {
function uniswapV3SwapCallback(int256 amount0Delta, int256 amount1Delta, bytes calldata _data) external override {
require(amount0Delta > 0 || amount1Delta > 0); // swaps entirely within 0-liquidity regions are not supported
SwapCallbackData memory data = abi.decode(_data, (SwapCallbackData));
(address tokenIn, address tokenOut, uint24 fee) = data.path.decodeFirstPool();
CallbackValidation.verifyCallback(factory, tokenIn, tokenOut, fee);

(bool isExactInput, uint256 amountToPay) =
amount0Delta > 0
? (tokenIn < tokenOut, uint256(amount0Delta))
: (tokenOut < tokenIn, uint256(amount1Delta));
(bool isExactInput, uint256 amountToPay) = amount0Delta > 0
? (tokenIn < tokenOut, uint256(amount0Delta))
: (tokenOut < tokenIn, uint256(amount1Delta));

if (isExactInput) {
pay(tokenIn, data.payer, msg.sender, amountToPay);
Expand Down Expand Up @@ -88,27 +79,23 @@ abstract contract V3SwapRouter is IV3SwapRouter, PeripheryPaymentsWithFeeExtende

bool zeroForOne = tokenIn < tokenOut;

(int256 amount0, int256 amount1) =
getPool(tokenIn, tokenOut, fee).swap(
recipient,
zeroForOne,
amountIn.toInt256(),
sqrtPriceLimitX96 == 0
? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1)
: sqrtPriceLimitX96,
abi.encode(data)
);
(int256 amount0, int256 amount1) = getPool(tokenIn, tokenOut, fee).swap(
recipient,
zeroForOne,
amountIn.toInt256(),
sqrtPriceLimitX96 == 0
? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1)
: sqrtPriceLimitX96,
abi.encode(data)
);

return uint256(-(zeroForOne ? amount1 : amount0));
}

/// @inheritdoc IV3SwapRouter
function exactInputSingle(ExactInputSingleParams memory params)
external
payable
override
returns (uint256 amountOut)
{
function exactInputSingle(
ExactInputSingleParams memory params
) external payable override returns (uint256 amountOut) {
// use amountIn == Constants.CONTRACT_BALANCE as a flag to swap the entire balance of the contract
bool hasAlreadyPaid;
if (params.amountIn == Constants.CONTRACT_BALANCE) {
Expand Down Expand Up @@ -182,16 +169,15 @@ abstract contract V3SwapRouter is IV3SwapRouter, PeripheryPaymentsWithFeeExtende

bool zeroForOne = tokenIn < tokenOut;

(int256 amount0Delta, int256 amount1Delta) =
getPool(tokenIn, tokenOut, fee).swap(
recipient,
zeroForOne,
-amountOut.toInt256(),
sqrtPriceLimitX96 == 0
? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1)
: sqrtPriceLimitX96,
abi.encode(data)
);
(int256 amount0Delta, int256 amount1Delta) = getPool(tokenIn, tokenOut, fee).swap(
recipient,
zeroForOne,
-amountOut.toInt256(),
sqrtPriceLimitX96 == 0
? (zeroForOne ? TickMath.MIN_SQRT_RATIO + 1 : TickMath.MAX_SQRT_RATIO - 1)
: sqrtPriceLimitX96,
abi.encode(data)
);

uint256 amountOutReceived;
(amountIn, amountOutReceived) = zeroForOne
Expand All @@ -203,12 +189,9 @@ abstract contract V3SwapRouter is IV3SwapRouter, PeripheryPaymentsWithFeeExtende
}

/// @inheritdoc IV3SwapRouter
function exactOutputSingle(ExactOutputSingleParams calldata params)
external
payable
override
returns (uint256 amountIn)
{
function exactOutputSingle(
ExactOutputSingleParams calldata params
) external payable override returns (uint256 amountIn) {
// avoid an SLOAD by using the swap return data
amountIn = exactOutputInternal(
params.amountOut,
Expand Down
14 changes: 6 additions & 8 deletions contracts/base/ApproveAndCall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,9 @@ import './ImmutableState.sol';
/// for any token, and then make calls into the position manager
abstract contract ApproveAndCall is IApproveAndCall, ImmutableState {
function tryApprove(address token, uint256 amount) private returns (bool) {
(bool success, bytes memory data) =
token.call(abi.encodeWithSelector(IERC20.approve.selector, positionManager, amount));
(bool success, bytes memory data) = token.call(
abi.encodeWithSelector(IERC20.approve.selector, positionManager, amount)
);
return success && (data.length == 0 || abi.decode(data, (bool)));
}

Expand Down Expand Up @@ -102,12 +103,9 @@ abstract contract ApproveAndCall is IApproveAndCall, ImmutableState {
}

/// @inheritdoc IApproveAndCall
function increaseLiquidity(IncreaseLiquidityParams calldata params)
external
payable
override
returns (bytes memory result)
{
function increaseLiquidity(
IncreaseLiquidityParams calldata params
) external payable override returns (bytes memory result) {
return
callPositionManager(
abi.encodeWithSelector(
Expand Down
22 changes: 8 additions & 14 deletions contracts/base/MulticallExtended.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,18 @@ import '../base/PeripheryValidationExtended.sol';
/// @notice Enables calling multiple methods in a single call to the contract
abstract contract MulticallExtended is IMulticallExtended, Multicall, PeripheryValidationExtended {
/// @inheritdoc IMulticallExtended
function multicall(uint256 deadline, bytes[] calldata data)
external
payable
override
checkDeadline(deadline)
returns (bytes[] memory)
{
function multicall(
uint256 deadline,
bytes[] calldata data
) external payable override checkDeadline(deadline) returns (bytes[] memory) {
return multicall(data);
}

/// @inheritdoc IMulticallExtended
function multicall(bytes32 previousBlockhash, bytes[] calldata data)
external
payable
override
checkPreviousBlockhash(previousBlockhash)
returns (bytes[] memory)
{
function multicall(
bytes32 previousBlockhash,
bytes[] calldata data
) external payable override checkPreviousBlockhash(previousBlockhash) returns (bytes[] memory) {
return multicall(data);
}
}
39 changes: 21 additions & 18 deletions contracts/base/OracleSlippage.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,9 @@ abstract contract OracleSlippage is IOracleSlippage, PeripheryImmutableState, Bl
using Path for bytes;

/// @dev Returns the tick as of the beginning of the current block, and as of right now, for the given pool.
function getBlockStartingAndCurrentTick(IUniswapV3Pool pool)
internal
view
returns (int24 blockStartingTick, int24 currentTick)
{
function getBlockStartingAndCurrentTick(
IUniswapV3Pool pool
) internal view returns (int24 blockStartingTick, int24 currentTick) {
uint16 observationIndex;
uint16 observationCardinality;
(, currentTick, observationIndex, observationCardinality, , , ) = pool.slot0();
Expand All @@ -35,8 +33,9 @@ abstract contract OracleSlippage is IOracleSlippage, PeripheryImmutableState, Bl
blockStartingTick = currentTick;
} else {
uint256 prevIndex = (uint256(observationIndex) + observationCardinality - 1) % observationCardinality;
(uint32 prevObservationTimestamp, int56 prevTickCumulative, , bool prevInitialized) =
pool.observations(prevIndex);
(uint32 prevObservationTimestamp, int56 prevTickCumulative, , bool prevInitialized) = pool.observations(
prevIndex
);

require(prevInitialized, 'ONI');

Expand All @@ -57,11 +56,10 @@ abstract contract OracleSlippage is IOracleSlippage, PeripheryImmutableState, Bl
/// @dev Returns the synthetic time-weighted average tick as of secondsAgo, as well as the current tick,
/// for the given path. Returned synthetic ticks always represent tokenOut/tokenIn prices,
/// meaning lower ticks are worse.
function getSyntheticTicks(bytes memory path, uint32 secondsAgo)
internal
view
returns (int256 syntheticAverageTick, int256 syntheticCurrentTick)
{
function getSyntheticTicks(
bytes memory path,
uint32 secondsAgo
) internal view returns (int256 syntheticAverageTick, int256 syntheticCurrentTick) {
bool lowerTicksAreWorse;

uint256 numPools = path.numPools();
Expand Down Expand Up @@ -130,10 +128,12 @@ abstract contract OracleSlippage is IOracleSlippage, PeripheryImmutableState, Bl
) internal view returns (int256 averageSyntheticAverageTick, int256 averageSyntheticCurrentTick) {
require(paths.length == amounts.length);

OracleLibrary.WeightedTickData[] memory weightedSyntheticAverageTicks =
new OracleLibrary.WeightedTickData[](paths.length);
OracleLibrary.WeightedTickData[] memory weightedSyntheticCurrentTicks =
new OracleLibrary.WeightedTickData[](paths.length);
OracleLibrary.WeightedTickData[] memory weightedSyntheticAverageTicks = new OracleLibrary.WeightedTickData[](
paths.length
);
OracleLibrary.WeightedTickData[] memory weightedSyntheticCurrentTicks = new OracleLibrary.WeightedTickData[](
paths.length
);

for (uint256 i = 0; i < paths.length; i++) {
(int256 syntheticAverageTick, int256 syntheticCurrentTick) = getSyntheticTicks(paths[i], secondsAgo);
Expand Down Expand Up @@ -164,8 +164,11 @@ abstract contract OracleSlippage is IOracleSlippage, PeripheryImmutableState, Bl
uint24 maximumTickDivergence,
uint32 secondsAgo
) external view override {
(int256 averageSyntheticAverageTick, int256 averageSyntheticCurrentTick) =
getSyntheticTicks(paths, amounts, secondsAgo);
(int256 averageSyntheticAverageTick, int256 averageSyntheticCurrentTick) = getSyntheticTicks(
paths,
amounts,
secondsAgo
);
require(averageSyntheticAverageTick - averageSyntheticCurrentTick < maximumTickDivergence, 'TD');
}
}
8 changes: 7 additions & 1 deletion contracts/interfaces/IApproveAndCall.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,13 @@ pragma solidity =0.7.6;
pragma abicoder v2;

interface IApproveAndCall {
enum ApprovalType {NOT_REQUIRED, MAX, MAX_MINUS_ONE, ZERO_THEN_MAX, ZERO_THEN_MAX_MINUS_ONE}
enum ApprovalType {
NOT_REQUIRED,
MAX,
MAX_MINUS_ONE,
ZERO_THEN_MAX,
ZERO_THEN_MAX_MINUS_ONE
}

/// @dev Lens to be called off-chain to determine which (if any) of the relevant approval functions should be called
/// @param token The token to approve
Expand Down
Loading
Loading