Skip to content

Commit

Permalink
gas optimization for chainlinkDS
Browse files Browse the repository at this point in the history
  • Loading branch information
radar bear committed Sep 4, 2024
1 parent dffe9cb commit 87939ee
Show file tree
Hide file tree
Showing 2 changed files with 64 additions and 81 deletions.
18 changes: 10 additions & 8 deletions script/deployChainlinkDSPortal.s.sol
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,21 @@ contract ChainlinkDSPortalScript is Script {
uint256 _usdcHeartbeat = 86400;
address _usdcSource = 0x7e860098F58bBFC8648a4311b374B1D669a2bc6B;
address _reportSubmitter = 0x58D6e7ACcC617758F890Ba796d34777d2c46210C;
address _feeTokenAddress = 0x4200000000000000000000000000000000000006;

ChainlinkDSPortal chainlinkDSPortal = new ChainlinkDSPortal(
_dsVerifyProxy,
_reportSubmitter,
_usdcHeartbeat,
_usdcSource
_usdcSource,
_feeTokenAddress
);

vm.stopBroadcast();

// 自动执行验证
string memory chainId = vm.envString("CHAIN_ID");
bytes memory arguments = abi.encode(_dsVerifyProxy,_reportSubmitter,_usdcHeartbeat,_usdcSource);
bytes memory arguments = abi.encode(_dsVerifyProxy,_reportSubmitter,_usdcHeartbeat,_usdcSource,_feeTokenAddress);
string[] memory inputs = new string[](8);
inputs[0] = "forge";
inputs[1] = "verify-contract";
Expand All @@ -41,7 +43,6 @@ contract ChainlinkDSPortalScript is Script {
inputs[7] = Utils.bytesToStringWithout0x(arguments);
Utils.logInputs(inputs);
}
}

