Skip to content

Commit 13fe7ac

Browse files
Le-Caigneczguesmi
andauthored
feat: add Result Callback guide (#67)
Co-authored-by: Zied Guesmi <[email protected]>
1 parent c8e4e65 commit 13fe7ac

File tree

5 files changed

+215
-338
lines changed

5 files changed

+215
-338
lines changed

.vitepress/sidebar.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,10 @@ export function getSidebar() {
165165
text: 'End-to-end Encryption',
166166
link: '/guides/build-iapp/advanced/protect-the-result',
167167
},
168+
{
169+
text: 'Result Callback',
170+
link: '/guides/build-iapp/advanced/result-callback',
171+
},
168172
{
169173
text: 'Access Confidential Assets',
170174
link: '/guides/build-iapp/advanced/access-confidential-assets',
@@ -545,10 +549,6 @@ export function getSidebar() {
545549
text: 'Pay Per Task Model',
546550
link: '/protocol/pay-per-task',
547551
},
548-
{
549-
text: 'Oracle',
550-
link: '/protocol/oracle',
551-
},
552552
{
553553
text: 'Workers & Workerpools',
554554
collapsed: false,

README.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,25 @@ We welcome contributions to improve the iExec documentation!
134134
📖 **For detailed contribution guidelines, component usage, and best practices,
135135
please see our [CONTRIBUTING.md](CONTRIBUTING.md) guide.**
136136

137+
### Some conventions
138+
139+
In order to keep the documentation consistent, we have some naming conventions
140+
for input parameters:
141+
142+
- `protectedData`: '0x123abc...',
143+
- `authorizedApp`: '0x456def...',
144+
- `authorizedUser`: '0x789cba...',
145+
- `userAddress`: '0x789cba...',
146+
- `appWhitelist`: '0xba46d6...',
147+
- `owner`: '0xa0c15e...',
148+
- `newOwner`: '0xc5e9f4...',
149+
- `voucherOwner`: '0x5714eB...',
150+
- `renterAddress`: '0x246bdf...'
151+
- `subscriberAddress`: '0x246bdf...'
152+
- `workerpool`: '0xa5de76...'
153+
- `taskId`: '0x7ac398...'
154+
- `smartContractAddress`: '0x8e5bB6...'
155+
137156
### Quick Contribution Steps
138157

139158
1. **Fork** this repository

src/guides/build-iapp/advanced/build-your-first-sgx-iapp.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ You can check your deployed apps with their index, let's check your last
395395
deployed app:
396396

397397
```bash twoslash
398-
iexec app show --chain arbitrum-mainnet
398+
iexec app show --chain {{chainName}}
399399
```
400400

401401
## Run the iApp
@@ -420,20 +420,20 @@ At any time you can:
420420
- view your balance
421421

422422
```bash twoslash
423-
iexec account show --chain arbitrum-mainnet
423+
iexec account show --chain {{chainName}}
424424
```
425425

426426
- deposit RLC from your wallet to your iExec Account
427427

428428
```bash twoslash
429-
iexec account deposit --chain arbitrum-mainnet <amount>
429+
iexec account deposit --chain {{chainName}} <amount>
430430
```
431431

432432
- withdraw RLC from your iExec account to your wallet \(only stake can be
433433
withdrawn\)
434434

435435
```bash twoslash
436-
iexec account withdraw --chain arbitrum-mainnet <amount>
436+
iexec account withdraw --chain {{chainName}} <amount>
437437
```
438438

439439
:::
@@ -478,13 +478,13 @@ is a 32Bytes hexadecimal string\).
478478
Download the result of your task
479479

480480
```bash twoslash
481-
iexec task show --chain arbitrum-mainnet <taskid> --download my-result
481+
iexec task show --chain {{chainName}} <taskid> --download my-result
482482
```
483483

484484
You can get your taskid with the command:
485485

486486
```bash twoslash
487-
iexec deal show --chain arbitrum-mainnet <dealid>
487+
iexec deal show --chain {{chainName}} <dealid>
488488
```
489489

490490
::: info
@@ -526,7 +526,7 @@ The conditions to use an app are defined in the **apporder**.
526526
Publish a new apporder for your application.
527527

528528
```bash twoslash
529-
iexec app publish --chain arbitrum-mainnet
529+
iexec app publish --chain {{chainName}}
530530
```
531531

532532
::: info
@@ -545,7 +545,7 @@ conditions defined in apporder.
545545
You can check the published apporders for your app
546546

547547
```bash twoslash
548-
iexec orderbook app --chain arbitrum-mainnet <your app address>
548+
iexec orderbook app --chain {{chainName}} <your app address>
549549
```
550550

551551
Congratulation you just created a decentralized application! Anyone can now
Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
---
2+
title: Result callback guide
3+
description:
4+
Use the iExec result callback feature to have the protocol invoke a function
5+
on your smart contract at the end of a task execution.
6+
---
7+
8+
# Result callback
9+
10+
This guide explains how to trigger a callback function at the end of a
11+
successful task on your smart contract.
12+
13+
Use a callback when your smart contract should:
14+
15+
- Ingest off-chain computed data (API aggregation, ML inference, analytics) and
16+
persist it
17+
- React to an execution outcome (conditional trigger, state transition)
18+
- Store a timestamped record (price feed, score, KPI, proof hash)
19+
- Bridge logic between external systems and on-chain state
20+
21+
## 🧩 High-level flow
22+
23+
1. A requester deploys the smart contract that should receive the callback data.
24+
2. The requester executes an iApp and specifies the callback address.
25+
3. The iApp writes `${IEXEC_OUT}/computed.json` with a `callback-data` field
26+
(ABI‑encoded bytes you crafted).
27+
4. After the task completes and is validated, the iExec protocol invokes your
28+
contract’s `receiveResult(bytes32,bytes)`.
29+
5. Your contract decodes and processes those bytes if callback data have been
30+
provided.
31+
32+
## Step-by-step implementation
33+
34+
### Step 1: Implement the callback contract
35+
36+
Your contract must expose the function `receiveResult(bytes32,bytes)`
37+
[ERC1154](https://github.com/iExecBlockchainComputing/iexec-solidity/blob/master/contracts/ERC1154/IERC1154.sol).
38+
The protocol calls it with:
39+
40+
- `_callID`: This parameter represents the `taskId`, passed as the first
41+
argument
42+
- `callback`: exactly the bytes you encoded as `callback-data`
43+
44+
Decode using the same tuple. (Optional) Add protections: authorized caller check
45+
(iExec hub / proxy), replay guard, bounds checks.
46+
47+
```solidity
48+
contract IExecCallbackReceiver {
49+
// Your business logic here ...
50+
51+
// ERC1154 - Callback processing
52+
function receiveResult(bytes32 _callID, bytes memory callback) external {
53+
// Parse results
54+
(uint256 timestamp, string memory pairAndPrecision, uint256 scaledValue) =
55+
abi.decode(callback, (uint256, string, uint256));
56+
}
57+
}
58+
```
59+
60+
::: tip Important
61+
62+
The callback transaction is subject to a gas limit of {{ gasLimit }}.
63+
Ensure your callback logic fits within this limit to avoid out-of-gas errors.
64+
65+
:::
66+
67+
### Step 2: Prepare the callback payload in the iApp
68+
69+
You only need to write `computed.json` containing the key `callback-data`.
70+
That value must be the ABI‑encoded bytes your contract knows how to decode.
71+
Example tuple schema we'll use:
72+
`(uint256 timestamp, string pairAndPrecision, uint256 scaledValue)`.
73+
74+
```ts twoslash
75+
import { writeFileSync } from 'node:fs';
76+
import { AbiCoder } from 'ethers';
77+
78+
const timestamp = Math.floor(Date.now() / 1000);
79+
const pair = 'BTC-USD';
80+
const scaled = '9';
81+
// ---cut---
82+
83+
async function main() {
84+
// Your business logic here ...
85+
86+
const abiCoder = new AbiCoder();
87+
const abiPayload = abiCoder.encode(
88+
['uint256', 'string', 'uint256'],
89+
[timestamp, pair, scaled]
90+
);
91+
92+
writeFileSync(
93+
`${process.env.IEXEC_OUT}/computed.json`,
94+
JSON.stringify({
95+
'callback-data': abiPayload,
96+
})
97+
);
98+
}
99+
```
100+
101+
### Step 3: Run the iApp with a callback
102+
103+
When creating the request order, set the `callback` field to your callback
104+
contract address.
105+
After completion, the protocol calls your contract, passing the `callback-data`
106+
bytes.
107+
108+
First install the iExec SDK if you have not already (see
109+
[Getting Started](/guides/use-iapp/getting-started)).
110+
111+
```ts twoslash
112+
import { IExec, utils } from 'iexec';
113+
114+
const ethProvider = utils.getSignerFromPrivateKey(
115+
'chain', // blockchain node URL
116+
'PRIVATE_KEY'
117+
);
118+
const iexec = new IExec({
119+
ethProvider,
120+
});
121+
// ---cut---
122+
// Basic arguments
123+
const requestorderToSign = await iexec.order.createRequestorder({
124+
app: '0x456def...',
125+
category: 0,
126+
appmaxprice: 10,
127+
workerpool: '0xa5de76...',
128+
callback: '0x8e5bB6...', // Callback contract address
129+
});
130+
const requestOrder = await iexec.order.signRequestorder(requestorderToSign);
131+
132+
// Fetch app orders
133+
const appOrders = await iexec.orderbook.fetchAppOrderbook(
134+
'0x456def...' // Filter by specific app
135+
);
136+
if (appOrders.orders.length === 0) {
137+
throw new Error('No app orders found for the specified app');
138+
}
139+
140+
// Fetch workerpool orders
141+
const workerpoolOrders = await iexec.orderbook.fetchWorkerpoolOrderbook({
142+
workerpool: '0xa5de76...', // Filter by specific workerpool
143+
});
144+
if (workerpoolOrders.orders.length === 0) {
145+
throw new Error('No workerpool orders found for the specified workerpool');
146+
}
147+
148+
// Execute the task
149+
const taskId = await iexec.order.matchOrders({
150+
requestorder: requestOrder,
151+
apporder: appOrders.orders[0].order,
152+
workerpoolorder: workerpoolOrders.orders[0].order,
153+
});
154+
```
155+
156+
## 🔄 Other use cases
157+
158+
| Use Case | Description |
159+
| -------------------- | ---------------------------------------- |
160+
| Price oracle | Multi-source API aggregation |
161+
| Reputation / scoring | Off-chain ML / analytics pushed on-chain |
162+
| Audit hash | Security scan or verification artifact |
163+
| Automation | Workflow step completion signal |
164+
| Dynamic parameters | Adjust rates / thresholds / quorums |
165+
| Logical bridge | Sync external (IoT / legacy) state |
166+
167+
<script setup>
168+
import { computed } from 'vue';
169+
import useUserStore from '@/stores/useUser.store';
170+
import { getChainById } from '@/utils/chain.utils';
171+
172+
// Get current chain info
173+
const userStore = useUserStore();
174+
const selectedChain = computed(() => userStore.getCurrentChainId());
175+
const chainData = computed(() => getChainById(selectedChain.value));
176+
const chainName = computed(() => chainData.value.chainName);
177+
178+
const gasLimit = computed(() => {
179+
const chainId = selectedChain.value;
180+
if (chainId === 42161) return '100,000'; // Arbitrum One
181+
if (chainId === 134) return '200,000'; // Bellecour
182+
return '100,000'; // default
183+
});
184+
</script>

0 commit comments

Comments
 (0)