Skip to content
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

Stable price support2 #222

Merged
merged 18 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 2 additions & 7 deletions contracts/DisputeManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -126,12 +126,7 @@ contract DisputeManager is IDisputeManager, Initializable, OwnableUpgradeable {
emit DisputeFinalized(disputeId, state, indexerSlashAmount, newDeposit);
}

function isOnDispute(address indexer) external returns (bool) {
if(disputeIdByIndexer[indexer].length > 0){
return true;
}else{
return false;
}
function isOnDispute(address indexer) external view returns (bool) {
return disputeIdByIndexer[indexer].length > 0;
}

}
43 changes: 32 additions & 11 deletions contracts/PlanManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import './interfaces/IServiceAgreementRegistry.sol';
import './interfaces/ISettings.sol';
import './interfaces/IPlanManager.sol';
import './interfaces/IEraManager.sol';
import './interfaces/IPriceOracle.sol';

/**
* @title Plan Manager Contract
Expand Down Expand Up @@ -48,6 +49,9 @@ contract PlanManager is Initializable, OwnableUpgradeable, IPlanManager {
/// @notice indexer => deploymentId => already plan number
mapping(address => mapping(bytes32 => uint256)) private limits;

/// @notice TemplateId => Template
mapping(uint256 => PlanTemplateV2) private v2templates;

/// @dev ### EVENTS
/// @notice Emitted when owner create a PlanTemplate.
event PlanTemplateCreated(uint256 indexed templateId);
Expand Down Expand Up @@ -99,12 +103,12 @@ contract PlanManager is Initializable, OwnableUpgradeable, IPlanManager {
* @param rateLimit request rate limit
* @param metadata plan metadata
*/
function createPlanTemplate(uint256 period, uint256 dailyReqCap, uint256 rateLimit, bytes32 metadata) external onlyOwner {
function createPlanTemplate(uint256 period, uint256 dailyReqCap, uint256 rateLimit, address priceToken, bytes32 metadata) external onlyOwner {
require(period > 0, 'PM001');
require(dailyReqCap > 0, 'PM002');
require(rateLimit > 0, 'PM003');

templates[nextTemplateId] = PlanTemplate(period, dailyReqCap, rateLimit, metadata, true);
v2templates[nextTemplateId] = PlanTemplateV2(period, dailyReqCap, rateLimit, priceToken, metadata, true);

emit PlanTemplateCreated(nextTemplateId);
nextTemplateId++;
Expand All @@ -116,9 +120,9 @@ contract PlanManager is Initializable, OwnableUpgradeable, IPlanManager {
* @param metadata metadata to update
*/
function updatePlanTemplateMetadata(uint256 templateId, bytes32 metadata) external onlyOwner {
require(templates[templateId].period > 0, 'PM004');
require(v2templates[templateId].period > 0, 'PM004');

templates[templateId].metadata = metadata;
v2templates[templateId].metadata = metadata;

emit PlanTemplateMetadataChanged(templateId, metadata);
}
Expand All @@ -129,13 +133,21 @@ contract PlanManager is Initializable, OwnableUpgradeable, IPlanManager {
* @param active plan template active or not
*/
function updatePlanTemplateStatus(uint256 templateId, bool active) external onlyOwner {
require(templates[templateId].period > 0, 'PM004');
require(v2templates[templateId].period > 0, 'PM004');

templates[templateId].active = active;
v2templates[templateId].active = active;

emit PlanTemplateStatusChanged(templateId, active);
}

function convertPlanPriceToSQT(address priceToken, uint256 price) public view returns (uint256) {
if (priceToken != settings.getSQToken()){
return price * 1e18 / IPriceOracle(settings.getPriceOracle()).getAssetPrice(priceToken, settings.getSQToken());
} else {
return price;
}
}

/**
* @notice Allow Indexer to create a Plan basing on a specific plan template.
* @param price plan price
Expand All @@ -145,7 +157,7 @@ contract PlanManager is Initializable, OwnableUpgradeable, IPlanManager {
function createPlan(uint256 price, uint256 templateId, bytes32 deploymentId) external {
require(!(IEraManager(settings.getEraManager()).maintenance()), 'G019');
require(price > 0, 'PM005');
require(templates[templateId].active, 'PM006');
require(v2templates[templateId].active, 'PM006');
require(limits[msg.sender][deploymentId] < limit, 'PM007');

plans[nextPlanId] = Plan(msg.sender, price, templateId, deploymentId, true);
Expand Down Expand Up @@ -186,14 +198,18 @@ contract PlanManager is Initializable, OwnableUpgradeable, IPlanManager {
require(deploymentId != bytes32(0), 'PM011');
}

//stable price mode
PlanTemplateV2 memory template = v2templates[plan.templateId];
uint256 sqtPrice = convertPlanPriceToSQT(template.priceToken, plan.price);

// create closed service agreement contract
ClosedServiceAgreementInfo memory agreement = ClosedServiceAgreementInfo(
msg.sender,
plan.indexer,
deploymentId,
plan.price,
sqtPrice,
block.timestamp,
templates[plan.templateId].period,
v2templates[plan.templateId].period,
planId,
plan.templateId
);
Expand All @@ -219,7 +235,12 @@ contract PlanManager is Initializable, OwnableUpgradeable, IPlanManager {
* @notice Get a specific plan templates
* @param templateId plan template id
*/
function getPlanTemplate(uint256 templateId) external view returns (PlanTemplate memory) {
return templates[templateId];
function getPlanTemplate(uint256 templateId) external view returns (PlanTemplateV2 memory) {
if (v2templates[templateId].period > 0) {
return v2templates[templateId];
} else {
PlanTemplate memory v1template = templates[templateId];
return PlanTemplateV2(v1template.period, v1template.dailyReqCap, v1template.rateLimit, settings.getSQToken(), v1template.metadata, v1template.active);
}
}
}
69 changes: 69 additions & 0 deletions contracts/PriceOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright (C) 2020-2022 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity 0.8.15;

import '@openzeppelin/contracts/access/Ownable.sol';

contract PriceOracle is Ownable {
///@notice the price of assetA in assetB
mapping(address => mapping(address => uint256)) public prices;

///@notice the size limit when controller change price
uint256 public sizeLimit;

///@notice the block number limit when controller change price
uint256 public blockLimit;

///@notice the block number of latest set price
uint256 public latestPriceBlock;

///@notice the controller account which can change price
address public controller;

constructor(uint256 _sizeLimit, uint256 _blockLimit) Ownable() {
sizeLimit = _sizeLimit;
blockLimit = _blockLimit;
}

event PricePosted(address assetA, address assetB, uint256 previousPrice, uint256 newPrice);

///@notice update change price limit
function setLimit(uint256 _sizeLimit, uint256 _blockLimit) public onlyOwner {
sizeLimit = _sizeLimit;
blockLimit = _blockLimit;
}

///@notice update the controller account
function setController(address _controller) public onlyOwner {
controller = _controller;
}

///@notice get the price of assetA in assetB
function getAssetPrice(address assetA, address assetB) public view returns (uint256) {
uint256 price = prices[assetA][assetB];
require(price > 0, "OR001");
return price;
}

///set the price of assetA in assetB
///AssetA in AssetB with a fixed precision of 18 decimal places
///Thus, if we wanted set 1 USDC = 13 SQT The price be 13000000000000000000000000000000(13e30)
function setAssetPrice(address assetA, address assetB, uint256 price) public {
uint256 prePrice = prices[assetA][assetB];
if (msg.sender == controller) {
require(latestPriceBlock + blockLimit < block.number, "OR002");

uint256 priceChanged = prePrice > price ? prePrice - price : price - prePrice;
uint256 sizeChanged = priceChanged * 100 / prePrice;

require(sizeChanged < sizeLimit, "OR003");
} else {
require(msg.sender == owner(), "OR004");
}

latestPriceBlock = block.number;
prices[assetA][assetB] = price;
emit PricePosted(assetA, assetB, prePrice, price);
}
}
5 changes: 3 additions & 2 deletions contracts/PurchaseOfferMarket.sol
Original file line number Diff line number Diff line change
Expand Up @@ -180,8 +180,9 @@ contract PurchaseOfferMarket is Initializable, OwnableUpgradeable, IPurchaseOffe
require(_deposit > 0, 'PO003');
require(_limit > 0, 'PO004');
IPlanManager planManager = IPlanManager(settings.getPlanManager());
PlanTemplate memory template = planManager.getPlanTemplate(_planTemplateId);
PlanTemplateV2 memory template = planManager.getPlanTemplate(_planTemplateId);
require(template.active, 'PO005');
require(template.priceToken == settings.getSQToken());

offers[numOffers] = PurchaseOffer(
_deposit,
Expand Down Expand Up @@ -274,7 +275,7 @@ contract PurchaseOfferMarket is Initializable, OwnableUpgradeable, IPurchaseOffe
offerMmrRoot[_offerId][msg.sender] = _mmrRoot;

IPlanManager planManager = IPlanManager(settings.getPlanManager());
PlanTemplate memory template = planManager.getPlanTemplate(offer.planTemplateId);
PlanTemplateV2 memory template = planManager.getPlanTemplate(offer.planTemplateId);
// create closed service agreement contract
ClosedServiceAgreementInfo memory agreement = ClosedServiceAgreementInfo(
offer.consumer,
Expand Down
15 changes: 14 additions & 1 deletion contracts/Settings.sol
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ contract Settings is ISettings, Initializable, OwnableUpgradeable, Constants {
address public disputeManager;
address public stateChannel;
address public consumerRegistry;
address public priceOracle;

function initialize() external initializer {
__Ownable_init();
Expand Down Expand Up @@ -71,7 +72,8 @@ contract Settings is ISettings, Initializable, OwnableUpgradeable, Constants {
address _rewardsHelper,
address _inflationController,
address _vesting,
address _permissionedExchange
address _permissionedExchange,
address _priceOracle
) external override onlyOwner {
require(_sqToken != ZERO_ADDRESS);
require(_staking != ZERO_ADDRESS);
Expand All @@ -83,6 +85,7 @@ contract Settings is ISettings, Initializable, OwnableUpgradeable, Constants {
require(_rewardsStaking != ZERO_ADDRESS);
require(_rewardsHelper != ZERO_ADDRESS);
require(_permissionedExchange != ZERO_ADDRESS);
require(_priceOracle != ZERO_ADDRESS);

sqToken = _sqToken;
staking = _staking;
Expand All @@ -94,6 +97,8 @@ contract Settings is ISettings, Initializable, OwnableUpgradeable, Constants {
inflationController = _inflationController;
vesting = _vesting;
permissionedExchange = _permissionedExchange;
priceOracle = _priceOracle;

}

function setSQToken(address _sqToken) external override onlyOwner {
Expand Down Expand Up @@ -249,4 +254,12 @@ contract Settings is ISettings, Initializable, OwnableUpgradeable, Constants {
function getConsumerRegistry() external view returns (address) {
return consumerRegistry;
}

function setPriceOracle(address _priceOracle) external override onlyOwner {
priceOracle = _priceOracle;
}

function getPriceOracle() external view returns (address) {
return priceOracle;
}
}
13 changes: 12 additions & 1 deletion contracts/interfaces/IPlanManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,19 @@ struct PlanTemplate {
bool active;
}

struct PlanTemplateV2 {
uint256 period;
uint256 dailyReqCap;
uint256 rateLimit;
address priceToken;
bytes32 metadata;
bool active;
}

interface IPlanManager {
function getPlan(uint256 planId) external view returns (Plan memory);

function getPlanTemplate(uint256 templateId) external view returns (PlanTemplate memory);
function getPlanTemplate(uint256 templateId) external view returns (PlanTemplateV2 memory);

function convertPlanPriceToSQT(address priceToken, uint256 price) external view returns (uint256);
}
8 changes: 8 additions & 0 deletions contracts/interfaces/IPriceOracle.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (C) 2020-2022 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity 0.8.15;

interface IPriceOracle {
function getAssetPrice(address assetA, address assetB) external view returns (uint);
}
7 changes: 6 additions & 1 deletion contracts/interfaces/ISettings.sol
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ interface ISettings {
address _rewardsHelper,
address _inflationController,
address _vesting,
address _permissionedExchange
address _permissionedExchange,
address _priceOracle
) external;

function setSQToken(address _sqToken) external;
Expand Down Expand Up @@ -99,4 +100,8 @@ interface ISettings {
function setConsumerRegistry(address _consumerRegistry) external;

function getConsumerRegistry() external view returns (address);

function setPriceOracle(address _priceOracle) external;

function getPriceOracle() external view returns (address);
}
6 changes: 5 additions & 1 deletion publish/revertcode.json
Original file line number Diff line number Diff line change
Expand Up @@ -153,5 +153,9 @@
"V008": "Vesting: transfer failure",
"V009": "vesting start date must in the future",
"V010": "balance not enough for allocation",
"V011": "vesting is not set on the account"
"V011": "vesting is not set on the account",
"OR001": "invalid asset price",
"OR002": "not meet the block number limitation",
"OR003": "invalid price size change",
"OR004": "invalid owner"
}
10 changes: 5 additions & 5 deletions scripts/config/startup.mainnet.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"planTemplates": [
{"period": 129600, "dailyReqCap": 100000, "rateLimit": 6000},
{"period": 86400, "dailyReqCap": 50000, "rateLimit": 3000},
{"period": 43200, "dailyReqCap": 10000, "rateLimit": 1200},
{"period": 21600, "dailyReqCap": 2000, "rateLimit": 600},
{"period": 7200, "dailyReqCap": 100, "rateLimit": 300}
{"period": 129600, "dailyReqCap": 100000, "rateLimit": 6000, "token": "0xed3bb617dbC128095f8b0A00B9498C2Ef5c3D04a"},
{"period": 86400, "dailyReqCap": 50000, "rateLimit": 3000, "token": "0xed3bb617dbC128095f8b0A00B9498C2Ef5c3D04a"},
{"period": 43200, "dailyReqCap": 10000, "rateLimit": 1200, "token": "0xed3bb617dbC128095f8b0A00B9498C2Ef5c3D04a"},
{"period": 21600, "dailyReqCap": 2000, "rateLimit": 600, "token": "0xed3bb617dbC128095f8b0A00B9498C2Ef5c3D04a"},
{"period": 7200, "dailyReqCap": 100, "rateLimit": 300, "token": "0xed3bb617dbC128095f8b0A00B9498C2Ef5c3D04a"}
],
"QRCreator": [
"0x293a6d85DD0d7d290A719Fdeef43FaD10240bA77",
Expand Down
10 changes: 5 additions & 5 deletions scripts/config/startup.testnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@
],
"amounts": [100, 200, 300, 400, 500, 600, 900, 900, 1200],
"planTemplates": [
{"period": 604800, "dailyReqCap": 50000, "rateLimit": 3000},
{"period": 129600, "dailyReqCap": 100000, "rateLimit": 6000},
{"period": 43200, "dailyReqCap": 10000, "rateLimit": 1200},
{"period": 21600, "dailyReqCap": 2000, "rateLimit": 600},
{"period": 7200, "dailyReqCap": 100, "rateLimit": 300}
{"period": 604800, "dailyReqCap": 50000, "rateLimit": 3000, "token": "0xed3bb617dbC128095f8b0A00B9498C2Ef5c3D04a"},
{"period": 129600, "dailyReqCap": 100000, "rateLimit": 6000, "token": "0xed3bb617dbC128095f8b0A00B9498C2Ef5c3D04a"},
{"period": 43200, "dailyReqCap": 10000, "rateLimit": 1200, "token": "0xed3bb617dbC128095f8b0A00B9498C2Ef5c3D04a"},
{"period": 21600, "dailyReqCap": 2000, "rateLimit": 600, "token": "0xed3bb617dbC128095f8b0A00B9498C2Ef5c3D04a"},
{"period": 7200, "dailyReqCap": 100, "rateLimit": 300, "token": "0xed3bb617dbC128095f8b0A00B9498C2Ef5c3D04a"}
],
"exchange": {
"usdcAddress": "0xE097d6B3100777DC31B34dC2c58fB524C2e76921",
Expand Down
4 changes: 4 additions & 0 deletions scripts/contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ import {
DisputeManager__factory,
ConsumerRegistry__factory,
ConsumerRegistry,
PriceOracle,
PriceOracle__factory,
ContractName,
} from '../src';

Expand Down Expand Up @@ -88,6 +90,7 @@ export type Contracts = {
consumerHost: ConsumerHost;
disputeManager: DisputeManager;
consumerRegistry: ConsumerRegistry;
priceOracle: PriceOracle;
};

export const UPGRADEBAL_CONTRACTS: Partial<Record<keyof typeof CONTRACTS, [{bytecode: string}, FactoryContstructor]>> =
Expand Down Expand Up @@ -138,6 +141,7 @@ export const CONTRACT_FACTORY: Record<ContractName, FactoryContstructor> = {
ConsumerHost: ConsumerHost__factory,
DisputeManager: DisputeManager__factory,
ConsumerRegistry: ConsumerRegistry__factory,
PriceOracle: PriceOracle__factory,
};

export type Config = number | string | BigNumber | string[];
Expand Down
Loading
Loading