Skip to content

Commit

Permalink
adding UniV3 support for indices
Browse files Browse the repository at this point in the history
  • Loading branch information
ferencdg committed Sep 20, 2023
1 parent ca2165b commit 59f8c1f
Show file tree
Hide file tree
Showing 18 changed files with 324 additions and 127 deletions.
3 changes: 2 additions & 1 deletion .solhint.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"func-visibility": ["error", { "ignoreConstructors": true }],
"private-vars-leading-underscore": "off",
"no-empty-blocks": "off",
"not-rely-on-time": "off"
"not-rely-on-time": "off",
"no-console": "off"
}
}
15 changes: 12 additions & 3 deletions configs/arbitrum/deploy/strategy/IndexArbitrumMarketCap.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@
}
],
"swapRoutes": [
{
"token": { "$ref": "../../../../constants/arbitrum/addresses/Tokens.json#/usdc" },
"router": { "$ref": "../../../../constants/arbitrum/addresses/UniswapV3.json#/router" },
"dex": 6,
"pairData": {
"pair": { "$ref": "../../../../constants/arbitrum/addresses/UniswapV3.json#/wETH_usdc" },
"data": "0x0000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000004000000000000000000000000061fFE014bA17989E743c5F6cB21bF9697530B21e00000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000002710"
}
},
{
"token": { "$ref": "../../../../constants/arbitrum/addresses/Tokens.json#/usdc" },
"router": { "$ref": "../../../../constants/arbitrum/addresses/TraderJoe.json#/router_v2_1" },
Expand Down Expand Up @@ -104,17 +113,17 @@
"topLevel": true
},
{
"name": "IndexStrategyMint",
"name": "IndexStrategyManagement",
"dependencies": ["IndexStrategyUtils"],
"topLevel": true
},
{
"name": "IndexStrategyBurn",
"name": "IndexStrategyMint",
"dependencies": ["IndexStrategyUtils"],
"topLevel": true
},
{
"name": "IndexStrategyManagement",
"name": "IndexStrategyBurn",
"dependencies": ["IndexStrategyUtils"],
"topLevel": true
}
Expand Down
5 changes: 5 additions & 0 deletions constants/arbitrum/addresses/UniswapV3.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"router": "0xE592427A0AEce92De3Edee1F18E0157C05861564",
"quoterV2": "0x61fFE014bA17989E743c5F6cB21bF9697530B21e",
"wETH_usdc": "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8"
}
33 changes: 0 additions & 33 deletions contracts/index/bases/IndexArbitrum.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,6 @@ import { SwapAdapter } from "../libraries/SwapAdapter.sol";
* @dev Contract IndexArbitrum is an extension of IndexStrategyUpgradeable.
*/
contract IndexArbitrum is IndexStrategyUpgradeable {
/**
* @dev Calculates the equity valuation.
* @param maximize Boolean value to maximize.
* @param includeAmmPrice Boolean value to include AMM price.
* @return The equity valuation as a uint256.
*/
function equityValuation(bool maximize, bool includeAmmPrice)
public
view
override
returns (uint256)
{
uint256 totalSupply = indexToken.totalSupply();

if (totalSupply == 0) {
return 0;
}

uint256 amountWNATIVEUnit = _getAmountWNATIVEFromExactIndex(
Constants.PRECISION
);

uint256 priceWNATIVE = oracle.getPrice(
wNATIVE,
maximize,
includeAmmPrice
);

return
(amountWNATIVEUnit * priceWNATIVE * totalSupply) /
(Constants.DECIMALS * Constants.PRECISION);
}

/**
* @dev Adds a swap route.
* @param token The token address.
Expand Down
33 changes: 0 additions & 33 deletions contracts/index/bases/IndexAvalanche.sol
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,6 @@ import { SwapAdapter } from "../libraries/SwapAdapter.sol";
* @dev Contract IndexAvalanche is an extension of IndexStrategyUpgradeable.
*/
contract IndexAvalanche is IndexStrategyUpgradeable {
/**
* @dev Calculates the equity valuation.
* @param maximize Boolean value to maximize.
* @param includeAmmPrice Boolean value to include AMM price.
* @return The equity valuation as a uint256.
*/
function equityValuation(bool maximize, bool includeAmmPrice)
public
view
override
returns (uint256)
{
uint256 totalSupply = indexToken.totalSupply();

if (totalSupply == 0) {
return 0;
}

uint256 amountWNATIVEUnit = _getAmountWNATIVEFromExactIndex(
Constants.PRECISION
);

uint256 priceWNATIVE = oracle.getPrice(
wNATIVE,
maximize,
includeAmmPrice
);

return
(amountWNATIVEUnit * priceWNATIVE * totalSupply) /
(Constants.DECIMALS * Constants.PRECISION);
}

/**
* @dev Adds a swap route.
* @param token The token address.
Expand Down
82 changes: 65 additions & 17 deletions contracts/index/bases/IndexStrategyUpgradeable.sol
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,39 @@ abstract contract IndexStrategyUpgradeable is
}
}

/**
* @dev Calculates the equity valuation.
* @param maximize Boolean value to maximize.
* @param includeAmmPrice Boolean value to include AMM price.
* @return The equity valuation as a uint256.
*/
function equityValuation(bool maximize, bool includeAmmPrice)
public
view
override
returns (uint256)
{
uint256 totalSupply = indexToken.totalSupply();

if (totalSupply == 0) {
return 0;
}

uint256 amountWNATIVEUnit = _getAmountWNATIVEFromExactIndexView(
Constants.PRECISION
);

uint256 priceWNATIVE = oracle.getPrice(
wNATIVE,
maximize,
includeAmmPrice
);

return
(amountWNATIVEUnit * priceWNATIVE * totalSupply) /
(Constants.DECIMALS * Constants.PRECISION);
}

/**
* @dev Initializes the IndexStrategyUpgradeable contract.
* @param initParams The parameters needed for initialization.
Expand Down Expand Up @@ -368,7 +401,6 @@ abstract contract IndexStrategyUpgradeable is
*/
function getAmountIndexFromToken(address token, uint256 amountTokenMax)
external
view
onlyWhitelistedToken(token)
returns (uint256 amountIndex, uint256 amountToken)
{
Expand Down Expand Up @@ -403,7 +435,6 @@ abstract contract IndexStrategyUpgradeable is
*/
function getAmountIndexFromNATIVE(uint256 amountNATIVEMax)
external
view
returns (uint256 amountIndex, uint256 amountNATIVE)
{
MintingData memory mintingData = IndexStrategyMint
Expand Down Expand Up @@ -437,7 +468,6 @@ abstract contract IndexStrategyUpgradeable is
*/
function getAmountTokenFromExactIndex(address token, uint256 amountIndex)
external
view
onlyWhitelistedToken(token)
returns (uint256 amountToken)
{
Expand All @@ -460,7 +490,6 @@ abstract contract IndexStrategyUpgradeable is
*/
function getAmountNATIVEFromExactIndex(uint256 amountIndex)
external
view
returns (uint256 amountNATIVE)
{
amountNATIVE = _getAmountWNATIVEFromExactIndex(amountIndex);
Expand Down Expand Up @@ -613,18 +642,6 @@ abstract contract IndexStrategyUpgradeable is
return whitelistedTokens;
}

/**
* @dev Calculates the equity valuation of the index strategy.
* @param maximize A boolean indicating whether to maximize the valuation.
* @param includeAmmPrice A boolean indicating whether to include the AMM price in the valuation.
* @return The equity valuation of the index strategy.
*/
function equityValuation(bool maximize, bool includeAmmPrice)
public
view
virtual
returns (uint256);

/**
* @dev Checks if a token is whitelisted.
* @param token The address of the token to check.
Expand All @@ -647,7 +664,6 @@ abstract contract IndexStrategyUpgradeable is
*/
function _getAmountWNATIVEFromExactIndex(uint256 amountIndex)
internal
view
returns (uint256 amountWNATIVE)
{
for (uint256 i = 0; i < components.length; i++) {
Expand All @@ -671,6 +687,38 @@ abstract contract IndexStrategyUpgradeable is
}
}

/**
* @dev Calculates the amount of wNATIVE received from the exact index amount.
* @param amountIndex The exact index amount.
* @return amountWNATIVE The amount of wNATIVE received.
*/
function _getAmountWNATIVEFromExactIndexView(uint256 amountIndex)
internal
view
returns (uint256 amountWNATIVE)
{
for (uint256 i = 0; i < components.length; i++) {
if (weights[components[i]] == 0) {
continue;
}

uint256 amountComponent = (amountIndex * weights[components[i]]) /
Constants.PRECISION;

(uint256 amountWNATIVEOut, ) = IndexStrategyUtils
.getAmountOutMaxView(
routers[components[i]],
amountComponent,
components[i],
wNATIVE,
dexs,
pairData
);

amountWNATIVE += amountWNATIVEOut;
}
}

/**
* @dev Sets the weight of a token.
* @param token The token address.
Expand Down
2 changes: 0 additions & 2 deletions contracts/index/interfaces/IIndexStrategy.sol
Original file line number Diff line number Diff line change
Expand Up @@ -40,12 +40,10 @@ interface IIndexStrategy {

function getAmountIndexFromToken(address token, uint256 amountTokenMax)
external
view
returns (uint256 amountIndex, uint256 amountToken);

function getAmountTokenFromExactIndex(address token, uint256 amountIndex)
external
view
returns (uint256 amountToken);

function setOracle(address oracle) external;
Expand Down
1 change: 1 addition & 0 deletions contracts/index/libraries/Errors.sol
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ library Errors {
// SwapAdapter errors.
error SwapAdapter_WrongDEX(uint8 dex);
error SwapAdapter_WrongPair(address tokenIn, address tokenOut);
error SwapAdapter_WrongPathLength(uint256 wrongLength);

// IndexOracle errors.
error Oracle_TokenNotSupported(address token);
Expand Down
5 changes: 2 additions & 3 deletions contracts/index/libraries/IndexStrategyMint.sol
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,6 @@ library IndexStrategyMint {
mapping(address => address[]) storage routers
)
public
view
returns (
uint256 amountToken,
address bestRouter,
Expand Down Expand Up @@ -285,7 +284,7 @@ library IndexStrategyMint {
storage pairData,
mapping(address => SwapAdapter.DEX) storage dexs,
mapping(address => uint256) storage weights
) public view returns (MintingData memory mintingData) {
) public returns (MintingData memory mintingData) {
MintingData memory mintingDataUnit = getMintingDataForExactIndex(
Constants.PRECISION,
dexs,
Expand Down Expand Up @@ -358,7 +357,7 @@ library IndexStrategyMint {
address[] memory components,
mapping(address => address[]) storage routers,
address wNATIVE
) internal view returns (MintingData memory mintingData) {
) internal returns (MintingData memory mintingData) {
mintingData.amountIndex = amountIndex;
mintingData.amountWNATIVEs = new uint256[](components.length);
mintingData.bestRouters = new address[](components.length);
Expand Down
54 changes: 52 additions & 2 deletions contracts/index/libraries/IndexStrategyUtils.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ library IndexStrategyUtils {
mapping(address => SwapAdapter.DEX) storage dexs,
mapping(address => mapping(address => mapping(address => SwapAdapter.PairData)))
storage pairData
) external view returns (uint256 amountOutMax, address bestRouter) {
) external returns (uint256 amountOutMax, address bestRouter) {
if (tokenIn == tokenOut) {
return (amountIn, address(0));
}
Expand Down Expand Up @@ -54,6 +54,56 @@ library IndexStrategyUtils {
}
}

/**
* @dev Calculates the maximum amount of `tokenOut` tokens that can be received for a given `amountIn` of `tokenIn` tokens,
* and identifies the best router to use for the swap among a list of routers.
* @param routers The list of router addresses to consider for the swap.
* @param amountIn The amount of `tokenIn` tokens.
* @param tokenIn The address of the token to be swapped.
* @param tokenOut The address of the token to receive.
* @return amountOutMax The maximum amount of `tokenOut` tokens that can be received for the given `amountIn`.
* @return bestRouter The address of the best router to use for the swap.
*/
function getAmountOutMaxView(
address[] memory routers,
uint256 amountIn,
address tokenIn,
address tokenOut,
mapping(address => SwapAdapter.DEX) storage dexs,
mapping(address => mapping(address => mapping(address => SwapAdapter.PairData)))
storage pairData
) external view returns (uint256 amountOutMax, address bestRouter) {
if (tokenIn == tokenOut) {
return (amountIn, address(0));
}

if (routers.length == 0) {
revert Errors.Index_WrongPair(tokenIn, tokenOut);
}

amountOutMax = type(uint256).min;

for (uint256 i = 0; i < routers.length; i++) {
address router = routers[i];

// UniswapV3 estimation functions are not view, so skipping them
if (dexs[router] == SwapAdapter.DEX.UniswapV3) continue;

uint256 amountOut = SwapAdapter
.Setup(
dexs[router],
router,
pairData[router][tokenIn][tokenOut]
)
.getAmountOutView(amountIn, tokenIn, tokenOut);

if (amountOut > amountOutMax) {
amountOutMax = amountOut;
bestRouter = router;
}
}
}

/**
* @dev Calculates the minimum amount of `tokenIn` tokens required to receive a given `amountOut` of `tokenOut` tokens,
* and identifies the best router to use for the swap among a list of routers.
Expand All @@ -72,7 +122,7 @@ library IndexStrategyUtils {
mapping(address => SwapAdapter.DEX) storage dexs,
mapping(address => mapping(address => mapping(address => SwapAdapter.PairData)))
storage pairData
) external view returns (uint256 amountInMin, address bestRouter) {
) external returns (uint256 amountInMin, address bestRouter) {
if (tokenIn == tokenOut) {
return (amountOut, address(0));
}
Expand Down
Loading

0 comments on commit 59f8c1f

Please sign in to comment.