contract ChainlinkDSPortalScriptBaseTestnet is Script {
// add this to be excluded from coverage report
Expand All @@ -54,24 +55,25 @@ contract ChainlinkDSPortalScriptBaseTestnet is Script {
uint256 _usdcHeartbeat = 86400;
address _usdcSource = 0xd30e2101a97dcbAeBCBC04F14C3f624E67A35165;
address _reportSubmitter = 0x1cA8dd11fF12Fc22cd2ab83317cFd90df6a73694;
// address _owner = 0x1cA8dd11fF12Fc22cd2ab83317cFd90df6a73694;
address _feeTokenAddress = 0x4200000000000000000000000000000000000006;// address _owner = 0x1cA8dd11fF12Fc22cd2ab83317cFd90df6a73694;

ChainlinkDSPortal chainlinkDSPortal = new ChainlinkDSPortal(
_dsVerifyProxy,
_reportSubmitter,
_usdcHeartbeat,
_usdcSource
_usdcSource,
_feeTokenAddress
);

chainlinkDSPortal.newPriceSources(
chainlinkDSPortal.newPriceSourceConfig(
"BTCUSDC",
0x0FB99723Aee6f420beAD13e6bBB79b7E6F034298,
0x00037da06d56d083fe599397a4769a042d63aa73dc4ef57709d31e9971a5b439,
20,
30,
86400
);
chainlinkDSPortal.newPriceSources(
chainlinkDSPortal.newPriceSourceConfig(
"ETHUSDC",
0x4aDC67696bA383F43DD60A9e78F2C97Fbbfc7cb1,
0x000359843a543ee2fe414dc14c7e7920ef10f4372990b79d6361cdc0dd1ba782,
Expand All @@ -84,7 +86,7 @@ contract ChainlinkDSPortalScriptBaseTestnet is Script {

// 自动执行验证
string memory chainId = vm.envString("CHAIN_ID");
bytes memory arguments = abi.encode(_dsVerifyProxy,_reportSubmitter,_usdcHeartbeat,_usdcSource);
bytes memory arguments = abi.encode(_dsVerifyProxy,_reportSubmitter,_usdcHeartbeat,_usdcSource,_feeTokenAddress);
string[] memory inputs = new string[](8);
inputs[0] = "forge";
inputs[1] = "verify-contract";
Expand Down
127 changes: 54 additions & 73 deletions src/oracle/ChainlinkDS.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct Report {
bytes32 feedId; // The feed ID the report has data for
uint32 validFromTimestamp; // Earliest timestamp for which price is applicable
uint32 observationsTimestamp; // Latest timestamp for which price is applicable
uint192 nativeFee; // Base cost to validate a transaction using the report, denominated in the chains native token (WETH/ETH)
uint192 nativeFee; // Base cost to validate a transaction using the report, denominated in the chain's native token (WETH/ETH)
uint192 linkFee; // Base cost to validate a transaction using the report, denominated in LINK
uint32 expiresAt; // Latest timestamp where the report can be verified onchain
int192 price; // DON consensus median price (8 or 18 decimals)
Expand Down Expand Up @@ -85,7 +85,7 @@ interface IFeeManager {
}

/**
* @title PriceSources
* @title PriceSourceConfig
* @dev Struct to store information about price sources.
* This struct holds various details about a price source, including the Chainlink feed,
* decimal corrections, the last data stream report, and heartbeat intervals.
Expand All @@ -94,7 +94,7 @@ interface IFeeManager {
* So, if we want to increase the decimal by 6 places, we should set DecimalsCorrection to 1e12.
* If we want to decrease the decimal by 12 places, we should set DecimalsCorrection to 1e30.
*/
struct PriceSources {
struct PriceSourceConfig {
IChainlink chainlinkFeed; // The Chainlink feed interface for fetching price data
uint256 feedDecimalsCorrection; // Decimal correction factor for the Chainlink feed price
Report lastDSReport; // The last verified data stream report
Expand All @@ -116,10 +116,11 @@ contract ChainlinkDSPortal is Ownable {
// chainlink datastream proxy
IVerifierProxy public immutable dsVerifyProxy;
address public reportSubmitter;
address public immutable feeTokenAddress;

// registered sources
string[] public registeredNames;
mapping(bytes32 => PriceSources) public priceSourcesMap;
mapping(bytes32 => PriceSourceConfig) public priceSourceConfigMap;

// The prices from pricesources are all calculated in USD, and the price of USDC needs to be considered as well, using Chainlink's USDC price.
uint256 public immutable usdcHeartbeat;
Expand All @@ -144,19 +145,23 @@ contract ChainlinkDSPortal is Ownable {
/**
* @dev Constructor to initialize the contract state.
* @param _dsVerifyProxy The address of the Data Stream verification proxy.
* @param _reportSubmitter The address of the report submitter.
* @param _usdcHeartbeat The heartbeat interval for USDC.
* @param _usdcSource The address of the USDC data source.
* @param _feeTokenAddress The address of the fee token.
*/
constructor(
address _dsVerifyProxy,
address _reportSubmitter,
uint256 _usdcHeartbeat,
address _usdcSource
address _usdcSource,
address _feeTokenAddress
) {
dsVerifyProxy = IVerifierProxy(_dsVerifyProxy);
reportSubmitter = _reportSubmitter;
usdcHeartbeat = _usdcHeartbeat;
usdcSource = _usdcSource;
feeTokenAddress = _feeTokenAddress;
}

/**
Expand Down Expand Up @@ -187,42 +192,6 @@ contract ChainlinkDSPortal is Ownable {
reportSubmitter = _reportSubmitter;
}

/**
* @dev Verifies a report.
* @param unverifiedReport The raw report to be verified.
* @return verifiedReport The verified report.
*/
function _verifyReport(
bytes memory unverifiedReport
) private returns (Report memory verifiedReport) {
// Report verification fees, paid in Native token
IFeeManager feeManager = IFeeManager(
address(dsVerifyProxy.s_feeManager())
);

(, /* bytes32[3] reportContextData */ bytes memory reportData) = abi
.decode(unverifiedReport, (bytes32[3], bytes));

address feeTokenAddress = feeManager.i_nativeAddress();

(Common.Asset memory fee, , ) = feeManager.getFeeAndReward(
address(this),
reportData,
feeTokenAddress
);

// Approve feeManager to spend this contract's balance in fees
IERC20(feeTokenAddress).approve(address(feeManager), fee.amount);

// Verify the report
bytes memory verifiedReportData = IVerifierProxy(dsVerifyProxy).verify(
unverifiedReport,
abi.encode(feeTokenAddress)
);

verifiedReport = abi.decode(verifiedReportData, (Report));
}

/**
* @dev Returns an empty report.
* @return emptyReport The empty report.
Expand Down Expand Up @@ -254,7 +223,7 @@ contract ChainlinkDSPortal is Ownable {
* @param _DSDecimalCorrection The decimal correction for the Data Stream.
* @param _heartBeat The heartbeat interval for the price source.
*/
function newPriceSources(
function newPriceSourceConfig(
string memory name,
address _chainlinkFeed,
bytes32 _DSFeedId,
Expand All @@ -264,14 +233,14 @@ contract ChainlinkDSPortal is Ownable {
) public onlyOwner {
bytes32 key = nameToKey(name);
require(
bytes(priceSourcesMap[key].name).length == 0,
bytes(priceSourceConfigMap[key].name).length == 0,
"NAME_ALREADY_EXIST"
);
registeredNames.push(name);
address adaptor = address(new ChainlinkDSAdaptor(name, address(this)));
Report memory emptyReport = _getEmptyReport();

priceSourcesMap[key] = PriceSources({
priceSourceConfigMap[key] = PriceSourceConfig({
chainlinkFeed: IChainlink(_chainlinkFeed),
feedDecimalsCorrection: 10 ** _feedDecimalCorrection,
lastDSReport: emptyReport,
Expand All @@ -294,7 +263,7 @@ contract ChainlinkDSPortal is Ownable {
* @param _heartBeat The heartbeat interval for the price source.
* @param _resetReport Whether to reset the last report.
*/
function resetPriceSources(
function resetPriceSourceConfig(
string memory name,
address _chainlinkFeed,
bytes32 _DSFeedId,
Expand All @@ -304,15 +273,15 @@ contract ChainlinkDSPortal is Ownable {
bool _resetReport
) public onlyOwner {
bytes32 key = nameToKey(name);
require(bytes(priceSourcesMap[key].name).length > 0, "NAME_NOT_EXIST");
PriceSources storage priceSources = priceSourcesMap[key];
priceSources.chainlinkFeed = IChainlink(_chainlinkFeed);
priceSources.DSFeedId = _DSFeedId;
priceSources.feedDecimalsCorrection = 10 ** _feedDecimalCorrection;
priceSources.DSDecimalCorrection = 10 ** _DSDecimalCorrection;
priceSources.heartBeat = _heartBeat;
require(bytes(priceSourceConfigMap[key].name).length > 0, "NAME_NOT_EXIST");
PriceSourceConfig storage priceSourceConfig = priceSourceConfigMap[key];
priceSourceConfig.chainlinkFeed = IChainlink(_chainlinkFeed);
priceSourceConfig.DSFeedId = _DSFeedId;
priceSourceConfig.feedDecimalsCorrection = 10 ** _feedDecimalCorrection;
priceSourceConfig.DSDecimalCorrection = 10 ** _DSDecimalCorrection;
priceSourceConfig.heartBeat = _heartBeat;
if (_resetReport) {
priceSources.lastDSReport = _getEmptyReport();
priceSourceConfig.lastDSReport = _getEmptyReport();
}
}

Expand All @@ -325,25 +294,37 @@ contract ChainlinkDSPortal is Ownable {
string[] memory names,
bytes[] memory unverifiedReports
) public onlyOwnerOrReportSubmitter {
require(names.length == unverifiedReports.length, "LENGTH_MISMATCH");

// Verify reports in bulk
bytes[] memory verifiedReports = IVerifierProxy(dsVerifyProxy).verifyBulk(
unverifiedReports,
abi.encode(feeTokenAddress)
);

for (uint256 i = 0; i < names.length; i++) {
bytes32 key = nameToKey(names[i]);
PriceSources storage priceSources = priceSourcesMap[key];
Report memory newReport = _verifyReport(unverifiedReports[i]);
PriceSourceConfig storage priceSourceConfig = priceSourceConfigMap[key];

Report memory newReport = abi.decode(verifiedReports[i], (Report));

require(
newReport.validFromTimestamp >
priceSources.lastDSReport.validFromTimestamp,
priceSourceConfig.lastDSReport.validFromTimestamp,
"INVALID_REPORT_TIMESTAMP"
);
require(
newReport.feedId == priceSources.DSFeedId,
newReport.feedId == priceSourceConfig.DSFeedId,
"DS_FEED_ID_NOT_MATCH"
);
priceSources.lastDSReport = _verifyReport(unverifiedReports[i]);
priceSources.DSRoundId += 1;

priceSourceConfig.lastDSReport = newReport;
priceSourceConfig.DSRoundId += 1;

emit AnswerUpdated(
int256(priceSources.lastDSReport.price),
priceSources.lastDSReport.feedId,
priceSources.DSRoundId,
int256(priceSourceConfig.lastDSReport.price),
priceSourceConfig.lastDSReport.feedId,
priceSourceConfig.DSRoundId,
block.timestamp,
names[i]
);
Expand All @@ -369,14 +350,14 @@ contract ChainlinkDSPortal is Ownable {
}

function getPriceByKey(bytes32 key) public view returns (uint256 price) {
PriceSources memory priceSources = priceSourcesMap[key];
Report memory DSReport = priceSources.lastDSReport;
PriceSourceConfig memory priceSourceConfig = priceSourceConfigMap[key];
Report memory DSReport = priceSourceConfig.lastDSReport;

// Get original chainlink feed price
int256 feedPrice;
uint256 feedUpdatedAt;
if (address(priceSources.chainlinkFeed) != address(0)) {
(, feedPrice, , feedUpdatedAt, ) = priceSources
if (address(priceSourceConfig.chainlinkFeed) != address(0)) {
(, feedPrice, , feedUpdatedAt, ) = priceSourceConfig
.chainlinkFeed
.latestRoundData();
}
Expand All @@ -390,16 +371,16 @@ contract ChainlinkDSPortal is Ownable {
latestTimestamp = DSReport.validFromTimestamp;
price =
(SafeCast.toUint256(DSReport.price) * 1e18) /
priceSources.DSDecimalCorrection;
priceSourceConfig.DSDecimalCorrection;
} else {
latestTimestamp = feedUpdatedAt;
price =
(SafeCast.toUint256(feedPrice) * 1e18) /
priceSources.feedDecimalsCorrection;
priceSourceConfig.feedDecimalsCorrection;
}
// check heartbeat
require(
block.timestamp - latestTimestamp <= priceSources.heartBeat,
block.timestamp - latestTimestamp <= priceSourceConfig.heartBeat,
"ORACLE_HEARTBEAT_FAILED"
);

Expand Down Expand Up @@ -433,14 +414,14 @@ contract ChainlinkDSPortal is Ownable {
string memory name
) public view returns (Report memory) {
bytes32 key = nameToKey(name);
return priceSourcesMap[key].lastDSReport;
return priceSourceConfigMap[key].lastDSReport;
}

function getPriceSourcesByName(
function getPriceSourceConfigByName(
string memory name
) public view returns (PriceSources memory) {
) public view returns (PriceSourceConfig memory) {
bytes32 key = nameToKey(name);
return priceSourcesMap[key];
return priceSourceConfigMap[key];
}
}

Expand Down

0 comments on commit 87939ee

Please sign in to comment.