Skip to main content

Enterprise

The Enterprise module focuses on Web2.

Consumers can get access to the legacy Web2 world through this module and bring off-chain data of public or permissioned APIs and off-chain computation to their Web3 application in the supported blockchain ecosystems .

As the developer is in full control, they can define their requirement in-code, based specifically on their requirements.

Docusaurus themed imageDocusaurus themed image

Example Use Cases

  • Public API access
    • price feeds
    • sport results
    • weather data
  • Permissioned API access
  • Verifiable randomness
  • Off-chain computation

Examples

Code examples for the Acurast Enterprise module for off-chain data and computation.

HTTPS GET Request

Request from an API with the result of an asset price.

httpGET(
"https://api.binance.com/api/v3/ticker/price?symbol=BNBBTC",
{},
(response, certificate) => {
console.log("response", response);
const price = JSON.parse(response)["price"] * 10 ** 6;
console.log("price", price);
const payload = { price: price, timestamp: Date.now() / 1000 };
const packedPayload = pack(payload);
const signature = sign(payload);
httpPOST(
"https://oracle.free.beeceptor.com",
JSON.stringify({
signature: signature,
certificate: certificate,
payload: packedPayload,
}),
{},
(response, certificate) => {},
(errorMessage) => {}
);
},
(errorMessage) => {}
);
https://example.com

Verifiable Randomness

Getting a randomized output from the Trusted Execution Environment.

return fulfill(generateSecureRandomHex());
https://example.com

Complete Example

This is a complete example that includes multiple price points and calculations.

  • Fetches prices for multiple assets from multiple endpoints
  • Does certificate pinning to ensure that the data is coming from the instructed sources
  • Calculations for an index with multiple assets
  • Calculations for the median
  • Defines a minimal amount of sources
const INTERVAL = 900 * 1000;

const MINIMUM_SOURCES_PER_PRICE = 2;

const PREVIOUS_EPOCH_START_MILLIS =
(Math.floor(Date.now() / INTERVAL) - 1) * INTERVAL;
const PREVIOUS_EPOCH_END_MILLIS =
Math.floor(Date.now() / INTERVAL) * INTERVAL - 1;
const PREVIOUS_EPOCH_START_SECONDS = Math.floor(
PREVIOUS_EPOCH_START_MILLIS / 1000
);
const PREVIOUS_EPOCH_END_SECONDS = Math.floor(PREVIOUS_EPOCH_END_MILLIS / 1000);

const PREVIOUS_EPOCH_START_ISO = new Date(
PREVIOUS_EPOCH_START_MILLIS
).toISOString();
const PREVIOUS_EPOCH_END_ISO = new Date(
PREVIOUS_EPOCH_END_MILLIS
).toISOString();

// for index to USDT
const BINANCE_SYMBOLS = [
"UNIUSDT",
"CAKEUSDT",
"LUNAUSDT",
"LINKUSDT",
"AAVEUSDT",
];
const KUCOIN_SYMBOLS = [
"UNI-USDT",
"CAKE-USDT",
"LUNA-USDT",
"LINK-USDT",
"AAVE-USDT",
];
const GATEIO_SYMBOLS = [
"UNI_USDT",
"CAKE_USDT",
"LUNA_USDT",
"LINK_USDT",
"AAVE_USDT",
];

// for USDT<>USD
const COINBASE_SYMBOLS = ["BTC-USD", "XTZ-USD", "USDT-USD"];
const BITFINEX_SYMBOLS = ["BTCUSD", "XTZUSD", "USTUSD"];
const BINANCE_US_SYMBOLS = ["BTCUSD", "XTZUSD", "USDTUSD"];

const PRICE_PRECISION = 10 ** 6;

