From 212ce80fb1ec608a45bd975edc934a330fb27af4 Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Mon, 15 Sep 2025 10:25:57 +0200 Subject: [PATCH 01/17] feat: add Result Callback guide and update sidebar links --- .vitepress/sidebar.ts | 8 +- .../build-iapp/advanced/result-callback.md | 157 +++++++++ src/protocol/oracle.md | 326 ------------------ 3 files changed, 161 insertions(+), 330 deletions(-) create mode 100644 src/guides/build-iapp/advanced/result-callback.md delete mode 100644 src/protocol/oracle.md diff --git a/.vitepress/sidebar.ts b/.vitepress/sidebar.ts index 0590249..92129a2 100644 --- a/.vitepress/sidebar.ts +++ b/.vitepress/sidebar.ts @@ -165,6 +165,10 @@ export function getSidebar() { text: 'End-to-end Encryption', link: '/guides/build-iapp/advanced/protect-the-result', }, + { + text: 'Result Callback', + link: '/guides/build-iapp/advanced/result-callback', + }, { text: 'Access Confidential Assets', link: '/guides/build-iapp/advanced/access-confidential-assets', @@ -545,10 +549,6 @@ export function getSidebar() { text: 'Pay Per Task Model', link: '/protocol/pay-per-task', }, - { - text: 'Oracle', - link: '/protocol/oracle', - }, { text: 'Workers & Workerpools', collapsed: false, diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md new file mode 100644 index 0000000..4e39bf6 --- /dev/null +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -0,0 +1,157 @@ +--- +title: Result Callback Guide +description: Practical guide to using the iExec Result Callback mechanism to push an off-chain task result (iApp) directly into a smart contract—Oracles, automation, triggers, proofs, and more. +--- + +# Result Callback + +This guide explains how to securely push an iExec task result into a smart contract using the “callback” mechanism. +Oracles are only **one** of the possible use cases. + +## When to Use a Callback + +Use a callback whenever a smart contract should: + +- Consume off-chain computed data (API aggregation, ML inference, analytics) and store it on-chain +- React on-chain to an execution outcome (conditional trigger, state transition) +- Store a timestamped record (price feed, score, KPI, proof hash) on-chain +- Act as a logic bridge between external systems and on-chain logic + +## 🧩 High-Level Flow + +1. A requester executes an iApp on iExec. +2. The iApp writes in the file `${IEXEC_OUT}/computed.json` under the filed `callback-data`. +3. iExec decentralized Protocol trigger corresponding on-chain contract based on `callback-data` field. +4. Your smart contract (receiver) pulls, verifies, and ingests it. + +## 🗂️ computed.json Expected Format + +Minimal example: + +```json +{ + "callback-data": "0x...(ABI-encoded bytes)", + "deterministic-output-path": "result.bin", + "status": "success", + "metadata": { + "source": "coingecko", + "pair": "BTC-USD", + "ts": 1731412345 + } +} +``` + +Required key: + +- `callback-data`: ABI-encoded payload (hex string) – you control the schema + +Best practices: + +- Encode with `abi.encode(...)` +- Include an internal timestamp (anti-replay) +- Use a stable struct (avoid free-form JSON inside the bytes) + +## 🧪 Example iApp + +```bash +# Inside the container +node app.js "$PAIR" "$PRECISION" +``` + +```js +import fs from 'node:fs'; +import Web3 from 'web3'; +const web3 = new Web3(); + +async function main() { + const [pair = 'BTC-USD', precision = '9'] = process.argv.slice(2); + const price = await fetchPrice(pair); // Your logic + const scaled = BigInt(Math.round(price * 10 ** Number(precision))); + + const now = Math.floor(Date.now() / 1000); + const abiPayload = web3.eth.abi.encodeParameters( + ['uint256','string','uint256'], + [now, `${pair}-${precision}`, scaled.toString()] + ); + + fs.writeFileSync( + process.env.IEXEC_OUT + '/computed.json', + JSON.stringify({ + 'callback-data': abiPayload, + metadata: { pair, precision, now, source: 'demo' } + }) + ); +} + +main().catch(() => process.exit(1)); +``` + +## 🧩 Base Contract (Generic Receiver) + +```solidity +interface IIexecProxy { + struct Task { + uint256 status; + bytes resultsCallback; // raw callback-data bytes + // ...existing fields... + } + function viewTask(bytes32 _id) external view returns (Task memory); +} + +abstract contract IExecCallbackReceiver { + IIexecProxy public immutable iexec; + constructor(address _iexec) { iexec = IIexecProxy(_iexec); } + + function _getCallback(bytes32 taskid) internal view returns (bytes memory) { + IIexecProxy.Task memory t = iexec.viewTask(taskid); + require(t.status == 3, "task-not-completed"); // Example: 3 = COMPLETED + return t.resultsCallback; + } +} +``` + +## 🧾 Specialized Receiver (Price Feed) + +```solidity +contract PriceFeed is IExecCallbackReceiver { + struct FeedValue { + uint256 date; + uint256 value; + string key; + } + + mapping(bytes32 => FeedValue) public feeds; + event FeedUpdate(bytes32 indexed id, uint256 date, uint256 value, string key); + + constructor(address _iexec) IExecCallbackReceiver(_iexec) {} + + function ingest(bytes32 taskid) external { + bytes memory payload = _getCallback(taskid); + (uint256 date, string memory pairPrecision, uint256 scaled) = + abi.decode(payload, (uint256,string,uint256)); + bytes32 id = keccak256(bytes(pairPrecision)); + require(date > feeds[id].date, "stale"); + feeds[id] = FeedValue(date, scaled, pairPrecision); + emit FeedUpdate(id, date, scaled, pairPrecision); + } + + function latest(string calldata pairPrecision) + external + view + returns (FeedValue memory) + { + return feeds[keccak256(bytes(pairPrecision))]; + } +} +``` + +## 🔄 Other Use Cases + +| Use Case | Description | +|----------|-------------| +| Price oracle | Multi-source API aggregation | +| Reputation / scoring | Off-chain ML / analytics pushed on-chain | +| Audit hash | Security scan or verification artifact | +| Automation | Workflow step completion signal | +| Dynamic parameters | Adjust rates / thresholds / quorums | +| Logical bridge | Sync external (IoT / legacy) state | diff --git a/src/protocol/oracle.md b/src/protocol/oracle.md deleted file mode 100644 index 1435805..0000000 --- a/src/protocol/oracle.md +++ /dev/null @@ -1,326 +0,0 @@ ---- -title: iExec Oracle -description: A flexible and secure Oracle Solution ---- - -# iExec Oracle - -> A flexible and secure Oracle Solution - -## Why are Oracles needed? - -The Ethereum blockchain provides a global trustless computer: given some input -and the smart contract code, the blockchain guarantees execution according to -the Ethereum specification, by replicating the execution across thousands of -nodes and implementing a consensus mechanism across all these nodes. Hence the -execution of the contract decentralizes, and will happen without the need to -trust any single actor. - -Unfortunately decentralizing the execution is not sufficient. To be of any -real-world use, a smart contract most often requires access to external, -real-world information. For example an insurance smart contract could require -data about the weather to trigger payment, or a hedging contract could require -pricing data. This information is already available in the digital world: the -web 2.0 is full of nice APIs that provide all kinds of data. It is however not -straightforward to put this information on the blockchain: if the update message -comes from a single wallet, then this wallet controls the whole execution -outcome. It means the smart contract has to trust an off-chain actor \(the owner -of an Ethereum wallet\) to provide such information, which defeats the purpose -of decentralization: now the information provider becomes the trusted third -party that decentralization was supposed to do away with in the first place! - -Oracles are systems designed to solve this problem: providing the blockchain -with data from the real world in the most secure and robust way possible. It -iExec has been working on this problem for a long time. Indeed an update to an -Oracle \(for example the price of a stock or the average temperature for a day\) -can appear as the result of a specific type of off-chain computation, one that -would involve calling an API and processing the response to return the final -result. As a result the iExec infrastructure is perfectly suited to build an -efficient and secure Oracle system: the iExec Oracle. - -## The iExec solution: the Decentralized Oracle \(Oracle\) - -For two years iExec has been working on the design of the -[Proof of Contribution protocol](/protocol/proof-of-contribution.md), which -provides a flexible and highly robust solution to the problem of off-chain -computation. At its core it is a simple Schelling game between off-chain -computation providers \(Workers\): a given number of Workers are randomly chosen -in a much bigger group, and receive the same computation. Each of them proposes -a result, and the result that proposed by the biggest number of workers becomes -the overall computation result \(see PoCo documentation for more details\). - -The PoCo is both flexible and robust: the trust level for the computation \(e.g. -for the Oracle update in the Oracle case\) can set arbitrarily, and determines -the number of replications. It also includes a coherent on-chain incentive -mechanism, that protects the whole system against any \(financially -sustainable\) attack. Last but not least, it is cheap and scalable: the more -Workers join the iExec platform, the more secure and the cheaper running a -Oracle will be. iExec Oracle relies on random sampling among all the Workers on -the iExec platform, along with an on-chain consensus algorithm and an integrated -trust score system to make an attack on the Oracle result exponentially -expensive. - -iExec Oracle builds on top of the decentralized cloud computing platform -developed by iExec to allow developers to easily create robust and secure -decentralized oracle. Building an Oracle with iExec is therefore extremely -simple: just create a dApp with the logic of the Oracle \(querying an API, -processing different results into a final one\); the iExec platform will -automatically replicate it across many different workers; then the PoCo will -realize a consensus on the different values. The whole process is simple and as -secure as you wish - provided enough money pays for each execution / oracle -update. - -### Why you should use iExec Oracle - -iExec Oracle allows you to create your own Oracle, with custom logic, while -benefiting from the security guarantees of the whole iExec platform. - -- It is secure. You can set the desired level of trust for your Oracle - execution. The conjunction of random sampling and iExec’s built-in incentive - and reputation systems makes your Oracle highly secured. -- It is easy-to-use. It relies on 2 years of research and development to make - the iExec platform simple and developer friendly. Creating your own - personalized Decentralized Oracle only takes a simple dockerized application - and a few lines of Solidity! -- It is cheap. It does not rely on bribing or incentivizing honest behavior, - only on random sampling and a powerful reputation system to make attack - impractical. - -## How does it work? - -### Background: task execution on the iExec platform - -The iExec architecture is two-sided: the on-chain part is a set of smart -contracts that implement the PoCo, handle the incentive and adjudication -systems; and the off-chain part consists of workers, that provide computing -resources to execute the tasks, and schedulers, that dispatch the tasks to -execute between the workers of the worker-pool they manage. Each side of the -iExec platform \(worker-pool, computation requester\) create and sign orders -that describe the kind of transaction they are willing to enter \(type of -hardware, minimum price, etc…\). When several orders of different types are -compatible they match together on the blockchain, to create a deal. Once a deal -creates, the scheduler that is part of the deal will choose a set of workers in -his workerpool to execute the task. Each worker will download the dApp \(a -docker container\) and run it. Upon execution of the task, each worker sends -back two values on the blockchain: - -- a hash of the result. -- after consensus reaches, the corresponding result. - -A normal execution ends when the deal finalizes; all the stakeholders are paid, -and the computation requester is free to download the data pointed to by the -results field of the `Deal` object on the blockchain. - -### iExec d'Oracle: general architecture - -An iExec Oracle can appear as an “on-chain API”: fundamentally it is a simple -value-store smart contract, with accessors for other smart contracts to get its -data, and an internal mechanism to update the data in the most secure way -possible. The Oracle architecture consists of two parts: an on-chain smart -contract and a classical iExec dApp \(packaged in a docker container\). - -**Off-chain component:** - -The off-chain part of a Oracle is a classical iExec dApp, that will execute on -the iExec platform and replicate on several workers as part of an iExec -computation deal. It contains the oracle logic, for example to query a web API -and process the result. Whenever an operator wishes to update the Oracle, it -requests a computation like in a normal iExec deal, specifying the Oracle app as -dApp, and the parameters if applicable. The Oracle result writes in the -`${IEXEC_OUT}/computed.json` file by the dApp, under the `callback_data` key. - -```bash -$ cat ${IEXEC_OUT}/computed.json -{ 'callback-data': '0x456def...'} -``` - -When the computation ends the worker will send both this `callback-data` -\(containing the oracle result\) on the blockchain. The `callback-data` value is -stored in the `resultsCallback` field of the `Task` object in the `IexecProxy` -smart contract. - -**On-chain component:** - -The on-chain part is the Oracle contract. Anyone can request an update of its -internal state by sending the id of a task corresponding to the execution of the -corresponding dApp. With this id, the Oracle contract will query the blockchain -and retrieve the deal object. It then checks that the execution passes the -Oracle requirements \(trust level, execution tag, that the app is right\). If it -does the Oracle contract then decodes the value in the results field and update -its fields accordingly. The value is then accessible like a normal value on a -smart contract. - -## Example: development and workflow of a price-feed application - -A simple example of Oracle is available on Github. The following section goes -through its different components, explaining what each of them does. - -### The PriceFeed dApp - -The PriceFeed dApp is a simple Node.js script, available at -[Kaiko PriceFeed Source](https://github.com/iExecBlockchainComputing/iexec-apps/blob/master/offchain-computing/offchain-tee-kaiko-pricefeed/src/app.py). -Given a set of parameters, the application encodes its result so that it can be -interpreted by the corresponding Oracle smart contract, stores it in -`${IEXEC_OUT}/computed.json`, and stores the hash of this encoded value to -perform the consensus. The Worker will then send these values on-chain as part -of the task finalization, where they will be accessible by the Oracle smart -contract. - -For example, given the parameters `"BTC USD 9 2019-04-11T13:08:32.605Z"` the -price-oracle application will: - -- Retrieve the price of BTC in USD at 2019-04-11T13:08:32.605Z -- Multiply this value by `10e9` \(to capture the price value more accurately as - it will represent by an integer onchain\) -- Encode the date, the description \(`"btc-usd-9"`\) and the value using - `abi.encode` -- Store this result in `${IEXEC_OUT}/computed.json` under the `callback-data` - key - -iExec will then achieve PoCo consensus on the hash of the `callback-data` value, -and will then submit `callback-data` values on-chain, in the `Task` object on -the `IexecProxy` smart contract. - -Once your oracle dApp writes, you can build it into a Docker image and make it -available on the iExec platform as explained here. - -### The Oracle generic contract - -Every Oracle must inherit from the `IexecDoracle` contract \(source available on -[Github](https://github.com/iExecBlockchainComputing/iexec-Oracle-base) and -[npm](https://www.npmjs.com/package/iexec-Oracle-base)\). - -This contract stores the following fields: - -```text -IexecInterfaceToken public iexecproxy; -address public m_authorizedApp; -address public m_authorizedDataset; -address public m_authorizedWorkerpool; -bytes32 public m_requiredtag; -uint256 public m_requiredtrust; -``` - -In particular, the `m_authorizedApp` must be the address of the smart contract -of the Oracle dApp, and the `m_requiredtag` describes the parameters of the -iExec `Task` necessary to validate the Oracle update. - -The Oracle exposes mainly three internal functions, that may use by the -contracts that inherit from it: - -A constructor: - -```text -constructor(address _iexecproxy) public -``` - -A function to initialize/update the settings: - -```text -function _iexecDoracleUpdateSettings( - address _authorizedApp -, address _authorizedDataset -, address _authorizedWorkerpoo -, bytes32 _requiredtag -, uint256 _requiredtrust -) -internal -``` - -The update function, that takes in input a task id, and reads the `Task` object -data from the `IexecProxy` smart contract to perform the required checks (the -execution must complete; the app, the dataset, and the workerpool must be -authorized; the trust level and tags mus be valid). The `IexecProxy` already -checked that the hash of the `resultsCallback` is equal to the `resultDigest` -\(over which the consensus reached\). If the task passes the checks then it -returns the `results` field of the `Task` object, i.e. the result of the Oracle -dApp computation. - -```text -function _iexecDoracleGetVerifiedResult(bytes32 _doracleCallId) -internal view returns (bytes memory) -``` - -A Oracle smart contract should inherit from the generic `IexecDOracle` contract, -and expose two main functionalities: - -- An update function, that will call the internal \(and inherited\) - `_iexecDoracleGetVerifiedResult` function and process its result to update the - Oracle contract internal state. -- One or several accessor functions, that allows other smart contract to access - the oracle value\(s\). - -### The PriceOracle Oracle contract - -In the PriceFeed example, the -[PriceOracle](https://github.com/iExecBlockchainComputing/iexec-Oracle-base/blob/bb4c04dc77c822d16d7ca8baed99f5626e44d7be/contracts/example/PriceOracle.sol) -smart contract consists of three parts: - -- Its internal state description: a `TimedValue` struct storing the oracle data - for a given value, and a `values` field that maps an index of the form - `“BTC-USD-9”` to the corresponding `TimedValue` struct value. - -```text -struct TimedValue -{ - bytes32 oracleCallID; - uint256 date; - uint256 value; - string details; -} - -mapping(bytes32 => TimedValue) public values; -``` - -This also allows to read the resulting prices. For example, to get the most -recent price of BTC in USD with 9 place precision \(as described above\), query -`values(keccak256(bytes("BTC-USD-9")))` from the Oracle contract and this will -return a structure containing the value, the associated date, and the details of -the request. - -- The update function `processResult`, that takes the task id of an execution of - the Oracle dApp, calls the internal `_iexecDoracleGetVerifiedResult` and - processes the result to update the `values` map. - -```text -function processResult(bytes32 _oracleCallID) -public -{ - uint256 date; - string memory details; - uint256 value; - - // Parse results - (date, details, value) = decodeResults(_iexecDoracleGetVerifiedResult(_oracleCallID)); - - // Process results - bytes32 id = keccak256(bytes(details)); - require(values[id].date < date, "new-value-is-too-old"); - emit ValueChange(id, _oracleCallID, values[id].date, values[id].value, date, value); - values[id].oracleCallID = _oracleCallID; - values[id].date = date; - values[id].value = value; - values[id].details = details; -} -``` - -The PriceFeed Oracle also declares an event `ValueChange`, that fires whenever -an update creates. - -- An `updateEnv` function, that can use by the owner of the Oracle to update its - parameters. It simply calls the `_iexecDoracleUpdateSettings` function of its - parent `IexecDoracle` contract. - -```text -function updateEnv( - address _authorizedApp -, address _authorizedDataset -, address _authorizedWorkerpool -, bytes32 _requiredtag -, uint256 _requiredtrust -) -public onlyOwner -{ - _iexecDoracleUpdateSettings(_authorizedApp, _authorizedDataset, _authorizedWorkerpool, _requiredtag, _requiredtrust); -} -``` From b1ecd0a8ffe3949d6bbc5e395d82abe96ffdf47c Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Mon, 15 Sep 2025 10:55:10 +0200 Subject: [PATCH 02/17] refactor: streamline Result Callback guide and enhance clarity in implementation steps --- .../build-iapp/advanced/result-callback.md | 79 +++---------------- 1 file changed, 11 insertions(+), 68 deletions(-) diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md index 4e39bf6..17a54a8 100644 --- a/src/guides/build-iapp/advanced/result-callback.md +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -20,43 +20,15 @@ Use a callback whenever a smart contract should: ## 🧩 High-Level Flow 1. A requester executes an iApp on iExec. -2. The iApp writes in the file `${IEXEC_OUT}/computed.json` under the filed `callback-data`. -3. iExec decentralized Protocol trigger corresponding on-chain contract based on `callback-data` field. -4. Your smart contract (receiver) pulls, verifies, and ingests it. - -## 🗂️ computed.json Expected Format - -Minimal example: - -```json -{ - "callback-data": "0x...(ABI-encoded bytes)", - "deterministic-output-path": "result.bin", - "status": "success", - "metadata": { - "source": "coingecko", - "pair": "BTC-USD", - "ts": 1731412345 - } -} -``` - -Required key: +2. The iApp writes in the file `${IEXEC_OUT}/computed.json` under the filed `callback-data` the necessary calldata to make on-chain call. +3. iExec decentralized Protocol trigger corresponding on-chain contract based on the deal & task result `callback-data` field. +4. Your smart contract (receiver) callback data, verifies, and ingests it. -- `callback-data`: ABI-encoded payload (hex string) – you control the schema +## Step-by-Step Implementation -Best practices: +### Step 1: Write the iApp -- Encode with `abi.encode(...)` -- Include an internal timestamp (anti-replay) -- Use a stable struct (avoid free-form JSON inside the bytes) - -## 🧪 Example iApp - -```bash -# Inside the container -node app.js "$PAIR" "$PRECISION" -``` +Your iApp must write a JSON file named `computed.json` in the directory pointed to by the environment variable `IEXEC_OUT`. This file must contain at least the key `callback-data`, which holds the ABI-encoded data you want to send to your smart contract. ```js import fs from 'node:fs'; @@ -86,7 +58,9 @@ async function main() { main().catch(() => process.exit(1)); ``` -## 🧩 Base Contract (Generic Receiver) +### Step 2: Deploy the Callback Contract + +The callback contract is the receiver of the off-chain computed data. Implement your custom logic based on your use case. ```solidity interface IIexecProxy { @@ -110,40 +84,9 @@ abstract contract IExecCallbackReceiver { } ``` -## 🧾 Specialized Receiver (Price Feed) +### Step 3: Run the iApp with Callback -```solidity -contract PriceFeed is IExecCallbackReceiver { - struct FeedValue { - uint256 date; - uint256 value; - string key; - } - - mapping(bytes32 => FeedValue) public feeds; - event FeedUpdate(bytes32 indexed id, uint256 date, uint256 value, string key); - - constructor(address _iexec) IExecCallbackReceiver(_iexec) {} - - function ingest(bytes32 taskid) external { - bytes memory payload = _getCallback(taskid); - (uint256 date, string memory pairPrecision, uint256 scaled) = - abi.decode(payload, (uint256,string,uint256)); - bytes32 id = keccak256(bytes(pairPrecision)); - require(date > feeds[id].date, "stale"); - feeds[id] = FeedValue(date, scaled, pairPrecision); - emit FeedUpdate(id, date, scaled, pairPrecision); - } - - function latest(string calldata pairPrecision) - external - view - returns (FeedValue memory) - { - return feeds[keccak256(bytes(pairPrecision))]; - } -} -``` +When running the iApp, specify the callback contract address in the deal parameters. The iExec protocol will automatically call the specified contract with the `callback-data` once the task is completed. ## 🔄 Other Use Cases From e3aee486f91b25d337a1ed7d67efa64f52ea7c38 Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Mon, 15 Sep 2025 11:17:51 +0200 Subject: [PATCH 03/17] refactor: enhance clarity and update examples in Result Callback guide --- .../build-iapp/advanced/result-callback.md | 86 ++++++++++++------- 1 file changed, 57 insertions(+), 29 deletions(-) diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md index 17a54a8..c1c029e 100644 --- a/src/guides/build-iapp/advanced/result-callback.md +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -1,57 +1,77 @@ --- title: Result Callback Guide -description: Practical guide to using the iExec Result Callback mechanism to push an off-chain task result (iApp) directly into a smart contract—Oracles, automation, triggers, proofs, and more. +description: Guide to using the iExec result callback to push an off-chain iApp task result directly into a smart contract (price feeds, automation, triggers, proofs, scoring, etc.). --- # Result Callback -This guide explains how to securely push an iExec task result into a smart contract using the “callback” mechanism. -Oracles are only **one** of the possible use cases. +This guide explains how to securely push an iExec task result into a smart contract using the callback mechanism. +Oracles are only one of many possible use cases. ## When to Use a Callback -Use a callback whenever a smart contract should: +Use a callback when a smart contract should: -- Consume off-chain computed data (API aggregation, ML inference, analytics) and store it on-chain -- React on-chain to an execution outcome (conditional trigger, state transition) -- Store a timestamped record (price feed, score, KPI, proof hash) on-chain +- Ingest off-chain computed data (API aggregation, ML inference, analytics) and persist it on-chain +- React to an execution outcome (conditional trigger, state transition) +- Store a timestamped record (price feed, score, KPI, proof hash) - Act as a logic bridge between external systems and on-chain logic ## 🧩 High-Level Flow 1. A requester executes an iApp on iExec. -2. The iApp writes in the file `${IEXEC_OUT}/computed.json` under the filed `callback-data` the necessary calldata to make on-chain call. -3. iExec decentralized Protocol trigger corresponding on-chain contract based on the deal & task result `callback-data` field. -4. Your smart contract (receiver) callback data, verifies, and ingests it. +2. The iApp writes `${IEXEC_OUT}/computed.json` with a `callback-data` field containing ABI‑encoded calldata. +3. The iExec protocol, once the task is completed, invokes the specified callback contract with that data. +4. Your callback smart contract (receiver) ingests the data. ## Step-by-Step Implementation ### Step 1: Write the iApp -Your iApp must write a JSON file named `computed.json` in the directory pointed to by the environment variable `IEXEC_OUT`. This file must contain at least the key `callback-data`, which holds the ABI-encoded data you want to send to your smart contract. +The iApp MUST write a file named `computed.json` in the directory pointed to by `IEXEC_OUT`. +Required key: `callback-data` (raw ABI‑encoded bytes you want passed to your contract). + +Replaced Web3.js example with ethers v6: ```js -import fs from 'node:fs'; -import Web3 from 'web3'; -const web3 = new Web3(); +// ethers v6 example producing ABI-encoded callback data + +import { writeFileSync } from 'node:fs'; +import { AbiCoder } from 'ethers'; + +// Placeholder: replace with real price retrieval / aggregation logic +async function fetchPrice(pair) { + // e.g. query multiple APIs, median, etc. + return 12345.6789; +} async function main() { const [pair = 'BTC-USD', precision = '9'] = process.argv.slice(2); - const price = await fetchPrice(pair); // Your logic - const scaled = BigInt(Math.round(price * 10 ** Number(precision))); - const now = Math.floor(Date.now() / 1000); - const abiPayload = web3.eth.abi.encodeParameters( - ['uint256','string','uint256'], - [now, `${pair}-${precision}`, scaled.toString()] + const price = await fetchPrice(pair); + const scale = 10n ** BigInt(Number(precision)); + const scaled = BigInt(Math.round(price * Number(scale))); + + const timestamp = Math.floor(Date.now() / 1000); + + const abiCoder = new AbiCoder(); + const abiPayload = abiCoder.encode( + ['uint256', 'string', 'uint256'], + [timestamp, `${pair}-${precision}`, scaled] ); - fs.writeFileSync( - process.env.IEXEC_OUT + '/computed.json', - JSON.stringify({ - 'callback-data': abiPayload, - metadata: { pair, precision, now, source: 'demo' } - }) + writeFileSync( + `${process.env.IEXEC_OUT}/computed.json`, + JSON.stringify( + { + 'callback-data': abiPayload, + metadata: { + pair, + precision, + timestamp, + } + } + ) ); } @@ -60,7 +80,8 @@ main().catch(() => process.exit(1)); ### Step 2: Deploy the Callback Contract -The callback contract is the receiver of the off-chain computed data. Implement your custom logic based on your use case. +The callback contract receives and processes the off-chain result. +It can read the stored `resultsCallback` from the iExec hub (or proxy) to independently verify the task state. ```solidity interface IIexecProxy { @@ -78,7 +99,7 @@ abstract contract IExecCallbackReceiver { function _getCallback(bytes32 taskid) internal view returns (bytes memory) { IIexecProxy.Task memory t = iexec.viewTask(taskid); - require(t.status == 3, "task-not-completed"); // Example: 3 = COMPLETED + require(t.status == 3, "task-not-completed"); // 3 = COMPLETED (example) return t.resultsCallback; } } @@ -86,7 +107,14 @@ abstract contract IExecCallbackReceiver { ### Step 3: Run the iApp with Callback -When running the iApp, specify the callback contract address in the deal parameters. The iExec protocol will automatically call the specified contract with the `callback-data` once the task is completed. +When requesting the execution, set the callback contract address in the deal (or order) parameters. +After completion, the protocol calls your contract passing the `callback-data` bytes. + +Checklist: + +- Ensure the contract adheres to the expected callback function signature. +- Guard against replay (e.g. track processed task IDs). +- Validate business invariants (timestamps, ranges, freshness). ## 🔄 Other Use Cases From a44d05998f318aaf959d094a227293ca4ef26dc9 Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Mon, 15 Sep 2025 11:47:34 +0200 Subject: [PATCH 04/17] refactor: improve clarity and formatting in Result Callback guide --- .../build-iapp/advanced/result-callback.md | 115 ++++++++---------- 1 file changed, 48 insertions(+), 67 deletions(-) diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md index c1c029e..bf08ea7 100644 --- a/src/guides/build-iapp/advanced/result-callback.md +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -1,18 +1,19 @@ --- title: Result Callback Guide -description: Guide to using the iExec result callback to push an off-chain iApp task result directly into a smart contract (price feeds, automation, triggers, proofs, scoring, etc.). +description: + Guide to using the iExec result callback to push an off-chain iApp task result + directly into a smart contract (price feeds, automation, triggers, proofs, + scoring, etc.). --- # Result Callback -This guide explains how to securely push an iExec task result into a smart contract using the callback mechanism. -Oracles are only one of many possible use cases. - -## When to Use a Callback - +This guide explains how to securely push an iExec task result into a smart +contract using the callback mechanism. Use a callback when a smart contract should: -- Ingest off-chain computed data (API aggregation, ML inference, analytics) and persist it on-chain +- Ingest off-chain computed data (API aggregation, ML inference, analytics) and + persist it on-chain - React to an execution outcome (conditional trigger, state transition) - Store a timestamped record (price feed, score, KPI, proof hash) - Act as a logic bridge between external systems and on-chain logic @@ -20,95 +21,75 @@ Use a callback when a smart contract should: ## 🧩 High-Level Flow 1. A requester executes an iApp on iExec. -2. The iApp writes `${IEXEC_OUT}/computed.json` with a `callback-data` field containing ABI‑encoded calldata. -3. The iExec protocol, once the task is completed, invokes the specified callback contract with that data. +2. The iApp writes `${IEXEC_OUT}/computed.json` with a `callback-data` field + containing ABI‑encoded calldata. +3. The iExec protocol, once the task is completed, invokes the specified + callback contract with that data. 4. Your callback smart contract (receiver) ingests the data. ## Step-by-Step Implementation ### Step 1: Write the iApp -The iApp MUST write a file named `computed.json` in the directory pointed to by `IEXEC_OUT`. -Required key: `callback-data` (raw ABI‑encoded bytes you want passed to your contract). +The iApp MUST write a file named `computed.json` in the directory pointed to by +`IEXEC_OUT`. +Required key: `callback-data` (raw ABI‑encoded bytes you want passed to your +contract). Replaced Web3.js example with ethers v6: -```js -// ethers v6 example producing ABI-encoded callback data - +```ts twoslash import { writeFileSync } from 'node:fs'; import { AbiCoder } from 'ethers'; -// Placeholder: replace with real price retrieval / aggregation logic -async function fetchPrice(pair) { - // e.g. query multiple APIs, median, etc. - return 12345.6789; -} +const timestamp = Math.floor(Date.now() / 1000); +const pair = 'BTC-USD'; +const scaled = '9'; +// ---cut--- async function main() { - const [pair = 'BTC-USD', precision = '9'] = process.argv.slice(2); - - const price = await fetchPrice(pair); - const scale = 10n ** BigInt(Number(precision)); - const scaled = BigInt(Math.round(price * Number(scale))); - - const timestamp = Math.floor(Date.now() / 1000); + // Your business logic here const abiCoder = new AbiCoder(); const abiPayload = abiCoder.encode( ['uint256', 'string', 'uint256'], - [timestamp, `${pair}-${precision}`, scaled] + [timestamp, `${pair}`, scaled] ); writeFileSync( `${process.env.IEXEC_OUT}/computed.json`, - JSON.stringify( - { - 'callback-data': abiPayload, - metadata: { - pair, - precision, - timestamp, - } - } - ) + JSON.stringify({ + 'callback-data': abiPayload, + }) ); } - -main().catch(() => process.exit(1)); ``` ### Step 2: Deploy the Callback Contract -The callback contract receives and processes the off-chain result. -It can read the stored `resultsCallback` from the iExec hub (or proxy) to independently verify the task state. +The callback contract receives and processes the off-chain result. Your contract +must implement `receiveResult(bytes32,bytes)`interface from +[ERC1154](https://github.com/iExecBlockchainComputing/iexec-solidity/blob/master/contracts/ERC1154/IERC1154.sol) ```solidity -interface IIexecProxy { - struct Task { - uint256 status; - bytes resultsCallback; // raw callback-data bytes - // ...existing fields... - } - function viewTask(bytes32 _id) external view returns (Task memory); -} - -abstract contract IExecCallbackReceiver { - IIexecProxy public immutable iexec; - constructor(address _iexec) { iexec = IIexecProxy(_iexec); } - - function _getCallback(bytes32 taskid) internal view returns (bytes memory) { - IIexecProxy.Task memory t = iexec.viewTask(taskid); - require(t.status == 3, "task-not-completed"); // 3 = COMPLETED (example) - return t.resultsCallback; +contract IExecCallbackReceiver { + // ERC1154 - Callback processing + function receiveResult(bytes32 _callID, bytes memory callback) external { + // Parse results + (uint256 timestamp, string memory pairAndPrecision, uint256 scaledValue) = + abi.decode(callback, (uint256, string, uint256)); + + ...business logic... } } ``` ### Step 3: Run the iApp with Callback -When requesting the execution, set the callback contract address in the deal (or order) parameters. -After completion, the protocol calls your contract passing the `callback-data` bytes. +When requesting the execution, set the callback contract address in the deal (or +order) parameters. +After completion, the protocol calls your contract passing the `callback-data` +bytes. Checklist: @@ -118,11 +99,11 @@ Checklist: ## 🔄 Other Use Cases -| Use Case | Description | -|----------|-------------| -| Price oracle | Multi-source API aggregation | +| Use Case | Description | +| -------------------- | ---------------------------------------- | +| Price oracle | Multi-source API aggregation | | Reputation / scoring | Off-chain ML / analytics pushed on-chain | -| Audit hash | Security scan or verification artifact | -| Automation | Workflow step completion signal | -| Dynamic parameters | Adjust rates / thresholds / quorums | -| Logical bridge | Sync external (IoT / legacy) state | +| Audit hash | Security scan or verification artifact | +| Automation | Workflow step completion signal | +| Dynamic parameters | Adjust rates / thresholds / quorums | +| Logical bridge | Sync external (IoT / legacy) state | From eb1e10025e3d90c020b79ef9e8d3704f5be7af9f Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Mon, 15 Sep 2025 14:21:53 +0200 Subject: [PATCH 05/17] refactor: update command examples to use placeholder for chain name in iApp guides --- .../advanced/build-your-first-sgx-iapp.md | 16 ++++++++-------- .../build-iapp/advanced/result-callback.md | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/guides/build-iapp/advanced/build-your-first-sgx-iapp.md b/src/guides/build-iapp/advanced/build-your-first-sgx-iapp.md index c7b3cc4..7527b56 100644 --- a/src/guides/build-iapp/advanced/build-your-first-sgx-iapp.md +++ b/src/guides/build-iapp/advanced/build-your-first-sgx-iapp.md @@ -395,7 +395,7 @@ You can check your deployed apps with their index, let's check your last deployed app: ```bash twoslash -iexec app show --chain arbitrum-mainnet +iexec app show --chain {{chainName}} ``` ## Run the iApp @@ -420,20 +420,20 @@ At any time you can: - view your balance ```bash twoslash -iexec account show --chain arbitrum-mainnet +iexec account show --chain {{chainName}} ``` - deposit RLC from your wallet to your iExec Account ```bash twoslash -iexec account deposit --chain arbitrum-mainnet +iexec account deposit --chain {{chainName}} ``` - withdraw RLC from your iExec account to your wallet \(only stake can be withdrawn\) ```bash twoslash -iexec account withdraw --chain arbitrum-mainnet +iexec account withdraw --chain {{chainName}} ``` ::: @@ -478,13 +478,13 @@ is a 32Bytes hexadecimal string\). Download the result of your task ```bash twoslash -iexec task show --chain arbitrum-mainnet --download my-result +iexec task show --chain {{chainName}} --download my-result ``` You can get your taskid with the command: ```bash twoslash -iexec deal show --chain arbitrum-mainnet +iexec deal show --chain {{chainName}} ``` ::: info @@ -526,7 +526,7 @@ The conditions to use an app are defined in the **apporder**. Publish a new apporder for your application. ```bash twoslash -iexec app publish --chain arbitrum-mainnet +iexec app publish --chain {{chainName}} ``` ::: info @@ -545,7 +545,7 @@ conditions defined in apporder. You can check the published apporders for your app ```bash twoslash -iexec orderbook app --chain arbitrum-mainnet +iexec orderbook app --chain {{chainName}} ``` Congratulation you just created a decentralized application! Anyone can now diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md index bf08ea7..f9b5534 100644 --- a/src/guides/build-iapp/advanced/result-callback.md +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -48,7 +48,7 @@ const scaled = '9'; // ---cut--- async function main() { - // Your business logic here + // Your business logic here ... const abiCoder = new AbiCoder(); const abiPayload = abiCoder.encode( @@ -73,13 +73,13 @@ must implement `receiveResult(bytes32,bytes)`interface from ```solidity contract IExecCallbackReceiver { + // Your business logic here ... + // ERC1154 - Callback processing function receiveResult(bytes32 _callID, bytes memory callback) external { // Parse results (uint256 timestamp, string memory pairAndPrecision, uint256 scaledValue) = abi.decode(callback, (uint256, string, uint256)); - - ...business logic... } } ``` From da8b29afc6a2e958e14152a2bf9b3a2e6fbe3c78 Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Mon, 15 Sep 2025 14:37:33 +0200 Subject: [PATCH 06/17] refactor: clarify and streamline the Result Callback guide for better understanding --- .../build-iapp/advanced/result-callback.md | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md index f9b5534..a19b140 100644 --- a/src/guides/build-iapp/advanced/result-callback.md +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -1,42 +1,41 @@ --- title: Result Callback Guide description: - Guide to using the iExec result callback to push an off-chain iApp task result - directly into a smart contract (price feeds, automation, triggers, proofs, - scoring, etc.). + Use the iExec result callback to have the protocol invoke a function on your + smart contract at the end of an (optionally confidential TEE) task execution. --- # Result Callback -This guide explains how to securely push an iExec task result into a smart -contract using the callback mechanism. +This guide explains how to trigger a callback function at the end of a +successful task on your smart contract. + Use a callback when a smart contract should: - Ingest off-chain computed data (API aggregation, ML inference, analytics) and - persist it on-chain + persist it - React to an execution outcome (conditional trigger, state transition) - Store a timestamped record (price feed, score, KPI, proof hash) -- Act as a logic bridge between external systems and on-chain logic +- Bridge logic between external systems and on-chain state ## 🧩 High-Level Flow -1. A requester executes an iApp on iExec. +1. A requester executes an iApp. 2. The iApp writes `${IEXEC_OUT}/computed.json` with a `callback-data` field - containing ABI‑encoded calldata. -3. The iExec protocol, once the task is completed, invokes the specified - callback contract with that data. -4. Your callback smart contract (receiver) ingests the data. + (ABI‑encoded bytes you crafted). +3. Once the task is completed and validated, the iExec protocol invokes your + contract’s `receiveResult(bytes32,bytes)`. +4. Your contract decodes and processes those bytes if callback data have been + pushed. ## Step-by-Step Implementation -### Step 1: Write the iApp - -The iApp MUST write a file named `computed.json` in the directory pointed to by -`IEXEC_OUT`. -Required key: `callback-data` (raw ABI‑encoded bytes you want passed to your -contract). +### Step 1: Prepare the Callback Payload in the iApp -Replaced Web3.js example with ethers v6: +You only need to write `computed.json` containing the key `callback-data`. +That value must be the ABI‑encoded bytes your contract knows how to decode. +Example schema we’ll use: (uint256 timestamp, string pairAndPrecision, uint256 +scaledValue). ```ts twoslash import { writeFileSync } from 'node:fs'; @@ -53,7 +52,7 @@ async function main() { const abiCoder = new AbiCoder(); const abiPayload = abiCoder.encode( ['uint256', 'string', 'uint256'], - [timestamp, `${pair}`, scaled] + [timestamp, pair, scaled] ); writeFileSync( @@ -65,11 +64,16 @@ async function main() { } ``` -### Step 2: Deploy the Callback Contract +### Step 2: Implement the Callback Contract + +Your contract must expose `receiveResult(bytes32,bytes)` (ERC1154). The protocol +calls it with: + +- `_callID`: the task / call identifier. +- `callback`: exactly the bytes you encoded as `callback-data`. -The callback contract receives and processes the off-chain result. Your contract -must implement `receiveResult(bytes32,bytes)`interface from -[ERC1154](https://github.com/iExecBlockchainComputing/iexec-solidity/blob/master/contracts/ERC1154/IERC1154.sol) +You decode using the same tuple. Add (optional) protections: authorized caller +check (the iExec hub / proxy address), replay guard, bounds checks. ```solidity contract IExecCallbackReceiver { From 83f99298ecf6b8f0e8b3f48f24a7cd27a91f8b19 Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Mon, 15 Sep 2025 15:19:02 +0200 Subject: [PATCH 07/17] refactor: add naming conventions for input parameters and enhance callback execution steps --- README.md | 20 +++++ .../build-iapp/advanced/result-callback.md | 83 ++++++++++++++----- 2 files changed, 83 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index e24bd08..f7926c7 100644 --- a/README.md +++ b/README.md @@ -134,6 +134,26 @@ We welcome contributions to improve the iExec documentation! 📖 **For detailed contribution guidelines, component usage, and best practices, please see our [CONTRIBUTING.md](CONTRIBUTING.md) guide.** +### Some conventions + +In order to keep the documentation consistent, we have some naming conventions +for input parameters: + +- `protectedData`: '0x123abc...', +- `protectedDataAddress`: '0x123abc...', +- `authorizedApp`: '0x456def...', +- `authorizedUser`: '0x789cba...', +- `userAddress`: '0x789cba...', +- `appWhitelist`: '0xba46d6...', +- `owner`: '0xa0c15e...', +- `newOwner`: '0xc5e9f4...', +- `voucherOwner`: '0x5714eB...', +- `renterAddress`: '0x246bdf...' +- `subscriberAddress`: '0x246bdf...' +- `workerpool`: '0xa5de76...' +- `taskId`: '0x7ac398...' +- `smartContractAddress`: '0x8e5bB6...' + ### Quick Contribution Steps 1. **Fork** this repository diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md index a19b140..f6c1f82 100644 --- a/src/guides/build-iapp/advanced/result-callback.md +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -2,7 +2,7 @@ title: Result Callback Guide description: Use the iExec result callback to have the protocol invoke a function on your - smart contract at the end of an (optionally confidential TEE) task execution. + smart contract at the end of a (optionally confidential TEE) task execution. --- # Result Callback @@ -10,7 +10,7 @@ description: This guide explains how to trigger a callback function at the end of a successful task on your smart contract. -Use a callback when a smart contract should: +Use a callback when your smart contract should: - Ingest off-chain computed data (API aggregation, ML inference, analytics) and persist it @@ -23,10 +23,10 @@ Use a callback when a smart contract should: 1. A requester executes an iApp. 2. The iApp writes `${IEXEC_OUT}/computed.json` with a `callback-data` field (ABI‑encoded bytes you crafted). -3. Once the task is completed and validated, the iExec protocol invokes your +3. After the task completes and is validated, the iExec protocol invokes your contract’s `receiveResult(bytes32,bytes)`. 4. Your contract decodes and processes those bytes if callback data have been - pushed. + provided. ## Step-by-Step Implementation @@ -34,8 +34,8 @@ Use a callback when a smart contract should: You only need to write `computed.json` containing the key `callback-data`. That value must be the ABI‑encoded bytes your contract knows how to decode. -Example schema we’ll use: (uint256 timestamp, string pairAndPrecision, uint256 -scaledValue). +Example tuple schema we'll use: +`(uint256 timestamp, string pairAndPrecision, uint256 scaledValue)`. ```ts twoslash import { writeFileSync } from 'node:fs'; @@ -66,14 +66,15 @@ async function main() { ### Step 2: Implement the Callback Contract -Your contract must expose `receiveResult(bytes32,bytes)` (ERC1154). The protocol -calls it with: +Your contract must expose `receiveResult(bytes32,bytes)` +[ERC1154](https://github.com/iExecBlockchainComputing/iexec-solidity/blob/master/contracts/ERC1154/IERC1154.sol). +The protocol calls it with: -- `_callID`: the task / call identifier. -- `callback`: exactly the bytes you encoded as `callback-data`. +- `_callID`: the first arg the taskId +- `callback`: exactly the bytes you encoded as `callback-data` -You decode using the same tuple. Add (optional) protections: authorized caller -check (the iExec hub / proxy address), replay guard, bounds checks. +Decode using the same tuple. (Optional) Add protections: authorized caller check +(iExec hub / proxy), replay guard, bounds checks. ```solidity contract IExecCallbackReceiver { @@ -88,18 +89,60 @@ contract IExecCallbackReceiver { } ``` -### Step 3: Run the iApp with Callback +### Step 3: Run the iApp with a Callback -When requesting the execution, set the callback contract address in the deal (or -order) parameters. -After completion, the protocol calls your contract passing the `callback-data` +When creating the request order, set the `callback` field to your callback +contract address. +After completion, the protocol calls your contract, passing the `callback-data` bytes. -Checklist: +First install the iExec SDK if you have not already (see +[Getting Started](/guides/use-iapp/getting-started)). -- Ensure the contract adheres to the expected callback function signature. -- Guard against replay (e.g. track processed task IDs). -- Validate business invariants (timestamps, ranges, freshness). +```ts twoslash +import { IExec, utils } from 'iexec'; + +const ethProvider = utils.getSignerFromPrivateKey( + 'chain', // blockchain node URL + 'PRIVATE_KEY' +); +const iexec = new IExec({ + ethProvider, +}); +// ---cut--- +// Basic arguments +const requestorderToSign = await iexec.order.createRequestorder({ + app: '0x456def...', + category: 0, + appmaxprice: 10, + workerpool: '0xa5de76...', + callback: '0x8e5bB6...', // Callback contract address +}); +const requestOrder = await iexec.order.signRequestorder(requestorderToSign); + +// Fetch app orders +const appOrders = await iexec.orderbook.fetchAppOrderbook( + '0x456def...' // Filter by specific app +); +if (appOrders.orders.length === 0) { + throw new Error('No app orders found for the specified app'); +} + +// Fetch workerpool orders +const workerpoolOrders = await iexec.orderbook.fetchWorkerpoolOrderbook({ + workerpool: '0xa5de76...', // Filter by specific workerpool +}); +if (workerpoolOrders.orders.length === 0) { + throw new Error('No workerpool orders found for the specified workerpool'); +} + +// Execute the task +const taskId = await iexec.order.matchOrders({ + requestorder: requestOrder, + apporder: appOrders.orders[0].order, + workerpoolorder: workerpoolOrders.orders[0].order, +}); +``` ## 🔄 Other Use Cases From cd2e0a1076091a8acdb3b46a953b7fd3296f2d52 Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Tue, 16 Sep 2025 10:24:29 +0200 Subject: [PATCH 08/17] refactor: add gas limit information and ensure callback logic adheres to it --- .../build-iapp/advanced/result-callback.md | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md index f6c1f82..a60911b 100644 --- a/src/guides/build-iapp/advanced/result-callback.md +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -89,6 +89,13 @@ contract IExecCallbackReceiver { } ``` +::: tip Important + +The callback transaction is subject to a gas limit of {{ gasLimit }}. +Ensure your callback logic fits within this limit to avoid out-of-gas errors. + +::: + ### Step 3: Run the iApp with a Callback When creating the request order, set the `callback` field to your callback @@ -154,3 +161,22 @@ const taskId = await iexec.order.matchOrders({ | Automation | Workflow step completion signal | | Dynamic parameters | Adjust rates / thresholds / quorums | | Logical bridge | Sync external (IoT / legacy) state | + + From 76133c03df812fa23ed2bd887c67fc143a0b86b8 Mon Sep 17 00:00:00 2001 From: Robin Le Caignec <72495599+Le-Caignec@users.noreply.github.com> Date: Thu, 18 Sep 2025 09:57:22 +0200 Subject: [PATCH 09/17] Update src/guides/build-iapp/advanced/result-callback.md Co-authored-by: Zied Guesmi <26070035+zguesmi@users.noreply.github.com> --- src/guides/build-iapp/advanced/result-callback.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md index a60911b..7acb470 100644 --- a/src/guides/build-iapp/advanced/result-callback.md +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -1,5 +1,5 @@ --- -title: Result Callback Guide +title: Result callback guide description: Use the iExec result callback to have the protocol invoke a function on your smart contract at the end of a (optionally confidential TEE) task execution. From d0722d69d64b705f43ef9da788988e9551265f72 Mon Sep 17 00:00:00 2001 From: Robin Le Caignec <72495599+Le-Caignec@users.noreply.github.com> Date: Thu, 18 Sep 2025 09:59:20 +0200 Subject: [PATCH 10/17] Update src/guides/build-iapp/advanced/result-callback.md Co-authored-by: Zied Guesmi <26070035+zguesmi@users.noreply.github.com> --- src/guides/build-iapp/advanced/result-callback.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md index 7acb470..2f3e56c 100644 --- a/src/guides/build-iapp/advanced/result-callback.md +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -1,8 +1,8 @@ --- title: Result callback guide description: - Use the iExec result callback to have the protocol invoke a function on your - smart contract at the end of a (optionally confidential TEE) task execution. + Use the iExec result callback feature to have the protocol invoke a function on your + smart contract at the end of a task execution. --- # Result Callback From 4688133cbfcb7df7486edd247e7765314c1a68e3 Mon Sep 17 00:00:00 2001 From: Robin Le Caignec <72495599+Le-Caignec@users.noreply.github.com> Date: Thu, 18 Sep 2025 09:59:57 +0200 Subject: [PATCH 11/17] Update src/guides/build-iapp/advanced/result-callback.md Co-authored-by: Zied Guesmi <26070035+zguesmi@users.noreply.github.com> --- src/guides/build-iapp/advanced/result-callback.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md index 2f3e56c..6e595c6 100644 --- a/src/guides/build-iapp/advanced/result-callback.md +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -18,7 +18,7 @@ Use a callback when your smart contract should: - Store a timestamped record (price feed, score, KPI, proof hash) - Bridge logic between external systems and on-chain state -## 🧩 High-Level Flow +## 🧩 High-level flow 1. A requester executes an iApp. 2. The iApp writes `${IEXEC_OUT}/computed.json` with a `callback-data` field From 998d4dc9fbc11a5622c3dc7be3824d4ec7f641ba Mon Sep 17 00:00:00 2001 From: Robin Le Caignec <72495599+Le-Caignec@users.noreply.github.com> Date: Thu, 18 Sep 2025 10:03:36 +0200 Subject: [PATCH 12/17] Update src/guides/build-iapp/advanced/result-callback.md Co-authored-by: Zied Guesmi <26070035+zguesmi@users.noreply.github.com> --- src/guides/build-iapp/advanced/result-callback.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md index 6e595c6..5a6854a 100644 --- a/src/guides/build-iapp/advanced/result-callback.md +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -20,7 +20,8 @@ Use a callback when your smart contract should: ## 🧩 High-level flow -1. A requester executes an iApp. +1. A requester deploys the smart contract that should receive the callback data. +2. The requester executes an iApp and specifies the callback address. 2. The iApp writes `${IEXEC_OUT}/computed.json` with a `callback-data` field (ABI‑encoded bytes you crafted). 3. After the task completes and is validated, the iExec protocol invokes your From 50246da04c66376794bfcb41fc708c13d8338685 Mon Sep 17 00:00:00 2001 From: Robin Le Caignec <72495599+Le-Caignec@users.noreply.github.com> Date: Thu, 18 Sep 2025 10:03:52 +0200 Subject: [PATCH 13/17] Update src/guides/build-iapp/advanced/result-callback.md Co-authored-by: Zied Guesmi <26070035+zguesmi@users.noreply.github.com> --- src/guides/build-iapp/advanced/result-callback.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md index 5a6854a..4fce481 100644 --- a/src/guides/build-iapp/advanced/result-callback.md +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -67,7 +67,7 @@ async function main() { ### Step 2: Implement the Callback Contract -Your contract must expose `receiveResult(bytes32,bytes)` +Your contract must expose the function `receiveResult(bytes32,bytes)` [ERC1154](https://github.com/iExecBlockchainComputing/iexec-solidity/blob/master/contracts/ERC1154/IERC1154.sol). The protocol calls it with: From 5325468c1f39b3fb9f8652b2e0416172c310f0c5 Mon Sep 17 00:00:00 2001 From: Robin Le Caignec <72495599+Le-Caignec@users.noreply.github.com> Date: Thu, 18 Sep 2025 10:04:20 +0200 Subject: [PATCH 14/17] Update src/guides/build-iapp/advanced/result-callback.md Co-authored-by: Zied Guesmi <26070035+zguesmi@users.noreply.github.com> --- src/guides/build-iapp/advanced/result-callback.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md index 4fce481..a16bce8 100644 --- a/src/guides/build-iapp/advanced/result-callback.md +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -71,7 +71,7 @@ Your contract must expose the function `receiveResult(bytes32,bytes)` [ERC1154](https://github.com/iExecBlockchainComputing/iexec-solidity/blob/master/contracts/ERC1154/IERC1154.sol). The protocol calls it with: -- `_callID`: the first arg the taskId +- `_callID`: This parameter represents the `taskId`, passed as the first argument - `callback`: exactly the bytes you encoded as `callback-data` Decode using the same tuple. (Optional) Add protections: authorized caller check From f26d075cda6bd19d3c5dc7c2a446f60999768599 Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Thu, 18 Sep 2025 10:12:06 +0200 Subject: [PATCH 15/17] refactor: reorganize callback contract implementation and clarify gas limit considerations --- .../build-iapp/advanced/result-callback.md | 66 +++++++++---------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md index 6e595c6..a9e7c0e 100644 --- a/src/guides/build-iapp/advanced/result-callback.md +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -30,7 +30,39 @@ Use a callback when your smart contract should: ## Step-by-Step Implementation -### Step 1: Prepare the Callback Payload in the iApp +### Step 1: Implement the Callback Contract + +Your contract must expose `receiveResult(bytes32,bytes)` +[ERC1154](https://github.com/iExecBlockchainComputing/iexec-solidity/blob/master/contracts/ERC1154/IERC1154.sol). +The protocol calls it with: + +- `_callID`: the first arg the taskId +- `callback`: exactly the bytes you encoded as `callback-data` + +Decode using the same tuple. (Optional) Add protections: authorized caller check +(iExec hub / proxy), replay guard, bounds checks. + +```solidity +contract IExecCallbackReceiver { + // Your business logic here ... + + // ERC1154 - Callback processing + function receiveResult(bytes32 _callID, bytes memory callback) external { + // Parse results + (uint256 timestamp, string memory pairAndPrecision, uint256 scaledValue) = + abi.decode(callback, (uint256, string, uint256)); + } +} +``` + +::: tip Important + +The callback transaction is subject to a gas limit of {{ gasLimit }}. +Ensure your callback logic fits within this limit to avoid out-of-gas errors. + +::: + +### Step 2: Prepare the Callback Payload in the iApp You only need to write `computed.json` containing the key `callback-data`. That value must be the ABI‑encoded bytes your contract knows how to decode. @@ -64,38 +96,6 @@ async function main() { } ``` -### Step 2: Implement the Callback Contract - -Your contract must expose `receiveResult(bytes32,bytes)` -[ERC1154](https://github.com/iExecBlockchainComputing/iexec-solidity/blob/master/contracts/ERC1154/IERC1154.sol). -The protocol calls it with: - -- `_callID`: the first arg the taskId -- `callback`: exactly the bytes you encoded as `callback-data` - -Decode using the same tuple. (Optional) Add protections: authorized caller check -(iExec hub / proxy), replay guard, bounds checks. - -```solidity -contract IExecCallbackReceiver { - // Your business logic here ... - - // ERC1154 - Callback processing - function receiveResult(bytes32 _callID, bytes memory callback) external { - // Parse results - (uint256 timestamp, string memory pairAndPrecision, uint256 scaledValue) = - abi.decode(callback, (uint256, string, uint256)); - } -} -``` - -::: tip Important - -The callback transaction is subject to a gas limit of {{ gasLimit }}. -Ensure your callback logic fits within this limit to avoid out-of-gas errors. - -::: - ### Step 3: Run the iApp with a Callback When creating the request order, set the `callback` field to your callback From 240e37f9c160a44b8c3f2e8d76d035bcd3b34734 Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Thu, 18 Sep 2025 10:15:03 +0200 Subject: [PATCH 16/17] style: standardize section titles and improve consistency in the result callback guide --- src/guides/build-iapp/advanced/result-callback.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/guides/build-iapp/advanced/result-callback.md b/src/guides/build-iapp/advanced/result-callback.md index 477c21f..c4c1c05 100644 --- a/src/guides/build-iapp/advanced/result-callback.md +++ b/src/guides/build-iapp/advanced/result-callback.md @@ -5,7 +5,7 @@ description: on your smart contract at the end of a task execution. --- -# Result Callback +# Result callback This guide explains how to trigger a callback function at the end of a successful task on your smart contract. @@ -29,9 +29,9 @@ Use a callback when your smart contract should: 5. Your contract decodes and processes those bytes if callback data have been provided. -## Step-by-Step Implementation +## Step-by-step implementation -### Step 1: Implement the Callback Contract +### Step 1: Implement the callback contract Your contract must expose the function `receiveResult(bytes32,bytes)` [ERC1154](https://github.com/iExecBlockchainComputing/iexec-solidity/blob/master/contracts/ERC1154/IERC1154.sol). @@ -64,7 +64,7 @@ Ensure your callback logic fits within this limit to avoid out-of-gas errors. ::: -### Step 2: Prepare the Callback Payload in the iApp +### Step 2: Prepare the callback payload in the iApp You only need to write `computed.json` containing the key `callback-data`. That value must be the ABI‑encoded bytes your contract knows how to decode. @@ -98,7 +98,7 @@ async function main() { } ``` -### Step 3: Run the iApp with a Callback +### Step 3: Run the iApp with a callback When creating the request order, set the `callback` field to your callback contract address. @@ -153,7 +153,7 @@ const taskId = await iexec.order.matchOrders({ }); ``` -## 🔄 Other Use Cases +## 🔄 Other use cases | Use Case | Description | | -------------------- | ---------------------------------------- | From d1a88fb1c791eaf9312459fbfd351028b5f30ba0 Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Thu, 18 Sep 2025 12:14:48 +0200 Subject: [PATCH 17/17] refactor: remove redundant parameter `protectedDataAddress` from naming conventions --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index f7926c7..4dde677 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,6 @@ In order to keep the documentation consistent, we have some naming conventions for input parameters: - `protectedData`: '0x123abc...', -- `protectedDataAddress`: '0x123abc...', - `authorizedApp`: '0x456def...', - `authorizedUser`: '0x789cba...', - `userAddress`: '0x789cba...',