feat: sample uniswap connector on arbitrum

This commit is contained in:
pradyuman-verma 2021-11-19 02:04:28 +05:30
parent 07f8fd07ad
commit 4736e8dadc
No known key found for this signature in database
GPG Key ID: 03CEE9087D1D5E4C
20 changed files with 2060 additions and 0 deletions

View File

@ -0,0 +1,94 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title BitMath
/// @dev This library provides functionality for computing bit properties of an unsigned integer
library BitMath {
/// @notice Returns the index of the most significant bit of the number,
/// where the least significant bit is at index 0 and the most significant bit is at index 255
/// @dev The function satisfies the property:
/// x >= 2**mostSignificantBit(x) and x < 2**(mostSignificantBit(x)+1)
/// @param x the value for which to compute the most significant bit, must be greater than 0
/// @return r the index of the most significant bit
function mostSignificantBit(uint256 x) internal pure returns (uint8 r) {
require(x > 0);
if (x >= 0x100000000000000000000000000000000) {
x >>= 128;
r += 128;
}
if (x >= 0x10000000000000000) {
x >>= 64;
r += 64;
}
if (x >= 0x100000000) {
x >>= 32;
r += 32;
}
if (x >= 0x10000) {
x >>= 16;
r += 16;
}
if (x >= 0x100) {
x >>= 8;
r += 8;
}
if (x >= 0x10) {
x >>= 4;
r += 4;
}
if (x >= 0x4) {
x >>= 2;
r += 2;
}
if (x >= 0x2) r += 1;
}
/// @notice Returns the index of the least significant bit of the number,
/// where the least significant bit is at index 0 and the most significant bit is at index 255
/// @dev The function satisfies the property:
/// (x & 2**leastSignificantBit(x)) != 0 and (x & (2**(leastSignificantBit(x)) - 1)) == 0)
/// @param x the value for which to compute the least significant bit, must be greater than 0
/// @return r the index of the least significant bit
function leastSignificantBit(uint256 x) internal pure returns (uint8 r) {
require(x > 0);
r = 255;
if (x & type(uint128).max > 0) {
r -= 128;
} else {
x >>= 128;
}
if (x & type(uint64).max > 0) {
r -= 64;
} else {
x >>= 64;
}
if (x & type(uint32).max > 0) {
r -= 32;
} else {
x >>= 32;
}
if (x & type(uint16).max > 0) {
r -= 16;
} else {
x >>= 16;
}
if (x & type(uint8).max > 0) {
r -= 8;
} else {
x >>= 8;
}
if (x & 0xf > 0) {
r -= 4;
} else {
x >>= 4;
}
if (x & 0x3 > 0) {
r -= 2;
} else {
x >>= 2;
}
if (x & 0x1 > 0) r -= 1;
}
}

View File

@ -0,0 +1,8 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.4.0;
/// @title FixedPoint128
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
library FixedPoint128 {
uint256 internal constant Q128 = 0x100000000000000000000000000000000;
}

View File

@ -0,0 +1,10 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.4.0;
/// @title FixedPoint96
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
/// @dev Used in SqrtPriceMath.sol
library FixedPoint96 {
uint8 internal constant RESOLUTION = 96;
uint256 internal constant Q96 = 0x1000000000000000000000000;
}

View File

@ -0,0 +1,124 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.0;
/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
/// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
/// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
function mulDiv(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
// 512-bit multiply [prod1 prod0] = a * b
// Compute the product mod 2**256 and mod 2**256 - 1
// then use the Chinese Remainder Theorem to reconstruct
// the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2**256 + prod0
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(a, b, not(0))
prod0 := mul(a, b)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division
if (prod1 == 0) {
require(denominator > 0);
assembly {
result := div(prod0, denominator)
}
return result;
}
// Make sure the result is less than 2**256.
// Also prevents denominator == 0
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0]
// Compute remainder using mulmod
uint256 remainder;
assembly {
remainder := mulmod(a, b, denominator)
}
// Subtract 256 bit number from 512 bit number
assembly {
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator
// Compute largest power of two divisor of denominator.
// Always >= 1.
uint256 twos = -denominator & denominator;
// Divide denominator by power of two
assembly {
denominator := div(denominator, twos)
}
// Divide [prod1 prod0] by the factors of two
assembly {
prod0 := div(prod0, twos)
}
// Shift in bits from prod1 into prod0. For this we need
// to flip `twos` such that it is 2**256 / twos.
// If twos is zero, then it becomes one
assembly {
twos := add(div(sub(0, twos), twos), 1)
}
prod0 |= prod1 * twos;
// Invert denominator mod 2**256
// Now that denominator is an odd number, it has an inverse
// modulo 2**256 such that denominator * inv = 1 mod 2**256.
// Compute the inverse by starting with a seed that is correct
// correct for four bits. That is, denominator * inv = 1 mod 2**4
uint256 inv = (3 * denominator) ^ 2;
// Now use Newton-Raphson iteration to improve the precision.
// Thanks to Hensel's lifting lemma, this also works in modular
// arithmetic, doubling the correct bits in each step.
inv *= 2 - denominator * inv; // inverse mod 2**8
inv *= 2 - denominator * inv; // inverse mod 2**16
inv *= 2 - denominator * inv; // inverse mod 2**32
inv *= 2 - denominator * inv; // inverse mod 2**64
inv *= 2 - denominator * inv; // inverse mod 2**128
inv *= 2 - denominator * inv; // inverse mod 2**256
// Because the division is now exact we can divide by multiplying
// with the modular inverse of denominator. This will give us the
// correct result modulo 2**256. Since the precoditions guarantee
// that the outcome is less than 2**256, this is the final result.
// We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inv;
return result;
}
/// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
/// @param a The multiplicand
/// @param b The multiplier
/// @param denominator The divisor
/// @return result The 256-bit result
function mulDivRoundingUp(
uint256 a,
uint256 b,
uint256 denominator
) internal pure returns (uint256 result) {
result = mulDiv(a, b, denominator);
if (mulmod(a, b, denominator) > 0) {
require(result < type(uint256).max);
result++;
}
}
}

View File

