-
Notifications
You must be signed in to change notification settings - Fork 7
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SolidlyAdapter #186
Open
a17
wants to merge
1
commit into
main
Choose a base branch
from
169-solidly-amm-adapter
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
SolidlyAdapter #186
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.23; | ||
|
||
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; | ||
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; | ||
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; | ||
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; | ||
import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; | ||
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; | ||
import {IControllable} from "../interfaces/IControllable.sol"; | ||
import {IAmmAdapter} from "../interfaces/IAmmAdapter.sol"; | ||
import {Controllable} from "../core/base/Controllable.sol"; | ||
import {AmmAdapterIdLib} from "./libs/AmmAdapterIdLib.sol"; | ||
import {ISolidlyPool} from "../integrations/solidly/ISolidlyPool.sol"; | ||
import {ConstantsLib} from "../core/libs/ConstantsLib.sol"; | ||
|
||
/// @title AMM adapter for Solidly forks | ||
/// @author Alien Deployer (https://github.com/a17) | ||
contract SolidlyAdapter is Controllable, IAmmAdapter { | ||
using SafeERC20 for IERC20; | ||
|
||
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | ||
/* CONSTANTS */ | ||
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | ||
|
||
/// @inheritdoc IControllable | ||
string public constant VERSION = "1.0.0"; | ||
|
||
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | ||
/* INITIALIZATION */ | ||
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | ||
|
||
/// @inheritdoc IAmmAdapter | ||
function init(address platform_) external initializer { | ||
__Controllable_init(platform_); | ||
} | ||
|
||
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | ||
/* USER ACTIONS */ | ||
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | ||
|
||
/// @inheritdoc IAmmAdapter | ||
//slither-disable-next-line reentrancy-events | ||
function swap( | ||
address pool, | ||
address tokenIn, | ||
address tokenOut, | ||
address recipient, | ||
uint priceImpactTolerance | ||
) external { | ||
uint amountIn = IERC20(tokenIn).balanceOf(address(this)); | ||
uint amountOut = ISolidlyPool(pool).getAmountOut(amountIn, tokenIn); | ||
uint balanceBefore = IERC20(tokenOut).balanceOf(recipient); | ||
|
||
uint amount1 = 10 ** IERC20Metadata(tokenIn).decimals(); | ||
uint priceBefore = getPrice(pool, tokenIn, tokenOut, amount1); | ||
|
||
uint amount0Out; | ||
uint amount1Out; | ||
{ | ||
(address token0,) = _sortTokens(tokenIn, tokenOut); | ||
(amount0Out, amount1Out) = tokenIn == token0 ? (uint(0), amountOut) : (amountOut, uint(0)); | ||
|
||
IERC20(tokenIn).safeTransfer(pool, amountIn); | ||
} | ||
ISolidlyPool(pool).swap(amount0Out, amount1Out, recipient, new bytes(0)); | ||
|
||
uint priceAfter = getPrice(pool, tokenIn, tokenOut, amount1); | ||
uint priceImpact = (priceBefore - priceAfter) * ConstantsLib.DENOMINATOR / priceBefore; | ||
if (priceImpact >= priceImpactTolerance) { | ||
revert(string(abi.encodePacked("!PRICE ", Strings.toString(priceImpact)))); | ||
} | ||
|
||
uint balanceAfter = IERC20(tokenOut).balanceOf(recipient); | ||
emit SwapInPool( | ||
pool, | ||
tokenIn, | ||
tokenOut, | ||
recipient, | ||
priceImpactTolerance, | ||
amountIn, | ||
balanceAfter > balanceBefore ? balanceAfter - balanceBefore : 0 | ||
); | ||
} | ||
|
||
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | ||
/* VIEW FUNCTIONS */ | ||
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | ||
|
||
/// @inheritdoc IAmmAdapter | ||
function ammAdapterId() external pure returns (string memory) { | ||
return AmmAdapterIdLib.SOLIDLY; | ||
} | ||
|
||
/// @inheritdoc IAmmAdapter | ||
function poolTokens(address pool) public view returns (address[] memory tokens) { | ||
tokens = new address[](2); | ||
tokens[0] = ISolidlyPool(pool).token0(); | ||
tokens[1] = ISolidlyPool(pool).token1(); | ||
} | ||
|
||
/// @inheritdoc IAmmAdapter | ||
function getLiquidityForAmounts( | ||
address pool, | ||
uint[] memory amounts | ||
) external view returns (uint liquidity, uint[] memory amountsConsumed) { | ||
amountsConsumed = new uint[](2); | ||
(uint reserveA, uint reserveB) = _getReserves(pool); | ||
uint amountBOptimal = _quoteAddLiquidity(amounts[0], reserveA, reserveB); | ||
if (amountBOptimal <= amounts[1]) { | ||
(amountsConsumed[0], amountsConsumed[1]) = (amounts[0], amountBOptimal); | ||
} else { | ||
uint amountAOptimal = _quoteAddLiquidity(amounts[1], reserveB, reserveA); | ||
(amountsConsumed[0], amountsConsumed[1]) = (amountAOptimal, amounts[1]); | ||
} | ||
|
||
uint _totalSupply = ISolidlyPool(pool).totalSupply(); | ||
liquidity = Math.min(amountsConsumed[0] * _totalSupply / reserveA, amountsConsumed[1] * _totalSupply / reserveB); | ||
} | ||
|
||
/// @inheritdoc IAmmAdapter | ||
function getProportions(address pool) external view returns (uint[] memory props) { | ||
props = new uint[](2); | ||
if (ISolidlyPool(pool).stable()) { | ||
address token1 = ISolidlyPool(pool).token1(); | ||
uint token1Decimals = IERC20Metadata(token1).decimals(); | ||
uint token1Price = getPrice(pool, token1, address(0), 10 ** token1Decimals); | ||
(uint reserve0, uint reserve1) = _getReserves(pool); | ||
uint reserve1Priced = reserve1 * token1Price / 10 ** token1Decimals; | ||
uint totalPriced = reserve0 + reserve1Priced; | ||
props[0] = reserve0 * 1e18 / totalPriced; | ||
props[1] = reserve1Priced * 1e18 / totalPriced; | ||
} else { | ||
props[0] = 5e17; | ||
props[1] = 5e17; | ||
} | ||
} | ||
Comment on lines
+122
to
+137
Check warning Code scanning / Slither Divide before multiply Medium
SolidlyAdapter.getProportions(address) performs a multiplication on the result of a division:
- reserve1Priced = reserve1 * token1Price / 10 ** token1Decimals - props[1] = reserve1Priced * 1e18 / totalPriced |
||
|
||
/// @inheritdoc IAmmAdapter | ||
function getPrice(address pool, address tokenIn, address, /*tokenOut*/ uint amount) public view returns (uint) { | ||
return ISolidlyPool(pool).getAmountOut(amount, tokenIn); | ||
} | ||
|
||
/// @inheritdoc IERC165 | ||
function supportsInterface(bytes4 interfaceId) public view override(Controllable, IERC165) returns (bool) { | ||
return interfaceId == type(IAmmAdapter).interfaceId || super.supportsInterface(interfaceId); | ||
} | ||
|
||
/*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/ | ||
/* INTERNAL LOGIC */ | ||
/*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/ | ||
|
||
/// @dev Returns sorted token addresses, used to handle return values from pairs sorted in this order | ||
function _sortTokens(address tokenA, address tokenB) internal pure returns (address token0, address token1) { | ||
(token0, token1) = tokenA < tokenB ? (tokenA, tokenB) : (tokenB, tokenA); | ||
} | ||
|
||
function _getReserves(address pool) internal view returns (uint reserveA, uint reserveB) { | ||
address[] memory tokens = poolTokens(pool); | ||
(address token0,) = _sortTokens(tokens[0], tokens[1]); | ||
(uint reserve0, uint reserve1,) = ISolidlyPool(pool).getReserves(); | ||
(reserveA, reserveB) = tokens[0] == token0 ? (reserve0, reserve1) : (reserve1, reserve0); | ||
} | ||
Comment on lines
+158
to
+163
Check warning Code scanning / Slither Unused return Medium
SolidlyAdapter._getReserves(address) ignores return value by (reserve0,reserve1,None) = ISolidlyPool(pool).getReserves()
|
||
|
||
function _quoteAddLiquidity(uint amountA, uint reserveA, uint reserveB) internal pure returns (uint) { | ||
return amountA * reserveB / reserveA; | ||
} | ||
} |
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,134 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.23; | ||
|
||
interface ISolidlyPool { | ||
/// @notice True if pool is stable, false if volatile | ||
function stable() external view returns (bool); | ||
|
||
/// @notice Returns the decimal (dec), reserves (r), stable (st), and tokens (t) of token0 and token1 | ||
function metadata() | ||
external | ||
view | ||
returns (uint dec0, uint dec1, uint r0, uint r1, bool st, address t0, address t1); | ||
|
||
/// @notice Claim accumulated but unclaimed fees (claimable0 and claimable1) | ||
function claimFees() external returns (uint, uint); | ||
|
||
/// @notice Returns [token0, token1] | ||
function tokens() external view returns (address, address); | ||
|
||
/// @notice Address of token in the pool with the lower address value | ||
function token0() external view returns (address); | ||
|
||
/// @notice Address of token in the poool with the higher address value | ||
function token1() external view returns (address); | ||
|
||
/// @notice Amount of token0 in pool | ||
function reserve0() external view returns (uint); | ||
|
||
/// @notice Amount of token1 in pool | ||
function reserve1() external view returns (uint); | ||
|
||
/** | ||
* @dev Returns the name of the token. | ||
*/ | ||
function name() external view returns (string memory); | ||
|
||
/** | ||
* @dev Returns the symbol of the token. | ||
*/ | ||
function symbol() external view returns (string memory); | ||
|
||
/** | ||
* @dev Returns the decimals places of the token. | ||
*/ | ||
function decimals() external view returns (uint8); | ||
|
||
/// @notice Returns the amount of tokens in existence. | ||
function totalSupply() external view returns (uint); | ||
|
||
/// @notice Amount of unclaimed, but claimable tokens from fees of token0 for an LP | ||
function claimable0(address) external view returns (uint); | ||
|
||
/// @notice Amount of unclaimed, but claimable tokens from fees of token1 for an LP | ||
function claimable1(address) external view returns (uint); | ||
|
||
/// @notice Get the amount of tokenOut given the amount of tokenIn | ||
/// @param amountIn Amount of token in | ||
/// @param tokenIn Address of token | ||
/// @return Amount out | ||
function getAmountOut(uint amountIn, address tokenIn) external view returns (uint); | ||
|
||
/// @notice Update reserves and, on the first call per block, price accumulators | ||
/// @return _reserve0 . | ||
/// @return _reserve1 . | ||
/// @return _blockTimestampLast . | ||
function getReserves() external view returns (uint _reserve0, uint _reserve1, uint _blockTimestampLast); | ||
|
||
/// @notice This low-level function should be called from a contract which performs important safety checks | ||
/// @param amount0Out Amount of token0 to send to `to` | ||
/// @param amount1Out Amount of token1 to send to `to` | ||
/// @param to Address to recieve the swapped output | ||
/// @param data Additional calldata for flashloans | ||
function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external; | ||
|
||
/// @notice This low-level function should be called from a contract which performs important safety checks | ||
/// standard uniswap v2 implementation | ||
/// @param to Address to receive token0 and token1 from burning the pool token | ||
/// @return amount0 Amount of token0 returned | ||
/// @return amount1 Amount of token1 returned | ||
function burn(address to) external returns (uint amount0, uint amount1); | ||
|
||
/// @notice This low-level function should be called by addLiquidity functions in Router.sol, which performs important safety checks | ||
/// standard uniswap v2 implementation | ||
/// @param to Address to receive the minted LP token | ||
/// @return liquidity Amount of LP token minted | ||
function mint(address to) external returns (uint liquidity); | ||
|
||
/// @notice Force balances to match reserves | ||
/// @param to Address to receive any skimmed rewards | ||
function skim(address to) external; | ||
|
||
/** | ||
* @dev Moves `amount` tokens from `from` to `to` using the | ||
* allowance mechanism. `amount` is then deducted from the caller's | ||
* allowance. | ||
* | ||
* Returns a boolean value indicating whether the operation succeeded. | ||
* | ||
* Emits a {Transfer} event. | ||
*/ | ||
function transferFrom(address from, address to, uint amount) external returns (bool); | ||
|
||
/** | ||
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, | ||
* given ``owner``'s signed approval. | ||
* | ||
* IMPORTANT: The same issues {IERC20-approve} has related to transaction | ||
* ordering also apply here. | ||
* | ||
* Emits an {Approval} event. | ||
* | ||
* Requirements: | ||
* | ||
* - `spender` cannot be the zero address. | ||
* - `deadline` must be a timestamp in the future. | ||
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` | ||
* over the EIP712-formatted function arguments. | ||
* - the signature must use ``owner``'s current nonce (see {nonces}). | ||
* | ||
* For more information on the signature format, see the | ||
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP | ||
* section]. | ||
*/ | ||
function permit( | ||
address owner, | ||
address spender, | ||
uint value, | ||
uint deadline, | ||
uint8 v, | ||
bytes32 r, | ||
bytes32 s | ||
) external; | ||
|
||
} |
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
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Check notice
Code scanning / Semgrep OSS
Semgrep Finding: rules.solidity.security.basic-arithmetic-underflow Note