-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
91 changed files
with
18,320 additions
and
3,529 deletions.
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
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -112,3 +112,4 @@ typechain | |
.openzeppelin | ||
.DS_Store | ||
typechain-types | ||
localConfig |
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,48 @@ | ||
##### | ||
# | ||
# This config file is used by the QP deploy script. | ||
# First, remove the hardcoded contract address for the contract you want to be deployed. | ||
# Then, after deployment, put the deployed addresses, in the config and send a commit | ||
# tagged with UPDATE_QP_CONFIG | ||
# | ||
# Use $in the DeployerKeys section to get the value from an environment variable | ||
# Note: If you provide `DeployerKeys.Owner`, you do NOT need to provide the `Owner` | ||
# | ||
##### | ||
|
||
Owner: "0xf1b1145f83cadd5199f8D8E937Aa07979e2806Aa" | ||
DeployerContract: "0xDACa19A67eB89D610eDFd891594108B569c379c0" | ||
DeployerSalt: "0x65ff7961289d09280b311966c72b708989daabefb729d1a5b64fe72a9ad26049" | ||
QuantumPortalGateway: | ||
QuantumPortalState: | ||
QuantumPortalPoc: | ||
QuantumPortalLedgerMgr: | ||
QuantumPortalAuthorityMgr: "0xe776Fedb74e85CFe6677b6774E5898c1d41867D7" | ||
QuantumPortalFeeConvertorDirect: "0x431D332F39A90e4478Ae3a13C8e17Bae1BfdDC73" | ||
QuantumPortalMinerMgr: "0x391870f7FFEC11582f3d1Dc6bc0EE7ec8e63C16f" | ||
QuantumPortalStake: "0xB1CcE8e7039395348283cbb267007351a7777876" | ||
QuantumPortalMinStake?: "10000000000000000000" | ||
FRM: | ||
97: "0xF3B61752E52B92BB0B8bF9eBb4FE487B8fD1047C" | ||
43113: "0xF3B61752E52B92BB0B8bF9eBb4FE487B8fD1047C" | ||
80001: "0xF3B61752E52B92BB0B8bF9eBb4FE487B8fD1047C" | ||
26100: "0x0000000000000000000000000000000000026026" | ||
81: "0x78B94f1C07b1467D25cde8803E5c8B85d98CCA12" | ||
WFRM: "0x0000000000000000000000000000000000026026" | ||
WETH: | ||
97: "0x0000000000000000000000000000000000026026" | ||
43113: "0x0000000000000000000000000000000000026026" | ||
80001: "0x0000000000000000000000000000000000026026" | ||
26026: "0x0000000000000000000000000000000000026026" | ||
81: "0x78B94f1C07b1467D25cde8803E5c8B85d98CCA12" | ||
UniV2Factory: | ||
97: "0x0000000000000000000000000000000000026026" | ||
43113: "0x0000000000000000000000000000000000026026" | ||
80001: "0x0000000000000000000000000000000000026026" | ||
26026: "0x0000000000000000000000000000000000026026" | ||
DirectFee: | ||
feePerByte: "10000000000000" | ||
DeployerKeys: | ||
DeployerContract: $CONTRACT_DEPLOYER_KEY | ||
Qp: $QP_DEPLOYER_KEY | ||
Owner: $QP_DEPLOYER_KEY |
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,10 @@ | ||
#Quantum Portal Smart Contracts | ||
|
||
This repo contains a set of smart contracts that build up QP. | ||
|
||
### Deployment for CI | ||
|
||
Use the following steps if you want your changes to QP contracts be deployed. | ||
|
||
### Deploying locally | ||
|
Empty file.
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,219 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
import "./IFrmFeeManager.sol"; | ||
import "./IPriceOracle.sol"; | ||
import "../staking/library/TokenReceivable.sol"; | ||
import "foundry-contracts/contracts/common/WithAdmin.sol"; | ||
import "foundry-contracts/contracts/math/FullMath.sol"; | ||
import "foundry-contracts/contracts/math/FixedPoint128.sol"; | ||
|
||
/** | ||
@notice Fee manager allows other "trusted" contracts to pay fees on their user's behalf. | ||
User deposits token into fee manager. | ||
Fee manager will collect fees, from the user based on the price of base (e.g. FRM) and the | ||
price of the fee to be paid. | ||
*/ | ||
contract FrmFeeManager is TokenReceivable, WithAdmin, IFrmFeeManager { | ||
struct PriceOracleInfo { | ||
address univ2Oracle; | ||
address[] pricePath; | ||
} | ||
mapping(address => PriceOracleInfo) public priceOracles; | ||
mapping(address => uint256) public balances; | ||
mapping(address => bool) public registeredOracles; | ||
mapping(address => bool) public trustedCallers; | ||
address feeToken; | ||
address liquidityBaseToken; | ||
address defaultPriceOracle; | ||
|
||
address constant FEE_REPO = address(0); | ||
|
||
/** | ||
@notice Deposit fee for an account | ||
@param to The fee receiver | ||
@return The deposited amount | ||
*/ | ||
function deposit(address to) external returns (uint256) { | ||
uint256 amount = sync(feeToken); | ||
balances[to] += amount; | ||
return amount; | ||
} | ||
|
||
/** | ||
@notice Withdraw deposited fee from an account | ||
@param amount The amount to withdraw | ||
*/ | ||
function withdraw(uint256 amount) external { | ||
uint256 balance = balances[msg.sender]; | ||
require(amount >= balance, "FFM: not enough balance"); | ||
balances[msg.sender] = balance - amount; | ||
sendToken(feeToken, msg.sender, amount); | ||
} | ||
|
||
/** | ||
@notice Add a trusted caller. Caller can charge fee from anybody. | ||
@param caller The caller | ||
*/ | ||
function allowTrustedCaller(address caller) external onlyOwner { | ||
trustedCallers[caller] = true; | ||
} | ||
|
||
/** | ||
@param caller The caller | ||
*/ | ||
function removeTrusterCaller(address caller) external onlyAdmin { | ||
delete trustedCallers[caller]; | ||
} | ||
|
||
/** | ||
@notice Allows a univ2 oracle | ||
@param univ2Oracle The Univ2 oracle | ||
@param isDefault If the oracle is default | ||
*/ | ||
function allowUniV2Oracle( | ||
address univ2Oracle, | ||
bool isDefault | ||
) external onlyAdmin { | ||
registeredOracles[univ2Oracle] = true; | ||
if (isDefault) { | ||
defaultPriceOracle = univ2Oracle; | ||
} | ||
} | ||
|
||
/** | ||
@notice Remoces a univ2 oracle | ||
@param univ2Oracle The univ2 orcale to remove | ||
*/ | ||
function removeUniV2Oracle(address univ2Oracle) external onlyAdmin { | ||
delete registeredOracles[univ2Oracle]; | ||
} | ||
|
||
/** | ||
@notice Registers a path for an oracle. TODO: consider doing more check and open this method to public | ||
@param univ2Oracle The univ2 oracle. Must already be registered | ||
@param pricePath The price path | ||
*/ | ||
function registerPriceOracle( | ||
address univ2Oracle, | ||
address[] calldata pricePath | ||
) external onlyAdmin { | ||
require( | ||
pricePath[pricePath.length - 1] == liquidityBaseToken, | ||
"FFM: pricePath should end in lp base token" | ||
); | ||
require(registeredOracles[univ2Oracle], "FFM: oracle not allowed"); | ||
address token = pricePath[0]; | ||
// Clean up | ||
uint256 len = priceOracles[token].pricePath.length; | ||
for (uint i = 0; i < len; i--) { | ||
priceOracles[token].pricePath.pop(); | ||
} | ||
priceOracles[token].univ2Oracle = univ2Oracle; | ||
priceOracles[token].pricePath = pricePath; | ||
} | ||
|
||
/** | ||
@notice Pay fee on behalf of a user. This will allow caller to charge fee from the user | ||
One common usecase is to provide discouts for the fees if the user for example | ||
uses FRM to pay the fee. | ||
This method will calculate the fee based on the price of the token vs the base token. | ||
@param user The user who will be paying the fee | ||
@param token The token to be used as fee | ||
@param amount The fee amount based on the provided token | ||
@return True if the fee charge was successful | ||
*/ | ||
function payFee( | ||
address user, | ||
address token, | ||
uint256 amount | ||
) external override returns (bool) { | ||
require(user != address(0), "FFM: user requried"); | ||
require(token != address(0), "FFM: token requried"); | ||
require(trustedCallers[msg.sender], "FFM: caller not allowed"); | ||
if (amount == 0) { | ||
return true; | ||
} | ||
address oracle = priceOracles[token].univ2Oracle; | ||
if (oracle == address(0)) { | ||
return false; | ||
} | ||
address[] memory path = priceOracles[token].pricePath; | ||
require(path[0] == token, "FFM: Invalid path start"); | ||
require( | ||
path[path.length - 1] == liquidityBaseToken, | ||
"FFM: Invalid path end" | ||
); | ||
|
||
(uint256 feeAmount, bool success) = convertTokenToFee(token, amount); | ||
if (!success) { | ||
return false; | ||
} | ||
uint256 userBalance = balances[user]; | ||
if (userBalance < feeAmount) { | ||
return false; | ||
} | ||
balances[FEE_REPO] += feeAmount; | ||
balances[user] = userBalance - feeAmount; | ||
return true; | ||
} | ||
|
||
/** | ||
@notice Sweeps the collected fees. | ||
@param to The receiver of the collected fees. | ||
*/ | ||
function sweep(address to) external onlyAdmin { | ||
uint256 balance = balances[FEE_REPO]; | ||
balances[FEE_REPO] = 0; | ||
sendToken(feeToken, to, balance); | ||
} | ||
|
||
/** | ||
@notice amount * Token price / feeToken price | ||
To trouble shoot, (if function returns false) first make sure token path is set | ||
and token price can be retrieved. Then the same for feeToken | ||
*/ | ||
function convertTokenToFee( | ||
address token, | ||
uint256 amount | ||
) internal returns (uint256, bool) { | ||
uint256 tokenPrice = getPriceX128(token); | ||
if (tokenPrice == 0) { | ||
return (0, false); | ||
} | ||
uint256 frmPrice = getPriceX128(feeToken); | ||
if (frmPrice == 0) { | ||
return (0, false); | ||
} | ||
uint256 ratio = FullMath.mulDiv( | ||
frmPrice, | ||
FixedPoint128.Q128, | ||
tokenPrice | ||
); | ||
return (FullMath.mulDiv(ratio, amount, FixedPoint128.Q128), true); | ||
} | ||
|
||
/** | ||
@notice Returns the token price. If token is not registered, tries the default oracle. | ||
If pair does notexist on the default oracle, caches the "0" as the result to avoid | ||
repeated calls to the oracle. | ||
TODO: How to make this deterministic, so that fee does not change from the submission | ||
to mining time. | ||
*/ | ||
function getPriceX128(address token) internal returns (uint256) { | ||
PriceOracleInfo memory oracleInfo = priceOracles[token]; | ||
address oracle = oracleInfo.univ2Oracle; | ||
address[] memory path = oracleInfo.pricePath; | ||
if (oracle == address(0)) { | ||
// Use the default oracle | ||
oracle = defaultPriceOracle; | ||
path = new address[](2); | ||
path[0] = token; | ||
path[1] = liquidityBaseToken; | ||
} | ||
if (IPriceOracle(oracle).updatePrice(path)) { | ||
return IPriceOracle(oracle).recentPriceX128(path); | ||
} | ||
return 0; | ||
} | ||
} |
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,12 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
interface IFrmFeeManager { | ||
/** | ||
* @notice Pays the fee on bahalf of the user | ||
* @param user The user | ||
* @param token The token | ||
* @param amount Amount | ||
*/ | ||
function payFee(address user, address token, uint256 amount) external returns (bool); | ||
} |
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,27 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.0; | ||
|
||
interface IPriceOracle { | ||
enum EmaType { | ||
_1Min, _1Hour, _1Day, _25Day, _50Day, _100Day | ||
} | ||
|
||
/** | ||
* @notice Updates price for a path | ||
* @param path The path as it will be used in the AMM | ||
*/ | ||
function updatePrice(address[] calldata path) external returns (bool); | ||
|
||
/** | ||
* @notice Recent price encoded as fixed floating point. | ||
* @param path The path as it will be used in the AMM | ||
*/ | ||
function recentPriceX128(address[] calldata path) external view returns (uint256); | ||
|
||
/** | ||
* @notice Exponential moving average as fixed floating point. | ||
* @param path The path as it will be used in the AMM | ||
* @param emaType The `EmaType`. See the type definition. | ||
*/ | ||
function emaX128(address[] calldata path, EmaType emaType) external view returns (uint256); | ||
} |
Oops, something went wrong.