Skip to content

Commit

Permalink
Merge pull request #188 from 1inch/feature/SC-1269
Browse files Browse the repository at this point in the history
[SC-1269] Patch SolidlyOracle
  • Loading branch information
zZoMROT authored Sep 19, 2024
2 parents cfe9a5e + 26e9352 commit 81284ea
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 63 deletions.
9 changes: 0 additions & 9 deletions contracts/interfaces/ISolidlyFactory.sol

This file was deleted.

61 changes: 53 additions & 8 deletions contracts/oracles/SolidlyOracle.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pragma solidity 0.8.23;

import "@openzeppelin/contracts/utils/math/Math.sol";
import "@openzeppelin/contracts/interfaces/IERC20Metadata.sol";
import "./OracleBase.sol";
import "../interfaces/IOracle.sol";
import "../interfaces/IUniswapV2Pair.sol";
Expand All @@ -23,21 +24,34 @@ contract SolidlyOracle is IOracle {
}

function getRate(IERC20 srcToken, IERC20 dstToken, IERC20 connector, uint256 thresholdFilter) external view override returns (uint256 rate, uint256 weight) {
uint256 srcDecimals = IERC20Metadata(address(srcToken)).decimals();
uint256 dstDecimals = IERC20Metadata(address(dstToken)).decimals();
if (connector == _NONE) {
(rate, weight) = _getWeightedRate(srcToken, dstToken, thresholdFilter);
(rate, weight) = _getWeightedRate(srcToken, dstToken, srcDecimals, dstDecimals, thresholdFilter);
} else {
(uint256 rateC0, uint256 weightC0) = _getWeightedRate(srcToken, connector, thresholdFilter);
(uint256 rateC1, uint256 weightC1) = _getWeightedRate(connector, dstToken, thresholdFilter);
uint256 connectorDecimals = IERC20Metadata(address(connector)).decimals();
(uint256 rateC0, uint256 weightC0) = _getWeightedRate(srcToken, connector, srcDecimals, connectorDecimals, thresholdFilter);
(uint256 rateC1, uint256 weightC1) = _getWeightedRate(connector, dstToken, connectorDecimals, dstDecimals, thresholdFilter);
rate = rateC0 * rateC1 / 1e18;
weight = Math.min(weightC0, weightC1);
}
}