@ -0,0 +1,339 @@
GNU GENERAL PUBLIC LICENSE
Version 2, June 1991
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The licenses for most software are designed to take away your
freedom to share and change it. By contrast, the GNU General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. This
General Public License applies to most of the Free Software
Foundation's software and to any other program whose authors commit to
using it. (Some other Free Software Foundation software is covered by
the GNU Lesser General Public License instead.) You can apply it to
your programs, too.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
this service if you wish), that you receive source code or can get it
if you want it, that you can change the software or use pieces of it
in new free programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must show them these terms so they know their
rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
Finally, any free program is threatened constantly by software
patents. We wish to avoid the danger that redistributors of a free
program will individually obtain patent licenses, in effect making the
program proprietary. To prevent this, we have made it clear that any
patent must be licensed for everyone's free use or not licensed at all.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License applies to any program or other work which contains
a notice placed by the copyright holder saying it may be distributed
under the terms of this General Public License. The "Program", below,
refers to any such program or work, and a "work based on the Program"
means either the Program or any derivative work under copyright law:
that is to say, a work containing the Program or a portion of it,
either verbatim or with modifications and/or translated into another
language. (Hereinafter, translation is included without limitation in
the term "modification".) Each licensee is addressed as "you".
Activities other than copying, distribution and modification are not
covered by this License; they are outside its scope. The act of
running the Program is not restricted, and the output from the Program
is covered only if its contents constitute a work based on the
Program (independent of having been made by running the Program).
Whether that is true depends on what the Program does.
1. You may copy and distribute verbatim copies of the Program's
source code as you receive it, in any medium, provided that you
conspicuously and appropriately publish on each copy an appropriate
copyright notice and disclaimer of warranty; keep intact all the
notices that refer to this License and to the absence of any warranty;
and give any other recipients of the Program a copy of this License
along with the Program.
You may charge a fee for the physical act of transferring a copy, and
you may at your option offer warranty protection in exchange for a fee.
2. You may modify your copy or copies of the Program or any portion
of it, thus forming a work based on the Program, and copy and
distribute such modifications or work under the terms of Section 1
above, provided that you also meet all of these conditions:
a) You must cause the modified files to carry prominent notices
stating that you changed the files and the date of any change.
b) You must cause any work that you distribute or publish, that in
whole or in part contains or is derived from the Program or any
part thereof, to be licensed as a whole at no charge to all third
parties under the terms of this License.
c) If the modified program normally reads commands interactively
when run, you must cause it, when started running for such
interactive use in the most ordinary way, to print or display an
announcement including an appropriate copyright notice and a
notice that there is no warranty (or else, saying that you provide
a warranty) and that users may redistribute the program under
these conditions, and telling the user how to view a copy of this
License. (Exception: if the Program itself is interactive but
does not normally print such an announcement, your work based on
the Program is not required to print an announcement.)
These requirements apply to the modified work as a whole. If
identifiable sections of that work are not derived from the Program,
and can be reasonably considered independent and separate works in
themselves, then this License, and its terms, do not apply to those
sections when you distribute them as separate works. But when you
distribute the same sections as part of a whole which is a work based
on the Program, the distribution of the whole must be on the terms of
this License, whose permissions for other licensees extend to the
entire whole, and thus to each and every part regardless of who wrote it.
Thus, it is not the intent of this section to claim rights or contest
your rights to work written entirely by you; rather, the intent is to
exercise the right to control the distribution of derivative or
collective works based on the Program.
In addition, mere aggregation of another work not based on the Program
with the Program (or with a work based on the Program) on a volume of
a storage or distribution medium does not bring the other work under
the scope of this License.
3. You may copy and distribute the Program (or a work based on it,
under Section 2) in object code or executable form under the terms of
Sections 1 and 2 above provided that you also do one of the following:
a) Accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of Sections
1 and 2 above on a medium customarily used for software interchange; or,
b) Accompany it with a written offer, valid for at least three
years, to give any third party, for a charge no more than your
cost of physically performing source distribution, a complete
machine-readable copy of the corresponding source code, to be
distributed under the terms of Sections 1 and 2 above on a medium
customarily used for software interchange; or,
c) Accompany it with the information you received as to the offer
to distribute corresponding source code. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form with such
an offer, in accord with Subsection b above.)
The source code for a work means the preferred form of the work for
making modifications to it. For an executable work, complete source
code means all the source code for all modules it contains, plus any
associated interface definition files, plus the scripts used to
control compilation and installation of the executable. However, as a
special exception, the source code distributed need not include
anything that is normally distributed (in either source or binary
form) with the major components (compiler, kernel, and so on) of the
operating system on which the executable runs, unless that component
itself accompanies the executable.
If distribution of executable or object code is made by offering
access to copy from a designated place, then offering equivalent
access to copy the source code from the same place counts as
distribution of the source code, even though third parties are not
compelled to copy the source along with the object code.
4. You may not copy, modify, sublicense, or distribute the Program
except as expressly provided under this License. Any attempt
otherwise to copy, modify, sublicense or distribute the Program is
void, and will automatically terminate your rights under this License.
However, parties who have received copies, or rights, from you under
this License will not have their licenses terminated so long as such
parties remain in full compliance.
5. You are not required to accept this License, since you have not
signed it. However, nothing else grants you permission to modify or
distribute the Program or its derivative works. These actions are
prohibited by law if you do not accept this License. Therefore, by
modifying or distributing the Program (or any work based on the
Program), you indicate your acceptance of this License to do so, and
all its terms and conditions for copying, distributing or modifying
the Program or works based on it.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the
original licensor to copy, distribute or modify the Program subject to
these terms and conditions. You may not impose any further
restrictions on the recipients' exercise of the rights granted herein.
You are not responsible for enforcing compliance by third parties to
this License.
7. If, as a consequence of a court judgment or allegation of patent
infringement or for any other reason (not limited to patent issues),
conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot
distribute so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you
may not distribute the Program at all. For example, if a patent
license would not permit royalty-free redistribution of the Program by
all those who receive copies directly or indirectly through you, then
the only way you could satisfy both it and this License would be to
refrain entirely from distribution of the Program.
If any portion of this section is held invalid or unenforceable under
any particular circumstance, the balance of the section is intended to
apply and the section as a whole is intended to apply in other
circumstances.
It is not the purpose of this section to induce you to infringe any
patents or other property right claims or to contest validity of any
such claims; this section has the sole purpose of protecting the
integrity of the free software distribution system, which is
implemented by public license practices. Many people have made
generous contributions to the wide range of software distributed
through that system in reliance on consistent application of that
system; it is up to the author/donor to decide if he or she is willing
to distribute software through any other system and a licensee cannot
impose that choice.
This section is intended to make thoroughly clear what is believed to
be a consequence of the rest of this License.
8. If the distribution and/or use of the Program is restricted in
certain countries either by patents or by copyrighted interfaces, the
original copyright holder who places the Program under this License
may add an explicit geographical distribution limitation excluding
those countries, so that distribution is permitted only in or among
countries not thus excluded. In such case, this License incorporates
the limitation as if written in the body of this License.
9. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of this License which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
this License, you may choose any version ever published by the Free Software
Foundation.
10. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
convey the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License.

View File

@ -0,0 +1,20 @@
Copyright (c) 2021 Remco Bloemen
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Math library for liquidity
library LiquidityMath {
/// @notice Add a signed liquidity delta to liquidity and revert if it overflows or underflows
/// @param x The liquidity before change
/// @param y The delta by which liquidity should be changed
/// @return z The liquidity delta
function addDelta(uint128 x, int128 y) internal pure returns (uint128 z) {
if (y < 0) {
require((z = x - uint128(-y)) < x, 'LS');
} else {
require((z = x + uint128(y)) >= x, 'LA');
}
}
}

View File

@ -0,0 +1,46 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.7.0;
/// @title Optimized overflow and underflow safe math operations
/// @notice Contains methods for doing math operations that revert on overflow or underflow for minimal gas cost
library LowGasSafeMath {
/// @notice Returns x + y, reverts if sum overflows uint256
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x + y) >= x);
}
/// @notice Returns x - y, reverts if underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(uint256 x, uint256 y) internal pure returns (uint256 z) {
require((z = x - y) <= x);
}
/// @notice Returns x * y, reverts if overflows
/// @param x The multiplicand
/// @param y The multiplier
/// @return z The product of x and y
function mul(uint256 x, uint256 y) internal pure returns (uint256 z) {
require(x == 0 || (z = x * y) / x == y);
}
/// @notice Returns x + y, reverts if overflows or underflows
/// @param x The augend
/// @param y The addend
/// @return z The sum of x and y
function add(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x + y) >= x == (y >= 0));
}
/// @notice Returns x - y, reverts if overflows or underflows
/// @param x The minuend
/// @param y The subtrahend
/// @return z The difference of x and y
function sub(int256 x, int256 y) internal pure returns (int256 z) {
require((z = x - y) <= x == (y >= 0));
}
}

View File