// for index to USDT
const BINANCE_TEMPLATE = `https://api.binance.com/api/v3/klines?symbol=<<SYMBOL>>&interval=15m&startTime=${PREVIOUS_EPOCH_START_MILLIS}&endTime=${PREVIOUS_EPOCH_END_MILLIS}`;
const KUCOIN_TEMPLATE = `https://api.kucoin.com/api/v1/market/candles?symbol=<<SYMBOL>>&type=15min&startAt=${PREVIOUS_EPOCH_START_SECONDS}&endAt=${PREVIOUS_EPOCH_END_SECONDS}`;
const GATE_IO_TEMPLATE = `https://api.gateio.ws/api/v4/spot/candlesticks/?currency_pair=<<SYMBOL>>&interval=15m&from=${PREVIOUS_EPOCH_START_SECONDS}&to=${PREVIOUS_EPOCH_END_SECONDS}`;

// for USDT<>USD
const BINANCE_US_TEMPLATE = `https://api.binance.us/api/v3/klines?symbol=<<SYMBOL>>&interval=15m&startTime=${PREVIOUS_EPOCH_START_MILLIS}&endTime=${PREVIOUS_EPOCH_END_MILLIS}`;
const COINBASE_TEMPLATE = `https://api.pro.coinbase.com/products/<<SYMBOL>>/candles?granularity=900&start=${PREVIOUS_EPOCH_START_ISO}&end=${PREVIOUS_EPOCH_END_ISO}`;
const BITFINEX_TEMPLATE = `https://api-pub.bitfinex.com/v2/candles/trade:15m:t<<SYMBOL>>/hist?start=${PREVIOUS_EPOCH_START_MILLIS}&end=${PREVIOUS_EPOCH_END_MILLIS}`;

const BINANCE_CONFIG = {
url: BINANCE_TEMPLATE,
exchange_id: "BNN",
timestamp_factor: 1,
timestamp_index: 0,
close_index: 4,
certificate:
"0D:ED:57:7F:B9:1B:4C:8E:B2:36:50:10:4A:BA:08:FF:6C:86:77:75:4E:32:8D:02:09:E1:BE:F3:42:7B:BD:D4",
};
const KUCOIN_CONFIG = {
url: KUCOIN_TEMPLATE,
exchange_id: "KUC",
timestamp_factor: 1000,
timestamp_index: 0,
close_index: 2,
certificate:
"A7:3F:12:F8:0A:A1:87:C0:97:86:B1:E1:0E:03:73:9C:3C:71:73:44:DA:32:C7:77:21:57:0F:48:5C:FC:18:6F",
};
const GATE_IO_CONFIG = {
url: GATE_IO_TEMPLATE,
exchange_id: "GAT",
timestamp_factor: 1000,
timestamp_index: 0,
close_index: 2,
certificate:
"45:8B:33:B8:2F:54:69:32:7B:FB:0B:02:51:29:08:E6:6F:62:BA:2E:93:0A:F7:9E:CB:03:07:C2:E8:E9:DC:74",
};

const BINANCE_US_CONFIG = {
url: BINANCE_US_TEMPLATE,
exchange_id: "BNU",
timestamp_factor: 1,
timestamp_index: 0,
close_index: 4,
certificate:
"9E:94:90:DB:57:6D:B8:17:A4:68:C4:30:9D:83:76:C1:49:04:26:C1:2E:55:93:9D:E0:92:A6:13:16:14:E0:FA",
};
const COINBASE_CONFIG = {
url: COINBASE_TEMPLATE,
exchange_id: "CBP",
timestamp_factor: 1000,
timestamp_index: 0,
close_index: 4,
certificate:
"8B:70:72:2E:AE:DC:63:68:D9:EA:59:6F:B3:30:C1:E7:F8:5F:30:81:8D:7C:90:05:73:BC:64:04:61:D1:03:03",
};
const BITFINEX_CONFIG = {
url: BITFINEX_TEMPLATE,
exchange_id: "BFX",
timestamp_factor: 1,
timestamp_index: 0,
close_index: 2,
certificate:
"57:E3:2B:C3:C5:AC:D8:86:E4:E4:25:9C:CB:A2:40:29:1F:07:7D:E1:61:21:2B:0F:B0:EC:8E:71:4A:82:C8:D1",
};

