Skip to content

Commit

Permalink
Merge pull request #102 from gammaswap/feat/lp-token-balance-viewer
Browse files Browse the repository at this point in the history
publish package
  • Loading branch information
0xDanr committed Mar 18, 2024
2 parents 2915f16 + a5b5007 commit 85d83d2
Show file tree
Hide file tree
Showing 5 changed files with 784 additions and 1 deletion.
53 changes: 53 additions & 0 deletions contracts/interfaces/lens/ILPViewer.sol
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;

/// @title Interface for LPViewer
/// @author Daniel D. Alcarraz (https://github.com/0xDanr)
/// @notice Interface used to send tokens and clear tokens and Ether from a contract
interface ILPViewer {

/// @dev NonStatic call to get total token balances in pools array belonging to a user.
/// @dev The index of the tokenBalances array will match the index of the tokens array.
/// @notice there may be more tokens than there are pools. E.g. WETH/USDC => 2 tokens and 1 pool
/// @param user - address of user to get information for
/// @param pools - array of addresses of pools to check token balance information in
/// @return tokens - addresses of tokens in pools array
/// @return tokenBalances - total balances of each token in tokens array belonging to user across all pools
/// @return size - number of elements in the tokens array
function tokenBalancesInPoolsNonStatic(address user, address[] calldata pools) external returns(address[] memory tokens,
uint256[] memory tokenBalances, uint256 size);

/// @dev Static call to get total token balances in pools array belonging to a user.
/// @dev The index of the tokenBalances array will match the index of the tokens array.
/// @notice there may be more tokens than there are pools. E.g. WETH/USDC => 2 tokens and 1 pool
/// @param user - address of user to get information for
/// @param pools - array of addresses of pools to check token balance information in
/// @return tokens - addresses of tokens in pools array
/// @return tokenBalances - total balances of each token in tokens array belonging to user across all pools
/// @return size - number of elements in the tokens array
function tokenBalancesInPools(address user, address[] calldata pools) external view returns(address[] memory tokens,
uint256[] memory tokenBalances, uint256 size);

/// @dev token quantity and GS LP balance information for a user in a given pool
/// @param user - address of user to get information for
/// @param pool - address of pool to get information for
/// @return token0 - address of token0 in pool
/// @return token1 - address of token1 in pool
/// @return token0Balance - balance of token0 in pool belonging to user
/// @return token1Balance - balance of token1 in pool belonging to user
/// @return lpBalance - GS LP Balance of user
function lpBalanceByPool(address user, address pool) external view returns(address token0, address token1,
uint256 token0Balance, uint256 token1Balance, uint256 lpBalance);

/// @dev token quantities and GS LP balance information for a user per pool
/// @dev the index of each array matches the index of each pool in hte pools array
/// @param user - address of user to get information for
/// @param pools - addresses of pools to get information for
/// @return token0 - addresses of token0 per pool
/// @return token1 - addresses of token1 per pool
/// @return token0Balance - array of balances of token0 per pool belonging to user
/// @return token1Balance - array of balances of token1 per pool belonging to user
/// @return lpBalance - array of GS LP Balances of user per pool
function lpBalanceByPools(address user, address[] calldata pools) external view returns(address[] memory token0, address[] memory token1,
uint256[] memory token0Balance, uint256[] memory token1Balance, uint256[] memory lpBalance);
}
174 changes: 174 additions & 0 deletions contracts/lens/LPViewer.sol
@@ -0,0 +1,174 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@gammaswap/v1-core/contracts/interfaces/IGammaPool.sol";
import "../interfaces/lens/ILPViewer.sol";