function _getWeightedRate(IERC20 srcToken, IERC20 dstToken, uint256 thresholdFilter) internal view returns (uint256 rate, uint256 weight) {
function _getWeightedRate(IERC20 srcToken, IERC20 dstToken, uint256 srcDecimals, uint256 dstDecimals, uint256 thresholdFilter) internal view returns (uint256 rate, uint256 weight) {
OraclePrices.Data memory ratesAndWeights = OraclePrices.init(2);
(uint256 b0, uint256 b1) = _getBalances(srcToken, dstToken, true);
if (b0 > 0) {
ratesAndWeights.append(OraclePrices.OraclePrice(Math.mulDiv(b1, 1e18, b0), (b0 * b1).sqrt()));
uint256 _x = (b0 * 1e18) / 10 ** srcDecimals; // b0 converted to 1e18 decimals format
uint256 _y = (b1 * 1e18) / 10 ** dstDecimals; // b1 converted to 1e18 decimals format
uint256 _a = (_x * _y) / 1e18;
uint256 _b = ((_x * _x) / 1e18 + (_y * _y) / 1e18);
uint256 xy = (_a * _b) / 1e18;

(uint256 y, bool error) = _getY(1e18 + _x , xy, _y); // calculation for 1 src token converted to 1e18 decimals format
if (!error) {
uint256 amountOut = b1 - y / (10 ** (18 - dstDecimals));
ratesAndWeights.append(OraclePrices.OraclePrice(amountOut, (b0 * b1).sqrt()));
}
}
(b0, b1) = _getBalances(srcToken, dstToken, false);
if (b0 > 0) {
Expand All @@ -46,8 +60,41 @@ contract SolidlyOracle is IOracle {
(rate, weight) = ratesAndWeights.getRateAndWeight(thresholdFilter);
}

// Helper function to compute 'y' based on the stable swap invariant
function _getY(uint256 x0, uint256 xy, uint256 y0) internal pure returns (uint256 y, bool error) {
y = y0;
for (uint256 i = 0; i < 255; i++) {
uint256 k = _f(x0, y);
if (k < xy) {
uint256 dy = ((xy - k) * 1e18) / _d(x0, y);
if (dy == 0) {
return (y, false);
}
y = y + dy;
} else {
uint256 dy = ((k - xy) * 1e18) / _d(x0, y);
if (dy == 0) {
return (y, false);
}
y = y - dy;
}
}
return (0, true);
}

// Internal functions '_f' and '_d' as per the original code
function _f(uint256 x0, uint256 y) internal pure returns (uint256) {
uint256 _a = (x0 * y) / 1e18;
uint256 _b = ((x0 * x0) / 1e18 + (y * y) / 1e18);
return (_a * _b) / 1e18;
}

function _d(uint256 x0, uint256 y) internal pure returns (uint256) {
return (3 * x0 * ((y * y) / 1e18)) / 1e18 + ((((x0 * x0) / 1e18) * x0) / 1e18);
}

// calculates the CREATE2 address for a pair without making any external calls
function _pairFor(IERC20 tokenA, IERC20 tokenB, bool stable) private view returns (address pair) {
function _pairFor(IERC20 tokenA, IERC20 tokenB, bool stable) internal virtual view returns (address pair) {
pair = address(uint160(uint256(keccak256(abi.encodePacked(
hex"ff",
FACTORY,
Expand All @@ -62,8 +109,6 @@ contract SolidlyOracle is IOracle {
if (success && data.length == 96) {
(srcBalance, dstBalance) = abi.decode(data, (uint256, uint256));
(srcBalance, dstBalance) = srcToken == token0 ? (srcBalance, dstBalance) : (dstBalance, srcBalance);
} else {
(srcBalance, dstBalance) = (1, 0);
}
}
}
44 changes: 0 additions & 44 deletions contracts/oracles/SolidlyOracleNoCreate2.sol

This file was deleted.

22 changes: 22 additions & 0 deletions contracts/oracles/SolidlyOracleZksync.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-License-Identifier: MIT

pragma solidity 0.8.23;

import "./SolidlyOracle.sol";

contract SolidlyOracleZksync is SolidlyOracle {
/// @dev keccak256("zksyncCreate2")
bytes32 public constant CREATE2_PREFIX = 0x2020dba91b30cc0006188af794c2fb30dd8520db7e2c088b7fc7c103c00ca494;

constructor(address _factory, bytes32 _initcodeHash) SolidlyOracle(_factory, _initcodeHash) {}

function _pairFor(IERC20 tokenA, IERC20 tokenB, bool stable) internal override view returns (address pair) {
pair = address(uint160(uint256(keccak256(abi.encodePacked(
CREATE2_PREFIX,
FACTORY,
keccak256(abi.encodePacked(tokenA, tokenB, stable)),
INITCODE_HASH,
keccak256(abi.encodePacked(""))
)))));
}
}
1 change: 1 addition & 0 deletions test/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const tokens = {
WETH: '0x4200000000000000000000000000000000000006',
DAI: '0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb',
axlUSDC: '0xEB466342C4d449BC9f53A865D5Cb90586f405215',
rETH: '0xb6fe221fe9eef5aba221c348ba20a1bf5e73624c',
},
optimistic: {
WETH: '0x4200000000000000000000000000000000000006',
Expand Down
21 changes: 19 additions & 2 deletions test/oracles/SolidlyOracle.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,8 @@ describe('SolidlyOracle', function () {
});

async function initContracts () {
const velocimeterV2Oracle = await deployContract('SolidlyOracle', [VelocimeterV2.factory, VelocimeterV2.initcodeHash]);
const uniswapV3Oracle = await deployContract('UniswapV3LikeOracle', [UniswapV3Base.factory, UniswapV3Base.initcodeHash, UniswapV3Base.fees]);
return { velocimeterV2Oracle, uniswapV3Oracle };
return { uniswapV3Oracle };
}

async function deployVelocimeterV2 () {
Expand Down Expand Up @@ -56,6 +55,24 @@ describe('SolidlyOracle', function () {
const { oracle, uniswapV3Oracle } = await loadFixture(fixture);
await testRate(tokens.base.DAI, tokens.base.WETH, tokens.NONE, oracle, uniswapV3Oracle, 0.1);
});

it('rETH -> WETH', async function () {
const { oracle, uniswapV3Oracle } = await loadFixture(fixture);
// Test only for Aerodrome
if (await oracle.FACTORY() !== Aerodrome.factory) {
this.skip();
}
await testRate(tokens.base.rETH, tokens.base.WETH, tokens.NONE, oracle, uniswapV3Oracle, 0.1);
});

it('WETH -> rETH', async function () {
const { oracle, uniswapV3Oracle } = await loadFixture(fixture);
// Test only for Aerodrome
if (await oracle.FACTORY() !== Aerodrome.factory) {
this.skip();
}
await testRate(tokens.base.WETH, tokens.base.rETH, tokens.NONE, oracle, uniswapV3Oracle, 0.1);
});
}

describe('VelocimeterV2', function () {
Expand Down

0 comments on commit 81284ea

Please sign in to comment.