@ -0,0 +1,325 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
/// @title Oracle
/// @notice Provides price and liquidity data useful for a wide variety of system designs
/// @dev Instances of stored oracle data, "observations", are collected in the oracle array
/// Every pool is initialized with an oracle array length of 1. Anyone can pay the SSTOREs to increase the
/// maximum length of the oracle array. New slots will be added when the array is fully populated.
/// Observations are overwritten when the full length of the oracle array is populated.
/// The most recent observation is available, independent of the length of the oracle array, by passing 0 to observe()
library Oracle {
struct Observation {
// the block timestamp of the observation
uint32 blockTimestamp;
// the tick accumulator, i.e. tick * time elapsed since the pool was first initialized
int56 tickCumulative;
// the seconds per liquidity, i.e. seconds elapsed / max(1, liquidity) since the pool was first initialized
uint160 secondsPerLiquidityCumulativeX128;
// whether or not the observation is initialized
bool initialized;
}
/// @notice Transforms a previous observation into a new observation, given the passage of time and the current tick and liquidity values
/// @dev blockTimestamp _must_ be chronologically equal to or greater than last.blockTimestamp, safe for 0 or 1 overflows
/// @param last The specified observation to be transformed
/// @param blockTimestamp The timestamp of the new observation
/// @param tick The active tick at the time of the new observation
/// @param liquidity The total in-range liquidity at the time of the new observation
/// @return Observation The newly populated observation
function transform(
Observation memory last,
uint32 blockTimestamp,
int24 tick,
uint128 liquidity
) private pure returns (Observation memory) {
uint32 delta = blockTimestamp - last.blockTimestamp;
return
Observation({
blockTimestamp: blockTimestamp,
tickCumulative: last.tickCumulative + int56(tick) * delta,
secondsPerLiquidityCumulativeX128: last.secondsPerLiquidityCumulativeX128 +
((uint160(delta) << 128) / (liquidity > 0 ? liquidity : 1)),
initialized: true
});
}
/// @notice Initialize the oracle array by writing the first slot. Called once for the lifecycle of the observations array
/// @param self The stored oracle array
/// @param time The time of the oracle initialization, via block.timestamp truncated to uint32
/// @return cardinality The number of populated elements in the oracle array
/// @return cardinalityNext The new length of the oracle array, independent of population
function initialize(Observation[65535] storage self, uint32 time)
internal
returns (uint16 cardinality, uint16 cardinalityNext)
{
self[0] = Observation({
blockTimestamp: time,
tickCumulative: 0,
secondsPerLiquidityCumulativeX128: 0,
initialized: true
});
return (1, 1);
}
/// @notice Writes an oracle observation to the array
/// @dev Writable at most once per block. Index represents the most recently written element. cardinality and index must be tracked externally.
/// If the index is at the end of the allowable array length (according to cardinality), and the next cardinality
/// is greater than the current one, cardinality may be increased. This restriction is created to preserve ordering.
/// @param self The stored oracle array
/// @param index The index of the observation that was most recently written to the observations array
/// @param blockTimestamp The timestamp of the new observation
/// @param tick The active tick at the time of the new observation
/// @param liquidity The total in-range liquidity at the time of the new observation
/// @param cardinality The number of populated elements in the oracle array
/// @param cardinalityNext The new length of the oracle array, independent of population
/// @return indexUpdated The new index of the most recently written element in the oracle array
/// @return cardinalityUpdated The new cardinality of the oracle array
function write(
Observation[65535] storage self,
uint16 index,
uint32 blockTimestamp,
int24 tick,
uint128 liquidity,
uint16 cardinality,
uint16 cardinalityNext
) internal returns (uint16 indexUpdated, uint16 cardinalityUpdated) {
Observation memory last = self[index];
// early return if we've already written an observation this block
if (last.blockTimestamp == blockTimestamp) return (index, cardinality);
// if the conditions are right, we can bump the cardinality
if (cardinalityNext > cardinality && index == (cardinality - 1)) {
cardinalityUpdated = cardinalityNext;
} else {
cardinalityUpdated = cardinality;
}
indexUpdated = (index + 1) % cardinalityUpdated;
self[indexUpdated] = transform(last, blockTimestamp, tick, liquidity);
}
/// @notice Prepares the oracle array to store up to `next` observations
/// @param self The stored oracle array
/// @param current The current next cardinality of the oracle array
/// @param next The proposed next cardinality which will be populated in the oracle array
/// @return next The next cardinality which will be populated in the oracle array
function grow(
Observation[65535] storage self,
uint16 current,
uint16 next
) internal returns (uint16) {
require(current > 0, 'I');
// no-op if the passed next value isn't greater than the current next value
if (next <= current) return current;
// store in each slot to prevent fresh SSTOREs in swaps
// this data will not be used because the initialized boolean is still false
for (uint16 i = current; i < next; i++) self[i].blockTimestamp = 1;
return next;
}
/// @notice comparator for 32-bit timestamps
/// @dev safe for 0 or 1 overflows, a and b _must_ be chronologically before or equal to time
/// @param time A timestamp truncated to 32 bits
/// @param a A comparison timestamp from which to determine the relative position of `time`
/// @param b From which to determine the relative position of `time`
/// @return bool Whether `a` is chronologically <= `b`
function lte(
uint32 time,
uint32 a,
uint32 b
) private pure returns (bool) {
// if there hasn't been overflow, no need to adjust
if (a <= time && b <= time) return a <= b;
uint256 aAdjusted = a > time ? a : a + 2**32;
uint256 bAdjusted = b > time ? b : b + 2**32;
return aAdjusted <= bAdjusted;
}
/// @notice Fetches the observations beforeOrAt and atOrAfter a target, i.e. where [beforeOrAt, atOrAfter] is satisfied.
/// The result may be the same observation, or adjacent observations.
/// @dev The answer must be contained in the array, used when the target is located within the stored observation
/// boundaries: older than the most recent observation and younger, or the same age as, the oldest observation
/// @param self The stored oracle array
/// @param time The current block.timestamp
/// @param target The timestamp at which the reserved observation should be for
/// @param index The index of the observation that was most recently written to the observations array
/// @param cardinality The number of populated elements in the oracle array
/// @return beforeOrAt The observation recorded before, or at, the target
/// @return atOrAfter The observation recorded at, or after, the target
function binarySearch(
Observation[65535] storage self,
uint32 time,
uint32 target,
uint16 index,
uint16 cardinality
) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
uint256 l = (index + 1) % cardinality; // oldest observation
uint256 r = l + cardinality - 1; // newest observation
uint256 i;
while (true) {
i = (l + r) / 2;
beforeOrAt = self[i % cardinality];
// we've landed on an uninitialized tick, keep searching higher (more recently)
if (!beforeOrAt.initialized) {
l = i + 1;
continue;
}
atOrAfter = self[(i + 1) % cardinality];
bool targetAtOrAfter = lte(time, beforeOrAt.blockTimestamp, target);
// check if we've found the answer!
if (targetAtOrAfter && lte(time, target, atOrAfter.blockTimestamp)) break;
if (!targetAtOrAfter) r = i - 1;
else l = i + 1;
}
}
/// @notice Fetches the observations beforeOrAt and atOrAfter a given target, i.e. where [beforeOrAt, atOrAfter] is satisfied
/// @dev Assumes there is at least 1 initialized observation.
/// Used by observeSingle() to compute the counterfactual accumulator values as of a given block timestamp.
/// @param self The stored oracle array
/// @param time The current block.timestamp
/// @param target The timestamp at which the reserved observation should be for
/// @param tick The active tick at the time of the returned or simulated observation
/// @param index The index of the observation that was most recently written to the observations array
/// @param liquidity The total pool liquidity at the time of the call
/// @param cardinality The number of populated elements in the oracle array
/// @return beforeOrAt The observation which occurred at, or before, the given timestamp
/// @return atOrAfter The observation which occurred at, or after, the given timestamp
function getSurroundingObservations(
Observation[65535] storage self,
uint32 time,
uint32 target,
int24 tick,
uint16 index,
uint128 liquidity,
uint16 cardinality
) private view returns (Observation memory beforeOrAt, Observation memory atOrAfter) {
// optimistically set before to the newest observation
beforeOrAt = self[index];
// if the target is chronologically at or after the newest observation, we can early return
if (lte(time, beforeOrAt.blockTimestamp, target)) {
if (beforeOrAt.blockTimestamp == target) {
// if newest observation equals target, we're in the same block, so we can ignore atOrAfter
return (beforeOrAt, atOrAfter);
} else {
// otherwise, we need to transform
return (beforeOrAt, transform(beforeOrAt, target, tick, liquidity));
}
}
// now, set before to the oldest observation
beforeOrAt = self[(index + 1) % cardinality];
if (!beforeOrAt.initialized) beforeOrAt = self[0];
// ensure that the target is chronologically at or after the oldest observation
require(lte(time, beforeOrAt.blockTimestamp, target), 'OLD');
// if we've reached this point, we have to binary search
return binarySearch(self, time, target, index, cardinality);
}
/// @dev Reverts if an observation at or before the desired observation timestamp does not exist.
/// 0 may be passed as `secondsAgo' to return the current cumulative values.
/// If called with a timestamp falling between two observations, returns the counterfactual accumulator values
/// at exactly the timestamp between the two observations.
/// @param self The stored oracle array
/// @param time The current block timestamp
/// @param secondsAgo The amount of time to look back, in seconds, at which point to return an observation
/// @param tick The current tick
/// @param index The index of the observation that was most recently written to the observations array
/// @param liquidity The current in-range pool liquidity
/// @param cardinality The number of populated elements in the oracle array
/// @return tickCumulative The tick * time elapsed since the pool was first initialized, as of `secondsAgo`
/// @return secondsPerLiquidityCumulativeX128 The time elapsed / max(1, liquidity) since the pool was first initialized, as of `secondsAgo`
function observeSingle(
Observation[65535] storage self,
uint32 time,
uint32 secondsAgo,
int24 tick,
uint16 index,
uint128 liquidity,
uint16 cardinality
) internal view returns (int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128) {
if (secondsAgo == 0) {
Observation memory last = self[index];
if (last.blockTimestamp != time) last = transform(last, time, tick, liquidity);
return (last.tickCumulative, last.secondsPerLiquidityCumulativeX128);
}
uint32 target = time - secondsAgo;
(Observation memory beforeOrAt, Observation memory atOrAfter) =
getSurroundingObservations(self, time, target, tick, index, liquidity, cardinality);
if (target == beforeOrAt.blockTimestamp) {
// we're at the left boundary
return (beforeOrAt.tickCumulative, beforeOrAt.secondsPerLiquidityCumulativeX128);
} else if (target == atOrAfter.blockTimestamp) {
// we're at the right boundary
return (atOrAfter.tickCumulative, atOrAfter.secondsPerLiquidityCumulativeX128);
} else {
// we're in the middle
uint32 observationTimeDelta = atOrAfter.blockTimestamp - beforeOrAt.blockTimestamp;
uint32 targetDelta = target - beforeOrAt.blockTimestamp;
return (
beforeOrAt.tickCumulative +
((atOrAfter.tickCumulative - beforeOrAt.tickCumulative) / observationTimeDelta) *
targetDelta,
beforeOrAt.secondsPerLiquidityCumulativeX128 +
uint160(
(uint256(
atOrAfter.secondsPerLiquidityCumulativeX128 - beforeOrAt.secondsPerLiquidityCumulativeX128
) * targetDelta) / observationTimeDelta
)
);
}
}
/// @notice Returns the accumulator values as of each time seconds ago from the given time in the array of `secondsAgos`
/// @dev Reverts if `secondsAgos` > oldest observation
/// @param self The stored oracle array
/// @param time The current block.timestamp
/// @param secondsAgos Each amount of time to look back, in seconds, at which point to return an observation
/// @param tick The current tick
/// @param index The index of the observation that was most recently written to the observations array
/// @param liquidity The current in-range pool liquidity
/// @param cardinality The number of populated elements in the oracle array
/// @return tickCumulatives The tick * time elapsed since the pool was first initialized, as of each `secondsAgo`
/// @return secondsPerLiquidityCumulativeX128s The cumulative seconds / max(1, liquidity) since the pool was first initialized, as of each `secondsAgo`
function observe(
Observation[65535] storage self,
uint32 time,
uint32[] memory secondsAgos,
int24 tick,
uint16 index,
uint128 liquidity,
uint16 cardinality
) internal view returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) {
require(cardinality > 0, 'I');
tickCumulatives = new int56[](secondsAgos.length);
secondsPerLiquidityCumulativeX128s = new uint160[](secondsAgos.length);
for (uint256 i = 0; i < secondsAgos.length; i++) {
(tickCumulatives[i], secondsPerLiquidityCumulativeX128s[i]) = observeSingle(
self,
time,
secondsAgos[i],
tick,
index,
liquidity,
cardinality
);
}
}
}

