Skip to main content

Tezos Integration

Acurast can be used to get access to computational resources for various use cases and bring them to Tezos.

Integration Example

The following example shows a possible integration approach for Tezos.

Tezos smart contract implementation

The first step is the implementation and deployment of the Tezos contract.

The example used is a simple entropy provider written in SmartPy, which receives entropy from an Acurast Processor and exposes it to other contracts on Tezos.

SmartPy IDE

import smartpy as sp

@sp.module
def main():
t_storage: type = sp.record(
governance_address = sp.address,
entropy=sp.bytes,
last_update=sp.timestamp,
processors=sp.set[sp.address],
subscriptions=sp.big_map[sp.address, sp.timestamp]
).layout(("entropy", ("governance_address", ("last_update", ("processors", "subscriptions")))))

class EntropyProvider(sp.Contract):
def __init__(self, initial_storage):
self.data = sp.cast(initial_storage, t_storage)


@sp.entrypoint
def submit_entropy(self, entropy):
# Ensures that only the processor can call this function
assert self.data.processors.contains(sp.sender), "NOT_PROCESSOR"
# Update entropy
self.data.entropy = sp.blake2b(self.data.entropy + entropy)
self.data.last_update = sp.now

@sp.entrypoint
def subscribe(self, address):
# The subscription is <block_timestamp> + <mutez amount>
### Both values represent seconds
# 1 mutez = 1 second
# 1 tez = ~11.57 days

mutez_to_seconds = sp.fst(sp.ediv(sp.amount, sp.mutez(1)).unwrap_some())
expiration = sp.add(sp.now, sp.to_int(mutez_to_seconds))
self.data.subscriptions[address] = expiration

@sp.entry_point
def configure(self, actions):
# Only the governance address can call this entry point
assert self.data.governance_address == sp.sender, "NOT_ALLOWED"

# Perform actions
for action in actions:
with sp.match(action):
with sp.case.update_governance_address as governance_address:
self.data.governance_address = governance_address
with sp.case.update_processors as update_processor_actions:
for update_processor_action in update_processor_actions:
with sp.match(update_processor_action):
with sp.case.add as processor_address:
self.data.processors.add(processor_address)
with sp.case.remove as processor_address:
self.data.processors.remove(processor_address)


@sp.entry_point
def withdraw(self, address):
# Only the governance address can call this entry point
assert self.data.governance_address == sp.sender, "NOT_ALLOWED"
# Transfer balance
sp.send(address, sp.balance)

@sp.onchain_view()
def consume_entropy(self, max_age_in_seconds):
# Verify if the consumer has an active subscription
subscription = self.data.subscriptions.get(sp.sender, error = "NOT_SUBSCRIBED")
assert subscription >= sp.now, "SUBSCRIPTION_EXPIRED"
# Consumers can specify the maximum entropy age they are willing to accept
assert (sp.now - self.data.last_update) <= max_age_in_seconds, "ENTROPY_TOO_OLD"

return self.data.entropy

@sp.add_test(name = "EntropyProviderTest")
def test():
scenario = sp.test_scenario(main)

# Test accounts
governance = sp.test_account("governance")
consumer1 = sp.test_account("consumer1")
processor1 = sp.test_account("processor1")

initial_storage = sp.record(
governance_address = governance.address,
entropy = sp.bytes("0x"),
last_update = sp.timestamp(0),
processors = sp.set([processor1.address]),
subscriptions = sp.big_map()
)
contract = main.EntropyProvider(initial_storage)

scenario += contract

contract.subscribe(consumer1.address).run(amount = sp.mutez(1000000))
contract.submit_entropy(sp.pack(1)).run(
valid = False,
exception = "NOT_PROCESSOR",
sender = consumer1.address
)
contract.submit_entropy(sp.pack(1)).run(
sender = processor1.address,
now = sp.timestamp(100)
)

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 submit_entropy of the contract deployed above.

JobLogic.js
const TEZOS_RPCS = [
"https://rpc.ghostnet.teztnets.xyz",
"https://tezos-ghostnet-node.prod.gke.papers.tech",
];
const TEZOS_CONTRACT = "KT1JqvL3qUPDFRx9LZxDzRn6G4RpTg5pTFJY";

const entropy = "0x" + generateSecureRandomHex();

_STD_.chains.tezos.customCall(
TEZOS_RPCS,
entropy,
{
fee: 1500,
gasLimit: 3000,
storageLimit: 0,
destination: TEZOS_CONTRACT,
entrypoint: "submit_entropy",
},
(opHash) => {
print("Succeeded: " + opHash);
},
(err) => {
print("Failed: " + err);
}
);

Job Registration

Check out How to get started with the Acurast Console to register your Job.