mirror of
https://github.com/Instadapp/dsa-connectors.git
synced 2024-07-29 22:37:00 +00:00
feat: sample uniswap connector on arbitrum
This commit is contained in:
parent
07f8fd07ad
commit
4736e8dadc
94
contracts/sample/libraries/BitMath.sol
Normal file
94
contracts/sample/libraries/BitMath.sol
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
8
contracts/sample/libraries/FixedPoint128.sol
Normal file
8
contracts/sample/libraries/FixedPoint128.sol
Normal 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;
|
||||||
|
}
|
10
contracts/sample/libraries/FixedPoint96.sol
Normal file
10
contracts/sample/libraries/FixedPoint96.sol
Normal 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;
|
||||||
|
}
|
124
contracts/sample/libraries/FullMath.sol
Normal file
124
contracts/sample/libraries/FullMath.sol
Normal 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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
339
contracts/sample/libraries/LICENSE_GPL
Normal file
339
contracts/sample/libraries/LICENSE_GPL
Normal 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.
|
20
contracts/sample/libraries/LICENSE_MIT
Normal file
20
contracts/sample/libraries/LICENSE_MIT
Normal 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.
|
17
contracts/sample/libraries/LiquidityMath.sol
Normal file
17
contracts/sample/libraries/LiquidityMath.sol
Normal 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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
contracts/sample/libraries/LowGasSafeMath.sol
Normal file
46
contracts/sample/libraries/LowGasSafeMath.sol
Normal 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));
|
||||||
|
}
|
||||||
|
}
|
325
contracts/sample/libraries/Oracle.sol
Normal file
325
contracts/sample/libraries/Oracle.sol
Normal 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
88
contracts/sample/libraries/Position.sol
Normal file
88
contracts/sample/libraries/Position.sol
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
28
contracts/sample/libraries/SafeCast.sol
Normal file
28
contracts/sample/libraries/SafeCast.sol
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
227
contracts/sample/libraries/SqrtPriceMath.sol
Normal file
227
contracts/sample/libraries/SqrtPriceMath.sol
Normal 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();
|
||||||
|
}
|
||||||
|
}
|
98
contracts/sample/libraries/SwapMath.sol
Normal file
98
contracts/sample/libraries/SwapMath.sol
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
185
contracts/sample/libraries/Tick.sol
Normal file
185
contracts/sample/libraries/Tick.sol
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
78
contracts/sample/libraries/TickBitmap.sol
Normal file
78
contracts/sample/libraries/TickBitmap.sol
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
205
contracts/sample/libraries/TickMath.sol
Normal file
205
contracts/sample/libraries/TickMath.sol
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
23
contracts/sample/libraries/TransferHelper.sol
Normal file
23
contracts/sample/libraries/TransferHelper.sol
Normal 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');
|
||||||
|
}
|
||||||
|
}
|
17
contracts/sample/libraries/UnsafeMath.sol
Normal file
17
contracts/sample/libraries/UnsafeMath.sol
Normal 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
116
contracts/sample/uniswap-sample.sol
Normal file
116
contracts/sample/uniswap-sample.sol
Normal 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
12
test/sample/sample.js
Normal 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 () => {});
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user