View File

@ -0,0 +1,88 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import './FullMath.sol';
import './FixedPoint128.sol';
import './LiquidityMath.sol';
/// @title Position
/// @notice Positions represent an owner address' liquidity between a lower and upper tick boundary
/// @dev Positions store additional state for tracking fees owed to the position
library Position {
// info stored for each user's position
struct Info {
// the amount of liquidity owned by this position
uint128 liquidity;
// fee growth per unit of liquidity as of the last update to liquidity or fees owed
uint256 feeGrowthInside0LastX128;
uint256 feeGrowthInside1LastX128;
// the fees owed to the position owner in token0/token1
uint128 tokensOwed0;
uint128 tokensOwed1;
}
/// @notice Returns the Info struct of a position, given an owner and position boundaries
/// @param self The mapping containing all user positions
/// @param owner The address of the position owner
/// @param tickLower The lower tick boundary of the position
/// @param tickUpper The upper tick boundary of the position
/// @return position The position info struct of the given owners' position
function get(
mapping(bytes32 => Info) storage self,
address owner,
int24 tickLower,
int24 tickUpper
) internal view returns (Position.Info storage position) {
position = self[keccak256(abi.encodePacked(owner, tickLower, tickUpper))];
}
/// @notice Credits accumulated fees to a user's position
/// @param self The individual position to update
/// @param liquidityDelta The change in pool liquidity as a result of the position update
/// @param feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries
/// @param feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries
function update(
Info storage self,
int128 liquidityDelta,
uint256 feeGrowthInside0X128,
uint256 feeGrowthInside1X128
) internal {
Info memory _self = self;
uint128 liquidityNext;
if (liquidityDelta == 0) {
require(_self.liquidity > 0, 'NP'); // disallow pokes for 0 liquidity positions
liquidityNext = _self.liquidity;
} else {
liquidityNext = LiquidityMath.addDelta(_self.liquidity, liquidityDelta);
}
// calculate accumulated fees
uint128 tokensOwed0 =
uint128(
FullMath.mulDiv(
feeGrowthInside0X128 - _self.feeGrowthInside0LastX128,
_self.liquidity,
FixedPoint128.Q128
)
);
uint128 tokensOwed1 =
uint128(
FullMath.mulDiv(
feeGrowthInside1X128 - _self.feeGrowthInside1LastX128,
_self.liquidity,
FixedPoint128.Q128
)
);
// update the position
if (liquidityDelta != 0) self.liquidity = liquidityNext;
self.feeGrowthInside0LastX128 = feeGrowthInside0X128;
self.feeGrowthInside1LastX128 = feeGrowthInside1X128;
if (tokensOwed0 > 0 || tokensOwed1 > 0) {
// overflow is acceptable, have to withdraw before you hit type(uint128).max fees
self.tokensOwed0 += tokensOwed0;
self.tokensOwed1 += tokensOwed1;
}
}
}

View File

@ -0,0 +1,28 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Safe casting methods
/// @notice Contains methods for safely casting between types
library SafeCast {
/// @notice Cast a uint256 to a uint160, revert on overflow
/// @param y The uint256 to be downcasted
/// @return z The downcasted integer, now type uint160
function toUint160(uint256 y) internal pure returns (uint160 z) {
require((z = uint160(y)) == y);
}
/// @notice Cast a int256 to a int128, revert on overflow or underflow
/// @param y The int256 to be downcasted
/// @return z The downcasted integer, now type int128
function toInt128(int256 y) internal pure returns (int128 z) {
require((z = int128(y)) == y);
}
/// @notice Cast a uint256 to a int256, revert on overflow
/// @param y The uint256 to be casted
/// @return z The casted integer, now type int256
function toInt256(uint256 y) internal pure returns (int256 z) {
require(y < 2**255);
z = int256(y);
}
}

