An Oraclize price ticker that fetches the ETH/Bitcoin (ETH/XBT) rate from Kraken, deployed at DEVCON1 in November 2015.
Historical Significance
This is one of the earliest oracle-consuming contracts on Ethereum mainnet and a direct artifact of DEVCON1, Ethereum's first developer conference. Because smart contracts cannot make HTTP requests on their own, services like Oraclize were the standard way to bring off-chain data such as price feeds on-chain in the Frontier era, years before Chainlink and the DeFi ecosystem existed. The contract is a canonical demonstration of the usingOraclize pattern: an authenticated HTTPS query to an exchange API, a callback that stores the result, and a self-renewing schedule that keeps the feed current. The embedded DEVCON1 coupon ties it specifically to the November 2015 conference and to Oraclize's developer outreach at the event.
Context
The contract was deployed on November 19, 2015, during Ethereum's Frontier release, which had launched on July 30, 2015. It was compiled with Solidity v0.1.6, a compiler from November 2015, and predates the Homestead hard fork of March 2016. At this time there was intense interest in oracles, since a contract has no native way to read data from the outside world. Oraclize, using TLSNotary proofs, was the dominant solution for delivering authenticated web data to contracts. Kraken was a leading exchange and one of the few offering an ETH/XBT trading pair, which made its public ticker a natural source for an Ether against Bitcoin price feed. DEVCON1, held in London in November 2015, was where much of this early tooling was showcased.
Key Facts
Description
This contract is an Oraclize-powered price feed that tracks the ETH/XBT (Ether against Bitcoin) exchange rate. Its update function asks the Oraclize service to fetch the price from Kraken's public ticker API, using the URL data source and the JSON query json(https://api.kraken.com/0/public/Ticker?pair=ETHXBT).result.XETHXXBT.c.0. When Oraclize delivers the result, the __callback function stores the returned price string in a public state variable named ETHXBT and immediately schedules the next query with a 180 second delay, so the contract refreshes its price roughly every three minutes without any further interaction. A kill function lets the deploying account selfdestruct the contract.
The contract was deployed on November 19, 2015, the week after DEVCON1, Ethereum's first developer conference, held in London from November 9 to 13, 2015. Each query is paid for with a promotional coupon code, DEVCON1, that Oraclize distributed so that conference attendees and developers could run oracle queries free of charge. The coupon string is embedded directly in the bytecode and is applied through the Oraclize useCoupon mechanism before every query.
Off-chain data delivery relies on the Oraclize service, later renamed Provable. The contract resolves the live Oraclize contract address at call time through the Oraclize Address Resolver at 0x1d11e5eae3112dbd44f99266872ff1d07c77dce8, the resolver Oraclize operated on Ethereum mainnet during this period. The constructor requests the TLSNotary plus IPFS proof type, the authenticity proof Oraclize offered to attest that returned data genuinely came from the stated HTTPS endpoint.
The contract was not verified on Etherscan. Its source has been reconstructed and its bytecode reproduced byte for byte, both the runtime and the creation code, using the Oraclize API library of the period and Solidity v0.1.6 with the optimizer enabled.
Source Verified
Exact bytecode match on both runtime and creation. Runtime: 2276 bytes, SHA-256 77a4131967d4b179374fbf73fe5a167782d5cf1c38cefd5c60ef112ac02d3575. Creation: 3822 bytes (1546 bytes of init code plus the embedded 2276 byte runtime payload, 0 constructor arguments), reproduced byte for byte (compile verdict exact-match, creation_diff.total_diff_bytes 0, init_prefix_match true), SHA-256 6ce714bd48f1a93b606c0c158a1d7940ad7b3f9c5e28187f9b162d4a478dd30c.
Heuristic Analysis
The following characteristics were detected through bytecode analysis and may not be accurate.
Frontier Era
The initial release of Ethereum. A bare-bones implementation for technical users.
Bytecode Overview
Verified Source Available
Source verified through compiler archaeology and exact bytecode matching.
View Verification ProofShow source code (Solidity)
// <ORACLIZE_API>
/*
Copyright (c) 2015 Oraclize srl, Thomas Bertani
*/
contract OraclizeI {
address public cbAddress;
function query(uint _timestamp, string _datasource, string _arg) returns (bytes32 _id);
function query_withGasLimit(uint _timestamp, string _datasource, string _arg, uint _gaslimit) returns (bytes32 _id);
function query2(uint _timestamp, string _datasource, string _arg1, string _arg2) returns (bytes32 _id);
function query2_withGasLimit(uint _timestamp, string _datasource, string _arg1, string _arg2, uint _gaslimit) returns (bytes32 _id);
function getPrice(string _datasource) returns (uint _dsprice);
function getPrice(string _datasource, uint gaslimit) returns (uint _dsprice);
function useCoupon(string _coupon);
function setProofType(byte _proofType);
}
contract OraclizeAddrResolverI {
function getAddress() returns (address _addr);
}
contract usingOraclize {
uint constant day = 60*60*24;
uint constant week = 60*60*24*7;
uint constant month = 60*60*24*30;
byte constant proofType_NONE = 0x00;
byte constant proofType_TLSNotary = 0x10;
byte constant proofStorage_IPFS = 0x01;
OraclizeI oraclize;
modifier oraclizeAPI {
OraclizeAddrResolverI OAR = OraclizeAddrResolverI(0x1d11e5eae3112dbd44f99266872ff1d07c77dce8);
oraclize = OraclizeI(OAR.getAddress());
_
}
modifier coupon(string code){
OraclizeAddrResolverI OAR = OraclizeAddrResolverI(0x1d11e5eae3112dbd44f99266872ff1d07c77dce8);
oraclize = OraclizeI(OAR.getAddress());
oraclize.useCoupon(code);
_
}
function oraclize_query(string datasource, string arg) oraclizeAPI internal returns (bytes32 id){
return oraclize.query.value(oraclize.getPrice(datasource))(0, datasource, arg);
}
function oraclize_query(uint timestamp, string datasource, string arg) oraclizeAPI internal returns (bytes32 id){
return oraclize.query.value(oraclize.getPrice(datasource))(timestamp, datasource, arg);
}
function oraclize_query(uint timestamp, string datasource, string arg, uint gaslimit) oraclizeAPI internal returns (bytes32 id){
return oraclize.query_withGasLimit.value(oraclize.getPrice(datasource, gaslimit))(timestamp, datasource, arg, gaslimit);
}
function oraclize_query(string datasource, string arg, uint gaslimit) oraclizeAPI internal returns (bytes32 id){
return oraclize.query_withGasLimit.value(oraclize.getPrice(datasource, gaslimit))(0, datasource, arg, gaslimit);
}
function oraclize_query(string datasource, string arg1, string arg2) oraclizeAPI internal returns (bytes32 id){
return oraclize.query2.value(oraclize.getPrice(datasource))(0, datasource, arg1, arg2);
}
function oraclize_query(uint timestamp, string datasource, string arg1, string arg2) oraclizeAPI internal returns (bytes32 id){
return oraclize.query2.value(oraclize.getPrice(datasource))(timestamp, datasource, arg1, arg2);
}
function oraclize_query(uint timestamp, string datasource, string arg1, string arg2, uint gaslimit) oraclizeAPI internal returns (bytes32 id){
return oraclize.query2_withGasLimit.value(oraclize.getPrice(datasource, gaslimit))(timestamp, datasource, arg1, arg2, gaslimit);
}
function oraclize_query(string datasource, string arg1, string arg2, uint gaslimit) oraclizeAPI internal returns (bytes32 id){
return oraclize.query2_withGasLimit.value(oraclize.getPrice(datasource, gaslimit))(0, datasource, arg1, arg2, gaslimit);
}
function oraclize_cbAddress() oraclizeAPI internal returns (address){
return oraclize.cbAddress();
}
function oraclize_setProof(byte proofP) oraclizeAPI {
return oraclize.setProofType(proofP);
}
function parseAddr(string _a) internal returns (address){
bytes memory tmp = bytes(_a);
uint160 iaddr = 0;
uint160 b1;
uint160 b2;
for (uint i=2; i<2+2*20; i+=2){
iaddr *= 256;
b1 = uint160(tmp[i]);
b2 = uint160(tmp[i+1]);
if ((b1 >= 97)&&(b1 <= 102)) b1 -= 87;
else if ((b1 >= 48)&&(b1 <= 57)) b1 -= 48;
if ((b2 >= 97)&&(b2 <= 102)) b2 -= 87;
else if ((b2 >= 48)&&(b2 <= 57)) b2 -= 48;
iaddr += (b1*16+b2);
}
return address(iaddr);
}
function strCompare(string _a, string _b) internal returns (int) {
bytes memory a = bytes(_a);
bytes memory b = bytes(_b);
uint minLength = a.length;
if (b.length < minLength) minLength = b.length;
for (uint i = 0; i < minLength; i ++)
if (a[i] < b[i])
return -1;
else if (a[i] > b[i])
return 1;
if (a.length < b.length)
return -1;
else if (a.length > b.length)
return 1;
else
return 0;
}
function indexOf(string _haystack, string _needle) internal returns (int)
{
bytes memory h = bytes(_haystack);
bytes memory n = bytes(_needle);
if(h.length < 1 || n.length < 1 || (n.length > h.length))
return -1;
else if(h.length > (2**128 -1))
return -1;
else
{
uint subindex = 0;
for (uint i = 0; i < h.length; i ++)
{
if (h[i] == n[0])
{
subindex = 1;
while(subindex < n.length && (i + subindex) < h.length && h[i + subindex] == n[subindex])
{
subindex++;
}
if(subindex == n.length)
return int(i);
}
}
return -1;
}
}
function strConcat(string _a, string _b) internal returns (string) {
bytes memory ba = bytes(_a);
bytes memory bb = bytes(_b);
bytes memory bc = " ";
uint k = 0;
for (uint i = 0; i < ba.length; i ++){
bc[k] = ba[i];
k++;
}
for (i = 0; i < bb.length; i ++){
bc[k] = bb[i];
k++;
}
for (i = k; i < bc.length; i ++){
bc[i] = 0;
}
return string(bc);
}
// parseInt
function parseInt(string _a) internal returns (uint) {
bytes memory bresult = bytes(_a);
uint mint = 0;
for (uint i=0; i<bresult.length; i++){
if ((bresult[i] >= 48)&&(bresult[i] <= 57)){
mint *= 10;
mint += uint(bresult[i]) - 48;
} else break;
}
return mint;
}
// parseInt(parseFloat*10^_b)
function parseInt(string _a, uint _b) internal returns (uint) {
bytes memory bresult = bytes(_a);
uint mint = 0;
bool decimals = false;
for (uint i=0; i<bresult.length; i++){
if ((bresult[i] >= 48)&&(bresult[i] <= 57)){
if (decimals){
if (_b == 0) break;
else _b--;
}
mint *= 10;
mint += uint(bresult[i]) - 48;
} else if (bresult[i] == 46) decimals = true;
}
return mint;
}
}
// </ORACLIZE_API>
contract EthXbt is usingOraclize {
address cb;
string public ETHXBT;
function __callback(bytes32 myid, string result, bytes proof) {
if (msg.sender != oraclize_cbAddress()) throw;
ETHXBT = result;
update();
}
function kill() {
if (msg.sender == cb) suicide(msg.sender);
}
function EthXbt() {
cb = msg.sender;
oraclize_setProof(proofType_TLSNotary | proofStorage_IPFS);
update();
}
function update() coupon("DEVCON1") {
oraclize_query(180, "URL", "json(https://api.kraken.com/0/public/Ticker?pair=ETHXBT).result.XETHXXBT.c.0", 500000);
}
}