-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
7dbfcd6
commit dfe24b3
Showing
7 changed files
with
179 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.0; | ||
|
||
import "./Constants.sol"; | ||
import "./interfaces/INonfungiblePositionManager.sol"; | ||
import "./interfaces/IUniswapV3Factory.sol"; | ||
import "./interfaces/IUniswapV3Pool.sol"; | ||
import "@openzeppelin/token/ERC20/extensions/IERC20Metadata.sol"; | ||
|
||
contract PositionObserver { | ||
address public safe; | ||
constructor( | ||
address _safe | ||
){ | ||
safe = _safe; | ||
} | ||
|
||
function positionCount() external view returns (uint256) { | ||
return INonfungiblePositionManager(UNISWAP_POSITION_MANAGER).balanceOf(address(safe)); | ||
} | ||
|
||
function tokenIdByIndex(uint256 index) external view returns (uint256) { | ||
return INonfungiblePositionManager(UNISWAP_POSITION_MANAGER).tokenOfOwnerByIndex(address(safe), index); | ||
} | ||
|
||
function poolAddress(uint256 tokenId) external view returns (address) { | ||
( | ||
uint96 nonce, | ||
address operator, | ||
address token0, | ||
address token1, | ||
uint24 fee, | ||
int24 tickLower, | ||
int24 tickUpper, | ||
uint128 liquidity, | ||
uint256 feeGrowthInside0LastX128, | ||
uint256 feeGrowthInside1LastX128, | ||
uint128 tokensOwed0, | ||
uint128 tokensOwed1 | ||
) = INonfungiblePositionManager(UNISWAP_POSITION_MANAGER).positions(tokenId); | ||
return IUniswapV3Factory(UNISWAP_V3_FACTORY).getPool(token0, token1, fee); | ||
} | ||
|
||
function calculatePrice(int56 tick0, int56 tick1, int56 secs, uint256 decimals0, uint256 decimals1) internal pure returns (uint256) { | ||
int56 exponent = (tick0 - tick1) / secs; | ||
uint256 base = 10001; | ||
uint256 price = 10000; | ||
while (exponent > 0) { | ||
if (exponent & 1 == 1) { | ||
price = (price * base) / 10000; | ||
} | ||
base = (base * base) / 10000; | ||
exponent >>= 1; | ||
} | ||
price *= 10 ** 18; | ||
if (decimals0 > decimals1) { | ||
price /= 10 ** (decimals0 - decimals1); | ||
} else { | ||
price *= 10 ** (decimals1 - decimals0); | ||
} | ||
return price; | ||
} | ||
|
||
function positionPriceByTokenId(uint256 tokenId) external view returns (uint256) { | ||
( | ||
uint96 nonce, | ||
address operator, | ||
address token0, | ||
address token1, | ||
uint24 fee, | ||
int24 tickLower, | ||
int24 tickUpper, | ||
uint128 liquidity, | ||
uint256 feeGrowthInside0LastX128, | ||
uint256 feeGrowthInside1LastX128, | ||
uint128 tokensOwed0, | ||
uint128 tokensOwed1 | ||
) = INonfungiblePositionManager(UNISWAP_POSITION_MANAGER).positions(tokenId); | ||
uint8 token0Decimals = IERC20Metadata(token0).decimals(); | ||
uint8 token1Decimals = IERC20Metadata(token1).decimals(); | ||
address pool = IUniswapV3Factory(UNISWAP_V3_FACTORY).getPool(token0, token1, fee); | ||
uint32[] memory secondsAgos = new uint32[](2); | ||
secondsAgos[0] = 0; | ||
secondsAgos[1] = 1; | ||
( | ||
int56[] memory tickCumulatives, | ||
uint160[] memory secondsPerLiquidityCumulativeX128s | ||
) = IUniswapV3Pool(pool).observe(secondsAgos); | ||
require(tickCumulatives.length == 2); | ||
return calculatePrice(tickCumulatives[0], tickCumulatives[1], 1, token0Decimals, token1Decimals); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.0; | ||
|
||
interface INonfungiblePositionManager { | ||
function positions(uint256 tokenId) | ||
external | ||
view | ||
returns ( | ||
uint96 nonce, | ||
address operator, | ||
address token0, | ||
address token1, | ||
uint24 fee, | ||
int24 tickLower, | ||
int24 tickUpper, | ||
uint128 liquidity, | ||
uint256 feeGrowthInside0LastX128, | ||
uint256 feeGrowthInside1LastX128, | ||
uint128 tokensOwed0, | ||
uint128 tokensOwed1 | ||
); | ||
|
||
function balanceOf(address owner) external view returns (uint256); | ||
|
||
function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.0; | ||
|
||
interface IUniswapV3Factory { | ||
|
||
function getPool( | ||
address tokenA, | ||
address tokenB, | ||
uint24 fee | ||
) external view returns (address pool); | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
// SPDX-License-Identifier: UNLICENSED | ||
pragma solidity ^0.8.0; | ||
|
||
interface IUniswapV3Pool { | ||
function observe(uint32[] calldata secondsAgos) | ||
external | ||
view | ||
returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
pragma solidity ^0.8.0; | ||
|
||
import {Test, console2} from "forge-std/Test.sol"; | ||
import {PositionObserver} from "../src/PositionObserver.sol"; | ||
|
||
contract PositionObserverTest is Test { | ||
PositionObserver observer; | ||
|
||
address constant SAFE_ADDRESS = 0xD4928171A52f420855Bf59bcaF3C7077F42298D5; | ||
|
||
function setUp() public { | ||
observer = new PositionObserver(address(SAFE_ADDRESS)); | ||
} | ||
|
||
function testPositionCount() public { | ||
assertEq(observer.positionCount(), 2); | ||
} | ||
|
||
function testTokenIdByIndex() public { | ||
assertEq(observer.tokenIdByIndex(0), 1071846); | ||
assertEq(observer.tokenIdByIndex(1), 1081648); | ||
} | ||
|
||
function testPoolAddress() public { | ||
assertEq(observer.poolAddress(1071846), 0x0e44cEb592AcFC5D3F09D996302eB4C499ff8c10); | ||
assertEq(observer.poolAddress(1081648), 0x8Fc5e02d85891BA2855af1904dfc5cf1d82e4a44); | ||
} | ||
|
||
function testPositionPrice() public { | ||
console2.log("price", observer.positionPriceByTokenId(1071846)); | ||
console2.log("price", observer.positionPriceByTokenId(1081648)); | ||
// assertEq(observer.positionPriceByTokenId(1071846), 1000000000000000000); | ||
// assertEq(observer.positionPriceByTokenId(1081648), 1000000000000000000); | ||
} | ||
} |