View File

@ -0,0 +1,227 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import './LowGasSafeMath.sol';
import './SafeCast.sol';
import './FullMath.sol';
import './UnsafeMath.sol';
import './FixedPoint96.sol';
/// @title Functions based on Q64.96 sqrt price and liquidity
/// @notice Contains the math that uses square root of price as a Q64.96 and liquidity to compute deltas
library SqrtPriceMath {
using LowGasSafeMath for uint256;
using SafeCast for uint256;
/// @notice Gets the next sqrt price given a delta of token0
/// @dev Always rounds up, because in the exact output case (increasing price) we need to move the price at least
/// far enough to get the desired output amount, and in the exact input case (decreasing price) we need to move the
/// price less in order to not send too much output.
/// The most precise formula for this is liquidity * sqrtPX96 / (liquidity +- amount * sqrtPX96),
/// if this is impossible because of overflow, we calculate liquidity / (liquidity / sqrtPX96 +- amount).
/// @param sqrtPX96 The starting price, i.e. before accounting for the token0 delta
/// @param liquidity The amount of usable liquidity
/// @param amount How much of token0 to add or remove from virtual reserves
/// @param add Whether to add or remove the amount of token0
/// @return The price after adding or removing amount, depending on add
function getNextSqrtPriceFromAmount0RoundingUp(
uint160 sqrtPX96,
uint128 liquidity,
uint256 amount,
bool add
) internal pure returns (uint160) {
// we short circuit amount == 0 because the result is otherwise not guaranteed to equal the input price
if (amount == 0) return sqrtPX96;
uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;
if (add) {
uint256 product;
if ((product = amount * sqrtPX96) / amount == sqrtPX96) {
uint256 denominator = numerator1 + product;
if (denominator >= numerator1)
// always fits in 160 bits
return uint160(FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator));
}
return uint160(UnsafeMath.divRoundingUp(numerator1, (numerator1 / sqrtPX96).add(amount)));
} else {
uint256 product;
// if the product overflows, we know the denominator underflows
// in addition, we must check that the denominator does not underflow
require((product = amount * sqrtPX96) / amount == sqrtPX96 && numerator1 > product);
uint256 denominator = numerator1 - product;
return FullMath.mulDivRoundingUp(numerator1, sqrtPX96, denominator).toUint160();
}
}
/// @notice Gets the next sqrt price given a delta of token1
/// @dev Always rounds down, because in the exact output case (decreasing price) we need to move the price at least
/// far enough to get the desired output amount, and in the exact input case (increasing price) we need to move the
/// price less in order to not send too much output.
/// The formula we compute is within <1 wei of the lossless version: sqrtPX96 +- amount / liquidity
/// @param sqrtPX96 The starting price, i.e., before accounting for the token1 delta
/// @param liquidity The amount of usable liquidity
/// @param amount How much of token1 to add, or remove, from virtual reserves
/// @param add Whether to add, or remove, the amount of token1
/// @return The price after adding or removing `amount`
function getNextSqrtPriceFromAmount1RoundingDown(
uint160 sqrtPX96,
uint128 liquidity,
uint256 amount,
bool add
) internal pure returns (uint160) {
// if we're adding (subtracting), rounding down requires rounding the quotient down (up)
// in both cases, avoid a mulDiv for most inputs
if (add) {
uint256 quotient =
(
amount <= type(uint160).max
? (amount << FixedPoint96.RESOLUTION) / liquidity
: FullMath.mulDiv(amount, FixedPoint96.Q96, liquidity)
);
return uint256(sqrtPX96).add(quotient).toUint160();
} else {
uint256 quotient =
(
amount <= type(uint160).max
? UnsafeMath.divRoundingUp(amount << FixedPoint96.RESOLUTION, liquidity)
: FullMath.mulDivRoundingUp(amount, FixedPoint96.Q96, liquidity)
);
require(sqrtPX96 > quotient);
// always fits 160 bits
return uint160(sqrtPX96 - quotient);
}
}
/// @notice Gets the next sqrt price given an input amount of token0 or token1
/// @dev Throws if price or liquidity are 0, or if the next price is out of bounds
/// @param sqrtPX96 The starting price, i.e., before accounting for the input amount
/// @param liquidity The amount of usable liquidity
/// @param amountIn How much of token0, or token1, is being swapped in
/// @param zeroForOne Whether the amount in is token0 or token1
/// @return sqrtQX96 The price after adding the input amount to token0 or token1
function getNextSqrtPriceFromInput(
uint160 sqrtPX96,
uint128 liquidity,
uint256 amountIn,
bool zeroForOne
) internal pure returns (uint160 sqrtQX96) {
require(sqrtPX96 > 0);
require(liquidity > 0);
// round to make sure that we don't pass the target price
return
zeroForOne
? getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountIn, true)
: getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountIn, true);
}
/// @notice Gets the next sqrt price given an output amount of token0 or token1
/// @dev Throws if price or liquidity are 0 or the next price is out of bounds
/// @param sqrtPX96 The starting price before accounting for the output amount
/// @param liquidity The amount of usable liquidity
/// @param amountOut How much of token0, or token1, is being swapped out
/// @param zeroForOne Whether the amount out is token0 or token1
/// @return sqrtQX96 The price after removing the output amount of token0 or token1
function getNextSqrtPriceFromOutput(
uint160 sqrtPX96,
uint128 liquidity,
uint256 amountOut,
bool zeroForOne
) internal pure returns (uint160 sqrtQX96) {
require(sqrtPX96 > 0);
require(liquidity > 0);
// round to make sure that we pass the target price
return
zeroForOne
? getNextSqrtPriceFromAmount1RoundingDown(sqrtPX96, liquidity, amountOut, false)
: getNextSqrtPriceFromAmount0RoundingUp(sqrtPX96, liquidity, amountOut, false);
}
/// @notice Gets the amount0 delta between two prices
/// @dev Calculates liquidity / sqrt(lower) - liquidity / sqrt(upper),
/// i.e. liquidity * (sqrt(upper) - sqrt(lower)) / (sqrt(upper) * sqrt(lower))
/// @param sqrtRatioAX96 A sqrt price
/// @param sqrtRatioBX96 Another sqrt price
/// @param liquidity The amount of usable liquidity
/// @param roundUp Whether to round the amount up or down
/// @return amount0 Amount of token0 required to cover a position of size liquidity between the two passed prices
function getAmount0Delta(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity,
bool roundUp
) internal pure returns (uint256 amount0) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
uint256 numerator1 = uint256(liquidity) << FixedPoint96.RESOLUTION;
uint256 numerator2 = sqrtRatioBX96 - sqrtRatioAX96;
require(sqrtRatioAX96 > 0);
return
roundUp
? UnsafeMath.divRoundingUp(
FullMath.mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96),
sqrtRatioAX96
)
: FullMath.mulDiv(numerator1, numerator2, sqrtRatioBX96) / sqrtRatioAX96;
}
/// @notice Gets the amount1 delta between two prices
/// @dev Calculates liquidity * (sqrt(upper) - sqrt(lower))
/// @param sqrtRatioAX96 A sqrt price
/// @param sqrtRatioBX96 Another sqrt price
/// @param liquidity The amount of usable liquidity
/// @param roundUp Whether to round the amount up, or down
/// @return amount1 Amount of token1 required to cover a position of size liquidity between the two passed prices
function getAmount1Delta(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
uint128 liquidity,
bool roundUp
) internal pure returns (uint256 amount1) {
if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
return
roundUp
? FullMath.mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96)
: FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96);
}
/// @notice Helper that gets signed token0 delta
/// @param sqrtRatioAX96 A sqrt price
/// @param sqrtRatioBX96 Another sqrt price
/// @param liquidity The change in liquidity for which to compute the amount0 delta
/// @return amount0 Amount of token0 corresponding to the passed liquidityDelta between the two prices
function getAmount0Delta(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
int128 liquidity
) internal pure returns (int256 amount0) {
return
liquidity < 0
? -getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()
: getAmount0Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();
}
/// @notice Helper that gets signed token1 delta
/// @param sqrtRatioAX96 A sqrt price
/// @param sqrtRatioBX96 Another sqrt price
/// @param liquidity The change in liquidity for which to compute the amount1 delta
/// @return amount1 Amount of token1 corresponding to the passed liquidityDelta between the two prices
function getAmount1Delta(
uint160 sqrtRatioAX96,
uint160 sqrtRatioBX96,
int128 liquidity
) internal pure returns (int256 amount1) {
return
liquidity < 0
? -getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(-liquidity), false).toInt256()
: getAmount1Delta(sqrtRatioAX96, sqrtRatioBX96, uint128(liquidity), true).toInt256();
}
}

