-
Notifications
You must be signed in to change notification settings - Fork 0
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
Nicholas Fett
authored and
Nicholas Fett
committed
Sep 16, 2024
1 parent
0e2ed6f
commit a9e665a
Showing
13 changed files
with
7,782 additions
and
1 deletion.
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 |
---|---|---|
@@ -0,0 +1,17 @@ | ||
node_modules | ||
.env | ||
|
||
# Hardhat files | ||
/cache | ||
/artifacts | ||
|
||
# TypeChain files | ||
/typechain | ||
/typechain-types | ||
|
||
# solidity-coverage files | ||
/coverage | ||
/coverage.json | ||
|
||
# Hardhat Ignition default folder for deployments against a local node | ||
ignition/deployments/chain-31337 |
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 |
---|---|---|
@@ -1 +1,21 @@ | ||
# SampleLayerUser | ||
# SampleLayerUser | ||
|
||
The purpose of this is to show some best practices with regards on how to think about security of your data. | ||
|
||
|
||
Areas to think about: | ||
|
||
- The data is at least as old as 2 tellor blocks (1 sec each), and one block on the users chain (e.g. 12 sec for ethereum per block). | ||
However as with any blockchain, the blocks can fill up and validators can select or censor certain transactions so the data is not guarunteed to be fast. | ||
- All oracles can fail. Have a go to for if this happens (a pause or fallback if compromised, a method if the oracle just pauses). | ||
- data definitions - what data are you actually getting? What are the dispute thresholds? Are they strict or loose? | ||
So answer in code: | ||
|
||
Do you take the given oracle value upon submission? What aggregate power do you require? | ||
Do you use it optimistically if consnensus not reached? How long do you wait for disputes? Do you have a minimum power here? | ||
How old can the data be? (Be sure to verify that you can't dispute to go back in time) | ||
What happens if the oracle stops posting data? | ||
Can the contract be paused? How long, what are the details? | ||
How can users exit your contract in the case of a pause or oracle not posting data | ||
Are there exits in your system w/ no oracle updates or stale oracle updates (locking users until valid updates prevents censorship) | ||
|
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,81 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.22; | ||
|
||
import "../interfaces/IBlobstreamO.sol"; | ||
|
||
// this contract has a pause button from a guardian | ||
// it does not fallback to anything, but if the price is not updated for 48 hours, the guardian can change the oracle address | ||
// the contract consensus or fallback to a 24 hour delay with no power threshold (if not sure on support) | ||
//data can only go forward in time and must be within 5 minutes old, , must prove it's latest value | ||
|
||
contract SampleCPIUser { | ||
IBlobstreamO public blobstreamO; | ||
PriceData[] public priceData; | ||
bytes32 public queryId; | ||
bool public paused; | ||
address public guardian; | ||
|
||
event OracleUpdated(uint256 price, uint256 timestamp, uint256 aggregatePower); | ||
|
||
struct PriceData { | ||
uint256 price; | ||
uint256 timestamp; | ||
uint256 aggregatePower; | ||
uint256 previousTimestamp; | ||
uint256 nextTimestamp; | ||
uint256 relayTimestamp; | ||
} | ||
|
||
constructor(address _blobstreamO, bytes32 _queryId, address _guardian) { | ||
blobstreamO = IBlobstreamO(_blobstreamO); | ||
queryId = _queryId; | ||
guardian = _guardian; | ||
} | ||
|
||
function pauseContract() external{ | ||
require(msg.sender == guardian, "should be guardian"); | ||
paused = true; | ||
} | ||
|
||
function updateOracleData( | ||
OracleAttestationData calldata _attestData, | ||
Validator[] calldata _currentValidatorSet, | ||
Signature[] calldata _sigs | ||
) external { | ||
require(!paused, "contract paused"); | ||
require(_attestData.queryId == queryId, "Invalid queryId"); | ||
blobstreamO.verifyOracleData(_attestData, _currentValidatorSet, _sigs); | ||
uint256 _price = abi.decode(_attestData.report.value, (uint256)); | ||
if(_attestData.report.aggregatePower < blobstreamO.powerThreshold()){//if not consensus data | ||
require(_attestData.attestationTimestamp - _attestData.report.timestamp >= 15 minutes);//must be at least 15 minutes old | ||
require(_attestData.report.aggregatePower > blobstreamO.powerThreshold()/2);//must have >1/3 aggregate power | ||
require(_attestData.report.nextTimestamp == 0 || | ||
_attestData.attestationTimestamp - _attestData.report.nextTimestamp < 15 minutes);//cannot have newer data you can push | ||
}else{ | ||
require(_attestData.report.nextTimestamp == 0, "should be no newer timestamp"); // must push the newest data | ||
} | ||
require(block.timestamp - _attestData.attestationTimestamp < 10 minutes);//data cannot be more than 10 minutes old (the relayed attestation) | ||
require(_attestData.report.timestamp > priceData[priceData.length - 1].timestamp);//cannot go back in time | ||
priceData.push(PriceData( | ||
_price, | ||
_attestData.report.timestamp, | ||
_attestData.report.aggregatePower, | ||
_attestData.report.previousTimestamp, | ||
_attestData.report.nextTimestamp, | ||
block.timestamp | ||
) | ||
); | ||
} | ||
|
||
function getCurrentPriceData() external view returns (PriceData memory) { | ||
return priceData[priceData.length - 1]; | ||
} | ||
|
||
function getAllPriceData() external view returns(PriceData[] memory){ | ||
return priceData; | ||
} | ||
|
||
function getValueCount() external view returns (uint256) { | ||
return priceData.length; | ||
} | ||
} |
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,82 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.22; | ||
|
||
import "../interfaces/IBlobstreamO.sol"; | ||
|
||
|
||
// this contract has a pause button from a guardian | ||
// for our oracle, if its not updated for 24 hours, the guardian can change the oracle address | ||
// the contract is always on a delay (no finality), so you have 1 hour delay with 1/3 aggregate power threshold | ||
|
||
//example user - liquity | ||
contract SampleEVMCalleUser { | ||
IBlobstreamO public blobstreamO; | ||
PriceData[] public priceData; | ||
bytes32 public queryId; | ||
bool public paused; | ||
address public guardian; | ||
|
||
event OracleUpdated(uint256 price, uint256 timestamp, uint256 aggregatePower); | ||
|
||
struct PriceData { | ||
uint256 price; | ||
uint256 timestamp; | ||
uint256 aggregatePower; | ||
uint256 previousTimestamp; | ||
uint256 nextTimestamp; | ||
uint256 relayTimestamp; | ||
} | ||
|
||
constructor(address _blobstreamO, bytes32 _queryId, address _guardian) { | ||
blobstreamO = IBlobstreamO(_blobstreamO); | ||
queryId = _queryId; | ||
guardian = _guardian; | ||
} | ||
|
||
function pauseContract() external{ | ||
require(msg.sender == guardian, "should be guardian"); | ||
paused = true; | ||
} | ||
|
||
function updateOracleData( | ||
OracleAttestationData calldata _attestData, | ||
Validator[] calldata _currentValidatorSet, | ||
Signature[] calldata _sigs | ||
) external { | ||
require(!paused, "contract paused"); | ||
require(_attestData.queryId == queryId, "Invalid queryId"); | ||
blobstreamO.verifyOracleData(_attestData, _currentValidatorSet, _sigs); | ||
uint256 _price = abi.decode(_attestData.report.value, (uint256)); | ||
if(_attestData.report.aggregatePower < blobstreamO.powerThreshold()){//if not consensus data | ||
require(_attestData.attestationTimestamp - _attestData.report.timestamp >= 15 minutes);//must be at least 15 minutes old | ||
require(_attestData.report.aggregatePower > blobstreamO.powerThreshold()/2);//must have >1/3 aggregate power | ||
require(_attestData.report.nextTimestamp == 0 || | ||
_attestData.attestationTimestamp - _attestData.report.nextTimestamp < 15 minutes);//cannot have newer data you can push | ||
}else{ | ||
require(_attestData.report.nextTimestamp == 0, "should be no newer timestamp"); // must push the newest data | ||
} | ||
require(block.timestamp - _attestData.attestationTimestamp < 10 minutes);//data cannot be more than 10 minutes old (the relayed attestation) | ||
require(_attestData.report.timestamp > priceData[priceData.length - 1].timestamp);//cannot go back in time | ||
priceData.push(PriceData( | ||
_price, | ||
_attestData.report.timestamp, | ||
_attestData.report.aggregatePower, | ||
_attestData.report.previousTimestamp, | ||
_attestData.report.nextTimestamp, | ||
block.timestamp | ||
) | ||
); | ||
} | ||
|
||
function getCurrentPriceData() external view returns (PriceData memory) { | ||
return priceData[priceData.length - 1]; | ||
} | ||
|
||
function getAllPriceData() external view returns(PriceData[] memory){ | ||
return priceData; | ||
} | ||
|
||
function getValueCount() external view returns (uint256) { | ||
return priceData.length; | ||
} | ||
} |
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,103 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.22; | ||
|
||
import "../interfaces/IBlobstreamO.sol"; | ||
|
||
|
||
// this contract has a pause button from a guardian. Just pauses centralized oracle for 24 hours (then 24 hr before can pause again) | ||
// has governance contract to upgrade centralized oracle | ||
// uses a centralized oracle, but fallsback IF: | ||
// oracle not updated in 1 hour | ||
// oracle frozen by guardian | ||
|
||
// for our oracle, if its not updated for 7 days, the guardian can change the oracle address | ||
// the contract is consensus or fallback to a 15 minute delay with 1/3 aggregate power threshold (assume a large pair, should be smaller if less supported) | ||
// data must be newer than 5 minutes ago, must prove it's latest value | ||
|
||
//example user - liquity | ||
contract SampleFallbackOracleUser { | ||
IBlobstreamO public blobstreamO; | ||
PriceData[] public priceData; | ||
bytes32 public queryId; | ||
bool public paused; | ||
uint256 public pauseTimestamp; | ||
address public guardian; | ||
|
||
event OracleUpdated(uint256 price, uint256 timestamp, uint256 aggregatePower); | ||
|
||
struct PriceData { | ||
uint256 price; | ||
uint256 timestamp; | ||
uint256 aggregatePower; | ||
uint256 previousTimestamp; | ||
uint256 nextTimestamp; | ||
uint256 relayTimestamp; | ||
} | ||
|
||
constructor(address _blobstreamO, bytes32 _queryId, address _guardian, address _centralizedOracle) { | ||
blobstreamO = IBlobstreamO(_blobstreamO); | ||
queryId = _queryId; | ||
guardian = _guardian; | ||
centralizedOracle = _centralizedOracle; | ||
} | ||
|
||
function pauseContract() external{ | ||
require(msg.sender == guardian, "should be guardian"); | ||
paused = true; | ||
} | ||
|
||
function updateOracleData( | ||
OracleAttestationData calldata _attestData, | ||
Validator[] calldata _currentValidatorSet, | ||
Signature[] calldata _sigs | ||
) external { | ||
require(_attestData.report.timestamp > priceData[priceData.length - 1].timestamp, "cannot go back in time");//cannot go back in time | ||
uint256 _price = abi.decode(_attestData.report.value, (uint256)); | ||
if(!paused && (block.timestamp - priceData[priceData.length - 1]) < 1 hours){ | ||
require(msg.sender == centralizedOracle, "must be proper signer"); | ||
priceData.push(PriceData( | ||
_price, | ||
_attestData.report.timestamp, | ||
_attestData.report.aggregatePower, | ||
_attestData.report.previousTimestamp, | ||
_attestData.report.nextTimestamp, | ||
block.timestamp | ||
) | ||
); | ||
return; | ||
} | ||
require(!paused, "contract paused"); | ||
require(_attestData.queryId == queryId, "Invalid queryId"); | ||
blobstreamO.verifyOracleData(_attestData, _currentValidatorSet, _sigs); | ||
if(_attestData.report.aggregatePower < blobstreamO.powerThreshold()){//if not consensus data | ||
require(_attestData.attestationTimestamp - _attestData.report.timestamp >= 15 minutes);//must be at least 15 minutes old | ||
require(_attestData.report.aggregatePower > blobstreamO.powerThreshold()/2);//must have >1/3 aggregate power | ||
require(_attestData.report.nextTimestamp == 0 || | ||
_attestData.attestationTimestamp - _attestData.report.nextTimestamp < 15 minutes);//cannot have newer data you can push | ||
}else{ | ||
require(_attestData.report.nextTimestamp == 0, "should be no newer timestamp"); // must push the newest data | ||
} | ||
require(block.timestamp - _attestData.attestationTimestamp < 5 minutes);//data cannot be more than 5 minutes old (the relayed attestation) | ||
priceData.push(PriceData( | ||
_price, | ||
_attestData.report.timestamp, | ||
_attestData.report.aggregatePower, | ||
_attestData.report.previousTimestamp, | ||
_attestData.report.nextTimestamp, | ||
block.timestamp | ||
) | ||
); | ||
} | ||
|
||
function getCurrentPriceData() external view returns (PriceData memory) { | ||
return priceData[priceData.length - 1]; | ||
} | ||
|
||
function getAllPriceData() external view returns(PriceData[] memory){ | ||
return priceData; | ||
} | ||
|
||
function getValueCount() external view returns (uint256) { | ||
return priceData.length; | ||
} | ||
} |
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,73 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity 0.8.22; | ||
|
||
import "../interfaces/IBlobstreamO.sol"; | ||
|
||
// this contract has a pause button from a guardian (if paused, it should settle invalid or go to governance) | ||
// the contract consensus or fallback to a 24 hour delay with no aggregate power threshold (since you don't know what the question is) | ||
// data age can be whenever (just one answer) | ||
|
||
contract SamplePredictionMarketUser { | ||
IBlobstreamO public blobstreamO; | ||
PriceData[] public priceData; | ||
bytes32 public queryId; | ||
bool public paused; | ||
address public guardian; | ||
|
||
event OracleUpdated(uint256 price, uint256 timestamp, uint256 aggregatePower); | ||
|
||
struct PriceData { | ||
uint256 price; | ||
uint256 timestamp; | ||
uint256 aggregatePower; | ||
uint256 previousTimestamp; | ||
uint256 nextTimestamp; | ||
uint256 relayTimestamp; | ||
} | ||
|
||
constructor(address _blobstreamO, bytes32 _queryId, address _guardian) { | ||
blobstreamO = IBlobstreamO(_blobstreamO); | ||
queryId = _queryId; | ||
guardian = _guardian; | ||
} | ||
|
||
function pauseContract() external{ | ||
require(msg.sender == guardian, "should be guardian"); | ||
paused = true; | ||
} | ||
|
||
function updateOracleData( | ||
OracleAttestationData calldata _attestData, | ||
Validator[] calldata _currentValidatorSet, | ||
Signature[] calldata _sigs | ||
) external { | ||
require(!paused, "contract paused"); | ||
require(_attestData.queryId == queryId, "Invalid queryId"); | ||
blobstreamO.verifyOracleData(_attestData, _currentValidatorSet, _sigs); | ||
uint256 _price = abi.decode(_attestData.report.value, (uint256)); | ||
if(_attestData.report.aggregatePower < blobstreamO.powerThreshold()){//if not consensus data | ||
require(_attestData.attestationTimestamp - _attestData.report.timestamp >= 24 hours);//must be at least 24 hours old | ||
} | ||
priceData.push(PriceData( | ||
_price, | ||
_attestData.report.timestamp, | ||
_attestData.report.aggregatePower, | ||
_attestData.report.previousTimestamp, | ||
_attestData.report.nextTimestamp, | ||
block.timestamp | ||
) | ||
); | ||
} | ||
|
||
function getCurrentPriceData() external view returns (PriceData memory) { | ||
return priceData[priceData.length - 1]; | ||
} | ||
|
||
function getAllPriceData() external view returns(PriceData[] memory){ | ||
return priceData; | ||
} | ||
|
||
function getValueCount() external view returns (uint256) { | ||
return priceData.length; | ||
} | ||
} |
Oops, something went wrong.