const INITIAL_PRICES = {
AAVEUSD: 150.8,
CAKEUSD: 6.52,
LINKUSD: 14.76,
LUNAUSD: 94.46,
UNIUSD: 9.36,
};

const WEIGHTS = {
AAVE: 0.0406,
CAKE: 0.0368,
LINK: 0.1335,
LUNA: 0.6654,
UNI: 0.1238,
};

const LAST_REBALANCING_PRICE = 1.21617108;

const internalFetch = (config, symbols) => {
return symbols.map((symbol) => {
return new Promise((resolve, reject) => {
httpGET(
config.url.replace("<<SYMBOL>>", symbol),
{},
(rawResponse, certificate) => {
let response = JSON.parse(rawResponse);
if (config.exchange_id === "KUC") {
response = response["data"];
}

if (certificate === config.certificate) {
const payload = {
symbol: symbol
.replace("-", "")
.replace("_", "")
.replace("UST", "USDT"),
exchange_id: config.exchange_id,
timestamp:
response[0][config.timestamp_index] * config.timestamp_factor,
close: parseFloat(response[0][config.close_index]),
certificate: certificate,
};
resolve(payload);
} else {
reject("certificate does not match");
}
},
(errorMessage) => {
reject(errorMessage);
}
);
});
});
};

const median = (values) => {
values.sort((a, b) => a - b);

if (values.length % 2 == 0) {
return (
(values[Math.floor(values.length / 2)] +
values[Math.floor(values.length / 2) - 1]) /
2.0
);
} else {
return values[Math.floor(values.length / 2)];
}
};

const promises = [
...internalFetch(BINANCE_CONFIG, BINANCE_SYMBOLS),
...internalFetch(KUCOIN_CONFIG, KUCOIN_SYMBOLS),
...internalFetch(GATE_IO_CONFIG, GATEIO_SYMBOLS),

...internalFetch(BINANCE_US_CONFIG, BINANCE_US_SYMBOLS),
...internalFetch(COINBASE_CONFIG, COINBASE_SYMBOLS),
...internalFetch(BITFINEX_CONFIG, BITFINEX_SYMBOLS),
];

Promise.allSettled(promises).then((results) => {
const fulfilledPayloads = results
.filter((result) => result.status === "fulfilled")
.map((result) => result.value)
.filter((item) => item.timestamp === PREVIOUS_EPOCH_START_MILLIS);

const prices = fulfilledPayloads.reduce((previousValue, currentValue) => {
if (currentValue.symbol in previousValue) {
previousValue[currentValue.symbol].push(currentValue.close);
} else {
previousValue[currentValue.symbol] = [currentValue.close];
}
return previousValue;
}, {});

const indexPrice =
(LAST_REBALANCING_PRICE *
PRICE_PRECISION *
(WEIGHTS["UNI"] * (median(prices["UNIUSDT"]) / INITIAL_PRICES["UNIUSD"]) +
WEIGHTS["LINK"] *
(median(prices["LINKUSDT"]) / INITIAL_PRICES["LINKUSD"]) +
WEIGHTS["LUNA"] *
(median(prices["LUNAUSDT"]) / INITIAL_PRICES["LUNAUSD"]) +
WEIGHTS["CAKE"] *
(median(prices["CAKEUSDT"]) / INITIAL_PRICES["CAKEUSD"]) +
WEIGHTS["AAVE"] *
(median(prices["AAVEUSDT"]) / INITIAL_PRICES["AAVEUSD"]))) /
median(prices["USDTUSD"]);

const sources_count = Math.min(
...Object.values(prices).map((values) => values.length)
);

if (sources_count >= MINIMUM_SOURCES_PER_PRICE) {
const oraclePayload = {
timestamp: PREVIOUS_EPOCH_START_SECONDS,
defi_price: Math.round(indexPrice),
xtz_price: Math.round(median(prices["XTZUSD"]) * PRICE_PRECISION),
btc_price: Math.round(median(prices["BTCUSD"]) * PRICE_PRECISION),
};
console.log(fulfill(oraclePayload));
}
});
https://example.com