View File

@ -0,0 +1,98 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import './FullMath.sol';
import './SqrtPriceMath.sol';
/// @title Computes the result of a swap within ticks
/// @notice Contains methods for computing the result of a swap within a single tick price range, i.e., a single tick.
library SwapMath {
/// @notice Computes the result of swapping some amount in, or amount out, given the parameters of the swap
/// @dev The fee, plus the amount in, will never exceed the amount remaining if the swap's `amountSpecified` is positive
/// @param sqrtRatioCurrentX96 The current sqrt price of the pool
/// @param sqrtRatioTargetX96 The price that cannot be exceeded, from which the direction of the swap is inferred
/// @param liquidity The usable liquidity
/// @param amountRemaining How much input or output amount is remaining to be swapped in/out
/// @param feePips The fee taken from the input amount, expressed in hundredths of a bip
/// @return sqrtRatioNextX96 The price after swapping the amount in/out, not to exceed the price target
/// @return amountIn The amount to be swapped in, of either token0 or token1, based on the direction of the swap
/// @return amountOut The amount to be received, of either token0 or token1, based on the direction of the swap
/// @return feeAmount The amount of input that will be taken as a fee
function computeSwapStep(
uint160 sqrtRatioCurrentX96,
uint160 sqrtRatioTargetX96,
uint128 liquidity,
int256 amountRemaining,
uint24 feePips
)
internal
pure
returns (
uint160 sqrtRatioNextX96,
uint256 amountIn,
uint256 amountOut,
uint256 feeAmount
)
{
bool zeroForOne = sqrtRatioCurrentX96 >= sqrtRatioTargetX96;
bool exactIn = amountRemaining >= 0;
if (exactIn) {
uint256 amountRemainingLessFee = FullMath.mulDiv(uint256(amountRemaining), 1e6 - feePips, 1e6);
amountIn = zeroForOne
? SqrtPriceMath.getAmount0Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, true)
: SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, true);
if (amountRemainingLessFee >= amountIn) sqrtRatioNextX96 = sqrtRatioTargetX96;
else
sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromInput(
sqrtRatioCurrentX96,
liquidity,
amountRemainingLessFee,
zeroForOne
);
} else {
amountOut = zeroForOne
? SqrtPriceMath.getAmount1Delta(sqrtRatioTargetX96, sqrtRatioCurrentX96, liquidity, false)
: SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioTargetX96, liquidity, false);
if (uint256(-amountRemaining) >= amountOut) sqrtRatioNextX96 = sqrtRatioTargetX96;
else
sqrtRatioNextX96 = SqrtPriceMath.getNextSqrtPriceFromOutput(
sqrtRatioCurrentX96,
liquidity,
uint256(-amountRemaining),
zeroForOne
);
}
bool max = sqrtRatioTargetX96 == sqrtRatioNextX96;
// get the input/output amounts
if (zeroForOne) {
amountIn = max && exactIn
? amountIn
: SqrtPriceMath.getAmount0Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, true);
amountOut = max && !exactIn
? amountOut
: SqrtPriceMath.getAmount1Delta(sqrtRatioNextX96, sqrtRatioCurrentX96, liquidity, false);
} else {
amountIn = max && exactIn
? amountIn
: SqrtPriceMath.getAmount1Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, true);
amountOut = max && !exactIn
? amountOut
: SqrtPriceMath.getAmount0Delta(sqrtRatioCurrentX96, sqrtRatioNextX96, liquidity, false);
}
// cap the output amount to not exceed the remaining output amount
if (!exactIn && amountOut > uint256(-amountRemaining)) {
amountOut = uint256(-amountRemaining);
}
if (exactIn && sqrtRatioNextX96 != sqrtRatioTargetX96) {
// we didn't reach the target, so take the remainder of the maximum input as fee
feeAmount = uint256(amountRemaining) - amountIn;
} else {
feeAmount = FullMath.mulDivRoundingUp(amountIn, feePips, 1e6 - feePips);
}
}
}

View File

