Finalize Uniswap Oracle
Signed-off-by: AlienTornadosaurusHex <>
This commit is contained in:
parent
cd2b889c2f
commit
e8b44ac3f8
@ -14,7 +14,7 @@ import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/
|
|||||||
|
|
||||||
// Local Imports
|
// Local Imports
|
||||||
|
|
||||||
import { InstanceRegistry } from "../v1/tornado-proxy/InstanceRegistry.sol";
|
import { InstanceRegistry } from "./InstanceRegistry.sol";
|
||||||
|
|
||||||
import { IFeeOracle } from "./interfaces/IFeeOracle.sol";
|
import { IFeeOracle } from "./interfaces/IFeeOracle.sol";
|
||||||
|
|
||||||
@ -31,13 +31,20 @@ struct FeeData {
|
|||||||
* FeeOracleManager (formerly FeeManager).
|
* FeeOracleManager (formerly FeeManager).
|
||||||
*/
|
*/
|
||||||
contract FeeManagerLegacyStorage {
|
contract FeeManagerLegacyStorage {
|
||||||
uint24 public deprecatedUniswapTornPoolSwappingFee;
|
/* @dev From first contract */
|
||||||
uint32 public deprecatedUniswapTimePeriod;
|
uint24 private _deprecatedUniswapTornPoolSwappingFee;
|
||||||
|
|
||||||
|
/* @dev From first contract */
|
||||||
|
uint32 private _deprecatedUniswapTimePeriod;
|
||||||
|
|
||||||
|
/* @dev From first contract, the only value we keep alive */
|
||||||
uint24 public feeUpdateInterval;
|
uint24 public feeUpdateInterval;
|
||||||
|
|
||||||
mapping(ITornadoInstance => uint160) public oldFeesForInstance;
|
/* @dev From first contract, only used for initialization to preserve old values in the moment */
|
||||||
mapping(ITornadoInstance => uint256) public oldFeesForInstanceUpdateTime;
|
mapping(ITornadoInstance => uint160) internal _oldFeesForInstance;
|
||||||
|
|
||||||
|
/* @dev From first contract, only used for initialization to preserve old values in the moment */
|
||||||
|
mapping(ITornadoInstance => uint256) internal _oldFeesForInstanceUpdateTime;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -50,14 +57,14 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
|
|||||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
|
|
||||||
/* @dev Divide protocol fee by this to get the percent value */
|
/* @dev Divide protocol fee by this to get the percent value */
|
||||||
uint64 public constant PROTOCOL_FEE_DIVISOR = 1 ether;
|
uint64 public constant FEE_PERCENT_DIVISOR = 1 ether;
|
||||||
|
|
||||||
/* @dev The address of the TORN token */
|
|
||||||
address public immutable tornTokenAddress;
|
|
||||||
|
|
||||||
/* @dev The Governance Proxy address */
|
/* @dev The Governance Proxy address */
|
||||||
address public immutable governanceProxyAddress;
|
address public immutable governanceProxyAddress;
|
||||||
|
|
||||||
|
/* @dev The TORN token */
|
||||||
|
IERC20 public immutable torn;
|
||||||
|
|
||||||
/* @dev The InstanceRegistry contract */
|
/* @dev The InstanceRegistry contract */
|
||||||
InstanceRegistry public instanceRegistry;
|
InstanceRegistry public instanceRegistry;
|
||||||
|
|
||||||
@ -69,14 +76,16 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
|
|||||||
|
|
||||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
|
|
||||||
|
event FeeUpdateIntervalUpdated(uint24 newLimit);
|
||||||
event FeeUpdated(address indexed instance, uint256 newFee);
|
event FeeUpdated(address indexed instance, uint256 newFee);
|
||||||
event NewOracleSet(address indexed instance, address oracle);
|
event OracleUpdated(address indexed instance, address oracle);
|
||||||
event NewFeeUpdateIntervalSet(uint24 limit);
|
event InstanceFeePercentUpdated(address indexed instance, uint64 newFeePercent);
|
||||||
|
event InstanceRegistryUpdated(address newAddress);
|
||||||
|
|
||||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
|
|
||||||
constructor(address _tornTokenAddress, address _governanceProxyAddress) public {
|
constructor(address _tornTokenAddress, address _governanceProxyAddress) public {
|
||||||
tornTokenAddress = _tornTokenAddress;
|
torn = IERC20(_tornTokenAddress);
|
||||||
governanceProxyAddress = _governanceProxyAddress;
|
governanceProxyAddress = _governanceProxyAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,43 +99,46 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
|
|||||||
* making sure to not reference old data anywhere.
|
* making sure to not reference old data anywhere.
|
||||||
*/
|
*/
|
||||||
function initialize(
|
function initialize(
|
||||||
address _uniswapPoolFeeOracle,
|
address _uniswapV3FeeOracle,
|
||||||
address _instanceRegistryAddress,
|
address _instanceRegistryAddress,
|
||||||
address[] calldata _instanceAddresses,
|
address[] calldata _instanceAddresses,
|
||||||
uint256[] calldata _feePercents
|
uint256[] calldata _feePercents
|
||||||
) external onlyGovernance initializer {
|
) external onlyGovernance initializer {
|
||||||
|
// Get num of existing instances
|
||||||
uint256 numInstances = _instanceAddresses.length;
|
uint256 numInstances = _instanceAddresses.length;
|
||||||
|
|
||||||
for (uint256 i = 0; i < numInstances; i++) {
|
for (uint256 i = 0; i < numInstances; i++) {
|
||||||
|
// For each instance
|
||||||
ITornadoInstance instance = ITornadoInstance(_instanceAddresses[i]);
|
ITornadoInstance instance = ITornadoInstance(_instanceAddresses[i]);
|
||||||
|
|
||||||
|
// Store it's old data and the percent fees which will Governance will command
|
||||||
feeDataForInstance[instance] = FeeData({
|
feeDataForInstance[instance] = FeeData({
|
||||||
feeAmount: oldFeesForInstance[instance],
|
feeAmount: _oldFeesForInstance[instance],
|
||||||
feePercent: uint64(_feePercents[i]),
|
feePercent: uint64(_feePercents[i]),
|
||||||
lastUpdated: uint32(oldFeesForInstanceUpdateTime[instance])
|
lastUpdated: uint32(_oldFeesForInstanceUpdateTime[instance])
|
||||||
});
|
});
|
||||||
|
|
||||||
instanceFeeOracles[instance] = IFeeOracle(_uniswapPoolFeeOracle);
|
// All old pools use the uniswap fee oracle
|
||||||
|
instanceFeeOracles[instance] = IFeeOracle(_uniswapV3FeeOracle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Finally also store the instance registry
|
||||||
instanceRegistry = InstanceRegistry(_instanceRegistryAddress);
|
instanceRegistry = InstanceRegistry(_instanceRegistryAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
function instanceFeeWithUpdate(ITornadoInstance _instance) public virtual returns (uint160) {
|
function instanceFeeWithUpdate(ITornadoInstance _instance) public virtual returns (uint160) {
|
||||||
FeeData memory feeData = feeDataForInstance[_instance];
|
return updateFee(_instance, true);
|
||||||
|
|
||||||
if (feeUpdateInterval < now - feeData.lastUpdated) {
|
|
||||||
feeData.feeAmount = updateFee(_instance);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return feeData.feeAmount;
|
function updateAllFees(bool _respectFeeUpdateInterval)
|
||||||
|
public
|
||||||
|
virtual
|
||||||
|
returns (uint160[] memory newFees)
|
||||||
|
{
|
||||||
|
return updateFees(instanceRegistry.getAllInstances(), _respectFeeUpdateInterval);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateAllFees() public virtual returns (uint160[] memory newFees) {
|
function updateFees(ITornadoInstance[] memory _instances, bool _respectFeeUpdateInterval)
|
||||||
return updateFees(instanceRegistry.getAllInstanceAddresses());
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateFees(ITornadoInstance[] memory _instances)
|
|
||||||
public
|
public
|
||||||
virtual
|
virtual
|
||||||
returns (uint160[] memory newFees)
|
returns (uint160[] memory newFees)
|
||||||
@ -136,49 +148,74 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
|
|||||||
newFees = new uint160[](numInstances);
|
newFees = new uint160[](numInstances);
|
||||||
|
|
||||||
for (uint256 i = 0; i < numInstances; i++) {
|
for (uint256 i = 0; i < numInstances; i++) {
|
||||||
newFees[i] = updateFee(_instances[i]);
|
newFees[i] = updateFee(_instances[i], _respectFeeUpdateInterval);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateFee(ITornadoInstance _instance) public virtual returns (uint160 newFee) {
|
function updateFee(ITornadoInstance _instance, bool _respectFeeUpdateInterval)
|
||||||
|
public
|
||||||
|
virtual
|
||||||
|
returns (uint160)
|
||||||
|
{
|
||||||
|
// First get fee data
|
||||||
|
FeeData memory feeData = feeDataForInstance[_instance];
|
||||||
|
|
||||||
|
// Now update if we do not respect the interval or we respect it and are in the interval
|
||||||
|
if (!_respectFeeUpdateInterval || feeUpdateInterval < -feeData.lastUpdated + now) {
|
||||||
// This will revert if no contract is set
|
// This will revert if no contract is set
|
||||||
newFee = instanceFeeOracles[_instance].getFee();
|
feeData.feeAmount = instanceFeeOracles[_instance].getFee(
|
||||||
|
torn,
|
||||||
|
_instance,
|
||||||
|
instanceRegistry.getInstanceData(_instance),
|
||||||
|
feeData.feePercent,
|
||||||
|
FEE_PERCENT_DIVISOR
|
||||||
|
);
|
||||||
|
|
||||||
|
// Store
|
||||||
feeDataForInstance[_instance] = FeeData({
|
feeDataForInstance[_instance] = FeeData({
|
||||||
feeAmount: newFee,
|
feeAmount: feeData.feeAmount,
|
||||||
feePercent: feeDataForInstance[_instance].feePercent,
|
feePercent: feeData.feePercent,
|
||||||
lastUpdated: uint32(now)
|
lastUpdated: uint32(now)
|
||||||
});
|
});
|
||||||
|
|
||||||
emit FeeUpdated(address(_instance), newFee);
|
// Log
|
||||||
|
emit FeeUpdated(address(_instance), feeData.feeAmount);
|
||||||
|
}
|
||||||
|
|
||||||
return newFee;
|
// In any case return a value
|
||||||
|
return feeData.feeAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
|
|
||||||
function setInstanceRegistry(address _newInstanceRegistryAddress) external onlyGovernance {
|
function setInstanceRegistry(address _newInstanceRegistryProxyAddress) external onlyGovernance {
|
||||||
instanceRegistry = InstanceRegistry(_newInstanceRegistryAddress);
|
instanceRegistry = InstanceRegistry(_newInstanceRegistryProxyAddress);
|
||||||
|
emit InstanceRegistryUpdated(_newInstanceRegistryProxyAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFeeOracle(address _instanceAddress, address _oracleAddress) external onlyGovernance {
|
function setFeeOracle(address _instanceAddress, address _oracleAddress) external onlyGovernance {
|
||||||
ITornadoInstance instance = ITornadoInstance(_instanceAddress);
|
ITornadoInstance instance = ITornadoInstance(_instanceAddress);
|
||||||
IFeeOracle oracle = IFeeOracle(_oracleAddress);
|
IFeeOracle oracle = IFeeOracle(_oracleAddress);
|
||||||
|
FeeData memory feeData = feeDataForInstance[instance];
|
||||||
|
|
||||||
// Reverts if no oracle or does not conform to interface
|
// Reverts if no oracle or does not conform to interface
|
||||||
uint160 fee = oracle.getFee();
|
uint160 fee = oracle.getFee(
|
||||||
|
torn,
|
||||||
|
instance,
|
||||||
|
instanceRegistry.getInstanceData(instance),
|
||||||
|
feeData.feePercent,
|
||||||
|
FEE_PERCENT_DIVISOR
|
||||||
|
);
|
||||||
|
|
||||||
// Ok, set the oracle
|
// Ok, set the oracle
|
||||||
instanceFeeOracles[instance] = oracle;
|
instanceFeeOracles[instance] = oracle;
|
||||||
|
|
||||||
// Note down updated fee
|
// Note down updated fee
|
||||||
feeDataForInstance[instance] = FeeData({
|
feeDataForInstance[instance] =
|
||||||
feeAmount: fee,
|
FeeData({ feeAmount: fee, feePercent: feeData.feePercent, lastUpdated: uint32(now) });
|
||||||
feePercent: feeDataForInstance[instance].feePercent,
|
|
||||||
lastUpdated: uint32(now)
|
|
||||||
});
|
|
||||||
|
|
||||||
emit NewOracleSet(_instanceAddress, _oracleAddress);
|
// Logs
|
||||||
|
emit OracleUpdated(_instanceAddress, _oracleAddress);
|
||||||
emit FeeUpdated(_instanceAddress, fee);
|
emit FeeUpdated(_instanceAddress, fee);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -187,11 +224,12 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
|
|||||||
onlyGovernance
|
onlyGovernance
|
||||||
{
|
{
|
||||||
feeDataForInstance[_instance].feePercent = _newFeePercent;
|
feeDataForInstance[_instance].feePercent = _newFeePercent;
|
||||||
|
emit InstanceFeePercentUpdated(address(_instance), _newFeePercent);
|
||||||
}
|
}
|
||||||
|
|
||||||
function setFeeUpdateInterval(uint24 newLimit) external onlyGovernance {
|
function setFeeUpdateInterval(uint24 newLimit) external onlyGovernance {
|
||||||
feeUpdateInterval = newLimit;
|
feeUpdateInterval = newLimit;
|
||||||
emit NewFeeUpdateIntervalSet(newLimit);
|
emit FeeUpdateIntervalUpdated(newLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
@ -201,7 +239,13 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getUpdatedFeeForInstance(ITornadoInstance instance) public view virtual returns (uint160) {
|
function getUpdatedFeeForInstance(ITornadoInstance instance) public view virtual returns (uint160) {
|
||||||
return instanceFeeOracles[instance].getFee();
|
return instanceFeeOracles[instance].getFee(
|
||||||
|
torn,
|
||||||
|
instance,
|
||||||
|
instanceRegistry.getInstanceData(instance),
|
||||||
|
feeDataForInstance[instance].feePercent,
|
||||||
|
FEE_PERCENT_DIVISOR
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLastFeeForInstance(ITornadoInstance instance) public view virtual returns (uint160) {
|
function getLastFeeForInstance(ITornadoInstance instance) public view virtual returns (uint160) {
|
||||||
@ -212,17 +256,25 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
|
|||||||
return feeDataForInstance[instance].lastUpdated;
|
return feeDataForInstance[instance].lastUpdated;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getFeeDeviations() public view virtual returns (int256[] memory results) {
|
function getFeeDeviations() public view virtual returns (int256[] memory deviations) {
|
||||||
ITornadoInstance[] memory instances = instanceRegistry.getAllInstanceAddresses();
|
ITornadoInstance[] memory instances = instanceRegistry.getAllInstances();
|
||||||
|
|
||||||
uint256 numInstances = instances.length;
|
uint256 numInstances = instances.length;
|
||||||
|
|
||||||
results = new int256[](numInstances);
|
deviations = new int256[](numInstances);
|
||||||
|
|
||||||
for (uint256 i = 0; i < numInstances; i++) {
|
for (uint256 i = 0; i < numInstances; i++) {
|
||||||
ITornadoInstance instance = instances[i];
|
ITornadoInstance instance = instances[i];
|
||||||
|
|
||||||
uint256 marketFee = instanceFeeOracles[instance].getFee();
|
FeeData memory feeData = feeDataForInstance[instance];
|
||||||
|
|
||||||
|
uint256 marketFee = instanceFeeOracles[instance].getFee(
|
||||||
|
torn,
|
||||||
|
instance,
|
||||||
|
instanceRegistry.getInstanceData(instance),
|
||||||
|
feeData.feePercent,
|
||||||
|
FEE_PERCENT_DIVISOR
|
||||||
|
);
|
||||||
|
|
||||||
int256 deviation;
|
int256 deviation;
|
||||||
|
|
||||||
@ -230,7 +282,7 @@ contract FeeOracleManager is FeeManagerLegacyStorage, Initializable {
|
|||||||
deviation = int256((feeDataForInstance[instance].feeAmount * 1000) / marketFee) - 1000;
|
deviation = int256((feeDataForInstance[instance].feeAmount * 1000) / marketFee) - 1000;
|
||||||
}
|
}
|
||||||
|
|
||||||
results[i] = deviation;
|
deviations[i] = deviation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,7 @@ import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/
|
|||||||
|
|
||||||
// Local imports
|
// Local imports
|
||||||
|
|
||||||
|
import { NameEncoder } from "./libraries/NameEncoder.sol";
|
||||||
import { TornadoRouter } from "./TornadoRouter.sol";
|
import { TornadoRouter } from "./TornadoRouter.sol";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,17 +24,18 @@ import { TornadoRouter } from "./TornadoRouter.sol";
|
|||||||
* @author AlienTornadosaurusHex
|
* @author AlienTornadosaurusHex
|
||||||
* @notice A contract which enumerates Tornado Cash pool instances, and also stores essential data regarding
|
* @notice A contract which enumerates Tornado Cash pool instances, and also stores essential data regarding
|
||||||
* them.
|
* them.
|
||||||
* @dev This is contract will help us layout storage properly for a proxy upgrade for the impl
|
* @dev This contract will help us layout storage properly for a proxy upgrade for the impl
|
||||||
* InstanceRegistry.
|
* InstanceRegistry.
|
||||||
*/
|
*/
|
||||||
contract InstanceRegistryLegacyStorage {
|
contract InstanceRegistryLegacyStorage {
|
||||||
/* From first contract */
|
/* From first contract, just so right uint type is chosen */
|
||||||
enum LegacyStatePlaceholder {
|
enum LegacyStatePlaceholder {
|
||||||
DISABLED,
|
DISABLED,
|
||||||
ENABLED
|
ENABLED
|
||||||
}
|
}
|
||||||
|
|
||||||
/* From first contract */
|
/* From first contract, just if necessary to be able to properly unpack and determine value storage
|
||||||
|
addresses and offsets */
|
||||||
struct LegacyInstanceStructPlaceholder {
|
struct LegacyInstanceStructPlaceholder {
|
||||||
bool deprecatedIsERC20;
|
bool deprecatedIsERC20;
|
||||||
address deprecatedToken;
|
address deprecatedToken;
|
||||||
@ -42,22 +44,25 @@ contract InstanceRegistryLegacyStorage {
|
|||||||
uint32 deprecatedProtocolFeePercentage;
|
uint32 deprecatedProtocolFeePercentage;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* From Initializable.sol of first contract */
|
/* @dev From Initializable.sol of first contract */
|
||||||
bool private _deprecatedInitialized;
|
bool private _deprecatedInitialized;
|
||||||
|
|
||||||
/* From Initializable.sol of first contract */
|
/* @dev From Initializable.sol of first contract */
|
||||||
bool private _deprecatedInitializing;
|
bool private _deprecatedInitializing;
|
||||||
|
|
||||||
/* From first contract */
|
/* @dev From first contract */
|
||||||
address private _deprecatedRouterAddress;
|
address private _deprecatedRouterAddress;
|
||||||
|
|
||||||
/* From first contract */
|
/* @dev From first contract */
|
||||||
mapping(address => LegacyInstanceStructPlaceholder) private _deprecatedInstances;
|
mapping(address => LegacyInstanceStructPlaceholder) private _deprecatedInstances;
|
||||||
|
|
||||||
/* From first contract */
|
/* @dev From first contract */
|
||||||
ITornadoInstance[] private _deprecatedInstanceIds;
|
ITornadoInstance[] private _deprecatedInstanceIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev This struct holds barebones information regarding an instance and its data location in storage.
|
||||||
|
*/
|
||||||
struct InstanceData {
|
struct InstanceData {
|
||||||
IERC20 token;
|
IERC20 token;
|
||||||
uint80 index;
|
uint80 index;
|
||||||
@ -91,7 +96,9 @@ contract InstanceRegistry is InstanceRegistryLegacyStorage, EnsResolve, Initiali
|
|||||||
|
|
||||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
|
|
||||||
event NewRouterRegistered(address newRouterAddress);
|
event RouterRegistered(address newRouterAddress);
|
||||||
|
event InstanceAdded(address indexed instance, uint80 dataIndex, bool isERC20);
|
||||||
|
event InstanceRemoved(address indexed instance);
|
||||||
|
|
||||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
|
|
||||||
@ -120,9 +127,12 @@ contract InstanceRegistry is InstanceRegistryLegacyStorage, EnsResolve, Initiali
|
|||||||
|
|
||||||
function addInstance(ITornadoInstance _instance) public virtual onlyGovernance {
|
function addInstance(ITornadoInstance _instance) public virtual onlyGovernance {
|
||||||
bool isEnabled = instanceData[_instance].isEnabled;
|
bool isEnabled = instanceData[_instance].isEnabled;
|
||||||
|
|
||||||
|
require(!isEnabled, "InstanceRegistry: can't add the same instance.");
|
||||||
|
|
||||||
bool isERC20 = false;
|
bool isERC20 = false;
|
||||||
|
|
||||||
IERC20 token;
|
IERC20 token = IERC20(address(0));
|
||||||
|
|
||||||
// ETH instances do not know of a `token()` call
|
// ETH instances do not know of a `token()` call
|
||||||
try _instance.token() returns (address _tokenAddress) {
|
try _instance.token() returns (address _tokenAddress) {
|
||||||
@ -132,8 +142,8 @@ contract InstanceRegistry is InstanceRegistryLegacyStorage, EnsResolve, Initiali
|
|||||||
/* It's an ETH instance, do nothing */
|
/* It's an ETH instance, do nothing */
|
||||||
}
|
}
|
||||||
|
|
||||||
require(!isEnabled, "InstanceRegistry: can't add the same instance.");
|
// If it is ERC20 then make the router give an approval for the Tornado instance to allow the token
|
||||||
|
// amount... if it hasn't already done so
|
||||||
if (isERC20) {
|
if (isERC20) {
|
||||||
uint256 routerAllowanceForInstance = token.allowance(address(router), address(_instance));
|
uint256 routerAllowanceForInstance = token.allowance(address(router), address(_instance));
|
||||||
|
|
||||||
@ -142,20 +152,33 @@ contract InstanceRegistry is InstanceRegistryLegacyStorage, EnsResolve, Initiali
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Add it to the enumerable
|
||||||
instances.push(_instance);
|
instances.push(_instance);
|
||||||
|
|
||||||
instanceData[_instance] = InstanceData({
|
uint64 instanceIndex = uint64(instances.length - 1);
|
||||||
token: token,
|
|
||||||
index: uint64(instances.length - 1),
|
// Set data
|
||||||
isERC20: token != IERC20(address(0)), // TODO: Is this true?
|
instanceData[_instance] =
|
||||||
isEnabled: isEnabled
|
InstanceData({ token: token, index: instanceIndex, isERC20: isERC20, isEnabled: isEnabled });
|
||||||
});
|
|
||||||
|
// Log
|
||||||
|
emit InstanceAdded(address(_instance), instanceIndex, isERC20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Remove an instance, only callable by Governance.
|
||||||
|
* @dev The modifier is in the internal call.
|
||||||
|
* @param _instanceIndex The index of the instance to remove.
|
||||||
|
*/
|
||||||
function removeInstanceByIndex(uint256 _instanceIndex) public virtual {
|
function removeInstanceByIndex(uint256 _instanceIndex) public virtual {
|
||||||
_removeInstanceByAddress(address(instances[_instanceIndex]));
|
_removeInstanceByAddress(address(instances[_instanceIndex]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @notice Remove an instance, only callable by Governance.
|
||||||
|
* @dev The modifier is in the internal call.
|
||||||
|
* @param _instanceAddress The adress of the instance to remove.
|
||||||
|
*/
|
||||||
function removeInstanceByAddress(address _instanceAddress) public virtual {
|
function removeInstanceByAddress(address _instanceAddress) public virtual {
|
||||||
_removeInstanceByAddress(_instanceAddress);
|
_removeInstanceByAddress(_instanceAddress);
|
||||||
}
|
}
|
||||||
@ -164,6 +187,8 @@ contract InstanceRegistry is InstanceRegistryLegacyStorage, EnsResolve, Initiali
|
|||||||
ITornadoInstance instance = ITornadoInstance(_instanceAddress);
|
ITornadoInstance instance = ITornadoInstance(_instanceAddress);
|
||||||
InstanceData memory data = instanceData[instance];
|
InstanceData memory data = instanceData[instance];
|
||||||
|
|
||||||
|
// Kill the allowance of the router first (arbitrary order)
|
||||||
|
|
||||||
if (data.isERC20) {
|
if (data.isERC20) {
|
||||||
uint256 routerAllowanceForInstance = data.token.allowance(address(router), _instanceAddress);
|
uint256 routerAllowanceForInstance = data.token.allowance(address(router), _instanceAddress);
|
||||||
|
|
||||||
@ -172,17 +197,34 @@ contract InstanceRegistry is InstanceRegistryLegacyStorage, EnsResolve, Initiali
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
instances[data.index] = instances[instances.length - 1];
|
// We need to revert all changes, meaning modify position of last instance and change index in data
|
||||||
|
|
||||||
|
uint64 lastInstanceIndex = uint64(instances.length - 1);
|
||||||
|
|
||||||
|
ITornadoInstance lastInstance = instances[lastInstanceIndex];
|
||||||
|
|
||||||
|
// Remove & swap
|
||||||
|
|
||||||
|
instances[data.index] = lastInstance;
|
||||||
|
|
||||||
|
instanceData[lastInstance].index = data.index;
|
||||||
|
|
||||||
instances.pop();
|
instances.pop();
|
||||||
|
|
||||||
|
// Delete for removed instance data
|
||||||
|
|
||||||
delete instanceData[instance];
|
delete instanceData[instance];
|
||||||
|
|
||||||
|
// Log
|
||||||
|
|
||||||
|
emit InstanceRemoved(_instanceAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
|
|
||||||
function setTornadoRouter(address _newRouterAddress) external onlyGovernance {
|
function setTornadoRouter(address _newRouterAddress) external onlyGovernance {
|
||||||
router = TornadoRouter(_newRouterAddress);
|
router = TornadoRouter(_newRouterAddress);
|
||||||
emit NewRouterRegistered(_newRouterAddress);
|
emit RouterRegistered(_newRouterAddress);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
@ -211,6 +253,15 @@ contract InstanceRegistry is InstanceRegistryLegacyStorage, EnsResolve, Initiali
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getInstanceData(ITornadoInstance _instance)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
virtual
|
||||||
|
returns (InstanceData memory data)
|
||||||
|
{
|
||||||
|
return instanceData[_instance];
|
||||||
|
}
|
||||||
|
|
||||||
function getInstanceToken(ITornadoInstance _instance) public view virtual returns (IERC20) {
|
function getInstanceToken(ITornadoInstance _instance) public view virtual returns (IERC20) {
|
||||||
return instanceData[_instance].token;
|
return instanceData[_instance].token;
|
||||||
}
|
}
|
||||||
@ -218,4 +269,37 @@ contract InstanceRegistry is InstanceRegistryLegacyStorage, EnsResolve, Initiali
|
|||||||
function getInstanceIndex(ITornadoInstance _instance) public view virtual returns (uint80) {
|
function getInstanceIndex(ITornadoInstance _instance) public view virtual returns (uint80) {
|
||||||
return instanceData[_instance].index;
|
return instanceData[_instance].index;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isRegisteredInstance(address _instanceAddress) public view virtual returns (bool) {
|
||||||
|
return isEnabledInstance(_instanceAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEnabledInstance(address _instanceAddress) public view virtual returns (bool) {
|
||||||
|
return instanceData[ITornadoInstance(_instanceAddress)].isEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ENS GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
|
|
||||||
|
function getInstanceByENSName(string memory _instanceENSName)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
virtual
|
||||||
|
returns (ITornadoInstance)
|
||||||
|
{
|
||||||
|
(, bytes32 node) = NameEncoder.dnsEncodeName(_instanceENSName);
|
||||||
|
return ITornadoInstance(resolve(node));
|
||||||
|
}
|
||||||
|
|
||||||
|
function isRegisteredInstanceByENSName(string memory _instanceENSName)
|
||||||
|
public
|
||||||
|
view
|
||||||
|
virtual
|
||||||
|
returns (bool)
|
||||||
|
{
|
||||||
|
return isEnabledInstanceByENSName(_instanceENSName);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isEnabledInstanceByENSName(string memory _instanceENSName) public view virtual returns (bool) {
|
||||||
|
return isEnabledInstance(address(getInstanceByENSName(_instanceENSName)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,24 @@ import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/
|
|||||||
import { RelayerRegistry } from "../v1/RelayerRegistry.sol";
|
import { RelayerRegistry } from "../v1/RelayerRegistry.sol";
|
||||||
import { InstanceRegistry, InstanceData } from "./InstanceRegistry.sol";
|
import { InstanceRegistry, InstanceData } from "./InstanceRegistry.sol";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title TornadoRouter
|
||||||
|
* @author AlienTornadosaurusHex
|
||||||
|
* @notice This contracts is a router for all Tornado Cash deposits and withdrawals
|
||||||
|
* @dev This is an improved version of the TornadoRouter with a modified design from the original contract.
|
||||||
|
*/
|
||||||
contract TornadoRouter is Initializable {
|
contract TornadoRouter is Initializable {
|
||||||
using SafeERC20 for IERC20;
|
using SafeERC20 for IERC20;
|
||||||
|
|
||||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
|
|
||||||
|
/* @dev The address of the Governance proxy */
|
||||||
address public immutable governanceProxyAddress;
|
address public immutable governanceProxyAddress;
|
||||||
|
|
||||||
|
/* @dev The instance registry */
|
||||||
InstanceRegistry public instanceRegistry;
|
InstanceRegistry public instanceRegistry;
|
||||||
|
|
||||||
|
/* @dev The relayer registry */
|
||||||
RelayerRegistry public relayerRegistry;
|
RelayerRegistry public relayerRegistry;
|
||||||
|
|
||||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
@ -50,7 +59,7 @@ contract TornadoRouter is Initializable {
|
|||||||
_;
|
_;
|
||||||
}
|
}
|
||||||
|
|
||||||
function initalize(address _instanceRegistryAddress, address _relayerRegistryAddress)
|
function initialize(address _instanceRegistryAddress, address _relayerRegistryAddress)
|
||||||
external
|
external
|
||||||
onlyGovernance
|
onlyGovernance
|
||||||
initializer
|
initializer
|
||||||
@ -112,12 +121,12 @@ contract TornadoRouter is Initializable {
|
|||||||
require(_to != address(0), "TORN: can not send to zero address");
|
require(_to != address(0), "TORN: can not send to zero address");
|
||||||
|
|
||||||
if (_token == IERC20(0)) {
|
if (_token == IERC20(0)) {
|
||||||
// for Ether
|
// For Ether
|
||||||
uint256 totalBalance = address(this).balance;
|
uint256 totalBalance = address(this).balance;
|
||||||
uint256 balance = Math.min(totalBalance, _amount);
|
uint256 balance = Math.min(totalBalance, _amount);
|
||||||
_to.transfer(balance);
|
_to.transfer(balance);
|
||||||
} else {
|
} else {
|
||||||
// any other erc20
|
// For any other ERC20
|
||||||
uint256 totalBalance = _token.balanceOf(address(this));
|
uint256 totalBalance = _token.balanceOf(address(this));
|
||||||
uint256 balance = Math.min(totalBalance, _amount);
|
uint256 balance = Math.min(totalBalance, _amount);
|
||||||
require(balance > 0, "TORN: trying to send 0 balance");
|
require(balance > 0, "TORN: trying to send 0 balance");
|
||||||
@ -134,6 +143,14 @@ contract TornadoRouter is Initializable {
|
|||||||
_token.safeApprove(_spender, _amount);
|
_token.safeApprove(_spender, _amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setInstanceRegistry(address _newInstanceRegistryProxyAddress) external onlyGovernance {
|
||||||
|
instanceRegistry = InstanceRegistry(_newInstanceRegistryProxyAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setRelayerRegistry(address _newRelayerRegistryProxyAddress) external onlyGovernance {
|
||||||
|
relayerRegistry = RelayerRegistry(_newRelayerRegistryProxyAddress);
|
||||||
|
}
|
||||||
|
|
||||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ GETTERS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
|
|
||||||
function version() public pure virtual returns (string memory) {
|
function version() public pure virtual returns (string memory) {
|
||||||
|
@ -6,12 +6,160 @@ pragma experimental ABIEncoderV2;
|
|||||||
// OZ imports
|
// OZ imports
|
||||||
|
|
||||||
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
|
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
|
||||||
|
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
|
||||||
|
// Uniswap imports
|
||||||
|
|
||||||
|
import { IUniswapV3Factory } from "@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol";
|
||||||
|
import { IUniswapV3PoolState } from "@uniswap/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol";
|
||||||
|
|
||||||
|
// Tornado imports
|
||||||
|
|
||||||
|
import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol";
|
||||||
|
|
||||||
// Local imports
|
// Local imports
|
||||||
|
|
||||||
import { UniswapV3OracleHelper } from "./libraries/UniswapV3OracleHelper.sol";
|
|
||||||
import { IFeeOracle } from "./interfaces/IFeeOracle.sol";
|
import { IFeeOracle } from "./interfaces/IFeeOracle.sol";
|
||||||
|
|
||||||
contract UniswapV3FeeOracle is IFeeOracle {
|
import { UniswapV3OracleHelper } from "./libraries/UniswapV3OracleHelper.sol";
|
||||||
function getFee() public view virtual override returns (uint160) { }
|
|
||||||
|
import { InstanceData } from "./InstanceRegistry.sol";
|
||||||
|
|
||||||
|
struct UniswapV3FeeData {
|
||||||
|
uint24 tornPoolFee;
|
||||||
|
uint24 tokenPoolFee;
|
||||||
|
uint32 timePeriod;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @title UniswapV3FeeOracle
|
||||||
|
* @author AlienTornadosaurusHex
|
||||||
|
* @notice A TORN fee oracle for any Uniswap V3 token pool.
|
||||||
|
*/
|
||||||
|
contract UniswapV3FeeOracle is IFeeOracle {
|
||||||
|
using SafeMath for uint256;
|
||||||
|
|
||||||
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ VARIABLES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
|
|
||||||
|
/* @dev The Governance Proxy address */
|
||||||
|
address public immutable governanceProxyAddress;
|
||||||
|
|
||||||
|
/* @dev The Uniswap V3 Factory */
|
||||||
|
IUniswapV3Factory public immutable v3Factory;
|
||||||
|
|
||||||
|
/* @dev The global pool fee value which will always be used for TORN */
|
||||||
|
uint24 public globalTornUniswapPoolFee;
|
||||||
|
|
||||||
|
/* @dev The global time period value which will be used for all calculations */
|
||||||
|
uint32 public globalUniswapTimePeriod;
|
||||||
|
|
||||||
|
/* @dev The minimum observation cardinality required for a token */
|
||||||
|
uint32 public globalMinObservationCardinality;
|
||||||
|
|
||||||
|
/* @dev The feeData in which the above + token specific will be stored */
|
||||||
|
mapping(IERC20 => UniswapV3FeeData) public uniswapFeeData;
|
||||||
|
|
||||||
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ EVENTS ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
|
|
||||||
|
event UniswapPoolFeeUpdated(IERC20 token, uint24 newUniswapPoolFee);
|
||||||
|
event GlobalTornUniswapPoolFeeUpdated(uint24 newUniswapPoolFee);
|
||||||
|
event GlobalUniswapTimePeriodUpdated(uint32 newUniswapTimePeriod);
|
||||||
|
event GlobalMinObservationCardinalityUpdated(uint16 newMinObservationCardinality);
|
||||||
|
|
||||||
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LOGIC ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
|
|
||||||
|
constructor(address _governanceProxyAddress, address _uniswapV3FactoryAddress) public {
|
||||||
|
governanceProxyAddress = _governanceProxyAddress;
|
||||||
|
v3Factory = IUniswapV3Factory(_uniswapV3FactoryAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
modifier onlyGovernance() {
|
||||||
|
require(msg.sender == governanceProxyAddress, "UniswapV3FeeOracle: onlyGovernance");
|
||||||
|
_;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGlobalTornUniswapPoolFee(uint24 _newGlobalTornUniswapPoolFee) public virtual onlyGovernance {
|
||||||
|
globalTornUniswapPoolFee = _newGlobalTornUniswapPoolFee;
|
||||||
|
emit GlobalTornUniswapPoolFeeUpdated(_newGlobalTornUniswapPoolFee);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGlobalUniswapTimePeriod(uint32 _newGlobalUniswapTimePeriod) public virtual onlyGovernance {
|
||||||
|
globalUniswapTimePeriod = _newGlobalUniswapTimePeriod;
|
||||||
|
emit GlobalUniswapTimePeriodUpdated(_newGlobalUniswapTimePeriod);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGlobalMinObservationCardinality(uint16 _newGlobalMinObservationCardinality)
|
||||||
|
public
|
||||||
|
virtual
|
||||||
|
onlyGovernance
|
||||||
|
{
|
||||||
|
globalMinObservationCardinality = _newGlobalMinObservationCardinality;
|
||||||
|
emit GlobalMinObservationCardinalityUpdated(_newGlobalMinObservationCardinality);
|
||||||
|
}
|
||||||
|
|
||||||
|
function setUniswapPoolFeeForToken(IERC20 _token, uint24 _tokenUniswapPoolFee)
|
||||||
|
public
|
||||||
|
virtual
|
||||||
|
onlyGovernance
|
||||||
|
{
|
||||||
|
uint24 _globalTornUniswapPoolFee = globalTornUniswapPoolFee;
|
||||||
|
uint32 _globalUniswapTimePeriod = globalUniswapTimePeriod;
|
||||||
|
|
||||||
|
require(_globalTornUniswapPoolFee != 0, "UniswapV3FeeOracle: torn pool fee not initialized");
|
||||||
|
require(_globalUniswapTimePeriod != 0, "UniswapV3FeeOracle: time period not initialized");
|
||||||
|
|
||||||
|
address poolAddress =
|
||||||
|
v3Factory.getPool(address(_token), UniswapV3OracleHelper.WETH, _tokenUniswapPoolFee);
|
||||||
|
|
||||||
|
require(poolAddress != address(0), "UniswapV3FeeOracle: pool for token and fee does not exist");
|
||||||
|
|
||||||
|
(,,,, uint16 observationCardinalityNext,,) = IUniswapV3PoolState(poolAddress).slot0();
|
||||||
|
|
||||||
|
require(
|
||||||
|
globalMinObservationCardinality <= observationCardinalityNext,
|
||||||
|
"UniswapV3FeeOracle: pool observation cardinality low"
|
||||||
|
);
|
||||||
|
|
||||||
|
uniswapFeeData[_token] = UniswapV3FeeData({
|
||||||
|
tornPoolFee: _globalTornUniswapPoolFee,
|
||||||
|
tokenPoolFee: _tokenUniswapPoolFee,
|
||||||
|
timePeriod: _globalUniswapTimePeriod
|
||||||
|
});
|
||||||
|
|
||||||
|
emit UniswapPoolFeeUpdated(_token, _tokenUniswapPoolFee);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFee(
|
||||||
|
IERC20 _torn,
|
||||||
|
ITornadoInstance _instance,
|
||||||
|
InstanceData memory _data,
|
||||||
|
uint64 _feePercent,
|
||||||
|
uint64 _feePercentDivisor
|
||||||
|
) public view virtual override returns (uint160) {
|
||||||
|
// For safety, we won't allow calculating fees for disabled instances
|
||||||
|
require(_data.isEnabled, "UniswapV3FeeOracle: instance not enabled");
|
||||||
|
|
||||||
|
// If fee is 0 return
|
||||||
|
if (_feePercent == 0) return 0;
|
||||||
|
|
||||||
|
// If it's not an ERC20 it has to be ETH, use the WETH token
|
||||||
|
_data.token = _data.isERC20 ? _data.token : IERC20(UniswapV3OracleHelper.WETH);
|
||||||
|
|
||||||
|
// After we have decided our token get fee data for it
|
||||||
|
UniswapV3FeeData memory uniFeeData = uniswapFeeData[_data.token];
|
||||||
|
|
||||||
|
// Calc price ratio
|
||||||
|
uint256 tokenPriceRatio = UniswapV3OracleHelper.getPriceRatioOfTokens(
|
||||||
|
[address(_torn), address(_data.token)],
|
||||||
|
[uniFeeData.tornPoolFee, uniFeeData.tokenPoolFee],
|
||||||
|
uniFeeData.timePeriod
|
||||||
|
);
|
||||||
|
|
||||||
|
// And now all according to legacy calculation
|
||||||
|
return uint160(
|
||||||
|
_instance.denomination().mul(UniswapV3OracleHelper.RATIO_DIVIDER).div(tokenPriceRatio).mul(
|
||||||
|
uint256(_feePercent)
|
||||||
|
).div(_feePercentDivisor)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,24 @@
|
|||||||
pragma solidity ^0.6.12;
|
pragma solidity ^0.6.12;
|
||||||
pragma experimental ABIEncoderV2;
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
// OZ Imports
|
||||||
|
|
||||||
|
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
|
||||||
|
|
||||||
|
// Tornado imports
|
||||||
|
|
||||||
|
import { ITornadoInstance } from "tornado-anonymity-mining/contracts/interfaces/ITornadoInstance.sol";
|
||||||
|
|
||||||
|
// Local imports
|
||||||
|
|
||||||
|
import { InstanceData } from "../InstanceRegistry.sol";
|
||||||
|
|
||||||
interface IFeeOracle {
|
interface IFeeOracle {
|
||||||
function getFee() external view returns (uint160);
|
function getFee(
|
||||||
|
IERC20 _torn,
|
||||||
|
ITornadoInstance _instance,
|
||||||
|
InstanceData memory _data,
|
||||||
|
uint64 _feePercent,
|
||||||
|
uint64 _feePercentDivisor
|
||||||
|
) external view returns (uint160);
|
||||||
}
|
}
|
||||||
|
57
src/v2/libraries/BytesUtils.sol
Normal file
57
src/v2/libraries/BytesUtils.sol
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.6.12;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
library BytesUtils {
|
||||||
|
/*
|
||||||
|
* @dev Returns the keccak-256 hash of a byte range.
|
||||||
|
* @param self The byte string to hash.
|
||||||
|
* @param offset The position to start hashing at.
|
||||||
|
* @param len The number of bytes to hash.
|
||||||
|
* @return The hash of the byte range.
|
||||||
|
*/
|
||||||
|
function keccak(bytes memory self, uint256 offset, uint256 len) internal pure returns (bytes32 ret) {
|
||||||
|
require(offset + len <= self.length);
|
||||||
|
assembly {
|
||||||
|
ret := keccak256(add(add(self, 32), offset), len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the ENS namehash of a DNS-encoded name.
|
||||||
|
* @param self The DNS-encoded name to hash.
|
||||||
|
* @param offset The offset at which to start hashing.
|
||||||
|
* @return The namehash of the name.
|
||||||
|
*/
|
||||||
|
function namehash(bytes memory self, uint256 offset) internal pure returns (bytes32) {
|
||||||
|
(bytes32 labelhash, uint256 newOffset) = readLabel(self, offset);
|
||||||
|
if (labelhash == bytes32(0)) {
|
||||||
|
require(offset == self.length - 1, "namehash: Junk at end of name");
|
||||||
|
return bytes32(0);
|
||||||
|
}
|
||||||
|
return keccak256(abi.encodePacked(namehash(self, newOffset), labelhash));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dev Returns the keccak-256 hash of a DNS-encoded label, and the offset to the start of the next label.
|
||||||
|
* @param self The byte string to read a label from.
|
||||||
|
* @param idx The index to read a label at.
|
||||||
|
* @return labelhash The hash of the label at the specified index, or 0 if it is the last label.
|
||||||
|
* @return newIdx The index of the start of the next label.
|
||||||
|
*/
|
||||||
|
function readLabel(bytes memory self, uint256 idx)
|
||||||
|
internal
|
||||||
|
pure
|
||||||
|
returns (bytes32 labelhash, uint256 newIdx)
|
||||||
|
{
|
||||||
|
require(idx < self.length, "readLabel: Index out of bounds");
|
||||||
|
uint256 len = uint256(uint8(self[idx]));
|
||||||
|
if (len > 0) {
|
||||||
|
labelhash = keccak(self, idx + 1, len);
|
||||||
|
} else {
|
||||||
|
labelhash = bytes32(0);
|
||||||
|
}
|
||||||
|
newIdx = idx + len + 1;
|
||||||
|
}
|
||||||
|
}
|
41
src/v2/libraries/NameEncoder.sol
Normal file
41
src/v2/libraries/NameEncoder.sol
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
|
||||||
|
pragma solidity ^0.6.12;
|
||||||
|
pragma experimental ABIEncoderV2;
|
||||||
|
|
||||||
|
import { BytesUtils } from "./BytesUtils.sol";
|
||||||
|
|
||||||
|
library NameEncoder {
|
||||||
|
using BytesUtils for bytes;
|
||||||
|
|
||||||
|
function dnsEncodeName(string memory name) internal pure returns (bytes memory dnsName, bytes32 node) {
|
||||||
|
uint8 labelLength = 0;
|
||||||
|
bytes memory bytesName = bytes(name);
|
||||||
|
uint256 length = bytesName.length;
|
||||||
|
dnsName = new bytes(length + 2);
|
||||||
|
node = 0;
|
||||||
|
if (length == 0) {
|
||||||
|
dnsName[0] = 0;
|
||||||
|
return (dnsName, node);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint256 i = length - 1; i >= 0; i--) {
|
||||||
|
if (bytesName[i] == ".") {
|
||||||
|
dnsName[i + 1] = bytes1(labelLength);
|
||||||
|
node = keccak256(abi.encodePacked(node, bytesName.keccak(i + 1, labelLength)));
|
||||||
|
labelLength = 0;
|
||||||
|
} else {
|
||||||
|
labelLength += 1;
|
||||||
|
dnsName[i + 1] = bytesName[i];
|
||||||
|
}
|
||||||
|
if (i == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node = keccak256(abi.encodePacked(node, bytesName.keccak(0, labelLength)));
|
||||||
|
|
||||||
|
dnsName[0] = bytes1(labelLength);
|
||||||
|
return (dnsName, node);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user