Skip to main content

Consumers

Consumers or Developers are the ones that are in need of computation for their applications. Consumers can define in accessible Javascript code their requirements and get access to computational resources through the Acurast Marketplace by being matched with Processors.

The so-called Acurast Jobs are executed by Processors in their Trusted Execution Environment that provide the Output of the Job and a Proof of Execution to the defined destination chain.

Consumers define their requirements in code.

Get Started

Head to Integration Levels ↗ for more details:

Level 2

  1. Go to the Acurast Console and log in with a supported wallet of your Ecosystem e.g., Metamask for Ethereum
  2. Fund Account - Get Acurast Canary (cACU) asset for your account, to be able to create Jobs, with "Fund Account" or head directly to the Faucet.
  3. Go to "Create Job" and select your destination, the ecosystem you're building in.
  4. Select an existing template, adapt it or write your own code that fits your needs. Test your code with "Test Code".
  5. Select "Public Processors"
    1. Alternatively select one or more of your "Personal Processors"
    2. or enter the address of one or more "Specific Processors"
  6. Define your "Execution Schedule" with the parameters such as start and endtime, interval etc.
  7. Specify "Additional Parameters"
    1. The number of Processors and their reputation
    2. The fulfillment fee, the transaction/gas fees for each submission
    3. The reward, paid in the native asset of your ecosystem e.g., ETH
  8. Publish your Job, it will be matched, acknowledged and then wait for your first fulfillment.

Level 1

  1. Go to the Acurast Console and log in with a supported Acurast wallet.
  2. Fund Account - Get Acurast Canary (cACU) asset for your account, to be able to create Jobs, with "Fund Account" or head directly to the Faucet.
  3. Go to "Create Job" and select your destination, the ecosystem you're building in.
  4. Select an existing template, adapt it or write your own code that fits your needs. Test your code with "Test Code".
  5. Select "Public Processors".
    1. Alternatively select one or more of your "Personal Processors"
    2. or enter the address of one or more "Specific Processors"
  6. Define your "Execution Schedule" with the parameters such as start and endtime, interval etc.
  7. Specify "Additional Parameters"
    1. The number of Processors and their reputation
    2. The reward, paid in the native asset of your ecosystem e.g., ETH
  8. Publish your Job, it will be matched, acknowledged
  9. Head to the "Job Details", check the address of each Processor for your destination ecosystem.
    1. Send transaction/gas fees to your unique address, that the Processor is able to inject the signed output directly.
  10. Wait for your first fulfillment

Not sure if your ecosystem is supported? Take a look at Ecosystems & Integrations.

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