@ -0,0 +1,185 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import './LowGasSafeMath.sol';
import './SafeCast.sol';
import './TickMath.sol';
import './LiquidityMath.sol';
/// @title Tick
/// @notice Contains functions for managing tick processes and relevant calculations
library Tick {
using LowGasSafeMath for int256;
using SafeCast for int256;
// info stored for each initialized individual tick
struct Info {
// the total position liquidity that references this tick
uint128 liquidityGross;
// amount of net liquidity added (subtracted) when tick is crossed from left to right (right to left),
int128 liquidityNet;
// fee growth per unit of liquidity on the _other_ side of this tick (relative to the current tick)
// only has relative meaning, not absolute the value depends on when the tick is initialized
uint256 feeGrowthOutside0X128;
uint256 feeGrowthOutside1X128;
// the cumulative tick value on the other side of the tick
int56 tickCumulativeOutside;
// the seconds per unit of liquidity on the _other_ side of this tick (relative to the current tick)
// only has relative meaning, not absolute the value depends on when the tick is initialized
uint160 secondsPerLiquidityOutsideX128;
// the seconds spent on the other side of the tick (relative to the current tick)
// only has relative meaning, not absolute the value depends on when the tick is initialized
uint32 secondsOutside;
// true iff the tick is initialized, i.e. the value is exactly equivalent to the expression liquidityGross != 0
// these 8 bits are set to prevent fresh sstores when crossing newly initialized ticks
bool initialized;
}
/// @notice Derives max liquidity per tick from given tick spacing
/// @dev Executed within the pool constructor
/// @param tickSpacing The amount of required tick separation, realized in multiples of `tickSpacing`
/// e.g., a tickSpacing of 3 requires ticks to be initialized every 3rd tick i.e., ..., -6, -3, 0, 3, 6, ...
/// @return The max liquidity per tick
function tickSpacingToMaxLiquidityPerTick(int24 tickSpacing) internal pure returns (uint128) {
int24 minTick = (TickMath.MIN_TICK / tickSpacing) * tickSpacing;
int24 maxTick = (TickMath.MAX_TICK / tickSpacing) * tickSpacing;
uint24 numTicks = uint24((maxTick - minTick) / tickSpacing) + 1;
return type(uint128).max / numTicks;
}
/// @notice Retrieves fee growth data
/// @param self The mapping containing all tick information for initialized ticks
/// @param tickLower The lower tick boundary of the position
/// @param tickUpper The upper tick boundary of the position
/// @param tickCurrent The current tick
/// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0
/// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1
/// @return feeGrowthInside0X128 The all-time fee growth in token0, per unit of liquidity, inside the position's tick boundaries
/// @return feeGrowthInside1X128 The all-time fee growth in token1, per unit of liquidity, inside the position's tick boundaries
function getFeeGrowthInside(
mapping(int24 => Tick.Info) storage self,
int24 tickLower,
int24 tickUpper,
int24 tickCurrent,
uint256 feeGrowthGlobal0X128,
uint256 feeGrowthGlobal1X128
) internal view returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128) {
Info storage lower = self[tickLower];
Info storage upper = self[tickUpper];
// calculate fee growth below
uint256 feeGrowthBelow0X128;
uint256 feeGrowthBelow1X128;
if (tickCurrent >= tickLower) {
feeGrowthBelow0X128 = lower.feeGrowthOutside0X128;
feeGrowthBelow1X128 = lower.feeGrowthOutside1X128;
} else {
feeGrowthBelow0X128 = feeGrowthGlobal0X128 - lower.feeGrowthOutside0X128;
feeGrowthBelow1X128 = feeGrowthGlobal1X128 - lower.feeGrowthOutside1X128;
}
// calculate fee growth above
uint256 feeGrowthAbove0X128;
uint256 feeGrowthAbove1X128;
if (tickCurrent < tickUpper) {
feeGrowthAbove0X128 = upper.feeGrowthOutside0X128;
feeGrowthAbove1X128 = upper.feeGrowthOutside1X128;
} else {
feeGrowthAbove0X128 = feeGrowthGlobal0X128 - upper.feeGrowthOutside0X128;
feeGrowthAbove1X128 = feeGrowthGlobal1X128 - upper.feeGrowthOutside1X128;
}
feeGrowthInside0X128 = feeGrowthGlobal0X128 - feeGrowthBelow0X128 - feeGrowthAbove0X128;
feeGrowthInside1X128 = feeGrowthGlobal1X128 - feeGrowthBelow1X128 - feeGrowthAbove1X128;
}
/// @notice Updates a tick and returns true if the tick was flipped from initialized to uninitialized, or vice versa
/// @param self The mapping containing all tick information for initialized ticks
/// @param tick The tick that will be updated
/// @param tickCurrent The current tick
/// @param liquidityDelta A new amount of liquidity to be added (subtracted) when tick is crossed from left to right (right to left)
/// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0
/// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1
/// @param secondsPerLiquidityCumulativeX128 The all-time seconds per max(1, liquidity) of the pool
/// @param tickCumulative The tick * time elapsed since the pool was first initialized
/// @param time The current block timestamp cast to a uint32
/// @param upper true for updating a position's upper tick, or false for updating a position's lower tick
/// @param maxLiquidity The maximum liquidity allocation for a single tick
/// @return flipped Whether the tick was flipped from initialized to uninitialized, or vice versa
function update(
mapping(int24 => Tick.Info) storage self,
int24 tick,
int24 tickCurrent,
int128 liquidityDelta,
uint256 feeGrowthGlobal0X128,
uint256 feeGrowthGlobal1X128,
uint160 secondsPerLiquidityCumulativeX128,
int56 tickCumulative,
uint32 time,
bool upper,
uint128 maxLiquidity
) internal returns (bool flipped) {
Tick.Info storage info = self[tick];
uint128 liquidityGrossBefore = info.liquidityGross;
uint128 liquidityGrossAfter = LiquidityMath.addDelta(liquidityGrossBefore, liquidityDelta);
require(liquidityGrossAfter <= maxLiquidity, 'LO');
flipped = (liquidityGrossAfter == 0) != (liquidityGrossBefore == 0);
if (liquidityGrossBefore == 0) {
// by convention, we assume that all growth before a tick was initialized happened _below_ the tick
if (tick <= tickCurrent) {
info.feeGrowthOutside0X128 = feeGrowthGlobal0X128;
info.feeGrowthOutside1X128 = feeGrowthGlobal1X128;
info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128;
info.tickCumulativeOutside = tickCumulative;
info.secondsOutside = time;
}
info.initialized = true;
}
info.liquidityGross = liquidityGrossAfter;
// when the lower (upper) tick is crossed left to right (right to left), liquidity must be added (removed)
info.liquidityNet = upper
? int256(info.liquidityNet).sub(liquidityDelta).toInt128()
: int256(info.liquidityNet).add(liquidityDelta).toInt128();
}
/// @notice Clears tick data
/// @param self The mapping containing all initialized tick information for initialized ticks
/// @param tick The tick that will be cleared
function clear(mapping(int24 => Tick.Info) storage self, int24 tick) internal {
delete self[tick];
}
/// @notice Transitions to next tick as needed by price movement
/// @param self The mapping containing all tick information for initialized ticks
/// @param tick The destination tick of the transition
/// @param feeGrowthGlobal0X128 The all-time global fee growth, per unit of liquidity, in token0
/// @param feeGrowthGlobal1X128 The all-time global fee growth, per unit of liquidity, in token1
/// @param secondsPerLiquidityCumulativeX128 The current seconds per liquidity
/// @param tickCumulative The tick * time elapsed since the pool was first initialized
/// @param time The current block.timestamp
/// @return liquidityNet The amount of liquidity added (subtracted) when tick is crossed from left to right (right to left)
function cross(
mapping(int24 => Tick.Info) storage self,
int24 tick,
uint256 feeGrowthGlobal0X128,
uint256 feeGrowthGlobal1X128,
uint160 secondsPerLiquidityCumulativeX128,
int56 tickCumulative,
uint32 time
) internal returns (int128 liquidityNet) {
Tick.Info storage info = self[tick];
info.feeGrowthOutside0X128 = feeGrowthGlobal0X128 - info.feeGrowthOutside0X128;
info.feeGrowthOutside1X128 = feeGrowthGlobal1X128 - info.feeGrowthOutside1X128;
info.secondsPerLiquidityOutsideX128 = secondsPerLiquidityCumulativeX128 - info.secondsPerLiquidityOutsideX128;
info.tickCumulativeOutside = tickCumulative - info.tickCumulativeOutside;
info.secondsOutside = time - info.secondsOutside;
liquidityNet = info.liquidityNet;
}
}

View File

@ -0,0 +1,78 @@
// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.5.0;
import './BitMath.sol';
/// @title Packed tick initialized state library
/// @notice Stores a packed mapping of tick index to its initialized state
/// @dev The mapping uses int16 for keys since ticks are represented as int24 and there are 256 (2^8) values per word.
library TickBitmap {
/// @notice Computes the position in the mapping where the initialized bit for a tick lives
/// @param tick The tick for which to compute the position
/// @return wordPos The key in the mapping containing the word in which the bit is stored
/// @return bitPos The bit position in the word where the flag is stored
function position(int24 tick) private pure returns (int16 wordPos, uint8 bitPos) {
wordPos = int16(tick >> 8);
bitPos = uint8(tick % 256);
}
/// @notice Flips the initialized state for a given tick from false to true, or vice versa
/// @param self The mapping in which to flip the tick
/// @param tick The tick to flip
/// @param tickSpacing The spacing between usable ticks
function flipTick(
mapping(int16 => uint256) storage self,
int24 tick,
int24 tickSpacing
) internal {
require(tick % tickSpacing == 0); // ensure that the tick is spaced
(int16 wordPos, uint8 bitPos) = position(tick / tickSpacing);
uint256 mask = 1 << bitPos;
self[wordPos] ^= mask;
}
/// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is either
/// to the left (less than or equal to) or right (greater than) of the given tick
/// @param self The mapping in which to compute the next initialized tick
/// @param tick The starting tick
/// @param tickSpacing The spacing between usable ticks
/// @param lte Whether to search for the next initialized tick to the left (less than or equal to the starting tick)
/// @return next The next initialized or uninitialized tick up to 256 ticks away from the current tick
/// @return initialized Whether the next tick is initialized, as the function only searches within up to 256 ticks
function nextInitializedTickWithinOneWord(
mapping(int16 => uint256) storage self,
int24 tick,
int24 tickSpacing,
bool lte
) internal view returns (int24 next, bool initialized) {
int24 compressed = tick / tickSpacing;
if (tick < 0 && tick % tickSpacing != 0) compressed--; // round towards negative infinity
if (lte) {
(int16 wordPos, uint8 bitPos) = position(compressed);
// all the 1s at or to the right of the current bitPos
uint256 mask = (1 << bitPos) - 1 + (1 << bitPos);
uint256 masked = self[wordPos] & mask;
// if there are no initialized ticks to the right of or at the current tick, return rightmost in the word
initialized = masked != 0;
// overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick
next = initialized
? (compressed - int24(bitPos - BitMath.mostSignificantBit(masked))) * tickSpacing
: (compressed - int24(bitPos)) * tickSpacing;
} else {
// start from the word of the next tick, since the current tick state doesn't matter
(int16 wordPos, uint8 bitPos) = position(compressed + 1);
// all the 1s at or to the left of the bitPos
uint256 mask = ~((1 << bitPos) - 1);
uint256 masked = self[wordPos] & mask;
// if there are no initialized ticks to the left of the current tick, return leftmost in the word
initialized = masked != 0;
// overflow/underflow is possible, but prevented externally by limiting both tickSpacing and tick
next = initialized
? (compressed + 1 + int24(BitMath.leastSignificantBit(masked) - bitPos)) * tickSpacing
: (compressed + 1 + int24(type(uint8).max - bitPos)) * tickSpacing;
}
}
}