/// @title Interface for LPViewer
/// @author Daniel D. Alcarraz (https://github.com/0xDanr)
/// @notice Implementation contract of ILPViewer to get token balance information per user per pool
/// @notice and across a given number of pools per user, aggregated or per pool
contract LPViewer is ILPViewer {

mapping(address => uint256) tokenIndex;

constructor(){
}

/// @inheritdoc ILPViewer
function tokenBalancesInPoolsNonStatic(address user, address[] calldata pools) public virtual override returns(address[] memory tokens, uint256[] memory tokenBalances, uint256 size) {
tokens = new address[](pools.length * 2);
tokenBalances = new uint256[](pools.length * 2);
size = 0;

for(uint256 i; i < pools.length;) {
address[] memory _tokens = IGammaPool(pools[i]).tokens();
if(tokenIndex[_tokens[0]] == 0) {
tokenIndex[_tokens[0]] = size + 1;
tokens[size] = _tokens[0];
unchecked{
++size;
}
}
if(tokenIndex[_tokens[1]] == 0) {
tokenIndex[_tokens[1]] = size + 1;
tokens[size] = _tokens[1];
unchecked{
++size;
}
}
unchecked{
++i;
}
}

for(uint256 i; i < pools.length;) {
(address token0, address token1, uint256 token0Balance, uint256 token1Balance,) = _lpBalanceByPool(user, pools[i]);
tokenBalances[tokenIndex[token0] - 1] += token0Balance;
tokenBalances[tokenIndex[token1] - 1] += token1Balance;
unchecked{
++i;
}
}

for(uint256 i; i < size;) {
tokenIndex[tokens[i]] = 0; // clear the mapping
unchecked{
++i;
}
}
}

/// @inheritdoc ILPViewer
function tokenBalancesInPools(address user, address[] calldata pools) public virtual override view returns(address[] memory tokens, uint256[] memory tokenBalances, uint256 size) {
tokens = new address[](pools.length * 2);
tokenBalances = new uint256[](pools.length * 2);
size = 0;
for(uint256 i; i < pools.length;) {
address[] memory _tokens = IGammaPool(pools[i]).tokens();
bool found0 = false;
bool found1 = false;
for(uint256 j; j < tokens.length;) {
if(tokens[j] == _tokens[0]) {
found0 = true;
} else if(tokens[j] == _tokens[1]) {
found1 = true;
} else if(tokens[j] == address(0)) {
if(!found0) {
tokens[j] = _tokens[0];
found0 = true;
unchecked {
++size;
}
} else if(!found1) {
tokens[j] = _tokens[1];
found1 = true;
unchecked {
++size;
}
}
}
if(found0 && found1) {
break;
}
unchecked{
++j;
}
}
unchecked{
++i;
}
}

for(uint256 i; i < pools.length;) {
(address token0, address token1, uint256 token0Balance, uint256 token1Balance,) = _lpBalanceByPool(user, pools[i]);
uint256 found = 0;
for(uint256 j; j < tokens.length;) {
if(token0 == tokens[j]) {
tokenBalances[j] += token0Balance;
found++;
} else if(token1 == tokens[j]) {
tokenBalances[j] += token1Balance;
found++;
}
if(found == 2) {
break;
}
unchecked{
++j;
}
}
unchecked{
++i;
}
}
}

/// @inheritdoc ILPViewer
function lpBalanceByPool(address user, address pool) public virtual override view returns(address token0, address token1,
uint256 token0Balance, uint256 token1Balance, uint256 lpBalance) {
return _lpBalanceByPool(user, pool);
}

/// @dev token quantity and GS LP balance information for a user in a given pool
/// @param user - address of user to get information for
/// @param pool - address of pool to get information for
/// @return token0 - address of token0 in pool
/// @return token1 - address of token1 in pool
/// @return token0Balance - balance of token0 in pool belonging to user
/// @return token1Balance - balance of token1 in pool belonging to user
/// @return lpBalance - GS LP Balance of user
function _lpBalanceByPool(address user, address pool) internal virtual view returns(address token0, address token1,
uint256 token0Balance, uint256 token1Balance, uint256 lpBalance) {
lpBalance = IERC20(pool).balanceOf(user);
uint256 lpTotalSupply = IERC20(pool).totalSupply();

address[] memory tokens = IGammaPool(pool).tokens();
token0 = tokens[0];
token1 = tokens[1];

(uint128[] memory cfmmReserves,, uint256 cfmmTotalSupply) = IGammaPool(pool).getLatestCFMMBalances();
(,uint256 lpTokenBalance,,,,) = IGammaPool(pool).getPoolBalances();

token0Balance = lpBalance * lpTokenBalance * uint256(cfmmReserves[0]) / (cfmmTotalSupply * lpTotalSupply);
token1Balance = lpBalance * lpTokenBalance * uint256(cfmmReserves[1]) / (cfmmTotalSupply * lpTotalSupply);
}

/// @inheritdoc ILPViewer
function lpBalanceByPools(address user, address[] calldata pools) public virtual override view returns(address[] memory token0,
address[] memory token1, uint256[] memory token0Balance, uint256[] memory token1Balance, uint256[] memory lpBalance) {
uint256 len = pools.length;
token0 = new address[](len);
token1 = new address[](len);
token0Balance = new uint256[](len);
token1Balance= new uint256[](len) ;
lpBalance = new uint256[](len);
for(uint256 i; i < len;) {
(token0[i], token1[i], token0Balance[i], token1Balance[i], lpBalance[i]) = _lpBalanceByPool(user, pools[i]);
unchecked{
++i;
}
}
}
}
38 changes: 38 additions & 0 deletions contracts/test/TestGammaPool3.sol
@@ -0,0 +1,38 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

import "./TestGammaPool2.sol";

contract TestGammaPool3 is TestGammaPool2 {

constructor(uint16 protocolId_, address factory_, address borrowStrategy_, address repayStrategy_, address rebalanceStrategy_,
address shortStrategy_, address singleLiquidationStrategy_, address batchLiquidationStrategy_, address viewer_)
TestGammaPool2(protocolId_, factory_, borrowStrategy_, repayStrategy_, rebalanceStrategy_, shortStrategy_,
singleLiquidationStrategy_, batchLiquidationStrategy_, viewer_) {
}

function mintTo(address user, uint256 amount) external {
_mint(user, amount);
}

function initialize(address _cfmm, address[] calldata _tokens, uint8[] calldata _decimals, uint72 _minBorrow, bytes calldata) external virtual override {
cfmm = _cfmm;
tokens_ = _tokens;
decimals_ = _decimals;
tester = msg.sender;
owner = msg.sender;
}

function getLatestCFMMBalances() external virtual override view returns(uint128[] memory cfmmReserves, uint256 cfmmInvariant, uint256 cfmmTotalSupply) {
cfmmReserves = new uint128[](2);
cfmmReserves[0] = uint128(1000 * 1e18);
cfmmReserves[1] = uint128(100000 * 1e18);
cfmmInvariant = 10000 * 1e18;
cfmmTotalSupply = 10000 * 1e18;
}

function getPoolBalances() external virtual override view returns(uint128[] memory tokenBalances, uint256 lpTokenBalance, uint256 lpTokenBorrowed,
uint256 lpTokenBorrowedPlusInterest, uint256 borrowedInvariant, uint256 lpInvariant) {
lpTokenBalance = 5000 * 1e18;
}
}
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "@gammaswap/v1-periphery",
"version": "1.2.2",
"version": "1.2.3",
"description": "Periphery contracts for the GammaSwap V1 protocol",
"homepage": "https://gammaswap.com",
"scripts": {
Expand Down

0 comments on commit 85d83d2

Please sign in to comment.