Ethereum Virtual Machine (EVM)
Acurast can be used to get access to computational resources for various use cases and bring them to EVM chains. For that, the Consumer
creates a Job that performs a given task on the Acurast Console.
Integration Example
The following example shows a possible integration approach for an EVM chain.
EVM smart contract implementation
The first step is the implementation and deployment of the EVM contract.
The example used is a simple entropy provider, which receives entropy from an Acurast Processor and exposes it to other contracts in the EVM environment.
In this example, the contract expects the Acurast Processor to be known in advance, this can also be adapted at a later stage, depending on how you want to assign your Job to one or more Processors.
For simplicity purposes, this example uses the address 0x176583fbcd4e378c138dea7a4630e7b2b759af96
as a Processor.
// SPDX-License-Identifier: None
pragma solidity ^0.8.0;
contract EntropyProvider {
bytes entropy;
uint last_update;
address processor;
constructor(address _processor) {
processor = _processor;
}
/**
* Receive entropy (random bytes) from an Acurast job.
*/
function receive_entropy(bytes memory _entropy) public {
// Ensures that only the processor can call this function
require(msg.sender == processor, "NOT_ALLOWED");
// Update entropy
entropy = _entropy;
last_update = block.timestamp;
}
/**
* A view that exposes the entropy to other contracts.
*/
function consume_entropy(uint maxAge) public view returns (bytes memory) {
// Consumers can specify the maximum entropy age they are willing to accept
require(block.timestamp - last_update <= maxAge, "ENTROPY_TOO_OLD");
return entropy;
}
}
Deployed contract example on Goerli chain: 0x2c503cfba7178eb0ac2dc5df45279527d437657a
Job specification
Now that the contract has been deployed, we can prepare the script that will get executed by the Processor to provision the entropy.
The script below computes some random bytes securely and calls the function receive_entropy
of the contract deployed above. The Consumer can also specify the gas limit and maximum costs per gas unit.
// Destination contract address
const destination = "<0x evm_contract_address>";
// Generate entropy
const entropy = _STD_.random.generateSecureRandomHex();
// Fulfill entropy
_STD_.chains.ethereum.fulfill(
"https://rpc.ankr.com/eth_goerli", // RPC
destination, // Destination contract address
entropy, // Payload
// Transaction parameters
{
methodSignature: "receive_entropy(bytes)",
gasLimit: "9000000",
maxFeePerGas: "255000000000",
maxPriorityFeePerGas: "2550000000",
},
// Success callback
(opHash) => {
print("Succeeded: " + opHash);
},
// Error callback
(err) => {
print("Failed: " + err);
}
);
Job Registration
Check out How to get started with the Acurast Console to register your Job.
Chainlink-compatible Price Feeds
Price feeds with Chainlink-compatible contracts have been deployed to multiple protocols. These can be called by other smart contracts to fetch asset pricing data like the BTC / USD
feed.
These feeds use data fetched by Acurast Processors and pushed directly to the contract. Each price feed has an on-chain address and functions that enable contracts to read pricing data from that address.
This guide shows you how to read Price Feeds and store the value on-chain using Solidity.
Consuming Price Feeds
This example contract obtains the latest price answer from the BTC / USD
feed, but you can modify it to read any of the different types of data, depending on your script
.
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "@chainlink/contracts/src/v0.6/interfaces/AggregatorInterface.sol";
contract DataConsumer {
AggregatorInterface internal dataFeed;
/**
* Network: Astar zKatana
* Aggregator: BTC/USD
*/
constructor() {
dataFeed = AggregatorInterface(
0xDBb23274EE9354367155C290c673733374d57967
);
}
/**
* Returns the latest answer.
*/
function getLatestAnswer() public view returns (int256) {
int256 answer = dataFeed.latestAnswer();
return answer;
}
}
The contract has the following components:
The
import
line imports an interface namedAggregatorInterface
. Interfaces define functions without their implementation, which leaves inheriting contracts to define the actual implementation themselves.The
constructor() {}
initializes an interface object named dataFeed that usesAggregatorInterface
and connects specifically to a proxy aggregator contract that is already deployed at0xDBb23274EE9354367155C290c673733374d57967
. The interface allows your contract to run functions on that deployed aggregator contract.The
getLatestAnswer()
function calls yourdataFeed
object and runs thelatestAnswer()
function. When you deploy the contract, it initializes thedataFeed
object to point to the aggregator at0xDBb23274EE9354367155C290c673733374d57967
, which is the proxy address for the Astar zKatana BTC / USD data feed. Your contract connects to that address and executes the function. The aggregator connects with one or several Acurast Processors and aggregates the pricing data from those nodes. The response from the aggregator includes several variables, butgetlatestAnswer()
returns only the answer variable.
Deployed Contracts
A list of deployed contracts updated regularly by Acurast Processors.