View File

@ -0,0 +1,205 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
library TickMath {
/// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
int24 internal constant MIN_TICK = -887272;
/// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
int24 internal constant MAX_TICK = -MIN_TICK;
/// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
uint160 internal constant MIN_SQRT_RATIO = 4295128739;
/// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;
/// @notice Calculates sqrt(1.0001^tick) * 2^96
/// @dev Throws if |tick| > max tick
/// @param tick The input tick for the above formula
/// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
/// at the given tick
function getSqrtRatioAtTick(int24 tick) internal pure returns (uint160 sqrtPriceX96) {
uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));
require(absTick <= uint256(MAX_TICK), 'T');
uint256 ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;
if (tick > 0) ratio = type(uint256).max / ratio;
// this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
// we then downcast because we know the result always fits within 160 bits due to our tick input constraint
// we round up in the division so getTickAtSqrtRatio of the output price is always consistent
sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));
}
/// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
/// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
/// ever return.
/// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96
/// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
// second inequality must be < because the price can never reach the price at the max tick
require(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO, 'R');
uint256 ratio = uint256(sqrtPriceX96) << 32;
uint256 r = ratio;
uint256 msb = 0;
assembly {
let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(5, gt(r, 0xFFFFFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(4, gt(r, 0xFFFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(3, gt(r, 0xFF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(2, gt(r, 0xF))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := shl(1, gt(r, 0x3))
msb := or(msb, f)
r := shr(f, r)
}
assembly {
let f := gt(r, 0x1)
msb := or(msb, f)
}
if (msb >= 128) r = ratio >> (msb - 127);
else r = ratio << (127 - msb);
int256 log_2 = (int256(msb) - 128) << 64;
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(63, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(62, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(61, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(60, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(59, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(58, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(57, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(56, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(55, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(54, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(53, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(52, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(51, f))
r := shr(f, r)
}
assembly {
r := shr(127, mul(r, r))
let f := shr(128, r)
log_2 := or(log_2, shl(50, f))
}
int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number
int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);
tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;
}
}

View File

@ -0,0 +1,23 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.6.0;
import '../interfaces/IERC20Minimal.sol';
/// @title TransferHelper
/// @notice Contains helper methods for interacting with ERC20 tokens that do not consistently return true/false
library TransferHelper {
/// @notice Transfers tokens from msg.sender to a recipient
/// @dev Calls transfer on token contract, errors with TF if transfer fails
/// @param token The contract address of the token which will be transferred
/// @param to The recipient of the transfer
/// @param value The value of the transfer
function safeTransfer(
address token,
address to,
uint256 value
) internal {
(bool success, bytes memory data) =
token.call(abi.encodeWithSelector(IERC20Minimal.transfer.selector, to, value));
require(success && (data.length == 0 || abi.decode(data, (bool))), 'TF');
}
}

View File

@ -0,0 +1,17 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;
/// @title Math functions that do not check inputs or outputs
/// @notice Contains methods that perform common math functions but do not do any overflow or underflow checks
library UnsafeMath {
/// @notice Returns ceil(x / y)
/// @dev division by 0 has unspecified behavior, and must be checked externally
/// @param x The dividend
/// @param y The divisor
/// @return z The quotient, ceil(x / y)
function divRoundingUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := add(div(x, y), gt(mod(x, y), 0))
}
}
}

View File

@ -0,0 +1,116 @@
pragma solidity ^0.7.6;
pragma abicoder v2;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./libraries/SqrtPriceMath.sol";
interface UniswapV3Pool {
struct Slot0 {
uint160 sqrtPriceX96;
int24 tick;
uint16 observationIndex;
uint16 observationCardinality;
uint16 observationCardinalityNext;
uint8 feeProtocol;
bool unlocked;
}
function liquidity() external view returns (uint128);
function slot0() external view returns (Slot0);
}
interface ISwapRouter {
struct ExactInputSingleParams {
address tokenIn;
address tokenOut;
uint24 fee;
address recipient;
uint256 deadline;
uint256 amountIn;
uint256 amountOutMinimum;
uint160 sqrtPriceLimitX96;
}
function exactInputSingle(ExactInputSingleParams calldata params)
external
payable
returns (uint256 amountOut);
}
contract uniswapSample {
ISwapRouter router =
ISwapRouter(0xE592427A0AEce92De3Edee1F18E0157C05861564);
UniswapV3Pool state =
UniswapV3Pool(0xCEda10b4d3bdE429DdA3A6daB87b38360313CBdB);
uint24 public constant poolFee = 3000;
function computeAddress(address factory_, PoolKey memory key_)
internal
pure
returns (address pool_)
{
require(key_.token0 < key_.token1);
pool_ = address(
uint160(
uint256(
keccak256(
abi.encodePacked(
hex"ff",
factory_,
keccak256(
abi.encode(key_.token0, key_.token1, key_.fee)
),
POOL_INIT_CODE_HASH
)
)
)
)
);
}
function getPriceLimit(
ISwapRouter.ExactInputSingleParams params,
bool zeroForOne
) public returns (uint160) {
return (
getNextSqrtPriceFromInput(
state.slot0().sqrtPriceX96,
state.liquidity(),
params.amountIn,
zeroForOne
)
);
}
function sell(ISwapRouter.ExactInputSingleParams params, bool zeroForOne) {
TransferHelper.safeTransferFrom(
params.tokenIn,
msg.sender,
address(this),
params.amountIn
);
TransferHelper.safeApprove(
params.tokenIn,
address(swapRouter),
params.amountIn
);
ISwapRouter.ExactInputSingleParams memory params1 = ISwapRouter
.ExactInputSingleParams({
tokenIn: params.tokenIn
tokenOut: params.tokenOut,
fee: poolFee,
recipient: address(this),
deadline: block.timestamp + 1,
amountIn: params.amountIn,
amountOutMinimum: 0,
sqrtPriceLimitX96: getPriceLimit(params, true)
});
amountOut = swapRouter.exactInputSingle(params1);
return (amountOut);
}
}

12
test/sample/sample.js Normal file
View File

@ -0,0 +1,12 @@
const { expect } = require("chai");
const hre = require("hardhat");
const { web3, deployments, waffle, ethers } = hre;
const { provider, deployContract } = waffle;
const deployAndEnableConnector = require("../../scripts/deployAndEnableConnector.js");
const buildDSAv2 = require("../../scripts/buildDSAv2");
const encodeSpells = require("../../scripts/encodeSpells.js");
desribe("SAMPLE UNISWAP", () => {
it("X", async () => {});
});