Created
August 13, 2019 17:24
-
-
Save coburncoburn/0993fb16f54f19b56018e4d7745a955d to your computer and use it in GitHub Desktop.
Created using remix-ide: Realtime Ethereum Contract Compiler and Runtime. Load this file by pasting this gists URL or ID at https://remix.ethereum.org/#version=soljson-v0.5.10+commit.5a6ea5b1.js&optimize=true&gist=
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
pragma solidity ^0.5.10; | |
pragma experimental ABIEncoderV2; | |
/** | |
* @title The Open Oracle View Base Contract | |
* @author Compound Labs, Inc. | |
*/ | |
contract OpenOracleView { | |
/** | |
* @notice The Oracle Data Contract backing this View | |
*/ | |
OpenOracleData public data; | |
/** | |
* @notice The static list of sources used by this View | |
* @dev Note that while it is possible to create a view with dynamic sources, | |
* that would not conform to the Open Oracle Standard specification. | |
*/ | |
address[] public sources; | |
/** | |
* @notice Construct a view given the oracle backing address and the list of sources | |
* @dev According to the protocol, Views must be immutable to be considered conforming. | |
* @param data_ The address of the oracle data contract which is backing the view | |
* @param sources_ The list of source addresses to include in the aggregate value | |
*/ | |
constructor(OpenOracleData data_, address[] memory sources_) public { | |
data = data_; | |
sources = sources_; | |
} | |
} | |
/** | |
* @notice The DelFi Price Feed View | |
* @author Compound Labs, Inc. | |
*/ | |
contract DelFiPrice is OpenOracleView { | |
/** | |
* @notice The event emitted when a price is written to storage | |
*/ | |
event Price(string symbol, uint64 price); | |
/** | |
* @notice The mapping of medianized prices per symbol | |
*/ | |
mapping(string => uint64) public prices; | |
constructor(OpenOraclePriceData data_, address[] memory sources_) public OpenOracleView(data_, sources_) {} | |
/** | |
* @notice Primary entry point to post and recalculate prices | |
* @dev We let anyone pay to post anything, but only sources count for prices. | |
* @param messages The messages to post to the oracle | |
* @param signatures The signatures for the corresponding messages | |
*/ | |
function postPrices(bytes[] calldata messages, bytes[] calldata signatures, string[] calldata symbols) external { | |
require(messages.length == signatures.length, "messages and signatures must be 1:1"); | |
// Post the messages, whatever they are | |
for (uint i = 0; i < messages.length; i++) { | |
OpenOraclePriceData(address(data)).put(messages[i], signatures[i]); | |
} | |
// Recalculate the asset prices for the symbols to update | |
for (uint i = 0; i < symbols.length; i++) { | |
string memory symbol = symbols[i]; | |
// Calculate the median price, write to storage, and emit an event | |
uint64 price = medianPrice(symbol, sources); | |
prices[symbol] = price; | |
emit Price(symbol, price); | |
} | |
} | |
/** | |
* @notice Calculates the median price over any set of sources | |
* @param symbol The symbol to calculate the median price of | |
* @param sources_ The sources to use when calculating the median price | |
* @return median The median price over the set of sources | |
*/ | |
function medianPrice(string memory symbol, address[] memory sources_) public view returns (uint64 median) { | |
require(sources_.length > 0, "sources list must not be empty"); | |
uint N = sources_.length; | |
uint64[] memory postedPrices = new uint64[](N); | |
for (uint i = 0; i < N; i++) { | |
postedPrices[i] = OpenOraclePriceData(address(data)).getPrice(sources_[i], symbol); | |
} | |
uint64[] memory sortedPrices = sort(postedPrices); | |
return sortedPrices[N / 2]; | |
} | |
/** | |
* @notice Helper to sort an array of uints | |
* @param array Array of integers to sort | |
* @return The sorted array of integers | |
*/ | |
function sort(uint64[] memory array) private pure returns (uint64[] memory) { | |
uint N = array.length; | |
for (uint i = 0; i < N; i++) { | |
for (uint j = i + 1; j < N; j++) { | |
if (array[i] > array[j]) { | |
uint64 tmp = array[i]; | |
array[i] = array[j]; | |
array[j] = tmp; | |
} | |
} | |
} | |
return array; | |
} | |
} | |
/** | |
* @title The Open Oracle Data Base Contract | |
* @author Compound Labs, Inc. | |
*/ | |
contract OpenOracleData { | |
/** | |
* @notice The event emitted when a source writes to its storage | |
*/ | |
//event Write(address indexed source, <Key> indexed key, string kind, uint64 timestamp, <Value> value); | |
/** | |
* @notice Write a bunch of signed datum to the authenticated storage mapping | |
* @param message The payload containing the timestamp, and (key, value) pairs | |
* @param signature The cryptographic signature of the message payload, authorizing the source to write | |
* @return The keys that were written | |
*/ | |
//function put(bytes calldata message, bytes calldata signature) external returns (<Key> memory); | |
/** | |
* @notice Read a single key with a pre-defined type signature from an authenticated source | |
* @param source The verifiable author of the data | |
* @param key The selector for the value to return | |
* @return The claimed Unix timestamp for the data and the encoded value (defaults to (0, 0x)) | |
*/ | |
//function get(address source, <Key> key) external view returns (uint, <Value>); | |
/** | |
* @notice Recovers the source address which signed a message | |
* @dev Comparing to a claimed address would add nothing, | |
* as the caller could simply perform the recover and claim that address. | |
* @param message The data that was presumably signed | |
* @param signature The fingerprint of the data + private key | |
* @return The source address which signed the message, presumably | |
*/ | |
function source(bytes memory message, bytes memory signature) public pure returns (address) { | |
(bytes32 r, bytes32 s, uint8 v) = abi.decode(signature, (bytes32, bytes32, uint8)); | |
bytes32 hash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", keccak256(message))); | |
return ecrecover(hash, v, r, s); | |
} | |
} | |
/** | |
* @title The Open Oracle Price Data Contract | |
* @notice Values stored in this contract should represent a USD price with 6 decimals precision | |
* @author Compound Labs, Inc. | |
*/ | |
contract OpenOraclePriceData is OpenOracleData { | |
/** | |
* @notice The event emitted when a source writes to its storage | |
*/ | |
event Write(address indexed source, string key, uint64 timestamp, uint64 value); | |
/** | |
* @notice The fundamental unit of storage for a reporter source | |
*/ | |
struct Datum { | |
uint64 timestamp; | |
uint64 value; | |
} | |
/** | |
* @notice The most recent authenticated data from all sources | |
* @dev This is private because dynamic mapping keys preclude auto-generated getters. | |
*/ | |
mapping(address => mapping(string => Datum)) private data; | |
/** | |
* @notice Write a bunch of signed datum to the authenticated storage mapping | |
* @param message The payload containing the timestamp, and (key, value) pairs | |
* @param signature The cryptographic signature of the message payload, authorizing the source to write | |
* @return The keys that were written | |
*/ | |
function put(bytes calldata message, bytes calldata signature) external returns (string memory) { | |
// Recover the source address | |
address source = source(message, signature); | |
// Decode the message and check the kind | |
(string memory kind, uint64 timestamp, string memory key, uint64 value) = abi.decode(message, (string, uint64, string, uint64)); | |
require(keccak256(abi.encodePacked(kind)) == keccak256(abi.encodePacked("prices")), "Kind of data must be 'prices'"); | |
// Only update if newer than stored, according to source | |
Datum storage prior = data[source][key]; | |
if (prior.timestamp < timestamp) { | |
data[source][key] = Datum(timestamp, value); | |
emit Write(source, key, timestamp, value); | |
} | |
return key; | |
} | |
/** | |
* @notice Read a single key from an authenticated source | |
* @param source The verifiable author of the data | |
* @param key The selector for the value to return | |
* @return The claimed Unix timestamp for the data and the price value (defaults to (0, 0)) | |
*/ | |
function get(address source, string calldata key) external view returns (uint64, uint64) { | |
Datum storage datum = data[source][key]; | |
return (datum.timestamp, datum.value); | |
} | |
/** | |
* @notice Read only the value for a single key from an authenticated source | |
* @param source The verifiable author of the data | |
* @param key The selector for the value to return | |
* @return The price value (defaults to 0) | |
*/ | |
function getPrice(address source, string calldata key) external view returns (uint64) { | |
return data[source][key].value; | |
} | |
} | |
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
pragma solidity ^0.5.10; | |
contract Logalitos { | |
mapping(string => uint64) public prices; | |
event Log(address x, string y); | |
function set(string memory x, uint64 y) public { | |
emit Log(msg.sender, "logger"); | |
prices[x] = y; | |
} | |
} |
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
contract Test { | |
for (uint i = 0; i < messages.length; i++) { for (uint i = 0; i < messages.length; i++) { | |
string memory symbol = OpenOraclePriceData(address(data)).put(messages[i], signatures[i]); OpenOraclePriceData(address(data)).put(messages[i], signatures[i]); | |
// Possibly add to the unique symbol set | |
bool found = false; | |
for (uint j = 0; j < symbols.length; j++) { | |
if (keccak256(abi.encodePacked(symbol)) == keccak256(abi.encodePacked(symbols[j]))) { | |
found = true; | |
break; | |
} | |
} | |
if (!found) { | |
symbols[i] = symbol; | |
numSymbols += 1; | |
} | |
} | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment