From 42d82f40f1e9e6df692a73827366a69748dd6447 Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Mon, 11 Aug 2025 21:24:22 +0200 Subject: [PATCH 1/8] Remove deprecated documentation for Web3Mail and Web3Telegram methods, including sendEmail, sendTelegram, and related integration guides. Clean up unused files and streamline the SDK documentation for better clarity and usability. --- .vitepress/config.mts | 9 +- .vitepress/sidebar.ts | 222 +++--- .vitepress/theme/Layout.vue | 2 +- src/build-iapp/guides/build-&-deploy.md | 362 ---------- src/build-iapp/guides/debugging.md | 175 ----- .../guides/how-to-get-and-decrypt-results.md | 314 --------- src/build-iapp/guides/inputs-and-outputs.md | 641 ------------------ src/build-iapp/guides/manage-access.md | 303 --------- src/build-iapp/guides/using-tdx.md | 192 ------ src/build-iapp/iapp-generator.md | 119 ---- .../iapp-generator/building-your-iexec-app.md | 204 ------ src/build-iapp/iapp-generator/deserializer.md | 83 --- .../iapp-generator/deserializer/getValue.md | 68 -- .../iapp-generator/getting-started.md | 57 -- src/build-iapp/what-is-iapp.md | 202 ------ src/index.md | 16 +- src/manage-data/dataProtector.md | 48 -- .../advanced/advanced-configuration.md | 166 ----- .../dataProtector/advanced/apps-whitelist.md | 80 --- .../addAppToAddOnlyAppWhitelist.md | 98 --- .../createAddOnlyAppWhitelist.md | 33 - .../getUserAddOnlyAppWhitelist.md | 61 -- .../advanced/dps-smart-contract.md | 123 ---- .../dataProtector/dataProtectorCore.md | 42 -- .../dataProtectorCore/getGrantedAccess.md | 219 ------ .../dataProtectorCore/getProtectedData.md | 193 ------ .../getResultFromCompletedTask.md | 136 ---- .../dataProtectorCore/grantAccess.md | 252 ------- .../dataProtectorCore/processProtectedData.md | 497 -------------- .../dataProtectorCore/protectData.md | 442 ------------ .../dataProtectorCore/revokeAllAccess.md | 143 ---- .../dataProtectorCore/revokeOneAccess.md | 91 --- .../dataProtectorCore/transferOwnership.md | 105 --- .../dataProtector/dataProtectorSharing.md | 65 -- .../dataProtectorSharing/collection.md | 70 -- .../collection/addToCollection.md | 166 ----- .../collection/createCollection.md | 36 - .../collection/removeCollection.md | 70 -- .../removeProtectedDataFromCollection.md | 76 --- .../consume/consumeProtectedData.md | 341 ---------- .../dataProtectorSharing/data-sharing-sc.png | Bin 93263 -> 0 bytes .../inside-a-collection.png | Bin 113538 -> 0 bytes .../read/getCollectionOwners.md | 70 -- .../read/getCollectionSubscriptions.md | 106 --- .../read/getCollectionsByOwner.md | 89 --- .../read/getProtectedDataInCollections.md | 229 ------- .../read/getProtectedDataPricingParams.md | 65 -- .../dataProtectorSharing/read/getRentals.md | 103 --- .../dataProtectorSharing/renting.md | 42 -- .../renting/removeProtectedDataFromRenting.md | 66 -- .../renting/rentProtectedData.md | 139 ---- .../renting/setProtectedDataRentingParams.md | 117 ---- .../renting/setProtectedDataToRenting.md | 118 ---- .../dataProtectorSharing/selling.md | 15 - .../selling/buyProtectedData.md | 164 ----- .../selling/removeProtectedDataForSale.md | 62 -- .../selling/setProtectedDataForSale.md | 89 --- .../dataProtectorSharing/subscription.md | 60 -- .../removeProtectedDataFromSubscription.md | 77 --- .../setProtectedDataToSubscription.md | 66 -- .../subscription/setSubscriptionParams.md | 114 ---- .../subscription/subscribeToCollection.md | 135 ---- .../dataProtector/getting-started.md | 249 ------- .../dataProtector/migrate-from-v1.md | 146 ---- src/manage-data/dataProtector/types.md | 124 ---- .../guides/handle-schemas-dataset-types.md | 271 -------- src/manage-data/guides/manage-access.md | 230 ------- .../guides/monetize-protected-data.md | 256 ------- src/manage-data/what-is-protected-data.md | 209 ------ src/modules/helloWorld/GrantAccess.vue | 2 +- src/modules/helloWorld/ProtectData.vue | 2 +- src/modules/helloWorld/ReownButton.vue | 4 +- src/overview/develop-with-ai.md | 61 -- src/overview/helloWorld.md | 122 ---- src/overview/helloWorld/1-overview.md | 184 ----- src/overview/helloWorld/2-protectData.md | 192 ------ src/overview/helloWorld/3-buildIApp.md | 324 --------- src/overview/helloWorld/4-manageDataAccess.md | 123 ---- src/overview/helloWorld/5-bonusChapter.md | 61 -- src/overview/quick-start.md | 116 ---- src/overview/rlc.md | 134 ---- .../blockchain-explorer.md | 53 -- src/overview/tooling-and-explorers/bridge.md | 119 ---- .../builder-dashboard.md | 170 ----- .../tooling-and-explorers/iexec-explorer.md | 211 ------ .../subgraph-explorer.md | 204 ------ src/overview/use-cases.md | 56 -- src/overview/welcome.md | 188 ----- src/protocol/glossary.md | 10 - src/protocol/sdk.md | 10 - src/protocol/workers.md | 12 - src/use-iapp/getting-started.md | 11 - .../guides/add-inputs-to-execution.md | 10 - .../guides/different-ways-to-execute.md | 10 - src/use-iapp/guides/find-iapps.md | 10 - src/use-iapp/guides/how-to-pay-executions.md | 10 - .../guides/use-iapp-with-protected-data.md | 10 - .../how-to-pay/how-to-pay-for-web3mail.md | 165 ----- .../how-to-pay/how-to-pay-for-web3telegram.md | 178 ----- .../how-to-pay/pricing-considerations.md | 10 - src/use-iapp/how-to-pay/voucher.md | 10 - src/use-iapp/introduction.md | 12 - src/use-iapp/web3mail.md | 49 -- .../web3mail/advanced-configuration.md | 121 ---- src/use-iapp/web3mail/getting-started.md | 115 ---- .../web3mail/methods/fetchMyContacts.md | 67 -- .../web3mail/methods/fetchUserContacts.md | 87 --- src/use-iapp/web3mail/methods/sendEmail.md | 392 ----------- src/use-iapp/web3telegram.md | 46 -- .../web3telegram/advanced-configuration.md | 121 ---- src/use-iapp/web3telegram/getting-started.md | 90 --- .../web3telegram/integration-guide.md | 112 --- .../web3telegram/methods/fetchMyContacts.md | 67 -- .../web3telegram/methods/fetchUserContacts.md | 87 --- .../web3telegram/methods/sendTelegram.md | 295 -------- 115 files changed, 124 insertions(+), 14252 deletions(-) delete mode 100644 src/build-iapp/guides/debugging.md delete mode 100644 src/build-iapp/guides/how-to-get-and-decrypt-results.md delete mode 100644 src/build-iapp/guides/inputs-and-outputs.md delete mode 100644 src/build-iapp/guides/manage-access.md delete mode 100644 src/build-iapp/guides/using-tdx.md delete mode 100644 src/build-iapp/iapp-generator.md delete mode 100644 src/build-iapp/iapp-generator/building-your-iexec-app.md delete mode 100644 src/build-iapp/iapp-generator/deserializer.md delete mode 100644 src/build-iapp/iapp-generator/deserializer/getValue.md delete mode 100644 src/build-iapp/iapp-generator/getting-started.md delete mode 100644 src/build-iapp/what-is-iapp.md delete mode 100644 src/manage-data/dataProtector.md delete mode 100644 src/manage-data/dataProtector/advanced/advanced-configuration.md delete mode 100644 src/manage-data/dataProtector/advanced/apps-whitelist.md delete mode 100644 src/manage-data/dataProtector/advanced/apps-whitelist/addAppToAddOnlyAppWhitelist.md delete mode 100644 src/manage-data/dataProtector/advanced/apps-whitelist/createAddOnlyAppWhitelist.md delete mode 100644 src/manage-data/dataProtector/advanced/apps-whitelist/getUserAddOnlyAppWhitelist.md delete mode 100644 src/manage-data/dataProtector/advanced/dps-smart-contract.md delete mode 100644 src/manage-data/dataProtector/dataProtectorCore.md delete mode 100644 src/manage-data/dataProtector/dataProtectorCore/getGrantedAccess.md delete mode 100644 src/manage-data/dataProtector/dataProtectorCore/getProtectedData.md delete mode 100644 src/manage-data/dataProtector/dataProtectorCore/getResultFromCompletedTask.md delete mode 100644 src/manage-data/dataProtector/dataProtectorCore/grantAccess.md delete mode 100644 src/manage-data/dataProtector/dataProtectorCore/processProtectedData.md delete mode 100644 src/manage-data/dataProtector/dataProtectorCore/protectData.md delete mode 100644 src/manage-data/dataProtector/dataProtectorCore/revokeAllAccess.md delete mode 100644 src/manage-data/dataProtector/dataProtectorCore/revokeOneAccess.md delete mode 100644 src/manage-data/dataProtector/dataProtectorCore/transferOwnership.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/collection.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/collection/addToCollection.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/collection/createCollection.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/collection/removeCollection.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/collection/removeProtectedDataFromCollection.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/consume/consumeProtectedData.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/data-sharing-sc.png delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/inside-a-collection.png delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/read/getCollectionOwners.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/read/getCollectionSubscriptions.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/read/getCollectionsByOwner.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataInCollections.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataPricingParams.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/read/getRentals.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/renting.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/renting/removeProtectedDataFromRenting.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/renting/rentProtectedData.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataRentingParams.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataToRenting.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/selling.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/selling/buyProtectedData.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/selling/removeProtectedDataForSale.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/selling/setProtectedDataForSale.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/subscription.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/subscription/removeProtectedDataFromSubscription.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/subscription/setProtectedDataToSubscription.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/subscription/setSubscriptionParams.md delete mode 100644 src/manage-data/dataProtector/dataProtectorSharing/subscription/subscribeToCollection.md delete mode 100644 src/manage-data/dataProtector/getting-started.md delete mode 100644 src/manage-data/dataProtector/migrate-from-v1.md delete mode 100644 src/manage-data/dataProtector/types.md delete mode 100644 src/manage-data/guides/handle-schemas-dataset-types.md delete mode 100644 src/manage-data/guides/manage-access.md delete mode 100644 src/manage-data/guides/monetize-protected-data.md delete mode 100644 src/manage-data/what-is-protected-data.md delete mode 100644 src/overview/develop-with-ai.md delete mode 100644 src/overview/helloWorld.md delete mode 100644 src/overview/helloWorld/1-overview.md delete mode 100644 src/overview/helloWorld/2-protectData.md delete mode 100644 src/overview/helloWorld/3-buildIApp.md delete mode 100644 src/overview/helloWorld/4-manageDataAccess.md delete mode 100644 src/overview/helloWorld/5-bonusChapter.md delete mode 100644 src/overview/quick-start.md delete mode 100644 src/overview/rlc.md delete mode 100644 src/overview/tooling-and-explorers/blockchain-explorer.md delete mode 100644 src/overview/tooling-and-explorers/bridge.md delete mode 100644 src/overview/tooling-and-explorers/builder-dashboard.md delete mode 100644 src/overview/tooling-and-explorers/iexec-explorer.md delete mode 100644 src/overview/tooling-and-explorers/subgraph-explorer.md delete mode 100644 src/overview/use-cases.md delete mode 100644 src/overview/welcome.md delete mode 100644 src/protocol/glossary.md delete mode 100644 src/protocol/sdk.md delete mode 100644 src/protocol/workers.md delete mode 100644 src/use-iapp/getting-started.md delete mode 100644 src/use-iapp/guides/add-inputs-to-execution.md delete mode 100644 src/use-iapp/guides/different-ways-to-execute.md delete mode 100644 src/use-iapp/guides/find-iapps.md delete mode 100644 src/use-iapp/guides/how-to-pay-executions.md delete mode 100644 src/use-iapp/guides/use-iapp-with-protected-data.md delete mode 100644 src/use-iapp/how-to-pay/how-to-pay-for-web3mail.md delete mode 100644 src/use-iapp/how-to-pay/how-to-pay-for-web3telegram.md delete mode 100644 src/use-iapp/how-to-pay/pricing-considerations.md delete mode 100644 src/use-iapp/how-to-pay/voucher.md delete mode 100644 src/use-iapp/introduction.md delete mode 100644 src/use-iapp/web3mail.md delete mode 100644 src/use-iapp/web3mail/advanced-configuration.md delete mode 100644 src/use-iapp/web3mail/getting-started.md delete mode 100644 src/use-iapp/web3mail/methods/fetchMyContacts.md delete mode 100644 src/use-iapp/web3mail/methods/fetchUserContacts.md delete mode 100644 src/use-iapp/web3mail/methods/sendEmail.md delete mode 100644 src/use-iapp/web3telegram.md delete mode 100644 src/use-iapp/web3telegram/advanced-configuration.md delete mode 100644 src/use-iapp/web3telegram/getting-started.md delete mode 100644 src/use-iapp/web3telegram/integration-guide.md delete mode 100644 src/use-iapp/web3telegram/methods/fetchMyContacts.md delete mode 100644 src/use-iapp/web3telegram/methods/fetchUserContacts.md delete mode 100644 src/use-iapp/web3telegram/methods/sendTelegram.md diff --git a/.vitepress/config.mts b/.vitepress/config.mts index c2fee0e6..616014d7 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -18,7 +18,6 @@ export default defineConfig({ 'Build decentralized applications that combine ownership, privacy, and monetization.', cleanUrls: true, lastUpdated: true, - ignoreDeadLinks: true, vite: { plugins: [tailwindcss(), groupIconVitePlugin()], resolve: { @@ -99,11 +98,9 @@ export default defineConfig({ themeConfig: { // https://vitepress.dev/reference/default-theme-config nav: [ - { text: 'Get Started', link: '/overview/welcome' }, - { text: 'Protect Data', link: '/manage-data/what-is-protected-data' }, - { text: 'Build iApp', link: '/build-iapp/what-is-iapp' }, - { text: 'Use iApp', link: '/use-iapp/introduction' }, - { text: 'Protocol', link: '/protocol/sdk' }, + { text: 'Documentation', link: '/documentation/welcome' }, + { text: 'Guides', link: '/' }, + { text: 'References', link: '/' }, { component: 'ChainSelector', props: { diff --git a/.vitepress/sidebar.ts b/.vitepress/sidebar.ts index 88f4601c..f68ba174 100644 --- a/.vitepress/sidebar.ts +++ b/.vitepress/sidebar.ts @@ -2,49 +2,49 @@ import type { DefaultTheme } from 'vitepress'; export function getSidebar() { return { - '/overview/': [ + '/documentation/': [ { text: 'GET STARTED', items: [ - { text: '💡 Welcome', link: '/overview/welcome' }, + { text: '💡 Welcome', link: '/documentation/welcome' }, { text: '👋 Hello World', - link: '/overview/helloWorld', + link: '/documentation/helloWorld', items: [ { text: 'iExec Overview', - link: '/overview/helloWorld/1-overview', + link: '/documentation/helloWorld/1-overview', }, { text: 'Protect Data', - link: '/overview/helloWorld/2-protectData', + link: '/documentation/helloWorld/2-protectData', }, - { text: 'Build iApp', link: '/overview/helloWorld/3-buildIApp' }, + { text: 'Build iApp', link: '/documentation/helloWorld/3-buildIApp' }, { text: 'Manage Data Access', - link: '/overview/helloWorld/4-manageDataAccess', + link: '/documentation/helloWorld/4-manageDataAccess', }, { text: 'Bonus Chapter !', - link: '/overview/helloWorld/5-bonusChapter', + link: '/documentation/helloWorld/5-bonusChapter', }, ], }, { text: '🚀 Quick Start', - link: '/overview/quick-start', + link: '/documentation/quick-start', }, { text: '📋 Use Cases', - link: '/overview/use-cases', + link: '/documentation/use-cases', }, { text: '🪙 RLC Token', - link: '/overview/rlc', + link: '/documentation/rlc', }, { text: '🤖 Develop with AI', - link: '/overview/develop-with-ai', + link: '/documentation/develop-with-ai', }, ], }, @@ -53,193 +53,191 @@ export function getSidebar() { items: [ { text: 'iExec Explorer', - link: '/overview/tooling-and-explorers/iexec-explorer', + link: '/documentation/tooling-and-explorers/iexec-explorer', }, { text: 'Builder Dashboard', - link: '/overview/tooling-and-explorers/builder-dashboard', + link: '/documentation/tooling-and-explorers/builder-dashboard', }, { text: 'RLC Bridge', - link: '/overview/tooling-and-explorers/bridge', + link: '/documentation/tooling-and-explorers/bridge', }, { text: 'Subgraph Explorer', - link: '/overview/tooling-and-explorers/subgraph-explorer', + link: '/documentation/tooling-and-explorers/subgraph-explorer', }, { text: 'Blockchain Explorer', - link: '/overview/tooling-and-explorers/blockchain-explorer', + link: '/documentation/tooling-and-explorers/blockchain-explorer', }, ], }, - ], - '/manage-data/': [ { text: 'PROTECT AND MANAGE DATA', items: [ { text: '❓  What is Protected Data?', - link: '/manage-data/what-is-protected-data', + link: '/documentation/manage-data/what-is-protected-data', }, { text: '📖 Guides', items: [ { text: 'Manage Access', - link: '/manage-data/guides/manage-access', + link: '/documentation/manage-data/guides/manage-access', }, { text: 'Handle Schemas and Dataset Types', - link: '/manage-data/guides/handle-schemas-dataset-types', + link: '/documentation/manage-data/guides/handle-schemas-dataset-types', }, { text: 'Monetize Protected Data', - link: '/manage-data/guides/monetize-protected-data', + link: '/documentation/manage-data/guides/monetize-protected-data', }, ], }, { text: '🔐 DataProtector', - link: '/manage-data/dataProtector', + link: '/documentation/manage-data/dataProtector', items: [ { text: 'Getting Started', - link: '/manage-data/dataProtector/getting-started', + link: '/documentation/manage-data/dataProtector/getting-started', }, { text: 'DataProtector Core', - link: '/manage-data/dataProtector/dataProtectorCore', + link: '/documentation/manage-data/dataProtector/dataProtectorCore', collapsed: true, items: [ { text: 'protectData', - link: '/manage-data/dataProtector/dataProtectorCore/protectData', + link: '/documentation/manage-data/dataProtector/dataProtectorCore/protectData', }, { text: 'getProtectedData', - link: '/manage-data/dataProtector/dataProtectorCore/getProtectedData', + link: '/documentation/manage-data/dataProtector/dataProtectorCore/getProtectedData', }, { text: 'transferOwnership', - link: '/manage-data/dataProtector/dataProtectorCore/transferOwnership', + link: '/documentation/manage-data/dataProtector/dataProtectorCore/transferOwnership', }, { text: 'grantAccess', - link: '/manage-data/dataProtector/dataProtectorCore/grantAccess', + link: '/documentation/manage-data/dataProtector/dataProtectorCore/grantAccess', }, { text: 'getGrantedAccess', - link: '/manage-data/dataProtector/dataProtectorCore/getGrantedAccess', + link: '/documentation/manage-data/dataProtector/dataProtectorCore/getGrantedAccess', }, { text: 'revokeOneAccess', - link: '/manage-data/dataProtector/dataProtectorCore/revokeOneAccess', + link: '/documentation/manage-data/dataProtector/dataProtectorCore/revokeOneAccess', }, { text: 'revokeAllAccess', - link: '/manage-data/dataProtector/dataProtectorCore/revokeAllAccess', + link: '/documentation/manage-data/dataProtector/dataProtectorCore/revokeAllAccess', }, { text: 'processProtectedData', - link: '/manage-data/dataProtector/dataProtectorCore/processProtectedData', + link: '/documentation/manage-data/dataProtector/dataProtectorCore/processProtectedData', }, { text: 'getResultFromCompletedTask', - link: '/manage-data/dataProtector/dataProtectorCore/getResultFromCompletedTask', + link: '/documentation/manage-data/dataProtector/dataProtectorCore/getResultFromCompletedTask', }, ], }, { text: 'DataProtector Sharing', - link: '/manage-data/dataProtector/dataProtectorSharing', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing', collapsed: true, items: [ { text: 'Collection', - link: '/manage-data/dataProtector/dataProtectorSharing/collection', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/collection', collapsed: true, items: [ { text: 'createCollection', - link: '/manage-data/dataProtector/dataProtectorSharing/collection/createCollection', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/collection/createCollection', }, { text: 'removeCollection', - link: '/manage-data/dataProtector/dataProtectorSharing/collection/removeCollection', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/collection/removeCollection', }, { text: 'addToCollection', - link: '/manage-data/dataProtector/dataProtectorSharing/collection/addToCollection', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/collection/addToCollection', }, { text: 'removeProtectedDataFromCollection', - link: '/manage-data/dataProtector/dataProtectorSharing/collection/removeProtectedDataFromCollection', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/collection/removeProtectedDataFromCollection', }, ], }, { text: 'Renting', - link: '/manage-data/dataProtector/dataProtectorSharing/renting', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/renting', collapsed: true, items: [ { text: 'setProtectedDataToRenting', - link: '/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataToRenting', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataToRenting', }, { text: 'setProtectedDataRentingParams', - link: '/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataRentingParams', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataRentingParams', }, { text: 'rentProtectedData', - link: '/manage-data/dataProtector/dataProtectorSharing/renting/rentProtectedData', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/renting/rentProtectedData', }, { text: 'removeProtectedDataFromRenting', - link: '/manage-data/dataProtector/dataProtectorSharing/renting/removeProtectedDataFromRenting', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/renting/removeProtectedDataFromRenting', }, ], }, { text: 'Selling', - link: '/manage-data/dataProtector/dataProtectorSharing/selling', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/selling', collapsed: true, items: [ { text: 'setProtectedDataForSale', - link: '/manage-data/dataProtector/dataProtectorSharing/selling/setProtectedDataForSale', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/selling/setProtectedDataForSale', }, { text: 'buyProtectedData', - link: '/manage-data/dataProtector/dataProtectorSharing/selling/buyProtectedData', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/selling/buyProtectedData', }, { text: 'removeProtectedDataForSale', - link: '/manage-data/dataProtector/dataProtectorSharing/selling/removeProtectedDataForSale', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/selling/removeProtectedDataForSale', }, ], }, { text: 'Subscription', - link: '/manage-data/dataProtector/dataProtectorSharing/subscription', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/subscription', collapsed: true, items: [ { text: 'setProtectedDataToSubscription', - link: '/manage-data/dataProtector/dataProtectorSharing/subscription/setProtectedDataToSubscription', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/setProtectedDataToSubscription', }, { text: 'setSubscriptionParams', - link: '/manage-data/dataProtector/dataProtectorSharing/subscription/setSubscriptionParams', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/setSubscriptionParams', }, { text: 'subscribeToCollection', - link: '/manage-data/dataProtector/dataProtectorSharing/subscription/subscribeToCollection', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/subscribeToCollection', }, { text: 'removeProtectedDataFromSubscription', - link: '/manage-data/dataProtector/dataProtectorSharing/subscription/removeProtectedDataFromSubscription', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/removeProtectedDataFromSubscription', }, ], }, @@ -249,7 +247,7 @@ export function getSidebar() { items: [ { text: 'consumeProtectedData', - link: '/manage-data/dataProtector/dataProtectorSharing/consume/consumeProtectedData', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/consume/consumeProtectedData', }, ], }, @@ -259,27 +257,27 @@ export function getSidebar() { items: [ { text: 'getProtectedDataInCollections', - link: '/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataInCollections', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataInCollections', }, { text: 'getProtectedDataPricingParams', - link: '/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataPricingParams', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataPricingParams', }, { text: 'getCollectionOwners', - link: '/manage-data/dataProtector/dataProtectorSharing/read/getCollectionOwners', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionOwners', }, { text: 'getCollectionsByOwner', - link: '/manage-data/dataProtector/dataProtectorSharing/read/getCollectionsByOwner', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionsByOwner', }, { text: 'getCollectionSubscriptions', - link: '/manage-data/dataProtector/dataProtectorSharing/read/getCollectionSubscriptions', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionSubscriptions', }, { text: 'getRentals', - link: '/manage-data/dataProtector/dataProtectorSharing/read/getRentals', + link: '/documentation/manage-data/dataProtector/dataProtectorSharing/read/getRentals', }, ], }, @@ -287,7 +285,7 @@ export function getSidebar() { }, { text: 'Types', - link: '/manage-data/dataProtector/types', + link: '/documentation/manage-data/dataProtector/types', }, { text: 'Advanced', @@ -295,28 +293,28 @@ export function getSidebar() { items: [ { text: 'Advanced Configuration', - link: '/manage-data/dataProtector/advanced/advanced-configuration', + link: '/documentation/manage-data/dataProtector/advanced/advanced-configuration', }, { text: 'Sharing smart contract', - link: '/manage-data/dataProtector/advanced/dps-smart-contract', + link: '/documentation/manage-data/dataProtector/advanced/dps-smart-contract', }, { text: 'Apps whitelist', - link: '/manage-data/dataProtector/advanced/apps-whitelist', + link: '/documentation/manage-data/dataProtector/advanced/apps-whitelist', collapsed: true, items: [ { text: 'createAddOnlyAppWhitelist', - link: '/manage-data/dataProtector/advanced/apps-whitelist/createAddOnlyAppWhitelist', + link: '/documentation/manage-data/dataProtector/advanced/apps-whitelist/createAddOnlyAppWhitelist', }, { text: 'addAppToAddOnlyAppWhitelist', - link: '/manage-data/dataProtector/advanced/apps-whitelist/addAppToAddOnlyAppWhitelist', + link: '/documentation/manage-data/dataProtector/advanced/apps-whitelist/addAppToAddOnlyAppWhitelist', }, { text: 'getUserAddOnlyAppWhitelist', - link: '/manage-data/dataProtector/advanced/apps-whitelist/getUserAddOnlyAppWhitelist', + link: '/documentation/manage-data/dataProtector/advanced/apps-whitelist/getUserAddOnlyAppWhitelist', }, ], }, @@ -324,67 +322,65 @@ export function getSidebar() { }, { text: 'Migrate from v1 to v2', - link: '/manage-data/dataProtector/migrate-from-v1', + link: '/documentation/manage-data/dataProtector/migrate-from-v1', }, ], }, ], }, - ], - '/build-iapp/': [ { text: 'BUILD YOUR iAPP', items: [ - { text: '❓ What is an iApp?', link: '/build-iapp/what-is-iapp' }, + { text: '❓ What is an iApp?', link: '/documentation/build-iapp/what-is-iapp' }, { text: '📖 Guides', items: [ { text: 'Build and Deploy', - link: '/build-iapp/guides/build-&-deploy', + link: '/documentation/build-iapp/guides/build-&-deploy', }, { text: 'Manage Access', - link: '/build-iapp/guides/manage-access', + link: '/documentation/build-iapp/guides/manage-access', }, { text: 'Inputs and Outputs', - link: '/build-iapp/guides/inputs-and-outputs', + link: '/documentation/build-iapp/guides/inputs-and-outputs', }, { text: 'Using TDX', - link: '/build-iapp/guides/using-tdx', + link: '/documentation/build-iapp/guides/using-tdx', }, { text: 'How to Get and Decrypt Results', - link: '/build-iapp/guides/how-to-get-and-decrypt-results', + link: '/documentation/build-iapp/guides/how-to-get-and-decrypt-results', }, { text: 'Debugging', - link: '/build-iapp/guides/debugging', + link: '/documentation/build-iapp/guides/debugging', }, ], }, { text: '🤖 iApp Generator', - link: '/build-iapp/iapp-generator', + link: '/documentation/build-iapp/iapp-generator', items: [ { text: 'Getting Started', - link: '/build-iapp/iapp-generator/getting-started', + link: '/documentation/build-iapp/iapp-generator/getting-started', }, { text: 'Building your iApp', - link: '/build-iapp/iapp-generator/building-your-iexec-app', + link: '/documentation/build-iapp/iapp-generator/building-your-iexec-app', }, { text: 'Deserialize ProtectedData', - link: '/build-iapp/iapp-generator/deserializer', + link: '/documentation/build-iapp/iapp-generator/deserializer', collapsed: true, items: [ { text: 'getValue', - link: '/build-iapp/iapp-generator/deserializer/getValue', + link: '/documentation/build-iapp/iapp-generator/deserializer/getValue', }, ], }, @@ -392,35 +388,33 @@ export function getSidebar() { }, ], }, - ], - '/use-iapp/': [ { text: 'USE AN iAPP', items: [ - { text: '📝 Introduction', link: '/use-iapp/introduction' }, - { text: '🚀 Getting Started', link: '/use-iapp/getting-started' }, + { text: '📝 Introduction', link: '/documentation/use-iapp/introduction' }, + { text: '🚀 Getting Started', link: '/documentation/use-iapp/getting-started' }, { text: '📖 Guides', items: [ { text: 'Different Ways to Execute an iApp', - link: '/use-iapp/guides/different-ways-to-execute', + link: '/documentation/use-iapp/guides/different-ways-to-execute', }, { text: 'Add Inputs to the Execution', - link: '/use-iapp/guides/add-inputs-to-execution', + link: '/documentation/use-iapp/guides/add-inputs-to-execution', }, { text: 'Use iApp with Protected Data', - link: '/use-iapp/guides/use-iapp-with-protected-data', + link: '/documentation/use-iapp/guides/use-iapp-with-protected-data', }, { text: 'Find iApps to Use', - link: '/use-iapp/guides/find-iapps', + link: '/documentation/use-iapp/guides/find-iapps', }, { text: 'How to Pay the Executions', - link: '/use-iapp/guides/how-to-pay-executions', + link: '/documentation/use-iapp/guides/how-to-pay-executions', }, ], }, @@ -429,19 +423,19 @@ export function getSidebar() { items: [ { text: 'How to Pay for Web3Mail', - link: '/use-iapp/how-to-pay/how-to-pay-for-web3mail', + link: '/documentation/use-iapp/how-to-pay/how-to-pay-for-web3mail', }, { text: 'How to Pay for Web3Telegram', - link: '/use-iapp/how-to-pay/how-to-pay-for-web3telegram', + link: '/documentation/use-iapp/how-to-pay/how-to-pay-for-web3telegram', }, { text: 'Pricing Considerations', - link: '/use-iapp/how-to-pay/pricing-considerations', + link: '/documentation/use-iapp/how-to-pay/pricing-considerations', }, { text: 'Voucher', - link: '/use-iapp/how-to-pay/voucher', + link: '/documentation/use-iapp/how-to-pay/voucher', }, ], }, @@ -452,7 +446,7 @@ export function getSidebar() { items: [ { text: 'Getting Started', - link: '/use-iapp/web3mail/getting-started', + link: '/documentation/use-iapp/web3mail/getting-started', }, { text: 'Methods', @@ -460,32 +454,32 @@ export function getSidebar() { items: [ { text: 'fetchMyContacts', - link: '/use-iapp/web3mail/methods/fetchMyContacts', + link: '/documentation/use-iapp/web3mail/methods/fetchMyContacts', }, { text: 'fetchUserContacts', - link: '/use-iapp/web3mail/methods/fetchUserContacts', + link: '/documentation/use-iapp/web3mail/methods/fetchUserContacts', }, { text: 'sendEmail', - link: '/use-iapp/web3mail/methods/sendEmail', + link: '/documentation/use-iapp/web3mail/methods/sendEmail', }, ], }, { text: 'Advanced Configuration', - link: '/use-iapp/web3mail/advanced-configuration', + link: '/documentation/use-iapp/web3mail/advanced-configuration', }, ], }, { text: '💬 Web3Telegram', - link: '/use-iapp/web3telegram', + link: '/documentation/use-iapp/web3telegram', collapsed: true, items: [ { text: 'Getting Started', - link: '/use-iapp/web3telegram/getting-started', + link: '/documentation/use-iapp/web3telegram/getting-started', }, { text: 'Methods', @@ -493,49 +487,49 @@ export function getSidebar() { items: [ { text: 'fetchMyContacts', - link: '/use-iapp/web3telegram/methods/fetchMyContacts', + link: '/documentation/use-iapp/web3telegram/methods/fetchMyContacts', }, { text: 'fetchUserContacts', - link: '/use-iapp/web3telegram/methods/fetchUserContacts', + link: '/documentation/use-iapp/web3telegram/methods/fetchUserContacts', }, { text: 'sendTelegram', - link: '/use-iapp/web3telegram/methods/sendTelegram', + link: '/documentation/use-iapp/web3telegram/methods/sendTelegram', }, ], }, { text: 'Integration Guide', - link: '/use-iapp/web3telegram/integration-guide', + link: '/documentation/use-iapp/web3telegram/integration-guide', }, { text: 'Advanced Configuration', - link: '/use-iapp/web3telegram/advanced-configuration', + link: '/documentation/use-iapp/web3telegram/advanced-configuration', }, ], }, ], }, - ], - '/protocol/': [ { text: 'PROTOCOL', items: [ { text: '🔧  iExec SDK', - link: '/protocol/sdk', + link: '/documentation/protocol/sdk', }, { text: '⚙️  Workers & Workerpools', - link: '/protocol/workers', + link: '/documentation/protocol/workers', }, { text: '📖  Glossary', - link: '/protocol/glossary', + link: '/documentation/protocol/glossary', }, ], }, ], + '/guides/': [], + '/references/': [], } satisfies DefaultTheme.Sidebar; } diff --git a/.vitepress/theme/Layout.vue b/.vitepress/theme/Layout.vue index a1a43b4c..37d777a9 100644 --- a/.vitepress/theme/Layout.vue +++ b/.vitepress/theme/Layout.vue @@ -1,6 +1,6 @@ diff --git a/src/build-iapp/guides/build-&-deploy.md b/src/build-iapp/guides/build-&-deploy.md index b5106cba..e69de29b 100644 --- a/src/build-iapp/guides/build-&-deploy.md +++ b/src/build-iapp/guides/build-&-deploy.md @@ -1,362 +0,0 @@ ---- -title: Create and Deploy an iApp -description: - How to create a confidential iExec application and deploy it on iExec protocol ---- - -# Create and Deploy an iApp - -iApps (iExec Applications) are decentralized applications that run on the iExec -network. They leverage confidential computing to ensure data privacy and -security while providing scalable off-chain computation. - -## About iApp Generator - -Bootstrap TEE-compatible applications in minutes without any hardcoding skills, -iApp Generator handles all the low-level complexity for you. - -- **Select your project mode & language** - Get started with either a basic or - advanced setup, depending on your experience with the iExec framework. You can - use Python or JavaScript—whichever you prefer! -- **Develop your iApp effortlessly** - Write your application logic using - familiar programming languages while the generator handles all TEE-specific - configurations. -- **Access to TEEs easily** - No need to dive into low-level requirements, - create iApps that connect to TEEs in minutes. -- **Check and deploy iApps quickly** - iApp Generator checks that your iApp - complies with the iExec Framework and streamlines its deployment. - -## Prerequisites - -Before getting started, make sure you have the following installed: - -- **Node.js** (version 18 or higher) - [Download here](https://nodejs.org/) -- **Docker** - [Download here](https://www.docker.com/get-started) -- **Docker Hub account** - [Sign up here](https://hub.docker.com/) (required for - deployment) - -## Installation - -First, install the iApp Generator CLI tool using your preferred package manager: - -::: code-group - -```sh [npm] -npm install -g @iexec/iapp -``` - -```sh [yarn] -yarn global add @iexec/iapp -``` - -```sh [pnpm] -pnpm add -g @iexec/iapp -``` - -```sh [bun] -bun add -g @iexec/iapp -``` - -::: - -## Quick Start - -Once installed, you can create and deploy your first iApp. The CLI will guide -you through an interactive setup process to configure your project name, -programming language, and template: - - - -After the interactive setup, continue with development and deployment: - -## Development and Testing - -Navigate to your project and run tests locally to simulate the TEE environment. -The CLI will build a Docker image, run your app, and show you the results: - - - -## Deployment - -After your tests pass and the package is built, you can deploy your iApp to a -supported network. During deployment, you'll enter your DockerHub credentials, -specify your app version, and push both standard and TEE-compatible images: - - - -## Real Examples - -Here are some real-world examples of iApps to help you understand how they work -in practice. - -### Email Notification iApp - -This iApp lets you send updates to your contacts without ever seeing their email -addresses, privacy is preserved by design. - -::: code-group - -```js [Node.js] -/* User runs: "Send updates to my contacts about my project" */ -const contacts = loadProtectedData(); // User's protected contact list -contacts.forEach((contact) => { - sendEmail(contact, projectUpdateMessage); -}); -// → Emails sent directly, you never see the addresses -``` - -```python [Python] -# User runs: "Send updates to my contacts about my project" -contacts = load_protecteddata() # User's protected contact list -for contact in contacts: - send_email(contact, project_update_message) -# → Emails sent directly, you never see the addresses -``` - -::: - -### Oracle Update iApp - -This iApp securely updates a price oracle using private trading data, ensuring -sensitive information stays confidential. - -::: code-group - -```js [Node.js] -// User runs: "Update price oracle with my private trading data" -const tradingData = loadProtectedData(); // User's protected trading history -const averagePrice = calculateWeightedAverage(tradingData); -updateOracleContract(averagePrice); -// → Oracle updated with real data, trading history stays private -``` - -```python [Python] -# User runs: "Update price oracle with my private trading data" -trading_data = load_protecteddata() # User's protected trading history -average_price = calculate_weighted_average(trading_data) -update_oracle_contract(average_price) -# → Oracle updated with real data, trading history stays private -``` - -::: - -### Automated Transactions iApp - -This iApp automates monthly payments using protected payment details, so -financial information remains private. - -::: code-group - -```js [Node.js] -// User runs: "Automate payments every month" -const paymentInfo = loadProtectedData(); // User's payment details -for (let month = 0; month < 12; month++) { - processPayment(paymentInfo); -} -// → Payments processed, payment details stay private -``` - -```python [Python] -# User runs: "Automate payments every month" -payment_info = load_protecteddata() # User's payment details -for month in range(12): - process_payment(payment_info) -# → Payments processed, payment details stay private -``` - -::: - - diff --git a/src/build-iapp/guides/debugging.md b/src/build-iapp/guides/debugging.md deleted file mode 100644 index 221fa3d4..00000000 --- a/src/build-iapp/guides/debugging.md +++ /dev/null @@ -1,175 +0,0 @@ ---- -title: Debugging your iApp -description: - Troubleshoot and optimize your iApp execution in the TEE environment ---- - -# 🐛 Debugging - -**When your iApp doesn't work as expected, debugging in the TEE environment -requires specific techniques.** This guide helps you identify issues and -optimize your iApp's performance. - -## Task Execution Lifecycle - -Understanding how your task progresses through the iExec network: - -### Key Stages - -1. **Deal Creation** - Orders matched, funds locked -2. **Task Initialization** - Workers selected for execution -3. **iApp Execution** - Your code runs inside TEE -4. **Result Processing** - Results encrypted and uploaded -5. **Task Completion** - Results available for download - -**Most failures happen during stages 2-4** - -## Monitoring your Tasks - -### iExec Explorer - -Track your tasks at [explorer.iex.ec](https://explorer.iex.ec): - -- Search by `taskId` or deal ID -- Check status: `PENDING` → `ACTIVE` → `COMPLETED/FAILED` -- View error messages if execution fails - -### Status in Code - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const response = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - onStatusUpdate: ({ title, isDone }) => { - console.log(`Status: ${title} - Done: ${isDone}`); - }, -}); -``` - -## Debug Commands - -### Local Testing - -```bash -# Test your iApp locally -iapp test --args "model=bert threshold=0.8" -iapp test --secrets "key1=value1,key2=value2" - -# Mock protected data for testing -iapp mock protectedData -iapp test --protectedData "mock_name" -``` - -### Remote Debugging - -```bash -# Deploy and run -iapp deploy -iapp run - -# Debug failed executions -iapp debug -``` - -### Task Information - -```bash -# View task details -iexec task show - -# Download results (if completed) -iexec task show --download -``` - -## Common Issues - -### ⏱️ **Task Timeout** - -- **Cause**: Code takes too long to execute -- **Solution**: Optimize algorithms, reduce input sizes, use appropriate task - category - -### 💾 **Memory Issues** - -- **Cause**: Loading large files, memory leaks, TEE constraints -- **Solution**: Process data in chunks, use streaming, optimize memory usage - -### 📁 **Input/Output Problems** - -- **Cause**: Wrong file paths, missing `computed.json` -- **Solution**: Always create `computed.json`, verify environment variables - -```python -# Always create computed.json -import json, os -computed = {"deterministic-output-path": f"{os.environ['IEXEC_OUT']}/result.json"} -with open(f"{os.environ['IEXEC_OUT']}/computed.json", 'w') as f: - json.dump(computed, f) -``` - -### ⚠️ **Dataset Type Unmatching** - -- **Cause**: The dataset type specified in the frontend (protectData) does not - match with the dataset type specified in the iApp -- **Solution**: Check both dataset types - -## Best Practices - -### 🔍 **Input Validation** - -```python -import os, sys - -# Check required environment variables -if not os.environ.get('IEXEC_IN') or not os.environ.get('IEXEC_OUT'): - print("ERROR: Missing IEXEC_IN or IEXEC_OUT") - sys.exit(1) - -# Validate arguments -if len(sys.argv) < 2: - print("ERROR: Missing required arguments") - sys.exit(1) -``` - -### 📝 **Clear Error Messages** - -```python -try: - # Your processing logic - result = process_data(data) -except Exception as e: - print(f"ERROR: Processing failed: {str(e)}") - sys.exit(1) -``` - -### 🔒 **Safe File Operations** - -```python -import os, json - -# Always ensure output directory exists -iexec_out = os.environ['IEXEC_OUT'] -os.makedirs(iexec_out, exist_ok=True) - -# Write results safely -try: - with open(f"{iexec_out}/result.json", 'w') as f: - json.dump(result_data, f) -except Exception as e: - print(f"ERROR: Failed to write results: {e}") - sys.exit(1) -``` - -## What's Next? - -Continue improving your iApps: - -- **[Inputs and Outputs](/build_iapp/guides/inputs-and-outputs)** - Handle data - in TEE -- **[How to Get and Decrypt Results](/build_iapp/guides/how-to-get-and-decrypt-results)** - - Retrieve results diff --git a/src/build-iapp/guides/how-to-get-and-decrypt-results.md b/src/build-iapp/guides/how-to-get-and-decrypt-results.md deleted file mode 100644 index 33ea7021..00000000 --- a/src/build-iapp/guides/how-to-get-and-decrypt-results.md +++ /dev/null @@ -1,314 +0,0 @@ ---- -title: How to Get and Decrypt Results -description: Download and decrypt iApp execution results from completed tasks ---- - -# 📦 How to Get and Decrypt Results - -**When an iApp execution completes, you need to retrieve and decrypt the -results.** This guide shows you how to download task results and decrypt them to -access the actual output files. - -Understanding the result retrieval process is essential for building -user-friendly applications with iExec. - -## Understanding Results Structure - -### Deal → Task → Result Flow - -**Every execution follows this hierarchy**: - -``` -Deal (agreement between parties) -├── Task 1 (individual execution instance) -│ └── Result (encrypted output files) -├── Task 2 -│ └── Result -└── ... -``` - -- **Deal**: Contains one or more tasks from your execution request -- **Task**: Individual computation instance with unique `taskId` -- **Result**: Encrypted ZIP file containing your iApp's output files - -### Result Accessibility - -**Results are publicly downloadable** but may be encrypted: - -- ✅ **Anyone can download** the result file from IPFS -- 🔒 **Only authorized parties can decrypt** the contents -- 📁 **Results contain** all files from `IEXEC_OUT` directory -- ⚡ **Available immediately** after task completion - -## Downloading Results - -### Using iExec SDK CLI - -**Get task information and download**: - -```bash -# Check task status and get result info -iexec task show - -# Download encrypted result -iexec task show --download my-result - -# Extract downloaded files -unzip my-result.zip -d my-result/ -ls my-result/ -``` - -**Get task ID from deal**: - -```bash -# If you only have the deal ID -iexec deal show - -# Lists all tasks in the deal with their IDs -``` - -### Using DataProtector SDK - -**Integrated download and decryption**: - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -// Get result from completed task -const result = await dataProtectorCore.getResultFromCompletedTask({ - taskId: '0x123abc...', // Your task ID -}); - -console.log('Result downloaded and decrypted:', result); -``` - -## Decrypting Results - -### Automatic Decryption with DataProtector - -**The easiest way** - decryption happens automatically: - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -// Execute and get results in one flow -const processResponse = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', -}); - -console.log('Task ID:', processResponse.taskId); - -// Get decrypted result -const result = await dataProtectorCore.getResultFromCompletedTask({ - taskId: processResponse.taskId, -}); - -// Result is automatically decrypted ArrayBuffer -const resultText = new TextDecoder().decode(result.result); -console.log('Decrypted result:', resultText); -``` - -### Manual Decryption with CLI - -**If you downloaded manually**: - -```bash -# Download the encrypted result -iexec task show --download my-result - -# Decrypt using your wallet (must be the beneficiary) -iexec result decrypt my-result.zip --force - -# Extract decrypted files -unzip decrypted-result.zip -d final-result/ -cat final-result/result.txt -``` - -## Result File Structure - -### What's Inside a Result - -**Typical result contents**: - -``` -result.zip -├── computed.json # Mandatory metadata file -├── result.txt # Your main output -├── analysis.json # Additional outputs -├── logs.txt # Optional logs -└── metadata.json # Optional metadata -``` - -### `computed.json` Structure - -**Always present in every result**: - -```json -{ - "deterministic-output-path": "/iexec_out/result.txt", - "execution-timestamp": "2024-01-15T10:30:00Z", - "app-version": "1.0.0" -} -``` - -**Key fields**: - -- `deterministic-output-path`: Main result file path -- `execution-timestamp`: When the computation completed -- Custom fields added by your iApp - -## Common Patterns - -### React Application Example - -**Integrate result retrieval in your frontend**: - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -async function downloadResult(taskId: string) { - try { - const resultResponse = await dataProtectorCore.getResultFromCompletedTask({ - taskId, - }); - - // Convert to text or JSON based on your result format - const resultText = new TextDecoder().decode(resultResponse.result); - const resultJson = JSON.parse(resultText); - - return resultJson; - } catch (error) { - console.error('Failed to download result:', error); - throw error; - } -} - -// Usage example -const taskId = '0x123abc...'; -const result = await downloadResult(taskId); -console.log('Analysis Result:', result); -``` - -### Node.js Backend Example - -**Server-side result processing**: - -```javascript -const { - IExecDataProtectorCore, - getWeb3Provider, -} = require('@iexec/dataprotector'); - -async function processTaskResult(taskId) { - const web3Provider = getWeb3Provider(process.env.PRIVATE_KEY); - const dataProtectorCore = new IExecDataProtectorCore(web3Provider); - - try { - // Get the result - const resultBuffer = await dataProtectorCore.getResultFromCompletedTask({ - taskId, - }); - - // Parse the result based on your format - const resultText = new TextDecoder().decode(resultBuffer); - - // If your result is JSON - const analysisResult = JSON.parse(resultText); - - // Store in database, send notifications, etc. - await saveToDatabase(taskId, analysisResult); - await notifyUser(analysisResult); - - return analysisResult; - } catch (error) { - console.error('Result processing failed:', error); - throw error; - } -} -``` - -## Troubleshooting - -### Common Issues - -**❌ "Task not completed"** - -``` -Error: Task is still running -``` - -**Solution**: Wait for task completion or check status with -`iexec task show ` - -**❌ "Decryption failed"** - -``` -Error: Failed to decrypt result -``` - -**Solutions**: - -- Ensure you're using the correct wallet (beneficiary) -- Check if result was actually encrypted -- Verify task completed successfully - -**❌ "Result not found"** - -``` -Error: Result not available -``` - -**Solutions**: - -- Check task status - it might have failed -- Verify the task ID is correct -- Wait for result upload to complete - -### Checking Task Status - -**Before downloading, verify completion**: - -```bash -# Check if task is completed -iexec task show - -# Look for status: "COMPLETED" -# And result information in the output -``` - -### Result Encryption Status - -**Not all results are encrypted**: - -- 🔒 **Encrypted**: When `beneficiary` is set in the request -- 📂 **Plain**: When no beneficiary specified (public results) -- ✅ **DataProtector handles both** automatically - -## What's Next? - -**You can now retrieve and decrypt iApp results!** - -Integrate result handling into your applications: - -- **[Inputs and Outputs](/build_iapp/guides/inputs-and-outputs)** - Understand - what your iApp can output -- **[Debugging Your iApp](/build_iapp/guides/debugging-your-iapp)** - - Troubleshoot execution issues -- **[App Access Control and Pricing](/build_iapp/guides/orders)** - Control who - can run your iApp - -### Advanced Topics - -- **[DataProtector SDK](/manage_data/dataProtector)** - Complete SDK - documentation -- **[SDK Deep Dive](/deep_dive/sdk)** - Advanced result handling techniques diff --git a/src/build-iapp/guides/inputs-and-outputs.md b/src/build-iapp/guides/inputs-and-outputs.md deleted file mode 100644 index 794e6e84..00000000 --- a/src/build-iapp/guides/inputs-and-outputs.md +++ /dev/null @@ -1,641 +0,0 @@ ---- -title: Inputs and Outputs -description: - Understand the different input types and output formats for iApps in the TEE - environment ---- - -# 📥📤 Inputs and Outputs - -**Your iApp runs inside a secure TEE environment with access to different types -of inputs.** Understanding what data you can access, how to access it, and when -to use each type is crucial for building effective privacy-preserving -applications. - -This guide covers all input types available to your iApp and how to generate -proper outputs that users can retrieve and decrypt. - -## Development vs User Execution - -**Two perspectives on inputs:** - -- 🔧 **As a developer** (using iApp Generator): You write code to access inputs - from the TEE environment -- 👤 **As a user** (using DataProtector): You provide inputs when executing the - iApp via `processProtectedData()` - -This guide shows both perspectives for each input type. - -## Input Types Overview - -When your iApp executes in the TEE, it can access four different types of -inputs: - -| Input Type | Visibility | Use Case | Access Method | -| --------------------- | ----------- | ------------------------ | ---------------------- | -| **Args** | Public | Configuration parameters | Command line arguments | -| **Input Files** | Public URLs | Large datasets, models | Download from URLs | -| **Requester Secrets** | Private | API keys, credentials | Environment variables | -| **Protected Data** | Encrypted | User's sensitive data | File system in TEE | - -## 1. Arguments (Args) - -**What they are:** Public parameters passed to your iApp during execution. - -**When to use:** Configuration settings, model parameters, processing options - -anything that doesn't need to be secret. - -::: danger - -Security Warning Args are **completely public** and visible on the blockchain -explorer. Never pass sensitive information through args. - -::: - -### How to Access Args - -In your iApp Generator project, args are passed as command-line arguments: - -::: code-group - -```python [Python] -import sys - -# Access args from command line -args = sys.argv[1:] # Skip first arg (script name) - -# Example: iapp run myapp --args "model=bert threshold=0.8" -if len(args) >= 2: - model_name = args[0] # "model=bert" - threshold = args[1] # "threshold=0.8" -``` - -```javascript [JavaScript] -// Access args from command line -const args = process.argv.slice(2); // Skip node and script name - -// Example: iapp run myapp --args "model=bert threshold=0.8" -if (args.length >= 2) { - const modelName = args[0]; // "model=bert" - const threshold = args[1]; // "threshold=0.8" -} -``` - -::: - -### How Users Provide Args - -Users pass args through the DataProtector `processProtectedData()` call: - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -// User provides args when executing your iApp -const response = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - args: 'model=sentiment-bert temperature=0.7 format=json', // Public arguments -}); -``` - -### Example Use Cases - -- Model configuration: `"model=sentiment-bert temperature=0.7"` -- Processing options: `"format=json output_size=small"` -- Analysis parameters: `"start_date=2024-01-01 end_date=2024-12-31"` - -## 2. Input Files - -**What they are:** Files downloaded from public URLs during iApp execution. - -**When to use:** Large datasets, ML models, reference files that don't contain -sensitive information. - -### How to Access Input Files - -Files are downloaded to the `IEXEC_INPUT_FILES_FOLDER` directory: - -::: code-group - -```python [Python] -import os - -# Get the input files directory -input_dir = os.environ.get('IEXEC_INPUT_FILES_FOLDER', './input') - -# List all downloaded files -for filename in os.listdir(input_dir): - file_path = os.path.join(input_dir, filename) - - # Process your file - with open(file_path, 'r') as f: - content = f.read() - print(f"Loaded file: {filename}") -``` - -```javascript [JavaScript] -const fs = require('fs'); -const path = require('path'); - -// Get the input files directory -const inputDir = process.env.IEXEC_INPUT_FILES_FOLDER || './input'; - -// List all downloaded files -fs.readdirSync(inputDir).forEach((filename) => { - const filePath = path.join(inputDir, filename); - - // Process your file - const content = fs.readFileSync(filePath, 'utf8'); - console.log(`Loaded file: ${filename}`); -}); -``` - -::: - -### How Users Provide Input Files - -Users specify input files when executing your iApp: - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -// User provides input files via DataProtector -const response = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - inputFiles: [ - 'https://example.com/sentiment-model.pkl', - 'https://myapp.com/config.json', - ], -}); -``` - -### Example Use Cases - -- ML model files: `"https://example.com/sentiment-model.pkl"` -- Reference datasets: `"https://data.gov/reference-corpus.csv"` -- Configuration files: `"https://myapp.com/config.json"` - -### Limits and Best Practices - -- **File size**: Limited by TEE enclave memory (typically several GB max) -- **Memory constraint**: Files are loaded into enclave memory - large files may - cause out-of-memory errors -- **Format**: Any format (binary, text, compressed) -- **URLs**: Must be direct download links (not web pages) -- **Security**: Files are public - don't use for sensitive data -- **Best practice**: Keep input files under 1-2GB for reliable execution - -## 3. Requester Secrets - -**What they are:** Confidential credentials provided by the user running your -iApp. - -**When to use:** API keys, database credentials, authentication tokens that the -user needs to provide. - -### How to Access Requester Secrets - -Secrets are available as environment variables with the pattern -`IEXEC_REQUESTER_SECRET_`: - -::: code-group - -```python [Python] -import os - -# Access requester secrets by index -api_key = os.environ.get('IEXEC_REQUESTER_SECRET_1') -db_password = os.environ.get('IEXEC_REQUESTER_SECRET_2') - -if api_key: - # Use the API key for external service calls - headers = {'Authorization': f'Bearer {api_key}'} - # Make API calls... -else: - print("No API key provided") -``` - -```javascript [JavaScript] -// Access requester secrets by index -const apiKey = process.env.IEXEC_REQUESTER_SECRET_1; -const dbPassword = process.env.IEXEC_REQUESTER_SECRET_2; - -if (apiKey) { - // Use the API key for external service calls - const headers = { Authorization: `Bearer ${apiKey}` }; - // Make API calls... -} else { - console.log('No API key provided'); -} -``` - -::: - -### How Users Provide Inputs - -Users provide all inputs when executing your iApp via DataProtector: - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -// Example: User executes your iApp with all input types -const processProtectedDataResponse = - await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', // Protected data address - app: '0x456def...', // Your iApp address - args: 'model=bert threshold=0.8', // Public arguments - inputFiles: [ - // Public input files - 'https://example.com/model.pkl', - 'https://example.com/config.json', - ], - secrets: { - // Requester secrets - 1: 'sk-1234567890abcdef', // API key - 2: 'mydbpassword123', // DB password - }, - }); -``` - -## 4. Protected Data - -**What it is:** Encrypted user data that's only decrypted inside your TEE -environment. - -**When to use:** Processing user's sensitive information like personal data, -financial records, health data. - -### How to Access Protected Data - -Protected data is available in the `IEXEC_IN` directory as decrypted files: - -::: code-group - -```python [Python] -import os -import json - -# Get the input directory -iexec_in = os.environ['IEXEC_IN'] - -# Protected data is decrypted and available as files -try: - # For single protected data - with open(f"{iexec_in}/protectedData", 'r') as f: - data = json.load(f) - - # Access user's sensitive data - user_email = data.get('email') - user_preferences = data.get('preferences') - - print(f"Processing data for user: {user_email}") - -except FileNotFoundError: - print("No protected data provided") -``` - -```javascript [JavaScript] -const fs = require('fs'); -const path = require('path'); - -// Get the input directory -const iexecIn = process.env.IEXEC_IN; - -try { - // Protected data is decrypted and available as files - const dataPath = path.join(iexecIn, 'protectedData'); - const data = JSON.parse(fs.readFileSync(dataPath, 'utf8')); - - // Access user's sensitive data - const userEmail = data.email; - const userPreferences = data.preferences; - - console.log(`Processing data for user: ${userEmail}`); -} catch (error) { - console.log('No protected data provided'); -} -``` - -::: - -### How Users Provide Protected Data - -Users specify the protected data address when executing your iApp: - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -// User provides their protected data for processing -const response = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', // Address of their protected data - app: '0x456def...', // Your iApp address -}); -``` - -### Working with Multiple Protected Datasets - -When multiple datasets are provided, they're available as separate files: - -::: code-group - -```python [Python] -import os - -iexec_in = os.environ['IEXEC_IN'] - -# List all available protected datasets -for filename in os.listdir(iexec_in): - if filename.startswith('dataset_'): - with open(f"{iexec_in}/{filename}", 'r') as f: - dataset = json.load(f) - print(f"Processing dataset: {filename}") -``` - -::: - -### Memory Limitations - -::: warning - -TEE Memory Constraints Protected data is decrypted and loaded into TEE enclave -memory. Very large datasets (>1-2GB) may cause out-of-memory errors. Consider -data preprocessing or chunking for large datasets. - -::: - -## Creating Outputs - -Your iApp must generate outputs in the `IEXEC_OUT` directory. **Every iApp must -create a `computed.json` file** with metadata about the computation. - -### Basic Output Structure - -::: code-group - -```python [Python] -import os -import json - -# Get output directory -iexec_out = os.environ['IEXEC_OUT'] - -# Create your result file -result_data = { - "analysis": "positive sentiment", - "confidence": 0.92, - "processed_at": "2024-01-15T10:30:00Z" -} - -# Save main result -with open(f"{iexec_out}/result.json", 'w') as f: - json.dump(result_data, f) - -# REQUIRED: Create `computed.json` metadata -computed_metadata = { - "deterministic-output-path": f"{iexec_out}/result.json", - "execution-timestamp": "2024-01-15T10:30:00Z", - "app-version": "1.0.0" -} - -with open(f"{iexec_out}/computed.json", 'w') as f: - json.dump(computed_metadata, f) -``` - -```javascript [JavaScript] -const fs = require('fs'); -const path = require('path'); - -// Get output directory -const iexecOut = process.env.IEXEC_OUT; - -// Create your result file -const resultData = { - analysis: 'positive sentiment', - confidence: 0.92, - processed_at: '2024-01-15T10:30:00Z', -}; - -// Save main result -fs.writeFileSync( - path.join(iexecOut, 'result.json'), - JSON.stringify(resultData, null, 2) -); - -// REQUIRED: Create computed.json metadata -const computedMetadata = { - 'deterministic-output-path': path.join(iexecOut, 'result.json'), - 'execution-timestamp': '2024-01-15T10:30:00Z', - 'app-version': '1.0.0', -}; - -fs.writeFileSync( - path.join(iexecOut, 'computed.json'), - JSON.stringify(computedMetadata, null, 2) -); -``` - -::: - -### Output Best Practices - -1. **Always create `computed.json`** - This is mandatory -2. **Use descriptive filenames** - `analysis_result.json` vs `output.txt` -3. **Include metadata** - Timestamps, versions, parameters used -4. **Structure your data** - Use JSON for structured results -5. **Keep files reasonable** - Large outputs increase retrieval time and may hit - memory limits -6. **Memory awareness** - TEE enclave memory is limited, avoid generating - multi-GB outputs - -### Example: Multi-file Output - -```python -import os -import json - -iexec_out = os.environ['IEXEC_OUT'] - -# Create multiple output files -summary = {"total_processed": 1000, "success_rate": 0.95} -with open(f"{iexec_out}/summary.json", 'w') as f: - json.dump(summary, f) - -# Create a detailed report -with open(f"{iexec_out}/detailed_report.txt", 'w') as f: - f.write("Detailed analysis results...\n") - -# Create visualization data -chart_data = {"labels": ["A", "B", "C"], "values": [10, 20, 30]} -with open(f"{iexec_out}/chart_data.json", 'w') as f: - json.dump(chart_data, f) - -# Required metadata file -computed = { - "deterministic-output-path": f"{iexec_out}/summary.json", - "additional-files": [ - f"{iexec_out}/detailed_report.txt", - f"{iexec_out}/chart_data.json" - ] -} -with open(f"{iexec_out}/computed.json", 'w') as f: - json.dump(computed, f) -``` - -## Testing Inputs Locally - -Use iApp Generator to test different input types: - -```bash -# Test with different input types -iapp test --args "model=bert threshold=0.8" # Test with arguments -iapp test --inputFiles "https://example.com/data.json" # Test with input files -iapp test --secrets "key1=value1,key2=value2" # Test with secrets - -# Mock protected data for testing -iapp mock protectedData # Generate sample protected data - -# Test your iApp locally with mocked protected data -iapp test --protectedData "mock_name" -``` - -## Common Patterns - -### 🔍 **Data Analysis iApp** - -**User execution (DataProtector):** - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -// User runs your data analysis iApp -const response = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', // Their business data - app: '0x456def...', // Your analysis iApp - args: 'analysis_type=sentiment period=monthly', - secrets: { 1: 'api-key-for-external-service' }, -}); -``` - -**Your iApp code (Python):** - -```python -# Access inputs in your iApp -args = sys.argv[1:] # Processing parameters -api_key = os.environ.get('IEXEC_REQUESTER_SECRET_1') # User's API access -protected_data = load_protected_data() # User's sensitive data - -# Process and output results -results = analyze_data(protected_data, args, api_key) -save_results(results) -``` - -### 🤖 **AI Model iApp** - -**User execution (DataProtector):** - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -// User runs your AI model with their data -const response = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', // Their personal data - app: '0x456def...', // Your AI model iApp - inputFiles: ['https://example.com/model-weights.pkl'], - args: 'model_type=classification confidence_threshold=0.8', -}); -``` - -**Your iApp code (Python):** - -```python -# Load model from input files -model = load_model_from_inputs() - -# Get user data to process -user_data = load_protected_data() - -# Run inference -predictions = model.predict(user_data) - -# Return encrypted results -save_encrypted_results(predictions) -``` - -### 📊 **Report Generator iApp** - -**User execution (DataProtector):** - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -// User generates a report from their business data -const response = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', // Their business data - app: '0x456def...', // Your report generator iApp - args: 'report_type=quarterly format=pdf include_charts=true', - inputFiles: ['https://example.com/company-template.xlsx'], -}); -``` - -**Your iApp code (Python):** - -```python -# Get configuration from args -report_type = get_arg('type', default='summary') - -# Access user's business data -business_data = load_protected_data() - -# Generate report -report = generate_report(business_data, report_type) -save_report(report) -``` - -## Output Retrieval - -Once your iApp completes execution, users can retrieve and decrypt the results: - -→ **Learn how users get results**: Check our -[How to Get and Decrypt Results](/build_iapp/guides/how-to-get-and-decrypt-results) -guide for the complete user workflow. - -## What's Next? - -**You now understand all input types and output requirements!** - -Continue building with these guides: - -- **[App Access Control and Pricing](/build_iapp/guides/orders)** - Control who - can use your iApp -- **[Debugging Your iApp](/build_iapp/guides/debugging-your-iapp)** - - Troubleshoot execution issues -- **[How to Get and Decrypt Results](/build_iapp/guides/how-to-get-and-decrypt-results)** - - User-side result handling - -### Technical Deep Dive - -- **[SDK Deep Dive](/deep_dive/sdk)** - Advanced SDK concepts -- **[Application I/O Protocol Docs](https://protocol.docs.iex.ec/for-developers/application-io)** - - Low-level protocol details diff --git a/src/build-iapp/guides/manage-access.md b/src/build-iapp/guides/manage-access.md deleted file mode 100644 index fad6bb19..00000000 --- a/src/build-iapp/guides/manage-access.md +++ /dev/null @@ -1,303 +0,0 @@ ---- -title: App Access Control and Pricing -description: Control who can use your iApp and set pricing with app orders ---- - -# 💰 Manage Access - -**Orders control who can use your iApp and under what conditions.** Once your -iApp is deployed with iApp Generator, you need to create app orders to make it -accessible to users and define your governance rules. - -Think of orders as **usage contracts** - they define pricing, access -restrictions, and execution conditions for your application. - -## What is an Order? - -An **app order** is a signed contract that defines the usage conditions for your -iApp: - -- **Price per execution** (in nRLC) -- **Number of authorized uses** -- **Access restrictions** (specific users, workerpools) -- **TEE configuration** (for confidential applications) - -::: tip - -Currently, order management is not yet available in iApp Generator. This guide -shows you how to use the iExec SDK CLI to create and manage your app orders. - -For complete SDK documentation, check the -[iExec SDK GitHub repository](https://github.com/iExecBlockchainComputing/iexec-sdk). - -::: - -## How Orders Work - -Here's the simplified process: - -1. **You create an app order** with your conditions (price, restrictions, etc.) -2. **You sign the order** with your wallet -3. **You publish the order** on the iExec marketplace -4. **Users can discover** and execute your iApp according to your conditions -5. **You automatically receive** payment in RLC for each execution - -``` -Deployed iApp + Signed App Order = Application accessible on iExec -``` - -## App Order Example - -Here's an example app order for a sentiment analysis iApp: - -```json -{ - "app": "0x123abc...", // Your iApp address - "appprice": "1000000000", // 1 RLC per execution - "volume": "100", // 100 authorized uses - "tag": "0x0000000000000000000000000000000000000000000000000000000000000003", // TEE required - "datasetrestrict": "0x0000000000000000000000000000000000000000", - "workerpoolrestrict": "0x0000000000000000000000000000000000000000", - "requesterrestrict": "0x0000000000000000000000000000000000000000" -} -``` - -## Creating an App Order - -### Step 1: Install the iExec SDK - -Since iApp Generator doesn't handle orders yet, you need to use the iExec SDK -CLI: - -::: code-group - -```bash [npm] -npm install -g iexec -``` - -```bash [yarn] -yarn global add iexec -``` - -::: - -Verify the installation: - -```bash -iexec --version -iexec --help -``` - -### Step 2: Configure your iExec Project - -In your iApp Generator project folder, initialize the iExec configuration: - -```bash -# In your iApp Generator project folder -iexec init --skip-wallet -``` - -This creates the necessary configuration files: - -- `iexec.json` - Project configuration -- `chain.json` - Blockchain configuration - -### Step 3: Configure your Wallet - -If you don't have an iExec wallet yet: - -```bash -iexec wallet create -``` - -Or import an existing wallet: - -```bash -iexec wallet import -``` - -::: tip iApp Generator Users - -If you used iApp Generator, you already have an `iexecconfig.json` file with a -generated private key. You can use this existing private key to initialize your -wallet: - -```bash -# Extract the private key from your `iexecconfig.json` -iexec wallet import -``` - -::: - -Check your wallet: - -```bash -iexec wallet show -``` - -### Step 4: Create the App Order - -Initialize the app order: - -```bash -iexec order init --app -``` - -This adds an `apporder` section to your `iexec.json`. Edit the parameters -according to your needs: - -```json -{ - "apporder": { - "app": "0xYourAppAddress", - "appprice": "1000000000", - "volume": "100", - "tag": "0x0000000000000000000000000000000000000000000000000000000000000003", - "datasetrestrict": "0x0000000000000000000000000000000000000000", - "workerpoolrestrict": "0x0000000000000000000000000000000000000000", - "requesterrestrict": "0x0000000000000000000000000000000000000000" - } -} -``` - -### Step 5: Sign and Publish the Order - -Sign your app order with your wallet: - -```bash -iexec order sign --app -``` - -Publish the order on the marketplace: - -```bash -iexec order publish --app -``` - -Your iApp is now accessible according to the conditions you defined! - -## Managing Orders - -### View Published Orders - -Check active orders for your app: - -```bash -iexec orderbook app -``` - -### Modify an Order - -To change conditions, create a new order with new parameters. - -### Cancel an Order - -Remove an order from the marketplace: - -```bash -iexec order unpublish --app -``` - -Completely invalidate an order: - -```bash -iexec order cancel --app -``` - -### 🛡️ **Confidential App (TEE Required)** - -```json -{ - "appprice": "2000000000", - "volume": "500", - "tag": "0x0000000000000000000000000000000000000000000000000000000000000003" -} -``` - -## App Order Parameters - -Here's the detailed description of each parameter: - -### `app` - -**Description:** Ethereum address of your deployed iApp - -**Example:** `"0x123abc456def..."` - -### `appprice` - -**Description:** Price to charge per execution (in nano RLC - nRLC) - -**Common values:** - -- `"0"` - Free -- `"1000000000"` - 1 RLC per execution -- `"500000000"` - 0.5 RLC per execution - -::: tip - -1 RLC = 1,000,000,000 nRLC (10^9) - -::: - -### `volume` - -**Description:** Number of authorized executions (decrements with each use) - -**Examples:** - -- `"1"` - Single use -- `"100"` - Limited campaign -- `"10000"` - Virtually unlimited usage - -### `tag` - -**Description:** Specifies the required execution environment - -**Supported values:** - -| Value | Description | -| -------------------------------------------------------------------- | -------------------- | -| `0x0000000000000000000000000000000000000000000000000000000000000000` | Standard execution | -| `0x0000000000000000000000000000000000000000000000000000000000000003` | TEE required (Scone) | - -### Access Restrictions - -All restrictions use `0x0000000000000000000000000000000000000000` to indicate -"no restriction". - -#### `datasetrestrict` - -**Description:** Restrict usage to a specific dataset - -**Typical usage:** `"0x0000000000000000000000000000000000000000"` (no -restriction) - -#### `workerpoolrestrict` - -**Description:** Restrict execution to a specific workerpool - -**Example:** `"prod-v8-bellecour.main.pools.iexec.eth"` for the main workerpool - -#### `requesterrestrict` - -**Description:** Restrict usage to a specific user - -**Typical usage:** `"0x0000000000000000000000000000000000000000"` (open to all) - -## What's Next? - -**Your iApp is now accessible with custom conditions!** - -Next steps: - -- **Monitor executions**: Track usage with `iexec task show` -- **Adjust pricing**: Create new orders based on demand -- **Manage revenue**: Check your earnings with `iexec account show` - -### Technical Deep Dive - -- **[iExec SDK Documentation](https://github.com/iExecBlockchainComputing/iexec-sdk)** - - Complete CLI reference -- **[Official Orders Documentation](https://protocol.docs.iex.ec/for-developers/advanced/manage-your-apporders)** - - Protocol-level order management diff --git a/src/build-iapp/guides/using-tdx.md b/src/build-iapp/guides/using-tdx.md deleted file mode 100644 index 64774d37..00000000 --- a/src/build-iapp/guides/using-tdx.md +++ /dev/null @@ -1,192 +0,0 @@ ---- -title: Using TDX (Experimental) -description: - Enable Intel TDX for enhanced TEE security in iApps - experimental feature ---- - -# 🛡️ Using TDX (Experimental) - -:::danger ⚠️ EXPERIMENTAL FEATURE - -**TDX support is currently experimental and should NOT be used in production.** -This feature is provided for testing and development purposes only. Expect -instabilities, limited compatibility, and potential outages. - -::: - -**Intel TDX (Trust Domain Extensions) is the next generation of TEE -technology.** This guide shows you how to enable TDX in your iApps and -understand the differences from the default SGX implementation. - -## What is TDX? - -**TDX (Trust Domain Extensions)** is Intel's newer confidential computing -technology, different from the default SGX implementation. - -### SGX vs TDX Differences - -**SGX (Current Default)**: - -- ✅ **Production ready** and stable -- ✅ **Widely supported** by iExec workers -- ❌ **Memory limitations** in TEE environment - -**TDX (Experimental)**: - -- ✅ **Potentially better** for memory-intensive workloads -- ❌ **Experimental** and unstable -- ❌ **Limited worker availability** -- ❌ **Not production ready** - -| Feature | Intel SGX | Intel TDX | -| ------------------------ | ----------------------------------------------------------------------------------- | -------------------------------------------- | -| Release Year | 2015 | 2023 | -| Enclave Scope | Application level | Virtual machine level | -| Code Adaptation Required | Yes - needs redesign of app's logic | No - supports lift-and-shift of full systems | -| Memory Size | Limited | Extensive (multi-GB+) | -| Integration Complexity | Higher (more dev work) | Lower (VM legacy code) | -| Best Fit For | Lightweight, high-assurance modules (e.g. wallets, crypto key ops, small AI models) | Heavier AI workloads, legacy apps, databases | - -## Enabling TDX in iApp Generator - -### Environment Variable Method - -**Enable TDX for deployment and execution**: - -```bash -# Set the experimental flag -export EXPERIMENTAL_TDX_APP=true - -# Deploy and run with TDX -iapp deploy -iapp run -``` - -:::warning Environment Variable Declaration - -The syntax for setting environment variables differs between operating systems: - -- **Mac/Linux**: `export EXPERIMENTAL_TDX_APP=true` -- **Windows**: `set EXPERIMENTAL_TDX_APP=true` - -::: - -### Per-Command Method - -**Enable TDX for specific commands**: - -```bash -# Deploy TDX-enabled iApp -EXPERIMENTAL_TDX_APP=true iapp deploy - -# Run with TDX -EXPERIMENTAL_TDX_APP=true iapp run - -# Debug TDX execution -EXPERIMENTAL_TDX_APP=true iapp debug -``` - -### Verification - -**Check if TDX is enabled**: - -```bash -# Your deployed iApp should show TDX-related tags -iexec app show -``` - -### - -⚠️ **To use** the iExec DataProtector SDK with TDX support, you must configure -the SDK with the right SMS endpoint. - -```jsx -const dataProtector = new IExecDataProtector(web3Provider, { - iexecOptions: { - smsURL: 'https://sms.labs.iex.ec', - }, -}); -``` - -⚠️**You need** to change the default worker pool in your protected Data -declaration - -```jsx -await dataProtector.core.processProtectedData({ - protectedData: protectedData.address, - workerpool: 'tdx-labs.pools.iexec.eth', - app: '0x1919ceb0c6e60f3B497936308B58F9a6aDf071eC', -}); -``` - -## Protected Data Compatibility - -:::warning Protected Data Requirements - -**TDX iApps may require TDX-compatible protected data.** Check compatibility -before using protected data with TDX iApps. - -::: - -**Important**: The exact process for creating TDX-compatible protected data may -differ from standard protected data creation. Consult the latest DataProtector -documentation for TDX-specific requirements. - -## Development Workflow - -### 1. **Local Testing** - -```bash -# Test locally (same as regular iApps) -iapp test --protectedData "mock_name" - -# TDX only affects remote deployment/execution -``` - -### 2. **Deployment** - -```bash -# Deploy TDX iApp -EXPERIMENTAL_TDX_APP=true iapp deploy -``` - -### 3. **Execution** - -```bash -# Run with TDX -EXPERIMENTAL_TDX_APP=true iapp run -``` - -## Current Limitations - -:::danger Production Warnings - -- **🚫 NOT for production use** -- **🚫 Limited worker availability** -- **🚫 Unstable execution** environment -- **🚫 Breaking changes** without notice - -::: - -## When to Use TDX - -**Consider TDX only for**: - -- 🔬 **Research/development** purposes -- 🧪 **Testing future capabilities** - -**Use SGX for**: - -- 🚀 **All production applications** -- ⚡ **Reliable execution** requirements - -## What's Next? - -**For production applications, use the standard SGX guides**: - -- **[Debugging Your iApp](/build_iapp/guides/debugging-your-iapp)** - - Troubleshoot execution issues -- **[Inputs and Outputs](/build_iapp/guides/inputs-and-outputs)** - Handle data - in TEE environment -- **[App Access Control and Pricing](/build_iapp/guides/orders)** - Deploy - production-ready iApps diff --git a/src/build-iapp/iapp-generator.md b/src/build-iapp/iapp-generator.md deleted file mode 100644 index b6a1d879..00000000 --- a/src/build-iapp/iapp-generator.md +++ /dev/null @@ -1,119 +0,0 @@ ---- -title: iApp Generator -description: - Build privacy-first applications that run in secure TEE environments. Your - complete toolkit for creating, testing, and deploying confidential iApps on - the iExec network. ---- - -# 🤖 iApp Generator - -**Build privacy-first applications that run in secure TEE environments.** iApp -Generator is your complete toolkit for creating, testing, and deploying -confidential iApps on the iExec network. - -Transform your ideas into production-ready privacy-preserving applications in -minutes, not months. - -## What is iApp Generator? - -**iApp Generator** is a CLI tool that simplifies building **iExec Applications -(iApps)** - applications that run inside **Trusted Execution Environments -(TEE)** for maximum privacy and security. - -### What you Can Build - -- **AI models** that process sensitive data privately -- **Data analysis** tools that protect user information -- **Custom algorithms** with confidential inputs and outputs -- **Privacy-preserving services** for Web3 applications - -### What iApp Generator Provides - -- ✅ **Project scaffolding** - Complete iApp structure ready to deploy -- ✅ **Local testing** - Debug and iterate quickly in simulation mode -- ✅ **One-click deployment** - Deploy to TEE workers with a single command -- ✅ **Input/output handling** - Seamless integration with protected data - -## Quick Start Path - -### 1. **Learn the Concepts** - -Start here to understand what iApps are and how they work: - -- **[What Is an iApp?](/build_iapp/iapp-generator/what-is-iapp)** - Core - concepts and TEE overview -- **[Getting Started](/build_iapp/iapp-generator/getting-started)** - Your first - iApp in 15 minutes -- **[Building Your iApp](/build_iapp/iapp-generator/building-your-iexec-app)** - - Complete development guide - -### 2. **Master the Development Workflow** - -Once you've built your first iApp, level up with these practical guides: - -- **[Inputs and Outputs](/build_iapp/guides/inputs-and-outputs)** - Handle data - flow in TEE environment -- **[Debugging Your iApp](/build_iapp/guides/debugging-your-iapp)** - - Troubleshoot execution issues -- **[App Access Control and Pricing](/build_iapp/guides/orders)** - Control who - can use your iApp -- **[How to Get and Decrypt Results](/build_iapp/guides/how-to-get-and-decrypt-results)** - - Retrieve and use outputs - -### 3. **Explore Advanced Features** - -Ready for production? Dive into specialized topics: - -- **[Using TDX (Experimental)](/build_iapp/guides/using-tdx-experimental)** - - Next-gen TEE technology -- **[Complete Guides Overview](/build_iapp/guides)** - All development guides in - one place - -## Why Choose iApp Generator? - -### 🔒 **Privacy by Design** - -Your applications run in hardware-secured enclaves where even the infrastructure -provider can't access your data or code. - -### ⚡ **Developer-Friendly** - -Focus on your application logic while iApp Generator handles the complex TEE -setup, deployment, and execution infrastructure. - -### 🌍 **Decentralized Infrastructure** - -Deploy on a global network of TEE-enabled workers without managing servers or -cloud infrastructure. - -### 🔧 **Complete Toolkit** - -From local development to production deployment, everything you need is included -in one CLI tool. - -## Ready to Build? - -**Start with the basics** and work your way up to advanced privacy-preserving -applications: - -::: tip Quick Path - -1. **[Getting Started](/build_iapp/iapp-generator/getting-started)** - Build - your first iApp (15 minutes) -2. **[Inputs and Outputs](/build_iapp/guides/inputs-and-outputs)** - Handle data - properly -3. **[Debugging](/build_iapp/guides/debugging-your-iapp)** - Fix issues quickly -4. **[App Access Control](/build_iapp/guides/orders)** - Go to production ::: - -### Need Help? - -- **[Complete Guides](/build_iapp/guides)** - All development guides -- **[iExec Discord](https://discord.com/invite/pbt9m98wnU)** - Community support -- **[Protocol Documentation](https://protocol.docs.iex.ec)** - Technical deep - dive - ---- - -**Ready to revolutionize privacy in computing?** Your first privacy-preserving -application is just a few commands away! 🚀 diff --git a/src/build-iapp/iapp-generator/building-your-iexec-app.md b/src/build-iapp/iapp-generator/building-your-iexec-app.md deleted file mode 100644 index d59293d8..00000000 --- a/src/build-iapp/iapp-generator/building-your-iexec-app.md +++ /dev/null @@ -1,204 +0,0 @@ ---- -title: Build Your iApp -description: - Learn how to initialize, configure, and build your iExec application using the - iApp Generator CLI with step-by-step guidance. ---- - -# 🧑‍🏭 Build your iApp - -## 🧰 Initialize your iApp - -The iApp (iExec Application) Generator CLI simplifies the setup of your iApp by -guiding you through a step-by-step initialization process. This ensures your -iApp is correctly configured and compatible with iExec’s confidential computing -environment. - -### 🏗 Define your Project - - - -Follow the prompts to specify: - -- **Project name** – Creates a folder for your project files. -- **Language** – Choose between JavaScript, Python, etc. -- **Project mode** – Choose Basic (Hello-World setup) or Advanced (full debug - capabilities). - -### ⚙ Configure - -::: info - -We are going to create and test our iApp locally. In **debug mode**, you can -quickly iterate and troubleshoot your code. Logs and output files are available -for debugging, helping you refine your app before moving to production. - -::: - -You'll set up: - -- **Arguments (Args)** – Public parameters for your iApp. -- **Input Files** – Files dynamically downloaded during execution. These can - come from **a specific URL**. -- **Requester Secrets** – Confidential authentication strings. -- **Protected Data** – Encrypted data accessible only inside the TEE. -- **App Secret** – Immutable secret provisioned by the iApp owner. - -::: warning 💡 - -The Secret Management Service (SMS) securely stores application developer -secrets. Once set, the App Secret is immutable and cannot be updated. Use with -caution. - -For more information on **App Secrets**, refer to -[Access confidential assets from your app](https://protocol.docs.iex.ec/for-developers/confidential-computing/access-confidential-assets) - -::: - -For more details and to learn how to use them in your application, refer here -[Application I/O](https://protocol.docs.iex.ec/for-developers/application-io) - -## 🚀 Launch your iApp - -After initialization, the following essential files and directories are -generated: - -- `iapp.config.json` -- `src/app.js` _(JavaScript)_ or `src/app.py` _(Python)_ -- `Dockerfile` -- Directories: - - `input/` - - `output/` - - `cache/` - -### 📝 Update your iApp - -To modify your main application logic open: - -```sh -src/app.js # For JavaScript -src/app.py # For Python -``` - -::: info - -💡 The `src/` directory contains the core logic of your iApp. Implement your -algorithms and data processing here. - -::: - -### 🧪 Test and Deploy your iApp - -Use the following CLI commands to **validate**, **deploy**, and **execute** your -iApp: - -```sh -iapp test # Runs a basic test locally. -iapp deploy # Turns your code into a TEE app and registers the iApp on iExec. - -iapp run # Executes the deployed iApp on a worker node. -iapp debug # Retrieve detailed execution logs from worker nodes for a specific task - -iapp mock # Creates a mocked input for testing. -iapp --help # Displays available commands. -``` - -::: info - -use `iapp debug ` if execution exceeds the timeout (default: 5 min). - -::: - -Once deployed, your iApp will run **securely in a TEE-enabled workerpool** -within the iExec network. - -::: info - -💡 A **workerpool** is a decentralized network of nodes that execute iApps -securely within a **Trusted Execution Environment (TEE)**. - -::: - -::: info - -🧪 While **TEE** iApp are base on **intel SGX** technology by default, iApp has -an experimental support for **intel TDX** applications. - -TDX mode is enabled by setting the environment variable -`EXPERIMENTAL_TDX_APP=true`. - -examples: - -- `EXPERIMENTAL_TDX_APP=true iapp test` -- `EXPERIMENTAL_TDX_APP=true iapp deploy` -- `EXPERIMENTAL_TDX_APP=true iapp run ` - -⚠️ Keep in mind: TDX mode is experimental and can be subject to instabilities or -discontinuity. - -::: - -### 🚀 Next Steps - -Your iApp is now running in **debug mode** on iExec! - -Once your application is **stable** and **functional**, you can: - -- Contact **iExec** to move to **production mode** (Full Privacy). -- Learn how to **manage orders** and integrate with the **iExec protocol**. - -#### 📚 Recommended Resources - -- 🔗 - [Order Management](https://protocol.docs.iex.ec/for-developers/advanced/manage-your-apporders) -- 🔗 [iExec Protocol Documentation](https://protocol.docs.iex.ec/) - - diff --git a/src/build-iapp/iapp-generator/deserializer.md b/src/build-iapp/iapp-generator/deserializer.md deleted file mode 100644 index 11c61f32..00000000 --- a/src/build-iapp/iapp-generator/deserializer.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -title: Deserialize a ProtectedData -description: - Learn how to deserialize protected data in your iApp using the - @iexec/dataprotector-deserializer utility package for accessing authorized - data. ---- - -# Deserialize a ProtectedData - -If you want to build your own iApp (iExec TEE Dapp), you may need to access -protected data that your wallet and iApp are authorized to use. To achieve this, -you must deserialize the content of the protected data with the expected data -schema. - -To simplify this process, you can use our lightweight utility package, -`@iexec/dataprotector-deserializer`, in your iApp. This package streamlines the -deserialization of protected data, making it easy for you to access and utilize -the information securely. - -## Overview - -This deserializer is built on the -[Borsh technical specification](https://borsh.io/). We developed this JavaScript -library to simplify deserialization in your iApp built with JavaScript. - -::: warning - -If you want to build your iApp in another language, you need to know how to -deserialize a protected data. - -Under the hood, protected data are **zip files** replicating the tree structure -of the original data object. Each value is stored in a dedicated file, binary -values are stored as is while boolean, numbers, and strings are serialized with -borsh. - -To access a value from a protected data, your app will need to unzip the iExec -dataset file at `$IEXEC_IN/$IEXEC_DATASET_FILENAME`. Then for `'bool'`, `'f64'`, -`'i128'` or `'string'` types, use the Borsh deserialization specification to -recover the original value. Borsh has -[implementations in various languages](https://github.com/near/borsh#implementations), -check your favorite one. - -::: - -### Prerequisites - -Before getting started, ensure that you have the following installed on your -system: - -\- [**Node.js**](https://nodejs.org/en/) version 14 or higher - -\- [**NPM**](https://docs.npmjs.com/) (Node.js package manager) - -### Installation - -::: code-group - -```sh [npm] -npm install @iexec/dataprotector-deserializer -``` - -```sh [yarn] -yarn add @iexec/dataprotector-deserializer -``` - -```sh [pnpm] -pnpm add @iexec/dataprotector-deserializer -``` - -```sh [bun] -bun add @iexec/dataprotector-deserializer -``` - -::: - -### Instantiate SDK - -```ts twoslash [NodeJS] -import { IExecDataProtectorDeserializer } from '@iexec/dataprotector-deserializer'; - -const deserializer = new IExecDataProtectorDeserializer(); -``` diff --git a/src/build-iapp/iapp-generator/deserializer/getValue.md b/src/build-iapp/iapp-generator/deserializer/getValue.md deleted file mode 100644 index 4fbafa27..00000000 --- a/src/build-iapp/iapp-generator/deserializer/getValue.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: getValue -description: - Method to deserialize a value given a provided type using the - IExecDataProtectorDeserializer. ---- - -# getValue - -Method to deserialize a value given a provided type. - -## Usage - -```ts twoslash [NodeJS] -import { IExecDataProtectorDeserializer } from '@iexec/dataprotector-deserializer'; - -const deserializer = new IExecDataProtectorDeserializer(); -// ---cut--- -const value1 = await deserializer.getValue('path.to.value1', 'bool'); -const value2 = await deserializer.getValue('path.to.value2', 'string'); -``` - -## Parameters - -```ts twoslash -import { IExecDataProtectorDeserializer } from '@iexec/dataprotector-deserializer'; -``` - -### path - -`string` - -The path of the value inside the protected data that you want to deserialize. - -```ts twoslash [NodeJS] -import { IExecDataProtectorDeserializer } from '@iexec/dataprotector-deserializer'; - -const deserializer = new IExecDataProtectorDeserializer(); -// ---cut--- -const value1 = await deserializer.getValue( - 'path.to.value1', // [!code focus] - 'bool' -); -``` - -### type - -`string` - -Type of the desired data. The supported types are: - -- `bool` | `f64` | `i128` | `bigint` | `string` | `Uint8Array` | `boolean` - (legacy schema) | `number` (legacy schema) - -```ts twoslash [NodeJS] -import { IExecDataProtectorDeserializer } from '@iexec/dataprotector-deserializer'; - -const deserializer = new IExecDataProtectorDeserializer(); -// ---cut--- -const value1 = await deserializer.getValue( - 'path.to.value1', - 'bool' // [!code focus] -); -``` - -## Return Value - -The recovered original value. diff --git a/src/build-iapp/iapp-generator/getting-started.md b/src/build-iapp/iapp-generator/getting-started.md deleted file mode 100644 index 6d381e56..00000000 --- a/src/build-iapp/iapp-generator/getting-started.md +++ /dev/null @@ -1,57 +0,0 @@ ---- -title: Getting Started -description: - Learn how to set up and start using the iApp Generator with prerequisites, - installation, and first steps. ---- - -# 🛠 Getting Started - -## 🕕 Prerequisites - -Before using the iApp Generator, make sure you have: - -\- [**Node.js**](https://nodejs.org/en/) version 20 or higher - -\- **Docker / Docker hub account** - -\- **Docker Buildx** _(for macOS users, check AMD64 compatibility)_ - -::: tip 🔍 Verify Docker Compatibility - -```bash -docker buildx inspect --bootstrap | grep -i platforms -``` - -If `linux/amd64` is not listed, **update your Docker installation.** - -::: - -### 📦 Installation - -::: code-group - -```sh [npm] -npm install -g @iexec/iapp -``` - -```sh [yarn] -yarn global add @iexec/iapp -``` - -```sh [pnpm] -pnpm add -g @iexec/iapp -``` - -```sh [bun] -bun add -g @iexec/iapp -``` - -::: - -Once installed, generate the auto-completion script and add it to your shell by -following the instructions: - -```bash -iapp completion -``` diff --git a/src/build-iapp/what-is-iapp.md b/src/build-iapp/what-is-iapp.md deleted file mode 100644 index fc1f23f9..00000000 --- a/src/build-iapp/what-is-iapp.md +++ /dev/null @@ -1,202 +0,0 @@ ---- -title: What is an iApp? -description: Privacy-first applications that run on decentralized infrastructure ---- - -# 🚀 What is an iApp? - -An iExec Application (iApp) is your regular application code (Python script, AI -model, data processor, ...) that can securely process protected data (created by -[DataProtector](/manage-data/dataProtector)) inside a confidential computing -environment called TEE (a Trusted Execution Environment). - -## Why iApps Matter ? - -iApps let you process sensitive data while keeping it private and secure. - -Imagine you want to build: - -
-
-
- 🤖 - An AI that analyzes personal health data -
-
- 📧 - An email tool that needs access to contact lists -
-
- 💰 - A financial advisor that processes bank statements -
-
- 🛡️ - A content filter that reads private messages -
-
-
- -Users have this data, but they won't give it to your regular app. **With iApps, -they will.** - -## Key Concepts - -
-
- -

True Privacy: Users never expose their raw data. Your app processes it privately inside secure enclaves.

-
-
- -

Trusted Execution: iExec ensures that your code runs inside a Trusted Execution Environment (TEE), which guarantees that only the specified Docker image is executed in a secure and isolated environment.

-
-
- -

Decentralized Infrastructure: No single point of failure. Your app runs across a distributed network of workers.

-
-
- -

Zero Trust Architecture: User data is protected by hardware-based TEEs, which keep data confidential and inaccessible to the host, cloud provider, or operating system during execution.

-
-
- -## How it Works - -Your code runs in a Trusted Execution Environment (TEE), a secure area inside -specific processors (Intel SGX/TDX chipset). Everything that happens there stays -private and protected, even from the operating system. - -An authorized user can trigger an iApp that processes someone's protected data -inside this private environment. The data is used, but never exposed, not even -to the person running the app. - -
-
-
- 1 - User provides private data -
-
- 2 - Data is protected with DataProtector -
-
- 3 - User builds and deploys a confidential iApp that processes protected data -
-
- 4 - Run the iApp with the corresponding protected data, performing confidential computing -
-
-
- -Your iApp can send emails, update contracts, make transactions, trigger -notifications - anything your code needs to do with the protected data. This -isn't about trust - it's about **cryptographic and hardware-enforced -guarantees** that privacy is preserved within the TEE execution environment. - -## Use Cases - -
-
-
- 📧 -

Private Communication

-
-

Users send emails, notifications, or messages using their protected contact lists without exposing recipient information.

-
- -
-
- 🔮 -

Trustworthy Oracles

-
-

Users contribute real data to oracles while keeping their private information confidential.

-
- -
-
- 🤖 -

Personal AI Assistants

-
-

Users let AI models perform actions based on their private data - trading, scheduling, recommendations...

-
- -
-
- -

Automated Actions

-
-

Users let AI models perform actions based on their private data - trading, scheduling, recommendations...

-
-
- -## ❓ Frequently Asked Questions - -::: details 📦 What can I build with iApps? - -Anything that runs in Docker! AI models, data processing scripts, web scrapers, -image processing, financial calculations, etc. If it runs in a container, it can -be an iApp. - -::: - -::: details ⚡How fast are iApps? - -Initial task scheduling takes a few seconds (depending on the resources the -worker download, congestion etc), then your code runs at normal speed depending -on complexity. - -::: - -::: details 🛡️ Are iApps really secure? - -Yes! Code runs in Intel SGX or TDX secure enclaves. Even the worker running your -iApp can't see what's happening inside the enclave. - -::: - -::: details 🚀 How do I deploy my first iApp? - -Try our [Hello World](/overview/helloWorld) for a quick start, or check the -[iApp Generator](/build-iapp/iapp-generator) section for detailed instructions. - -::: - -::: details 🔧 What programming languages are supported? - -iApps can be built in any language that runs in Docker (Python, JavaScript, R, -Java, Go, etc.). However, **iApp Generator** currently supports only Python and -Node.js for simplified development. - -::: - -## Next Steps - -
- -
-
-
📚
-
- Learn More - iApp Generator: - Complete DataProtector Documentation -
-
-
-
🚀
-
- Getting Started - deploy your first iApp: - DataProtector Quick Start Guide -
-
-
- -
- ---- - -**TL;DR**: iApps = Your code + Secure execution + User privacy + Verifiable -results. Cloud computing, but nobody can spy on your stuff. 🔒 diff --git a/src/index.md b/src/index.md index 5c728c45..c6134c94 100644 --- a/src/index.md +++ b/src/index.md @@ -10,44 +10,44 @@ hero: actions: - theme: brand text: Get Started - link: /overview/welcome + link: /documentation/welcome - theme: alt text: Hello World Tutorial - link: /overview/helloWorld + link: /documentation/helloWorld features: - icon: 🚀 title: Quick Start details: Jump in and start building on iExec in minutes. - link: /overview/helloWorld + link: /documentation/helloWorld - icon: 🔐 title: Protect & Manage Data details: Secure your data with advanced encryption and control access while maintaining privacy using DataProtector - link: /manage-data/what-is-protected-data + link: /documentation/manage-data/what-is-protected-data - icon: 🤖 title: Build iApps details: Create decentralized applications that run on confidential computing infrastructure with our iApp Generator - link: /build-iapp/what-is-iapp + link: /documentation/build-iapp/what-is-iapp - icon: ⚡ title: Use iApps details: Execute existing iApps including Web3Mail, Web3Telegram for your applications - link: /use-iapp/introduction + link: /documentation/use-iapp/introduction - icon: 💰 title: Monetize Data details: Create revenue streams from your data while maintaining full control and privacy - link: /use-iapp/how-to-pay/how-to-pay-for-web3mail + link: /documentation/use-iapp/how-to-pay/how-to-pay-for-web3mail - icon: 🧠 title: Protocols details: Deep dive into the core concepts of the protocol and understand how iExec enables privacy, governance, and monetization - link: /protocol/sdk + link: /documentation/protocol/sdk --- diff --git a/src/manage-data/dataProtector.md b/src/manage-data/dataProtector.md deleted file mode 100644 index e352f572..00000000 --- a/src/manage-data/dataProtector.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -title: DataProtector -description: - Discover DataProtector, iExec's secure data management solution. Encrypt, - share, and monetize your data with DataProtector Core and Sharing modules, all - powered by blockchain technology. ---- - -# 🔐 DataProtector - -DataProtector **simplifies secure data management**, offering users essential -tools for protecting, managing, and sharing their data effectively. - -## DataProtector Core - -As the foundational component of DataProtector, DataProtector Core provides -essential functionalities for data protection. Users can **encrypt their data** -and **record ownership on a smart contract**, ensuring confidentiality and -traceability. Granting access to authorized applications is streamlined, -facilitating secure data management. - -## DataProtector Sharing - -Building upon DataProtector Core, DataProtector Sharing introduces **advanced -features for data sharing** and ownership transfer. Users can securely transfer -ownership of protected data, enabling collaboration and potential **monetization -opportunities**. This module empowers users to share and manage their data -securely, fostering innovation and collaboration in the digital realm. - -## Which One to Use? - -With `DataProtector Core`, you can **grant access** to your protected data **to -a specific user**. - -- You define the number of times the user can access the data. -- You should choose an iApp (iExec TEE Dapp) that will be able to process your - protected data. -- You'll have to sign a transaction at the moment you grant access to the user - and the iApp (iExec TEE Dapp). - -With `DataProtector Sharing`, you can **distribute** your protected data to **a -wider audience**. - -- You don't need to know the user's Ethereum address. -- You define a period of time and a price for which the user can access the - data. -- Any user can access your content as long as they comply with your distribution - and monetization choices. diff --git a/src/manage-data/dataProtector/advanced/advanced-configuration.md b/src/manage-data/dataProtector/advanced/advanced-configuration.md deleted file mode 100644 index bbb23d20..00000000 --- a/src/manage-data/dataProtector/advanced/advanced-configuration.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: Advanced Configuration -description: - Explore advanced configuration options for the IExecDataProtector constructor - in the iExec DataProtector SDK. Customize Ethereum contract addresses, - subgraph URLs, IPFS nodes, and more for specific needs. ---- - -# Advanced Configuration - -The `IExecDataProtector` constructor accepts additional configuration -parameters. As these parameters are very specific, they are not needed for a -standard usage of `@iexec/dataprotector`. - -Similarly, not all functionalities need to be instantiated at once in the SDK, -as described in the [getting started](../getting-started.md#instantiate-sdk) -section. - -## Parameters - -```ts twoslash -import { type DataProtectorConfigOptions } from '@iexec/dataprotector'; -``` - -### dataprotectorContractAddress - -`AddressOrENS` - -The Ethereum contract address or ENS (Ethereum Name Service) for dataProtector -smart contract. If not provided, the default dataProtector contract address will -be used. - -```ts twoslash -import { IExecDataProtector, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const dataProtector = new IExecDataProtector(web3Provider, { - dataprotectorContractAddress: '0x123abc...', // [!code focus] -}); -``` - -### sharingContractAddress - -`AddressOrENS` - -The Ethereum contract address or ENS (Ethereum Name Service) for dataProtector -sharing smart contract. If not provided, the default dataProtector sharing -contract address will be used. - -```ts twoslash -import { IExecDataProtector, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const dataProtector = new IExecDataProtector(web3Provider, { - sharingContractAddress: '0x123abc...', // [!code focus] -}); -``` - -### subgraphUrl - -`string` - -The subgraph URL for querying data. - -If not provided, the default data protector subgraph provided by iExec will be -used. - -```ts twoslash -import { IExecDataProtector, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const dataProtector = new IExecDataProtector(web3Provider, { - subgraphUrl: - 'https://thegraph-product.iex.ec/subgraphs/name/bellecour/dataprotector', // [!code focus] -}); -``` - -### ipfsNode - -`string` - -The IPFS node URL for content uploads. Use this option if you want to use your -own IPFS node to upload content. - -If not provided, the default IPFS node provided by iExec will be used. - -```ts twoslash -import { IExecDataProtector, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const dataProtector = new IExecDataProtector(web3Provider, { - ipfsNode: 'https://ipfs-upload.v8-bellecour.iex.ec', // [!code focus] -}); -``` - -### ipfsGateway - -`string` - -The IPFS gateway URL used for content downloads. Mainly used for checking -content uploaded on the IPFS network. Use this option if you want to use your -own IPFS node for content downloads. - -If not provided, the default IPFS gateway provided by iExec will be used. - -```ts twoslash -import { IExecDataProtector, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const dataProtector = new IExecDataProtector(web3Provider, { - ipfsGateway: 'https://ipfs-gateway.v8-bellecour.iex.ec', // [!code focus] -}); -``` - -### iexecOptions - -Low level configuration options for `iexec` SDK, see -[iexec SDK documentation IExecConfigOptions](https://github.com/iExecBlockchainComputing/iexec-sdk/blob/master/docs/interfaces/IExecConfigOptions.md) -for more details. - -```ts twoslash -import { IExecDataProtector, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const dataProtector = new IExecDataProtector(web3Provider, { - iexecOptions: { smsURL: 'https://sms.scone-prod.v8-bellecour.iex.ec' }, // [!code focus] -}); -``` - -::: info - -🧪 While protected data are processed in **TEE** by **intel SGX** technology by -default, `@iexec/dataprotector` can be configured to create and process -protected data in the experimental **intel TDX** environment. - -TDX mode is enabled by setting connecting the TDX SMS and using the TDX -workerpool. - -```ts twoslash [Browser] -declare global { - interface Window { - ethereum: any; - } -} -// ---cut--- -import { IExecDataProtector } from '@iexec/dataprotector'; - -const web3Provider = window.ethereum; -// Instantiate dataProtector connected to the TDX SMS -const dataProtector = new IExecDataProtector(web3Provider, { - iexecOptions: { - smsURL: 'https://sms.labs.iex.ec', - }, -}); -``` - -⚠️ Keep in mind: TDX mode is experimental and can be subject to instabilities or -discontinuity. - -::: diff --git a/src/manage-data/dataProtector/advanced/apps-whitelist.md b/src/manage-data/dataProtector/advanced/apps-whitelist.md deleted file mode 100644 index 4fbc8a82..00000000 --- a/src/manage-data/dataProtector/advanced/apps-whitelist.md +++ /dev/null @@ -1,80 +0,0 @@ ---- -title: Apps Whitelist -description: - Understand the Apps Whitelist mechanism for consuming protected data in the - iExec DataProtector Sharing module. Learn about the Trusted Execution - Environment (TEE) dApp and whitelist usage for secure data delivery. ---- - -# Apps Whitelist - -In order to consume a protected data, an iExec TEE dApp needs to be provided. - -::: tip - -**TEE** stands for Trusted Execution Environment. Find more details here: - - -::: - -The story goes as follow: - -1. The collection owner adds a protected data to a collection. When doing so, - they need to set an `addOnlyAppWhitelist` parameter (see - [here](../dataProtectorSharing/collection/addToCollection.md#addonlyappwhitelist)). - This parameter is the address of a whitelist smart contract that contains - applications allowed to consume the protected data. - -2. When a user wants to consume the protected data, they need to provide the - address of the application they want to use to consume the data (See - [consumeProtectedData](../dataProtectorSharing/consume/consumeProtectedData.md#app-param) -  `app` parameter). This chosen application must be in the whitelist - defined by the collection owner. - -## Protected Data Delivery iApp - -Built for the needs of -[Content Creator usecase-demo](/overview/use-case-demo/content-creator.html), -this iExec TEE dApp is simple: - -1. Download the protected data from IPFS. It expects to find a property named - `file` in the protected data. - -```json -{ - "file": "" -} -``` - -2. Encrypt the protected data with the beneficiary public key. - -3. Re-upload the encrypted data to IPFS and return the URL. - -::: warning - -Please note: This application and its whitelist can only be used **within the -dataProtectorSharing module**, as it is owned by the DataProtector Sharing smart -contract. - -::: - -### Whitelist - -**Whitelist address:** `0x256bcd881c33bdf9df952f2a0148f27d439f2e64` - -This whitelist contains current and past versions of the "Protected data -delivery dApp" - -See it in -[https://blockscout-bellecour.iex.ec/](https://blockscout-bellecour.iex.ec/address/0x256bcd881c33bdf9df952f2a0148f27d439f2e64). - -### dApp - -**Most recent dApp from this whitelist:** -`0x1cb7D4F3FFa203F211e57357D759321C6CE49921` - -See it in -[https://explorer.iex.ec/bellecour](https://explorer.iex.ec/bellecour/app/0x1cb7d4f3ffa203f211e57357d759321c6ce49921) - -See it in -[https://blockscout-bellecour.iex.ec/](https://blockscout-bellecour.iex.ec/address/0x1cb7D4F3FFa203F211e57357D759321C6CE49921) diff --git a/src/manage-data/dataProtector/advanced/apps-whitelist/addAppToAddOnlyAppWhitelist.md b/src/manage-data/dataProtector/advanced/apps-whitelist/addAppToAddOnlyAppWhitelist.md deleted file mode 100644 index e56acb39..00000000 --- a/src/manage-data/dataProtector/advanced/apps-whitelist/addAppToAddOnlyAppWhitelist.md +++ /dev/null @@ -1,98 +0,0 @@ ---- -title: addAppToAddOnlyAppWhitelist -description: - Method to add an app (iExec TEE dApp) into the AddOnlyAppWhitelist for secure - data access control. ---- - -# addAppToAddOnlyAppWhitelist - -Method to add an app (iExec TEE dApp) into the `AddOnlyAppWhitelist`. - -::: warning - -Once added, you can't remove an app from the whitelist. - -_Why?_ - -This is mainly **to protect users** who have paid for protected data. Imagine -the collection owner could remove all apps from the initial whitelist, users -having rented the protected data could no longer consume it. - -::: - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const isAddedToAddAppToAddOnlyAppWhitelist = - await dataProtectorSharing.addAppToAddOnlyAppWhitelist({ - addOnlyAppWhitelist: '0x123abc...', - app: '0x127ahs...', - }); -``` - -## Parameters - -```ts twoslash -import { type AddAppToAppWhitelistParams } from '@iexec/dataprotector'; -``` - -### addOnlyAppWhitelist - -**Type:** `Address` - -Address of the `addOnlyAppWhitelist` in which you want to add an app. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const isAddedToAddAppToAddOnlyAppWhitelist = - await dataProtectorSharing.addAppToAddOnlyAppWhitelist({ - addOnlyAppWhitelist: '0x123abc...', // [!code focus] - app: '0x127ahs...', - }); -``` - -### app {#app-param} - -**Type:** `AddressOrENS` - -Address of app that you want to add to the `addOnlyAppWhitelist`. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const isAddedToAddAppToAddOnlyAppWhitelist = - await dataProtectorSharing.addAppToAddOnlyAppWhitelist({ - addOnlyAppWhitelist: '0x123abc...', - app: '0x127ahs...', // [!code focus] - }); -``` - -## Return Value - -```ts twoslash -import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; -``` - -See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/manage-data/dataProtector/advanced/apps-whitelist/createAddOnlyAppWhitelist.md b/src/manage-data/dataProtector/advanced/apps-whitelist/createAddOnlyAppWhitelist.md deleted file mode 100644 index 567f287a..00000000 --- a/src/manage-data/dataProtector/advanced/apps-whitelist/createAddOnlyAppWhitelist.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -title: createAddOnlyAppWhitelist -description: - Method to create an AddOnlyAppWhitelist for controlling app access. The caller - becomes the owner by default, with transferable ownership as an ERC721. ---- - -# createAddOnlyAppWhitelist - -Method to create an `AddOnlyAppWhitelist`. By default, the owner will be the -caller of the `createAddOnlyAppWhitelist` method, but as the -`AddOnlyAppWhitelist` is an ERC721, you can transfer ownership to someone else. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const isAddedToAddAppToAddOnlyAppWhitelist = - await dataProtectorSharing.createAddOnlyAppWhitelist(); -``` - -## Return Value - -```ts twoslash -import { type CreateAppWhitelistResponse } from '@iexec/dataprotector'; -``` diff --git a/src/manage-data/dataProtector/advanced/apps-whitelist/getUserAddOnlyAppWhitelist.md b/src/manage-data/dataProtector/advanced/apps-whitelist/getUserAddOnlyAppWhitelist.md deleted file mode 100644 index 9472a2b3..00000000 --- a/src/manage-data/dataProtector/advanced/apps-whitelist/getUserAddOnlyAppWhitelist.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: getUserAddOnlyAppWhitelist -description: - Method to get AddOnlyAppWhitelist with filtering by user ethereum address for - app access control management. ---- - -# getUserAddOnlyAppWhitelist - -Method to get `AddOnlyAppWhitelist`, you can filter by user ethereum address. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const allAppOnlyAppWhitelistAvailable = - await dataProtectorSharing.getUserAddOnlyAppWhitelist(); -``` - -## Parameters - -```ts twoslash -import { type GetUserAppWhitelistParams } from '@iexec/dataprotector'; -``` - -### user - -**Type:** `AddressOrENS` - -Address or ENS of the user that manages the `AddAppToAddOnlyAppWhitelist` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const allUserAddOnlyAppWhitelist = - await dataProtectorSharing.getUserAddOnlyAppWhitelist({ - user: '0x123abc...', // [!code focus] - }); -``` - -## Return Value - -```ts twoslash -import { type GetUserAppWhitelistResponse } from '@iexec/dataprotector'; - -// Child types -import { type AddOnlyAppWhitelist } from '@iexec/dataprotector'; -``` diff --git a/src/manage-data/dataProtector/advanced/dps-smart-contract.md b/src/manage-data/dataProtector/advanced/dps-smart-contract.md deleted file mode 100644 index dacaa0ed..00000000 --- a/src/manage-data/dataProtector/advanced/dps-smart-contract.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -title: DataProtector Sharing Smart Contracts -description: - Learn about the DataProtector Sharing smart contract for managing and sharing - protected data via collections, subscriptions, rentals, and sales. Explore the - Solidity code and features in iExec's implementation. ---- - -# DataProtector Sharing Smart Contracts - -A specific smart contract has been developed to support all of the "Sharing" -module features. - -It mainly serves as **a storage for collections**, their associated protected -data, and their owners. - -## Code - -You can find the Solidity code here: - - -## DataProtectorSharing - -This is a contract that provides a mechanism for managing and sharing protected -data through collections, subscriptions, rentals, and sales. This contract -extends several functionalities from OpenZeppelin libraries and incorporates -access control, token handling, and order management. - -::: tip FUNCTIONS - ---- - -#### General - -- `consumeProtectedData(protectedData, workerpoolOrder, app)` -- `getProtectedDataRenter(protectedData, renterAddress)` -- `getCollectionSubscriber(collectionTokenId, subscriberAddress)` -- `createCollection(to)` -- `addProtectedDataToCollection(collectionTokenId, protectedData, appWhitelist)` -- `removeProtectedDataFromCollection(protectedData)` - -#### Subscription - -- `subscribeToCollection(collectionTokenId, subscriptionParams)` -- `setProtectedDataToSubscription(protectedData)` -- `removeProtectedDataFromSubscription(protectedData)` -- `setSubscriptionParams(collectionTokenId, subscriptionParams)` - -#### Renting - -- `rentProtectedData(protectedData, rentingParams)` -- `setProtectedDataToRenting(protectedData, rentingParams)` -- `removeProtectedDataFromRenting(protectedData)` - -#### Sale - -- `buyProtectedData(protectedData, to, price)` -- `setProtectedDataForSale(protectedData, price)` -- `removeProtectedDataForSale(protectedData)` - -::: - -:::tip EVENTS - ---- - -#### General - -- `OwnershipTransferred(previousOwner, newOwner)` -- `ProtectedDataTransfer(protectedData, fromCollection, toCollection, appWhitelist)` - -#### Subscription - -- `NewSubscription(collectionTokenId, subscriber, endDate)` -- `ProtectedDataAddedForSubscription(collectionTokenId, protectedData)` -- `ProtectedDataRemovedFromSubscription(collectionTokenId, protectedData)` -- `NewSubscriptionParams(collectionTokenId, subscriptionParams)` - -#### Renting - -- `NewRental(collectionTokenId, protectedData, renter, endDate)` -- `ProtectedDataAddedForRenting(collectionTokenId, protectedData, rentingParams)` -- `ProtectedDataRemovedFromRenting(collectionTokenId, protectedData)` - -#### Sale - -- `ProtectedDataSold(collectionTokenId, newOwner, protectedData)` -- `ProtectedDataAddedForSale(collectionTokenId, protectedData, price)` -- `ProtectedDataRemovedFromSale(collectionTokenId, protectedData)` -- `ProtectedDataConsumed(dealid, protectedData, mode)` - -::: - -:::tip ERRORS - ---- - -#### General - -- `OnlyPocoCallerAuthorized(account)` -- `EmptyCallData()` - -#### Subscription - -- `InvalidSubscriptionParams(collectionTokenId, subscriptionParams)` -- `OnGoingCollectionSubscriptions(collectionTokenId)` -- `ProtectedDataAvailableInSubscription(collectionTokenId, protectedData)` - -#### Renting - -- `ProtectedDataCurrentlyBeingRented(protectedData)` -- `ProtectedDataNotAvailableForRenting(protectedData)` -- `DurationInvalid(duration)` -- `ProtectedDataAvailableForRenting(collectionTokenId, protectedData)` - -#### Sale - -- `ProtectedDataNotForSale(protectedData)` -- `InvalidPriceForPurchase(protectedData, price)` -- `WorkerpoolOrderNotFree(workerpoolOrder)` -- `NoValidRentalOrSubscription(collectionTokenId, protectedData)` - -::: diff --git a/src/manage-data/dataProtector/dataProtectorCore.md b/src/manage-data/dataProtector/dataProtectorCore.md deleted file mode 100644 index f87f0f49..00000000 --- a/src/manage-data/dataProtector/dataProtectorCore.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: DataProtector Core -description: - Learn how DataProtector Core gives developers powerful tools to encrypt data, - manage ownership with NFTs, and control access using smart contracts and - confidential computing. ---- - -# DataProtector Core - -The DataProtector tool allows application developers to provide users with -unparalleled ownership over their data. End users gain the ability to invoke -iExec apps without ever exposing their data to any other party. They gain -complete privacy of personally identifiable information (PII) or other sensitive -classes of data. - -This approach to data management relies on: - -- end-to-end encryption of data with access controlled entirely by the owner of - the data -- confidential computing technology that ensures only authorized apps are - permitted access to a user's data -- smart contracts to manage an iExec application's permissions for a user's - encrypted data - -DataProtector Core module contains the following set of methods: - -- **protectData** — safeguard data by encrypting it and recording ownership as - an NFT -- **getProtectedData** — retrieve a list of all protected data for one owner - and/or data schema -- **transferOwnership** — transfer a protected data to a new owner -- **grantAccess** — authorize an application to process a user's data without - exposing the data to any external system or user review -- **getGrantedAccess** — retrieve a list of all authorized users and - applications for a protected data -- **revokeOneAccess** — remove a specific access previously granted on a - protected data -- **revokeAllAccess** — remove all access granted to any iExec applications or - user for a protected data -- **processProtectedData** — process a protected data with a specified iExec - application diff --git a/src/manage-data/dataProtector/dataProtectorCore/getGrantedAccess.md b/src/manage-data/dataProtector/dataProtectorCore/getGrantedAccess.md deleted file mode 100644 index 2ee47134..00000000 --- a/src/manage-data/dataProtector/dataProtectorCore/getGrantedAccess.md +++ /dev/null @@ -1,219 +0,0 @@ ---- -title: getGrantedAccess -description: - Retrieve all granted access details for a protected data object with iExec's - getGrantedAccess method. Filter access by user, application, or both, and - manage access with pagination. ---- - -# getGrantedAccess - -This method provides a listing of all access grants given for the specified -protected data object. Options for filtering include specifying an authorized -user, an authorized app, or both. - -## Usage - -The request object is a JSON `GetGrantedAccessParams` object. Each address in -the object is a string representation of an ethereum address or ENS name -(Ethereum Name Service) reference. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const listGrantedAccess = await dataProtectorCore.getGrantedAccess({ - protectedData: '0x123abc...', - authorizedApp: '0x456def...', - authorizedUser: '0x789cba...', - page: 1, - pageSize: 100, -}); -``` - -## Parameters - -```ts twoslash -import { type GetGrantedAccessParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -Address of the protected data object for which you are querying access -authorization grants. It's a representation of ethereum address or ENS name -(Ethereum Name Service). If no address is specified, it will return all granted -access for any protected data. - -**Usage example:** - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const listGrantedAccess = await dataProtectorCore.getGrantedAccess({ - protectedData: '0x123abc...', // [!code focus] -}); -``` - -### authorizedApp - -**Type:** `AddressOrENS` - -Optional filter to restrict the results to include only authorizations for the -specified application. It's a representation of ethereum address or ENS name -(Ethereum Name Service). If no address is specified, it will return all granted -access for any application. - -**Usage example:** - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const listGrantedAccess = await dataProtectorCore.getGrantedAccess({ - authorizedApp: '0x456def...', // [!code focus] -}); -``` - -::: tip - -If you specified an application whitelist when using -[`grantAccess`](./grantAccess.md), you must specify that same whitelist address -when using this filtering option. The `getGrantedAccess` method does not check -against whitelist smart contracts when aggregating results. If you granted -authorization to a whitelist but specify an application address for the -`authorizedApp` parameter, you will not receive any results unless you _also_ -explicitly granted access to that application address. - -::: - -### authorizedUser - -**Type:** `AddressOrENS` - -Optional filter to restrict the results to include only authorizations for the -specified user. It's a string representation of ethereum address or ENS name -(Ethereum Name Service). If no address is specified, it will return all granted -access for any user. - -**Usage example:** - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const listGrantedAccess = await dataProtectorCore.getGrantedAccess({ - authorizedUser: '0x789cba...', // [!code focus] -}); -``` - -### isUserStrict - -**Type:** `boolean` -**Default:** `false` - -Optional filter to restrict the results to include only authorizations for the -specified user. Authorizations made for `any` user are not returned. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- - -const listGrantedAccess = await dataProtectorCore.getGrantedAccess({ - protectedData: '0x123abc...', - authorizedApp: '0x456def...', - authorizedUser: '0x789cba...', - isUserStrict: true, // [!code focus] -}); -``` - -### page - -**Type:** `number` -**Default:** `0` - -Specifies the page number of the result set to return. Pages are zero-based -indexed, with the default value being `0`, indicating the first page. If used, -you can also specify the `pageSize` parameter to control the number of records -per page. By default, when no page number is specified, the system returns the -first page (page 0) containing `20` elements. - -**Usage example:** - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const listGrantedAccess = await dataProtectorCore.getGrantedAccess({ - protectedData: '0x123abc...', - page: 1, // [!code focus] - pageSize: 100, -}); -``` - -### pageSize - -**Type:** `number` -**Default:** `20` -**Range:** `[10...1000]` - -Specifies the number of records to include in each page of the result set. This -is used in conjunction with the optional `page` parameter to limit the size of -each page. - -**Usage example:** - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const listGrantedAccess = await dataProtectorCore.getGrantedAccess({ - protectedData: '0x123abc...', - page: 1, - pageSize: 100, // [!code focus] -}); -``` - -## Return value - -```ts twoslash -import { type GrantedAccessResponse } from '@iexec/dataprotector'; -``` - -The return value for this method has two fields: a `count` parameter indicating -the number of results, and an array of `GrantedAccess` objects containing all -access data. When using the optional paging parameters, the `count` will be -limited by the selected `pageSize` parameter. You may use these result objects -in conjunction with the [revokeOneAccess](revokeOneAccess.md) method to revoke a -previously granted authorization for access. - -### count - -**Type:** `number` - -An integer value indicating the number of results returned by this method. This -is of particular note when using paging as the number of records returned may be -smaller than the page size. - -### grantedAccess - -**Type:** GrantedAccess - -See [`GrantedAccess`](../types.md#grantedaccess) diff --git a/src/manage-data/dataProtector/dataProtectorCore/getProtectedData.md b/src/manage-data/dataProtector/dataProtectorCore/getProtectedData.md deleted file mode 100644 index a25f42ae..00000000 --- a/src/manage-data/dataProtector/dataProtectorCore/getProtectedData.md +++ /dev/null @@ -1,193 +0,0 @@ ---- -title: getProtectedData -description: - Retrieve all protected data for a specific owner or schema with the - getProtectedData method in iExec DataProtector. Easily access encrypted data - and metadata, sorted by creation date. ---- - -# getProtectedData - -This method allows the user to retrieve all protected data for a given owner, -data schema, or both. - -Results are ordered by `creationTimestamp` desc. - -::: tip - -A data schema is the metadata describing the contents of the protected data -object. The schema is returned as part of the [protectData](protectData.md) -method invocation. - -::: - -## Usage - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const listProtectedData = await dataProtectorCore.getProtectedData({ - owner: '0xa0c15e...', - requiredSchema: { - email: 'string', - }, -}); -``` - -## Parameters - -```ts twoslash -import { type GetProtectedDataParams } from '@iexec/dataprotector'; -``` - -### protectedDataAddress - -**Type:** `AddressOrENS` - -Returns the protected data associated with this address. -Returns an empty array if the protected data is not found. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const oneProtectedData = await dataProtectorCore.getProtectedData({ - protectedDataAddress: '0x123abc...', // [!code focus] -}); -``` - -### requiredSchema - -**Type:** `SearchableDataSchema` - -Provides a list of protected data objects matching this schema. - - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const listProtectedData = await dataProtectorCore.getProtectedData({ - requiredSchema: { // [!code focus] - email: 'string', // [!code focus] - }, // [!code focus] -}); -``` - - -It's also possible to provide a list of accepted types for one schema field: - - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const listProtectedData = await dataProtectorCore.getProtectedData({ - requiredSchema: { // [!code focus] - picture: ['image/png', 'image/jpeg'], // [!code focus] - }, // [!code focus] -}); -``` - - -Available types are listed [here](./protectData#schema). - -### owner - -**Type:** `AddressOrENS` - -Provides a list of protected data objects owned by the user with this ETH -address. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const listProtectedData = await dataProtectorCore.getProtectedData({ - owner: '0xa0c15e...', // [!code focus] -}); -``` - -### createdAfterTimestamp - -**Type:** `number` - -Provides a list of protected data objects created after this timestamp value. -The provided value should be in seconds. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const listProtectedData = await dataProtectorCore.getProtectedData({ - owner: '0xa0c15e...', - createdAfterTimestamp: 1710257612, // March 12, 2024 15:33:32 GMT // [!code focus] -}); -``` - -### page - -**Type:** `number` -**Default:** `0` - -Specifies the results page to return. Pages are indexed starting at page 0. If -using this field you may also specify a `pageSize` to control the size of the -results. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const listProtectedData = await dataProtectorCore.getProtectedData({ - owner: '0xa0c15e...', - createdAfterTimestamp: 1710257612, // March 12, 2024 15:33:32 GMT - page: 1, // [!code focus] -}); -``` - -### pageSize - -**Type:** `number` -**Default:** `1000` -**Range:** `[10...1000]` - -Specifies the number of records in each page of the result set. This is used in -conjunction with the optional `page` parameter to constrain the size of the -result. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const listProtectedData = await dataProtectorCore.getProtectedData({ - owner: '0xa0c15e...', - createdAfterTimestamp: 1710257612, // March 12, 2024 15:33:32 GMT - page: 1, - pageSize: 100, // [!code focus] -}); -``` - -## Return Value - -```ts twoslash -import { type ProtectedData } from '@iexec/dataprotector'; -``` - -See [`ProtectedData`](../types.md#protecteddata) diff --git a/src/manage-data/dataProtector/dataProtectorCore/getResultFromCompletedTask.md b/src/manage-data/dataProtector/dataProtectorCore/getResultFromCompletedTask.md deleted file mode 100644 index 121b5152..00000000 --- a/src/manage-data/dataProtector/dataProtectorCore/getResultFromCompletedTask.md +++ /dev/null @@ -1,136 +0,0 @@ ---- -title: getResultFromCompletedTask -description: - Retrieve the result of a completed task with iExec's - getResultFromCompletedTask method. Easily access task outcomes by providing - the task ID. ---- - -# getResultFromCompletedTask - -Method to get the result of a completed task. - -## Usage - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const completedTaskResult = await dataProtectorCore.getResultFromCompletedTask({ - taskId: '0x7ac398...', -}); -``` - -## Parameters - -```ts twoslash -import { type GetResultFromCompletedTaskParams } from '@iexec/dataprotector'; -``` - -### taskId - -**Type:** `Address` - -Address of the task ID data you'd like to get the result from. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const completedTaskResult = await dataProtectorCore.getResultFromCompletedTask({ - taskId: '0x7ac398...', // [!code focus] -}); -``` - -### path - -**Type:** `string` - -Under the hood, a protected data is a zip file. With this `path` parameter, you -can specify the file you're interested in. The zip file will be uncompressed for -you, and only the desired file will be given as the `result`. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const completedTaskResult = await dataProtectorCore.getResultFromCompletedTask({ - taskId: '0x7ac398...', - path: 'content', // [!code focus] -}); -``` - -### pemPrivateKey - -**Type:** `string` - -If you have previously saved or generated a RSA keypair, you can reuse it in -further calls. - -It needs to be the private key corresponding to the public key initially used to -encrypt the protected data. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const completedTaskResult = await dataProtectorCore.getResultFromCompletedTask({ - taskId: '0x7ac398...', - pemPrivateKey: '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----', // [!code focus] -}); -``` - -### onStatusUpdate - -**Type:** `OnStatusUpdateFn` - -Callback function to be notified at intermediate steps. - - -```ts twoslash -import { - IExecDataProtectorCore, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const completedTaskResult = - await dataProtectorCore.getResultFromCompletedTask({ - taskId: '0x7ac398...', - onStatusUpdate: ({ title, isDone }) => { // [!code focus] - console.log(title, isDone); // [!code focus] - }, // [!code focus] - }); -``` - - -You can expect this callback function to be called with the following titles: - -```ts -'CONSUME_RESULT_DOWNLOAD'; -'CONSUME_RESULT_DECRYPT'; -``` - -Once with `isDone: false`, and then with `isDone: true` - -## Return Value - -```ts twoslash -import { type GetResultFromCompletedTaskResponse } from '@iexec/dataprotector'; -``` - -### result - -`ArrayBuffer` - -The actual content of the protected file. diff --git a/src/manage-data/dataProtector/dataProtectorCore/grantAccess.md b/src/manage-data/dataProtector/dataProtectorCore/grantAccess.md deleted file mode 100644 index cfa8e79f..00000000 --- a/src/manage-data/dataProtector/dataProtectorCore/grantAccess.md +++ /dev/null @@ -1,252 +0,0 @@ ---- -title: grantAccess -description: - Grant secure access to encrypted data with iExec's grantAccess method. - Authorize specific applications or users to process protected data, with - customizable access limits and pricing. ---- - -# grantAccess - -Data encrypted through the Data Protector tool requires explicit authorization -for runtime access. A newly created `protectedData` object has no inherent -authorizations. This method grants permission to securely access the specified -`protectedData` for processing using the `processProtectedData` method. -Authorization to use the `protectedData` is given to a user in the context of an -application (or a designated list of applications). - -## Usage - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const grantedAccess = await dataProtectorCore.grantAccess({ - protectedData: '0x123abc...', - authorizedApp: '0x456def...', - authorizedUser: '0x789cba...', - pricePerAccess: 3, - numberOfAccess: 10, - onStatusUpdate: ({ title, isDone }) => { - console.log(title, isDone); - }, -}); -``` - -## Parameters - -```ts twoslash -import { type GrantAccessParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -The ethereum address of the protected data supplied by the user (returned when -you created it). **You must own this data** to grant access. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const grantedAccess = await dataProtectorCore.grantAccess({ - protectedData: '0x123abc...', // [!code focus] - authorizedApp: '0x456def...', - authorizedUser: '0x789cba...', -}); -``` - -### authorizedApp - -**Type:** `AddressOrENS` - -The address of the application you wish to authorize to process the -`protectedData` within a secure execution environment. You may specify either a -single application or an application whitelist. To specify a whitelist, you -provide the ETH address of an -[iExec Whitelist Smart Contract](https://github.com/iExecBlockchainComputing/whitelist-smart-contract/tree/main). -This smart contract should aggregates multiple application versions. This allows -you to introduce new versions of your application without needing to grant -access for the `protectedData` each time you do so. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const grantedAccess = await dataProtectorCore.grantAccess({ - protectedData: '0x123abc...', - authorizedApp: '0x456def...', // [!code focus] - authorizedUser: '0x789cba...', -}); -``` - -::: tip - -You may authorize a specific app or a whitelist of apps to use the protected -data. - -iExec uses the ENS `web3mail.apps.iexec.eth` for the latest version of the -Web3Mail decentralized application. - -iExec also maintains a whitelist for current and past versions of Web3Mail -dApps. Granting access to this whitelist allows use of an email `protectedData` -with all versions of the Web3Mail application, ensuring you only have to grant -this access once. The ETH address for this whitelist is -**0x781482C39CcE25546583EaC4957Fb7Bf04C277D2**. - -::: - -### authorizedUser - -**Type:** `AddressOrENS` - -The address of the user you wish to authorize to use the `protectedData`. Note -that these users may not view or manipulate the data. This only grants -permission for the user to submit the data to an iExec application. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const grantedAccess = await dataProtectorCore.grantAccess({ - protectedData: '0x123abc...', - authorizedApp: '0x456def...', - authorizedUser: '0x789cba...', // [!code focus] -}); -``` - -::: tip - -You may authorize all users to use the protected data by setting this to -**0x0000000000000000000000000000000000000000**. - -::: - -### pricePerAccess - -**Type:** `number` -**Default:** `0` - -Specifies the usage fee in nano RLC (nRLC) associated with each access of the -data. It represents the cost incurred for each individual interaction with -application. - -By invoking the grantAccess method with a specific `pricePerAccess` you define -the fee that the specified user (`authorizedUser` parameter) must pay for each -access to the data when used with the specified application (`authorizedApp` -parameter). - -The fee is paid to the owner of the protected data. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const grantedAccess = await dataProtectorCore.grantAccess({ - protectedData: '0x123abc...', - authorizedApp: '0x456def...', - authorizedUser: '0x789cba...', - pricePerAccess: 3, // [!code focus] - numberOfAccess: 10, -}); -``` - -::: tip - -`pricePerAccess` is expressed in nano RLC (nRLC). nRLC is the smallest -subdivision of the RLC token, 1 RLC equals to 10^9 nRLC. - -When provided, `pricePerAccess` must be a non-negative integer value. - -::: - -### numberOfAccess - -**Type:** `number` -**Default:** `1` - -Allows restricting the number of times the protected data may be processed and -used. - -It is not technically possible to set an unlimited number of accesses, but you -can set `numberOfAccess` to `10000` for example. - -::: info - -If you attempt to process the protected data more times than specified in -`numberOfAccess`, you will encounter a **"no dataset orders"** error. - -To prevent this error, ensure the `numberOfAccess` is properly set when calling -the `grantAccess` method. - -::: - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const grantedAccess = await dataProtectorCore.grantAccess({ - protectedData: '0x123abc...', - authorizedApp: '0x456def...', - authorizedUser: '0x789cba...', - pricePerAccess: 3, - numberOfAccess: 10, // [!code focus] -}); -``` - -### onStatusUpdate - -**Type:** `OnStatusUpdateFn` - -Callback function to be notified at intermediate steps. - - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const grantedAccess = await dataProtectorCore.grantAccess({ - protectedData: '0x123abc...', - authorizedApp: '0x456def...', - authorizedUser: '0x789cba...', - onStatusUpdate: ({ title, isDone }) => { // [!code focus] - console.log(title, isDone); // [!code focus] - }, // [!code focus] -}); -``` - - -You can expect this callback function to be called with the following titles: - -```ts -'CREATE_DATASET_ORDER'; -'PUBLISH_DATASET_ORDER'; -``` - -Once with `isDone: false`, and then with `isDone: true` - -## Return Value - -```ts twoslash -import { type GrantedAccess } from '@iexec/dataprotector'; -``` - -The result of this method confirms the new access grant. It consists of a JSON -`grantedAccess` object. - -[`GrantedAccess`](../types.md#grantedaccess) diff --git a/src/manage-data/dataProtector/dataProtectorCore/processProtectedData.md b/src/manage-data/dataProtector/dataProtectorCore/processProtectedData.md deleted file mode 100644 index de1ca5b0..00000000 --- a/src/manage-data/dataProtector/dataProtectorCore/processProtectedData.md +++ /dev/null @@ -1,497 +0,0 @@ ---- -title: processProtectedData -description: - Process encrypted data securely with iExec's processProtectedData method. Use - authorized applications to process protected datasets while maintaining data - privacy and security. ---- - -# processProtectedData - -Allows processing a protected dataset through use of a specified iExec -application. - -> [!IMPORTANT] -> -> You must ensure this application has authorization to use the `protectedData`. -> You may grant this permission using the [`grantAccess`](./grantAccess.md) -> method. - -## Usage - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const processProtectedDataResponse = - await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - args: 'arg1 arg2', - inputFiles: ['https://example.com/file1', 'https://example.com/file2'], - secrets: { - 1: 'secret1', - 2: 'secret2', - }, - }); -``` - -## Parameters - -```ts twoslash -import { type ProcessProtectedDataParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -The ETH address or Ethereum Name Service (ENS) reference for the protected data -you wish the `app` to process. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const processProtectedDataResponse = - await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', // [!code focus] - app: '0x456def...', - }); -``` - -### app {#app-param} - -**Type:** `AddressOrENS` - -The ETH address or Ethereum Name Service (ENS) address for the iExec application -to process the protected data. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const processProtectedDataResponse = - await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', // [!code focus] - }); -``` - -### path - -**Type:** `string` - -Under the hood, a protected data is a zip file. With this `path` parameter, you -can specify the file you're interested in. The zip file will be uncompressed for -you, and only the desired file will be given as the `result`. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const processProtectedDataResponse = - await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - path: 'my-content', // [!code focus] - }); -``` - -### userWhitelist - -**Type:** `Address` - -If access to the protected data is granted to a group of users via a whitelist -contract, you must use this `userWhitelist` parameter. The value should be the -whitelist contract address that has access to the protected data. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const processProtectedDataResponse = - await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - userWhitelist: '0x656def...', // [!code focus] - }); -``` - -### useVoucher - -**Type:** `boolean` -**Default:** `false` - -This optional parameter allows you to pay for the task using a voucher. By -default, it uses the voucher associated with your connected wallet, but you can -override this by specifying a voucherAddress. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const processProtectedDataResponse = - await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - useVoucher: true, // [!code focus] - }); -``` - -::: tip - -If your voucher doesn't have enough xRLC to cover the deal, the SDK will -automatically get the required amount to your iExec account. Ensure that your -voucher is authorized to access your iExec account and that your account has -sufficient funds for this transfer to proceed. - -::: - -### voucherOwner - -**Type:** `Address` - -This optional parameter allows you to pay for the task using someone else’s -voucher. Make sure the voucher's owner has authorized you to use it. This -parameter must be used in combination with `useVoucher: true`. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const processProtectedDataResponse = - await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - useVoucher: true, // [!code focus] - voucherOwner: '0x5714eB...', // [!code focus] - }); -``` - -### args - -**Type:** `string` - -Set of execution arguments for the application. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const processProtectedDataResponse = - await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - args: 'arg1 arg2', // [!code focus] - }); -``` - -::: danger - -Do not use this to provide any sensitive information to the application. All -arguments passed this way are visible in plain text using the -[iExec blockchain explorer](https://explorer.iex.ec). - -::: - -### inputFiles - -**Type:** `string[]` - -A set of URLs representing the input files required for application execution. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const processProtectedDataResponse = - await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - inputFiles: ['https://example.com/file1', 'https://example.com/file2'], // [!code focus] - }); -``` - -### secrets - -**Type:** `Record` - -A set of requester secrets necessary for the application's execution. This is -represented as a mapping of numerical identifiers to corresponding secrets -stored in the secrets manager needed for the application's execution. - -Secrets are accessible during the application's execution as environment -variables. For more details, see -[Access requester secrets](https://protocol.docs.iex.ec/for-developers/confidential-computing/access-confidential-assets/requester-secrets). - - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const processProtectedDataResponse = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - secrets: { // [!code focus] - 1: 'secret1', // [!code focus] - 2: 'secret2', // [!code focus] - }, // [!code focus] -}); -``` - - -### workerpool - -**Type:** `AddressOrENS | 'any'` -**Default:** `prod-v8-bellecour.main.pools.iexec.eth` - -The ETH address or Ethereum Name Service (ENS) address for the iExec workerpool. -It's the confidential computer on which the iExec application will run. - -::: tip - -iExec currently offers a production workerpool located at the Ethereum Name -Service (ENS) address `prod-v8-bellecour.main.pools.iexec.eth`. This is the -default workerpool for running confidential computations on the iExec platform. - -If you don't specify a workerpool preference, -0x0000000000000000000000000000000000000000 represents any randomly available -workerpool. - -::: - -::: info - -🧪 While protected data are processed in **TEE** by **intel SGX** technology by -default, `@iexec/dataprotector` can be configured to create and process -protected data in the experimental **intel TDX** environment. - -TDX mode is enabled by setting connecting the **TDX SMS** and using the **TDX -workerpool**. - -```ts twoslash [Browser] -declare global { - interface Window { - ethereum: any; - } -} -// ---cut--- -import { IExecDataProtectorCore } from '@iexec/dataprotector'; - -const web3Provider = window.ethereum; -// Instantiate dataProtector connected to the TDX SMS -const dataProtectorCore = new IExecDataProtectorCore(web3Provider, { - iexecOptions: { - smsURL: 'https://sms.labs.iex.ec', // [!code focus] - }, -}); - -const processProtectedDataResponse = - await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - workerpool: 'tdx-labs.pools.iexec.eth', // [!code focus] - }); -``` - -⚠️ Keep in mind: TDX mode is experimental and can be subject to instabilities or -discontinuity. - -::: - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const processProtectedDataResponse = - await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - workerpool: '0xa5de76...', // [!code focus] - }); -``` - -### dataMaxPrice - -**Type:** `number` -**Default:** `0` - -Allows specifying the maximum amount (in nRLC) you are willing to pay the -protected data owner for using their data. The owner of the protected data -receives this as a payment for sharing their data. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const processProtectedDataResponse = - await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - dataMaxPrice: 42, // [!code focus] - }); -``` - -### appMaxPrice - -**Type:** `number` -**Default:** `0` - -Allows specifying the maximum amount (in nRLC) you are willing to pay the iApp -provider for using the deployed application. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const processProtectedDataResponse = - await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - appMaxPrice: 42, // [!code focus] - }); -``` - -### workerpoolMaxPrice - -**Type:** `number` -**Default:** `0` - -Allows specifying the maximum amount you want to pay the workerpool provider for -using their infrastructure to run the iApp in nRLC. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const processProtectedDataResponse = - await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - workerpoolMaxPrice: 42, // [!code focus] - }); -``` - -### onStatusUpdate - -**Type:** `OnStatusUpdateFn` - -Callback function to be notified at intermediate steps. - - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const processProtectedDataResponse = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - onStatusUpdate: ({ title, isDone }) => { // [!code focus] - console.log(title, isDone); // [!code focus] - }, // [!code focus] -}); -``` - - -You can expect this callback function to be called with the following titles: - -```ts -'FETCH_PROTECTED_DATA_ORDERBOOK'; -'FETCH_APP_ORDERBOOK'; -'FETCH_WORKERPOOL_ORDERBOOK'; -'PUSH_REQUESTER_SECRET'; -'REQUEST_TO_PROCESS_PROTECTED_DATA'; -'CONSUME_TASK'; -'CONSUME_RESULT_DOWNLOAD'; -'CONSUME_RESULT_DECRYPT'; -``` - -Once with `isDone: false`, and then with `isDone: true` - -## Return Value - -```ts twoslash -import { type ProcessProtectedDataResponse } from '@iexec/dataprotector'; -``` - -### txHash - -`string` - -The ID of the transaction that happened on iExec's side chain. You may view -details on the transaction using the [iExec explorer](https://explorer.iex.ec). - -### dealId - -`string` - -Identifies the specific deal associated with this transaction. - -### taskId - -`string` - -A unique identifier associated with a task currently running on the iExec -Bellecour side chain. You can monitor task execution using the -[iExec blockchain explorer](https://explorer.iex.ec). - -::: tip - -The -[getResultFromCompletedTask()](../dataProtectorCore/getResultFromCompletedTask.md) -function allows you to retrieve the result of a completed task using its -`taskId`. - -Additionally, you can specify a **file path** within the ZIP archive to extract -a specific file when required. - -::: - -### result - -`ArrayBuffer` - -The result is a ZIP file containing at least one mandatory file: - -- **computed.json**: This file contains metadata about the computation performed - by the application. -- additional files may be included depending on the dapp used. - -::: info - -In the case of the **Content Creator Delivery DApp**, the ZIP file will also -include a file named **content**, which corresponds to the protected data -processed during the task. - -::: diff --git a/src/manage-data/dataProtector/dataProtectorCore/protectData.md b/src/manage-data/dataProtector/dataProtectorCore/protectData.md deleted file mode 100644 index e5e775e4..00000000 --- a/src/manage-data/dataProtector/dataProtectorCore/protectData.md +++ /dev/null @@ -1,442 +0,0 @@ ---- -title: protectData -description: - Use the protectData method from iExec DataProtector to encrypt user data - client-side and ensure full privacy. Easily secure emails, credentials, or - custom data structures in your iApp. ---- - -# protectData - -The iExec tool suite supports deployment of applications where the user of the -application has complete and total control over access to their data. This -ensures privacy and security when invoking these applications. - -Through use of the `protectData` method, a user may encrypt and secure any type -of data. Encryption occurs on the client side, supporting the user's control -over their data. - -## Usage - -The method accepts a JSON object containing the data to encrypt and an optional -name to identify the data. - -An email address, for example, may be submitted as: - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const protectedData = await dataProtectorCore.protectData({ - data: { - email: 'example@gmail.com', - }, -}); -``` - -Your object may contain any number of custom keys. The following example -illustrates protection of multiple categories of data within one object: - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const protectedData = await dataProtectorCore.protectData({ - data: { - email: 'example@gmail.com', - SMTPserver: { - port: 5000, - smtp_server: 'smtp.gmail.com', - }, - }, -}); -``` - -::: info - -🧪 While protected data are processed in **TEE** by **intel SGX** technology by -default, `@iexec/dataprotector` can be configured to create and process -protected data in the experimental **intel TDX** environment. - -TDX mode is enabled by setting connecting the **TDX SMS** and using the **TDX -workerpool**. - -```ts twoslash [Browser] -declare global { - interface Window { - ethereum: any; - } -} -// ---cut--- -import { IExecDataProtectorCore } from '@iexec/dataprotector'; - -const web3Provider = window.ethereum; -// Instantiate dataProtector connected to the TDX SMS -const dataProtectorCore = new IExecDataProtectorCore(web3Provider, { - iexecOptions: { - smsURL: 'https://sms.labs.iex.ec', // [!code focus] - }, -}); -// create a protected data -const protectedData = await dataProtectorCore.protectData({ - data: { - email: 'example@gmail.com', - SMTPserver: { - port: 5000, - smtp_server: 'smtp.gmail.com', - }, - }, -}); -``` - -⚠️ Keep in mind: TDX mode is experimental and can be subject to instabilities or -discontinuity. - -::: - -## Parameters - -```ts twoslash -import { type ProtectDataParams } from '@iexec/dataprotector'; -``` - -### data - -**Type:** `DataObject` - -This is the actual data the user is protecting, provided as a JSON object with -any number of custom keys. The data is encrypted and stored as an NFT. - - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const protectedData = await dataProtectorCore.protectData({ - data: { // [!code focus] - email: 'example@gmail.com', // [!code focus] - }, // [!code focus] -}); -``` - - -::: tip - -If you'd like to **protect a file**, you first need to convert it to some kind -of buffer. To do so, you can use `createArrayBufferFromFile`. - -```ts twoslash -const file: File = new File([], 'emptyFile.txt'); -// ---cut--- -import { createArrayBufferFromFile } from '@iexec/dataprotector'; - -const fileAsArrayBuffer = await createArrayBufferFromFile(file); -``` - -::: - -::: tip - -If you want to **protect an `Array`**, you must represent it as a -`Record`. To do so, you can use the `reduceArray` method -implemented in this example. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const reduceArray = (array: Array): Record => - array.reduce((accumulator, current, i) => { - accumulator[i] = current; - return accumulator; - }, {}); - -const emailsArray = [ - 'example@gmail.com', - 'example@my-company.com', - 'example@example.com', -]; - -const protectedData = await dataProtectorCore.protectData({ - data: { - emails: reduceArray(emailsArray), - }, -}); - -/** - * protectedData.schema: - * { - * emails: { - * 0: 'string', - * 1: 'string', - * 2: 'string' - * } - * } - */ -``` - -::: - -### name - -**Type:** `string` -**Default:** `''` - -Allows providing a descriptive name for the protected data. This is considered -public metadata, describing the protected data. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const protectedData = await dataProtectorCore.protectData({ - name: 'myEmail', // [!code focus] - data: { - email: 'example@gmail.com', - }, -}); -``` - -::: tip - -The name is public and not encrypted. - -::: - -### uploadMode - -**Type:** `"ipfs" | "arweave"` -**Default:** `"ipfs"` - -Specify the storage solution to use for the protected data encrypted payload -hosting. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const protectedData = await dataProtectorCore.protectData({ - name: 'myEmail', - data: { - email: 'example@gmail.com', - }, - uploadMode: 'arweave', // [!code focus] -}); -``` - -### allowDebug - -**Type:** `boolean` -**Default:** `false` - -Allow using the protected data in TEE debug apps. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const protectedData = await dataProtectorCore.protectData({ - name: 'myEmail', - data: { - email: 'example@gmail.com', - }, - allowDebug: true, // [!code focus] -}); -``` - -::: tip - -This param is for development only. - -::: - -### onStatusUpdate - -**Type:** `OnStatusUpdateFn` - -Callback function to be notified at intermediate steps. - - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const protectedData = await dataProtectorCore.protectData({ - name: 'myEmail', - data: { - email: 'example@gmail.com', - }, - onStatusUpdate: ({ title, isDone }) => {// [!code focus] - console.log(title, isDone); // [!code focus] - }, // [!code focus] -}); -``` - - -You can expect this callback function to be called with the following titles: - -```ts -'EXTRACT_DATA_SCHEMA'; -'CREATE_ZIP_FILE'; -'CREATE_ENCRYPTION_KEY'; -'ENCRYPT_FILE'; -'UPLOAD_ENCRYPTED_FILE'; -'DEPLOY_PROTECTED_DATA'; -'PUSH_SECRET_TO_SMS'; -``` - -Once with `isDone: false`, and then with `isDone: true` - -## Return Value - -```ts twoslash -import type { - ProtectedDataWithSecretProps, - ProtectedData, -} from '@iexec/dataprotector'; -``` - -The `protectData` method returns the following fields, as a JSON object. - -### name - -`string` - -The optional name provided during invocation of the method. If no name is -specified this value defaults to `Untitled`. - -### address - -`Address` - -The ETH address of the newly created `protectedData`. - -### owner - -`Address` - -The ETH address of the creator and owner of this `protectedData`. - -### schema - -`DataSchema` - -Metadata describing the fields provided in the `data` parameter. The data types -are automatically detected and listed in the schema. - -::: tip - -The following data types are automatically detected: - -- Scalars - - `bool` - - `f64` (JavaScript `number`) - - `i128` (JavaScript `bigint` up to 128 bits) - - `string` -- Binary: - - `application/octet-stream` - - `application/pdf` - - `application/xml` - - `application/zip` - - `audio/midi` - - `audio/mpeg` - - `audio/x-wav` - - `image/bmp` - - `image/gif` - - `image/jpeg` - - `image/png` - - `image/webp` - - `video/mp4` - - `video/mpeg` - - `video/x-msvideo` - -Any undetected binary data type is categorized as `application/octet-stream` - -::: - -### creationTimestamp - -`number` - -A unix-style timestamp indicating the creation time of this `protectedData`. - -### transactionHash - -`string` - -The ID of the transaction that happened on iExec's side chain. You may view -details on the transaction using the [iExec explorer](https://explorer.iex.ec). - -### zipFile - -`Uint8Array` - -Under the hood, your protected data will be **compressed as a zip file**. In -this zip file, you'll find back all of your protected fields, each field being -serialized with a tool called `borsh`. You can find more details here: -[deserializer](../advanced/iApp/deserializer). - -This is mainly returned for debug purpose. - -### encryptionKey - -`Uint8Array` - -The encryption key generated by the client to encrypt the data. This key is for -your own usage. You will not have to share it in the context of the iExec -protocol or developer tools. - -Under the hood, it is a symmetric AES-256 key. - -::: tip - -The zip file generated is a `Uint8Array`, so if you want to handle the binary -data or download it consider adding a zip extension to it. - -::: - -### multiaddr - -`string` | `undefined` - -The multiaddr field is the IPFS path of your encrypted data. - -::: tip - -You can access your encrypted IPFS data with the link: - -`https://ipfs-gateway.v8-bellecour.iex.ec/ipfs/abc123...` - -`abc123...` is the second part of the returned string `/p2p/abc123...` - -::: - -## Created Protected Data - -To further check your data was correctly created, you can inspect it on the -[iExec explorer](https://explorer.iex.ec/). - - - iExec explorer - Dataset example - - - diff --git a/src/manage-data/dataProtector/dataProtectorCore/revokeAllAccess.md b/src/manage-data/dataProtector/dataProtectorCore/revokeAllAccess.md deleted file mode 100644 index becf31c3..00000000 --- a/src/manage-data/dataProtector/dataProtectorCore/revokeAllAccess.md +++ /dev/null @@ -1,143 +0,0 @@ ---- -title: revokeAllAccess -description: - Revoke all or specific access permissions to protected data with iExec's - revokeAllAccess method. Efficiently manage data security by removing access - from users or smart contract. ---- - -# revokeAllAccess - -This method allows revoking authorizations granted to a `protectedData` entity. -You may optionally specify application or user addresses for revocation. If you -do not specify either of these optional values, this method will revoke all -access for all users and applications. - -You must be the owner of the protected data. - -Under the hood, all granted access will be retrieved and be revoked one by one. -If by any chance there were **more than 20 granted access** to be revoked, you -would need to call this `revokeAllAccess()` method more than once for all -granted access to be actually revoked. Use `getGrantedAccess()` to ensure it is -all done. - -## Usage - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const revokeAllAccessResult = await dataProtectorCore.revokeAllAccess({ - protectedData: '0x123abc...', - authorizedApp: '0x456def...', - authorizedUser: '0x789cba...', -}); -``` - -## Parameters - -```ts twoslash -import { type RevokeAllAccessParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -The address of the `protectedData` subject to access revocation. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const revokeAllAccessResult = await dataProtectorCore.revokeAllAccess({ - protectedData: '0x123abc...', // [!code focus] -}); -``` - -### authorizedApp - -**Type:** `AddressOrENS` - -The application address to be removed from the authorization list for the -specified `protectedData`. If no address is specified, it will revoke all access -from the protected data, regardless of the app. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const revokeAllAccessResult = await dataProtectorCore.revokeAllAccess({ - protectedData: '0x123abc...', - authorizedApp: '0x456def...', // [!code focus] - authorizedUser: '0x789cba...', -}); -``` - -### authorizedUser - -**Type:** `AddressOrENS` - -The user address to be removed from the authorization list for the specified -`protectedData`. If no address is specified, it will revoke all access from the -protected data, regardless of the authorized user. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const revokeAllAccessResult = await dataProtectorCore.revokeAllAccess({ - protectedData: '0x123abc...', - authorizedApp: '0x456def...', - authorizedUser: '0x789cba...', // [!code focus] -}); -``` - -### onStatusUpdate - -**Type:** `OnStatusUpdateFn` - -Callback function to be notified at intermediate steps. - - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const revokeAllAccessResult = await dataProtectorCore.revokeAllAccess({ - protectedData: '0x123abc...', - authorizedApp: '0x456def...', - authorizedUser: '0x789cba...', - onStatusUpdate: ({ title, isDone }) => { // [!code focus] - console.log(title, isDone); // [!code focus] - }, // [!code focus] -}); -``` - - -You can expect this callback function to be called with the following titles: - -```ts -'RETRIEVE_ALL_GRANTED_ACCESS'; -'REVOKE_ONE_ACCESS'; -``` - -Once with `isDone: false`, and then with `isDone: true` - -## Return Value - -```ts twoslash -import { type RevokedAccess } from '@iexec/dataprotector'; -``` - -[`RevokedAccess[]`](../types.md#revokedaccess) diff --git a/src/manage-data/dataProtector/dataProtectorCore/revokeOneAccess.md b/src/manage-data/dataProtector/dataProtectorCore/revokeOneAccess.md deleted file mode 100644 index 866de9fe..00000000 --- a/src/manage-data/dataProtector/dataProtectorCore/revokeOneAccess.md +++ /dev/null @@ -1,91 +0,0 @@ ---- -title: revokeOneAccess -description: - Revoke specific access permissions to protected data with iExec's - revokeOneAccess method. Manage and remove access granted to users or - applications through blockchain transactions. ---- - -# revokeOneAccess - -This method allows revoking a specific access authorization from a -`protectedData` entity. The input parameter for this method is sourced from the -[getGrantedAccess](getGrantedAccess.md) method, which provides a list of all -authorizations on single `protectedData` entity. - -As this will generate a blockchain transaction, expect it to take a least 5sec -(a block time). - -## Usage - -The `revokeOneAccess` method requires a `grantedAccess` object as an input -parameter. This object is retrieved from the -[`getGrantedAccess`](./getGrantedAccess.md) method. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const revokeAccess = await dataProtectorCore.revokeOneAccess({ - apprestrict: '0xea...', - dataset: '0xA0C...', - datasetprice: '0', - requesterrestrict: '0xecb..', - salt: '0x0147...', - sign: '0xc22c1...', - tag: '0x0000000000000000000000000000000000000000000000000000000000000003', - volume: '1', - workerpoolrestrict: '0x000...', -}); -``` - -## Parameters - -```ts twoslash -import { type GrantedAccess } from '@iexec/dataprotector'; -``` - -### grantedAccess - -**Type:** `GrantedAccess` - -This is the complete `granted access` object retrieved from an invocation of -`getGrantedAccess`. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const revokeAccess = await dataProtectorCore.revokeOneAccess({ - apprestrict: '0xea...', // [!code focus] - dataset: '0xA0C...', // [!code focus] - datasetprice: '0', // [!code focus] - requesterrestrict: '0xecb..', // [!code focus] - salt: '0x0147...', // [!code focus] - sign: '0xc22c1...', // [!code focus] - tag: '0x0000000000000000000000000000000000000000000000000000000000000003', // [!code focus] - volume: '1', // [!code focus] - workerpoolrestrict: '0x000...', // [!code focus] -}); -``` - -::: warning - -The tag must always be set to -`0x0000000000000000000000000000000000000000000000000000000000000003`. This -specific value indicates that the order is for a confidential asset (a protected -data). - -::: - -## Result Value - -```ts twoslash -import { type RevokedAccess } from '@iexec/dataprotector'; -``` - -[`RevokedAccess`](../types.md#revokedaccess) diff --git a/src/manage-data/dataProtector/dataProtectorCore/transferOwnership.md b/src/manage-data/dataProtector/dataProtectorCore/transferOwnership.md deleted file mode 100644 index 9ce7f33b..00000000 --- a/src/manage-data/dataProtector/dataProtectorCore/transferOwnership.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -title: transferOwnership -description: - Transfer ownership of protected data to a new owner with iExec's - transferOwnership method. Securely update data ownership and automatically - revoke previous access permissions. ---- - -# transferOwnership - -Allows transferring ownership of a `protectedData` entity to a new owner, -identified by their ETH address. The return value provides a transaction hash -and confirmation of the new owner of the `protectedData`. Only the current owner -of the `protectedData` may invoke this method. - -When transferring the `protectedData`, the grantedAccess created by the previous -owner are revoked automatically. - -Ownership of the `protectedData` can be renounced by transferring it to the burn -address `0x000000000000000000000000000000000000dEaD`. - -## Usage - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const transferResponse = await dataProtectorCore.transferOwnership({ - protectedData: '0x123abc...', - newOwner: '0xc5e9f4...', -}); -``` - -## Parameters - -```ts twoslash -import { type TransferParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -ETH address of the `protectedData` owned by you which is to be transferred to a -new owner. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const transferResponse = await dataProtectorCore.transferOwnership({ - protectedData: '0x123abc...', // [!code focus] - newOwner: '0xc5e9f4...', -}); -``` - -### newOwner - -**Type:** `AddressOrENS` - -ETH address for the new owner for the `protectedData`. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const transferResponse = await dataProtectorCore.transferOwnership({ - protectedData: '0x123abc...', - newOwner: '0xc5e9f4...', // [!code focus] -}); -``` - -## Return Value - -```ts twoslash -import { type TransferResponse } from '@iexec/dataprotector'; -``` - -The result of this method is an array of objects identifying the new owner. The -objects contain the three fields: - -### address - -`Address` - -The ETH address of the `protectedData` you transferred. - -### to - -`AddressOrENS` - -The ETH address of the new owner of the `protectedData`. - -### txHash - -`string` - -The ID of the transaction that happened on iExec's side chain. You may view -details on the transaction using the [iExec explorer](https://explorer.iex.ec). diff --git a/src/manage-data/dataProtector/dataProtectorSharing.md b/src/manage-data/dataProtector/dataProtectorSharing.md deleted file mode 100644 index 086fd6a6..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: DataProtector Sharing -description: - Distribute and monetize your protected data effortlessly with DataProtector - Sharing. Use smart contracts to manage access, rent, or sell data while - maintaining control and privacy. ---- - -# DataProtector Sharing - -The DataProtector Sharing module includes a set of methods for distributing and -monetizing your protected data. These work seamlessly in your own applications -in a way that's transparent to end users. The Sharing module supports the -following options for distributing a user's protected data: - -- Sharing freely with other users -- Renting data for a pre-determined period of time -- Providing access for a recurring subscription fee -- Selling ownership of the data - -Once access is obtained through one of these distribution methods, the `consume` -methods allow interaction with the protected data. The application designer uses -these methods to securely retrieve data from IPFS and decrypt it on the client -side. This ensures only the consumer gains access to the data. - -## How Does it Work? - -The user's protected data is managed by a special Data Sharing smart contract. -This smart contract enforces the user's desired sharing paradigm. Protected data -is bundled by the user into one or more collections. Within the collection, the -user may specify different sharing options for different pieces of data. This -illustration shows the hierarchy of a Data Sharing smart contract: - -![Data Sharing smart contract](./dataProtectorSharing/data-sharing-sc.png) - -The Data Sharing smart contract enforces governance over the user's protected -data based on their desired distribution choices. The user has complete control -over the mode of sharing for each Collection and each Protected Data contained -in their collections. - -The smart contract also protects the consumer of a user's data. If a consumer -rents access to a video clip, for example, even if the owner of the video clip -removes the rental option, the renter maintains their access for the duration of -the rental period. - -## How Does this Differ from `DataProtector Core`? - -With `DataProtector Core` you must grant each consumer individual access to your -protected data. This has several prerequisites: - -- The data owner must know the consumer's Ethereum address -- The data owner must sign a transaction at the moment you grant the access -- The data owner must define the number of times the consumer can access the - data up front - -With `DataProtector Sharing` a user can easily distribute their protected data -to a wider audience. The user authorizes distribution channels up front and -consumers of the data trigger smart contracts on their own, requiring no -additional activity from the data owner. This has several key benefits - -- The data owner doesn't need to know the consumer's Ethereum address -- The data owner doesn't need to sign a transaction at the moment of - distribution, only the consumer of the data signs their transaction -- The data owner doesn't manage access, the Data Sharing smart contract - automatically enforces the distribution and monetization choices diff --git a/src/manage-data/dataProtector/dataProtectorSharing/collection.md b/src/manage-data/dataProtector/dataProtectorSharing/collection.md deleted file mode 100644 index 55921734..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/collection.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -title: Data Sharing - Collection Methods -description: - Organize your protected data into collections with iExec's Data Sharing - module. Choose distribution options like renting, free access, subscription, - or selling to control how your data is shared and monetized. ---- - -# Data Sharing - Collection Methods - -A collection is a way to group protected data for sharing by the Data Sharing -module. The Data Sharing smart contract operates on data contained within a -collection. You must group data within a collection before choosing a method of -distribution. The data owner is not restricted to only a single collection. This -diagram illustrates all the options for protected data within a collection: - -![Data Sharing collection](inside-a-collection.png) - -## Distribution Options - -The distribution choice impacts the visibility of the protected data when a -potential consumer is browsing the collection. Inside a collection, a data owner -may specify one of the following states: - -**Not distributed** - -Only the collection's owner can see it. This is useful for things in -development, or when the owner wishes to release things for a limited time. - -**For rent** - -The collection's owner allows a consumer to pay a set price to access the data -for a predetermined period of time. The smart contract ensures the consumer -maintains access. Even if the collection owner de-lists the data or changes the -rental terms, the consumer maintains their access for the duration of their -rental term. Once the rental term is up, the consumer loses access to the -protected data. If the user wishes to re-up their rental term, they are bound to -any new price or duration changes the data owner makes. - -**Free** - -This functions the same as a rental but with no price for the transaction. The -data owner may still set a duration for the free access. This supports scenarios -like a free giveaway, or a timed promotion. - -**Included in Subscription** - -The collection's owner may create one or more subscription models for -distribution of their data. A subscription bundle is a subset of the protected -data within the collection. Subscribers pay a set fee at a set cadence to -maintain access to all protected data within the subscription. The collection -owner may assign protected data to more than one subscription bundles at the -same time. - -The collection owner may add additional protected data to the subscription at -any time. They may not, however, remove protected data from the subscription -bundle if there are any active subscribers. A subscriber maintains access to all -protected data within the subscription as long as they continue paying the -subscription fee. The collection owner may de-list a subscription bundle by -setting either the price, the duration, or both, to zero. - -**Both For Rent and Included in Subscription** - -The collection owner may set any combination of rental and subscription terms -for any protected data in the collection. - -**For sale** - -The collection owner may list any of their protected data for sale. This is -especially useful for dealers of digital assets like NFTs. diff --git a/src/manage-data/dataProtector/dataProtectorSharing/collection/addToCollection.md b/src/manage-data/dataProtector/dataProtectorSharing/collection/addToCollection.md deleted file mode 100644 index 05ccde21..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/collection/addToCollection.md +++ /dev/null @@ -1,166 +0,0 @@ ---- -title: addToCollection -description: - Transfer a protected data to one of your collections in the Data Sharing smart - contract. The method approves the contract to manage the data and adds it to - the specified collection. ---- - -# addToCollection - -Method to transfer one of your protected data to a collection of yours in the -Data Sharing smart contract. - -Under the hood, this method performs two actions: - -- Approve the Data Sharing smart contract to manage your protected data. -- Add the protected data to your collection. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.addToCollection({ - protectedData: '0x123abc...', - collectionId: 12, - addOnlyAppWhitelist: '0x541abc...', -}); -``` - -## Parameters - -```ts twoslash -import { type AddToCollectionParams } from '@iexec/dataprotector'; -``` - -### collectionId - -**Type:** `number` - -Collection ID to which you'd like to add the protected data. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.addToCollection({ - collectionId: 12, // [!code focus] - protectedData: '0x123abc...', - addOnlyAppWhitelist: '0x541abc...', -}); -``` - -### protectedData - -**Type:** `AddressOrENS` - -Address of the protected data you'd like to add to your collection. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.addToCollection({ - collectionId: 12, - protectedData: '0x123abc...', // [!code focus] - addOnlyAppWhitelist: '0x541abc...', -}); -``` - -Before any smart contract interaction, the existence of the protected data will -be checked, as well as the ownership: it should be the wallet address you used -to instantiate DataProtector SDK. - -### addOnlyAppWhitelist - -**Type:** `AddressOrENS` - -Address of the whitelist smart contract that contains applications allowed to -consume the protected data. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.addToCollection({ - collectionId: 12, - protectedData: '0x123abc...', - addOnlyAppWhitelist: '0xba46d6...', // [!code focus] -}); -``` - -::: tip - -For this `addOnlyAppWhitelist`, you are free to use -`0x256bcd881c33bdf9df952f2a0148f27d439f2e64`. - -For more details on how to create and manage appsWhitelist, see -[Apps whitelist](../../advanced/apps-whitelist). - -::: - -### onStatusUpdate - -**Type:** `OnStatusUpdateFn` - -Callback function to be notified at intermediate steps. - - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.addToCollection({ - protectedData: '0x123abc...', - collectionId: 12, - addOnlyAppWhitelist: '0xba46d6...', - onStatusUpdate: ({ title, isDone }) => { // [!code focus] - console.log(title, isDone); // [!code focus] - }, // [!code focus] -}); -``` - - -You can expect this callback function to be called with the following titles: - -```ts -'APPROVE_COLLECTION_CONTRACT'; -'ADD_PROTECTED_DATA_TO_COLLECTION'; -``` - -Once with `isDone: false`, and then with `isDone: true` - -## Return Value - -```ts twoslash -import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; -``` - -See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/manage-data/dataProtector/dataProtectorSharing/collection/createCollection.md b/src/manage-data/dataProtector/dataProtectorSharing/collection/createCollection.md deleted file mode 100644 index 9f48e64e..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/collection/createCollection.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -title: createCollection -description: - Create a new NFT collection with iExec's createCollection method. Organize and - manage your protected data for seamless distribution and monetization through - DataProtector Sharing. ---- - -# createCollection - -Method to create a new collection in the Data Sharing smart contract. - -Having a collection is a required step before choosing how to distribute your -protected data. - -A wallet address may own multiple collections. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const createCollectionResult = await dataProtectorSharing.createCollection(); -``` - -## Return Value - -```ts twoslash -import { type CreateCollectionResponse } from '@iexec/dataprotector'; -``` diff --git a/src/manage-data/dataProtector/dataProtectorSharing/collection/removeCollection.md b/src/manage-data/dataProtector/dataProtectorSharing/collection/removeCollection.md deleted file mode 100644 index 64a24b3f..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/collection/removeCollection.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -title: removeCollection -description: - Remove a collection from the Data Sharing smart contract by burning its - associated NFT. Transfer the NFT to the zero address and permanently remove - the collection. ---- - -# removeCollection - -Method to remove one of your collections in the Data Sharing smart contract. - -By removing a collection, we mean to burn the associated NFT, ie. to **transfer -it to the zero address**. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.removeCollection({ - collectionId: 15, -}); -``` - -## Pre-conditions - -- You must be the owner of the collection. -- There should be no protected data in the collection. See - [`removeProtectedDataFromCollection`](./removeProtectedDataFromCollection.md). - -## Parameters - -```ts twoslash -import { type RemoveCollectionParams } from '@iexec/dataprotector'; -``` - -### collectionId - -**Type:** `number` - -The collection ID of the collection you want to remove. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.removeCollection({ - collectionId: 15, // [!code focus] -}); -``` - -## Return Value - -```ts twoslash -import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; -``` - -See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/manage-data/dataProtector/dataProtectorSharing/collection/removeProtectedDataFromCollection.md b/src/manage-data/dataProtector/dataProtectorSharing/collection/removeProtectedDataFromCollection.md deleted file mode 100644 index fde983da..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/collection/removeProtectedDataFromCollection.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: removeProtectedDataFromCollection -description: - Remove a protected data from one of your collections in the Data Sharing smart - contract. This method transfers the ownership of the protected data back to - the owner. ---- - -# removeProtectedDataFromCollection - -Method to remove one of your protected data from a collection of yours in the -Data Sharing smart contract. - -To put it differently, this method will transfer the ownership of the protected -data back to you. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.removeProtectedDataFromCollection( - { - protectedData: '0x123abc...', - } -); -``` - -## Pre-conditions - -- You must be the owner of the collection of which the protected data is - currently part of. -- There should be no active subscriptions to this collection. -- There should be no active rentals of this protected data. - -## Parameters - -```ts twoslash -import { type RemoveFromCollectionParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -Address of the protected data you'd like to remove from your collection. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.removeProtectedDataFromCollection( - { - protectedData: '0x123abc...', // [!code focus] - } -); -``` - -## Return Value - -```ts twoslash -import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; -``` - -See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/manage-data/dataProtector/dataProtectorSharing/consume/consumeProtectedData.md b/src/manage-data/dataProtector/dataProtectorSharing/consume/consumeProtectedData.md deleted file mode 100644 index 944a7198..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/consume/consumeProtectedData.md +++ /dev/null @@ -1,341 +0,0 @@ ---- -title: consumeProtectedData -description: - Consume protected data in iExec by visualizing or downloading it. This method - involves generating RSA keys, interacting with iExec's Secret Management - Service, and securely retrieving encrypted data from IPFS. ---- - - - -# consumeProtectedData - -Method to consume a protected data, ie. visualize it or download it. - -This method does a few things under the hood: - -- Generate an RSA key pair and save it to indexedDB (if available) -- Push the public key to iExec SMS (Secret Management Service) (For more info, - see - [iExec Protocol documentation](https://protocol.docs.iex.ec/for-developers/confidential-computing/access-confidential-assets#secret-management-service-sms)) -- Wait for the consuming task to be executed by a worker. The iExec TEE dApp - being executed is the one given with the `app` parameter. The iExec TEE dApp - will get the protected data from IPFS, encrypt it with the public key - generated in the first step, and re-upload it to IPFS. -- Retrieve the encrypted data from IPFS and decrypt it with the private key - generated in the first step. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const consumeProtectedDataResult = - await dataProtectorSharing.consumeProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - }); -``` - -## Pre-conditions - -You need to either have: - -- an active rental for the protected data, -- an active subscription to the corresponding collection if the protected data - is part of the collection subscription bundle. - -## Parameters - -```ts twoslash -import { type ConsumeProtectedDataParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -Address of the protected data you'd like to visualize. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const consumeProtectedDataResult = - await dataProtectorSharing.consumeProtectedData({ - protectedData: '0x123abc...', // [!code focus] - app: '0x456def...', - }); -``` - -### app {#app-param} - -**Type:** `AddressOrENS` - -Address or ENS of the iExec TEE dApp that will be used to consume the protected -data. This iExec TEE dApp is the one that runs within an iExec worker. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const consumeProtectedDataResult = - await dataProtectorSharing.consumeProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', // [!code focus] - }); -``` - -::: tip - -For this `app` parameter you can use the "Protected data delivery TEE dApp": - -``` -0x1cb7D4F3FFa203F211e57357D759321C6CE49921 -``` - -
- -
Please note: This application can only be used within the -dataProtectorSharing module, as it is owned by the DataProtector Sharing smart contract. - -For more details, see [Apps whitelist](../../advanced/apps-whitelist). - -::: - -::: tip - -If you want to provide **your own TEE dApp**, you will need to create a -whitelist that contains your app. - -For more details, see [Apps whitelist](../../advanced/apps-whitelist). - -::: - -### path - -**Type:** `string` - -Under the hood, a protected data is a zip file. With this `path` parameter, you -can specify the file you're interested in. The zip file will be uncompressed for -you, and only the desired file will be given as the `result`. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const consumeProtectedDataResult = - await dataProtectorSharing.consumeProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - path: 'my-content', // [!code focus] - }); -``` - -### workerpool - -**Type:** `AddressOrENS` -**Default:** `prod-v8-bellecour.main.pools.iexec.eth` - -Address or ENS of the workerpool. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const consumeProtectedDataResult = - await dataProtectorSharing.consumeProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - workerpool: 'prod-v8-bellecour.main.pools.iexec.eth', // [!code focus] - }); -``` - -::: tip - -iExec currently offers a production workerpool located at the Ethereum Name -Service (ENS) address `prod-v8-bellecour.main.pools.iexec.eth`. This is the -default workerpool for running confidential computations on the iExec platform. - -::: - -### maxPrice - -**Type:** `number` -**Default:** `0` - -The maximum price (in nRLC) that a requester is willing to pay for each task -related to consuming the protected data. It is the sum of the application price, -dataset price, and workerpool price per task. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const consumeProtectedDataResult = - await dataProtectorSharing.consumeProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - maxPrice: 10, // [!code focus] - }); -``` - -### pemPublicKey - -**Type:** `string` - -If you have previously called `consumeProtectedData()` and saved the returned -public key, you can reuse it in further calls. - -Alternatively, you can generate a RSA keypair on your own. - -If a public key is provided, its corresponding private key needs also to be -provided. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const consumeProtectedDataResult = - await dataProtectorSharing.consumeProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - pemPublicKey: '-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----', // [!code focus] - }); -``` - -### pemPrivateKey - -**Type:** `string` - -If you have previously called `consumeProtectedData()` and saved the returned -private key, you can reuse it in further calls. - -Alternatively, you can generate a RSA keypair on your own. - -If a private key is provided, its corresponding public key needs also to be -provided. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const consumeProtectedDataResult = - await dataProtectorSharing.consumeProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - pemPrivateKey: - '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----', // [!code focus] - }); -``` - -### onStatusUpdate - -**Type:** `OnStatusUpdateFn` - -Callback function to be notified at intermediate steps. - - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const consumeProtectedDataResult = - await dataProtectorSharing.consumeProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - onStatusUpdate: ({ title, isDone }) => { // [!code focus] - console.log(title, isDone); // [!code focus] - }, // [!code focus] - }); -``` - - -You can expect this callback function to be called with the following titles: - -```ts -'FETCH_WORKERPOOL_ORDERBOOK'; -'PUSH_ENCRYPTION_KEY'; -'CONSUME_ORDER_REQUESTED'; -'CONSUME_TASK'; -'CONSUME_RESULT_DOWNLOAD'; -'CONSUME_RESULT_DECRYPT'; -``` - -## Return Value - -```ts twoslash -import { type ConsumeProtectedDataResponse } from '@iexec/dataprotector'; -``` - -### txHash - -`string` - -The transaction hash corresponding to the execution of the function. - -### dealId - -`string` - -Identifies the specific deal associated with this transaction. - -### taskId - -`string` - -Identifies the specific task associated with the deal. - -### result - -`ArrayBuffer` - -The actual content of the protected file. diff --git a/src/manage-data/dataProtector/dataProtectorSharing/data-sharing-sc.png b/src/manage-data/dataProtector/dataProtectorSharing/data-sharing-sc.png deleted file mode 100644 index 189c41087644a4fea1142a0e18d39a6bde0a751d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 93263 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

-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

0@7GQASvsf)WEB8EZx9i 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 diff --git a/src/manage-data/dataProtector/dataProtectorSharing/inside-a-collection.png b/src/manage-data/dataProtector/dataProtectorSharing/inside-a-collection.png deleted file mode 100644 index aac8c11a574e6cbad5f426f19f3813a0382d9edd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 113538 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 z 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 TvldJR{?t{pE)<@-cISTpV+`6% diff --git a/src/manage-data/dataProtector/dataProtectorSharing/read/getCollectionOwners.md b/src/manage-data/dataProtector/dataProtectorSharing/read/getCollectionOwners.md deleted file mode 100644 index f8bc117b..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/read/getCollectionOwners.md +++ /dev/null @@ -1,70 +0,0 @@ ---- -title: getCollectionOwners -description: - Method to get all collection owners with results ordered by creation timestamp - in descending order. ---- - -# getCollectionOwners - -Method to get all collection owners. - -Results of `CollectionOwner.collections` are ordered by -`collections.creationTimestamp` desc. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const collectionOwners = await dataProtectorSharing.getCollectionOwners(); -``` - -## Parameters - -```ts twoslash -import { type GetCollectionOwnersParams } from '@iexec/dataprotector'; -``` - -### limit - -**Type:** `number` -**Default:** `100` -**Range:** `[1...1000]` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const collectionOwners = await dataProtectorSharing.getCollectionOwners({ - limit: 100, // [!code focus] -}); -``` - -## Return Value - -```ts twoslash -import type { GetCollectionOwnersResponse } from '@iexec/dataprotector'; - -// Child types -import type { CollectionOwner, SubscriptionParams } from '@iexec/dataprotector'; -``` - -See -Type ↗️ - -### hasActiveSubscription - -`true` if you (logged-in user) have an active subscription to one of the -collections. diff --git a/src/manage-data/dataProtector/dataProtectorSharing/read/getCollectionSubscriptions.md b/src/manage-data/dataProtector/dataProtectorSharing/read/getCollectionSubscriptions.md deleted file mode 100644 index 40f15f26..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/read/getCollectionSubscriptions.md +++ /dev/null @@ -1,106 +0,0 @@ ---- -title: getCollectionSubscriptions -description: - Fetch all subscriptions for a specific collection or user in iExec. Get - detailed information about subscription activity based on collection ID. ---- - -# getCollectionSubscriptions - -Method to get all subscriptions for: - -- a specific collection -- a specific user - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const collectionActiveSubscriptions = - await dataProtectorSharing.getCollectionSubscriptions({ - collectionId: 9, - }); -``` - -## Parameters - -```ts twoslash -import { type GetCollectionSubscriptionsParams } from '@iexec/dataprotector'; -``` - -### subscriberAddress - -**Type:** `AddressOrENS` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const userActiveSubscriptions = - await dataProtectorSharing.getCollectionSubscriptions({ - subscriberAddress: '0x246bdf...', // [!code focus] - }); -``` - -### collectionId - -**Type:** `number` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const collectionActiveSubscriptions = - await dataProtectorSharing.getCollectionSubscriptions({ - collectionId: 9, // [!code focus] - }); -``` - -### includePastSubscriptions - -**Type:** `boolean` -**Default:** `false` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const userRentals = await dataProtectorSharing.getCollectionSubscriptions({ - subscriberAddress: '0x246bdf...', - includePastSubscriptions: true, // [!code focus] -}); -``` - -## Return Value - -```ts twoslash -import { type GetCollectionSubscriptionsResponse } from '@iexec/dataprotector'; - -// Child types -import { type CollectionSubscription } from '@iexec/dataprotector'; -``` - -See -Type ↗️ diff --git a/src/manage-data/dataProtector/dataProtectorSharing/read/getCollectionsByOwner.md b/src/manage-data/dataProtector/dataProtectorSharing/read/getCollectionsByOwner.md deleted file mode 100644 index 06970e70..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/read/getCollectionsByOwner.md +++ /dev/null @@ -1,89 +0,0 @@ ---- -title: getCollectionsByOwner -description: - Method to get all collections for a specific user with filtering and - pagination options. ---- - -# getCollectionsByOwner - -Method to get all collections for a specific user. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const userCollections = await dataProtectorSharing.getCollectionsByOwner({ - owner: '0xa0c15e...', -}); -``` - -## Parameters - -```ts twoslash -import { type GetCollectionsByOwnerParams } from '@iexec/dataprotector'; -``` - -### owner - -**Type:** `AddressOrENS` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const userCollections = await dataProtectorSharing.getCollectionsByOwner({ - owner: '0xa0c15e...', // [!code focus] -}); -``` - -### includeHiddenProtectedDatas - -**Type:** `boolean` -**Default:** `false` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const userCollectionsWithAllProtectedData = - await dataProtectorSharing.getCollectionsByOwner({ - owner: '0xa0c15e...', - includeHiddenProtectedDatas: true, // [!code focus] - }); -``` - -## Return Value - -```ts twoslash -import type { GetCollectionsByOwnerResponse } from '@iexec/dataprotector'; - -// Child types -import type { - CollectionWithProtectedDatas, - ProtectedDataInCollection, - SubscriptionParams, - RentingParams, - SellingParams, -} from '@iexec/dataprotector'; -``` - -See -Type ↗️ diff --git a/src/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataInCollections.md b/src/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataInCollections.md deleted file mode 100644 index 1e77e275..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataInCollections.md +++ /dev/null @@ -1,229 +0,0 @@ ---- -title: getProtectedDataInCollections -description: - Retrieve protected data from collections in iExec. Each protected data can - belong to only one collection at a time, with results ordered by creation - timestamp in descending order. ---- - -# getProtectedDataInCollections - -Method to get protected data that are in collections. - -A protected data can only be in one collection at a time. - -Results are ordered by `creationTimestamp` desc. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const protectedData = - await dataProtectorSharing.getProtectedDataInCollections(); -``` - -## Parameters - -```ts twoslash -import { type GetProtectedDataInCollectionsParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const oneProtectedData = - await dataProtectorSharing.getProtectedDataInCollections({ - protectedData: '0x123abc...', // [!code focus] - }); -``` - -### collectionId - -**Type:** `number` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const protectedDataByCollection = - await dataProtectorSharing.getProtectedDataInCollections({ - collectionId: 12, // [!code focus] - }); -``` - -### collectionOwner - -**Type:** `AddressOrENS` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const protectedDataByOwner = - await dataProtectorSharing.getProtectedDataInCollections({ - collectionOwner: '0x123...', // [!code focus] - }); -``` - -### createdAfterTimestamp - -**Type:** `number` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const latestProtectedData = - await dataProtectorSharing.getProtectedDataInCollections({ - createdAfterTimestamp: 1707237580, // Feb 6th, 2024 16:39:40 GMT // [!code focus] - }); -``` - -### isRentable - -**Type:** `boolean` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const rentableProtectedData = - await dataProtectorSharing.getProtectedDataInCollections({ - isRentable: true, // [!code focus] - }); -``` - -### isForSale - -**Type:** `boolean` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const protectedDataForSale = - await dataProtectorSharing.getProtectedDataInCollections({ - isForSale: true, // [!code focus] - }); -``` - -### isDistributed - -**Type:** `boolean` - -Used to filter protected data that are either for sale, renting or part of a -subscription. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const protectedDataForSale = - await dataProtectorSharing.getProtectedDataInCollections({ - isDistributed: true, // [!code focus] - }); -``` - -### page - -**Type:** `number` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const protectedData = await dataProtectorSharing.getProtectedDataInCollections({ - collectionId: 12, - page: 3, // [!code focus] - pageSize: 25, -}); -``` - -### pageSize - -**Type:** `number` -**Range:** `[10...1000]` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const protectedData = await dataProtectorSharing.getProtectedDataInCollections({ - collectionId: 12, - page: 3, - pageSize: 25, // [!code focus] -}); -``` - -## Return Value - -```ts twoslash -import type { GetProtectedDataInCollectionsResponse } from '@iexec/dataprotector'; - -// Child types -import type { - ProtectedDataInCollection, - RentingParams, - SellingParams, -} from '@iexec/dataprotector'; -``` - -See -Type ↗️ diff --git a/src/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataPricingParams.md b/src/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataPricingParams.md deleted file mode 100644 index 68e53d18..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataPricingParams.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: getProtectedDataPricingParams -description: - Get pricing parameters for renting a specific protected data in iExec. - Retrieve rental price and duration to determine the cost and terms for data - access. ---- - -# getProtectedDataPricingParams - -Method to get all distribution params for a protected data. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const pricingParams = await dataProtectorSharing.getProtectedDataPricingParams({ - protectedData: '0x123abc...', -}); -``` - -## Parameters - -```ts twoslash -import { type GetProtectedDataPricingParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -Address of the protected data you'd like to get the pricing params for. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const pricingParams = await dataProtectorSharing.getProtectedDataPricingParams({ - protectedData: '0x123abc...', // [!code focus] -}); -``` - -## Return Value - -```ts twoslash -import type { GetProtectedDataPricingParamsResponse } from '@iexec/dataprotector'; - -// Child types -import type { SubscriptionParams, RentingParams } from '@iexec/dataprotector'; -``` - -See -Type ↗️ diff --git a/src/manage-data/dataProtector/dataProtectorSharing/read/getRentals.md b/src/manage-data/dataProtector/dataProtectorSharing/read/getRentals.md deleted file mode 100644 index 7fe042ce..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/read/getRentals.md +++ /dev/null @@ -1,103 +0,0 @@ ---- -title: getRentals -description: - Retrieve all rentals for a specific protected data or user in iExec. Access - detailed rental information based on the protected data address. ---- - -# getRentals - -Method to get all rentals for: - -- a specific protected data -- a specific user - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const protectedDataActiveRentals = await dataProtectorSharing.getRentals({ - protectedData: '0x123abc...', -}); -``` - -## Parameters - -```ts twoslash -import { type GetRentalsParams } from '@iexec/dataprotector'; -``` - -### renterAddress - -**Type:** `AddressOrENS` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const userActiveRentals = await dataProtectorSharing.getRentals({ - renterAddress: '0x246bdf...', // [!code focus] -}); -``` - -### protectedData - -**Type:** `AddressOrENS` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const protectedDataActiveRentals = await dataProtectorSharing.getRentals({ - protectedData: '0x123abc...', // [!code focus] -}); -``` - -### includePastRentals - -**Type:** `boolean` -**Default:** `false` - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const userRentals = await dataProtectorSharing.getRentals({ - renterAddress: '0x246bdf...', - includePastRentals: true, // [!code focus] -}); -``` - -## Return Value - -```ts twoslash -import { type GetRentalsResponse } from '@iexec/dataprotector'; - -// Child types -import type { ProtectedDataRental } from '@iexec/dataprotector'; -``` - -See -Type ↗️ diff --git a/src/manage-data/dataProtector/dataProtectorSharing/renting.md b/src/manage-data/dataProtector/dataProtectorSharing/renting.md deleted file mode 100644 index c6597a68..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/renting.md +++ /dev/null @@ -1,42 +0,0 @@ ---- -title: Data Sharing - Renting -description: - Renting options for distributing protected data allow consumers to access data - for a set price and duration. The Data Sharing smart contract ensures renter - protection, guaranteeing access for the agreed term and enforcing rental - terms, even if modified by the owner. ---- - -# Data Sharing - Renting - -Rental agreements are one of the options given for distributing a collection -owner's protected data. A rental agreement has the following attributes: - -**Rental Price** - -The collection owner allows a consumer to pay a set price to access the data. -This is a one-time up-front cost. - -**Rental Duration** - -The collection owner allows the renter access to the data for a set period of -time. - -## Renter Protection - -The Data Sharing smart contract ensures the renter maintains access for the -duration of their rental term. Once the rental term is up, the renter loses -access to the protected data. This assurance is critical to the renting paradigm -to ensure trust between the data owner and the renter. - -## Modifying Rental Terms - -The collection owner has a few options once they list protected data for rent: - -- The owner may remove the rental terms and effectively de-list the protected - data -- The owner may also modify the rental price or duration - -Making either of these chances is effective immediately but only for future -rentals. The Data Sharing smart contract enforces any ongoing rental agreements -until the terms expire. diff --git a/src/manage-data/dataProtector/dataProtectorSharing/renting/removeProtectedDataFromRenting.md b/src/manage-data/dataProtector/dataProtectorSharing/renting/removeProtectedDataFromRenting.md deleted file mode 100644 index 7bc1b923..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/renting/removeProtectedDataFromRenting.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: removeProtectedDataFromRenting -description: - The removeProtectedDataFromRenting method allows the collection owner to - remove a protected data item from being rented. Active rentals will still be - honored until their rental period ends. ---- - -# removeProtectedDataFromRenting - -Method to remove a protected data from renting. - -If there are still active rentals to the protected data, these rentals will be -honored until the end of their rental period. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const notForRentingAnymoreResult = - await dataProtectorSharing.removeProtectedDataFromRenting({ - protectedData: '0x123abc...', - }); -``` - -## Parameters - -```ts twoslash -import { type RemoveProtectedDataFromRentingParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -Address of the protected data you'd like to remove from renting. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const notForRentingAnymoreResult = - await dataProtectorSharing.removeProtectedDataFromRenting({ - protectedData: '0x123abc...', // [!code focus] - }); -``` - -## Return Value - -```ts twoslash -import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; -``` - -See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/manage-data/dataProtector/dataProtectorSharing/renting/rentProtectedData.md b/src/manage-data/dataProtector/dataProtectorSharing/renting/rentProtectedData.md deleted file mode 100644 index d717bfbb..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/renting/rentProtectedData.md +++ /dev/null @@ -1,139 +0,0 @@ ---- -title: rentProtectedData -description: - The rentProtectedData method allows you to rent a protected data item by - specifying the price and duration. If the parameters don't match the current - listing, the SDK will not submit the transaction to avoid paying gas fees for - a transaction that would revert. ---- - -# rentProtectedData - -Method to rent a protected data. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const rentResult = await dataProtectorSharing.rentProtectedData({ - protectedData: '0x123abc...', - price: 1, // 1 nRLC - duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days -}); -``` - -::: tip - -Technically, `price` and `duration` parameters could be avoided. It is mainly a -protection against front-running "attacks", ie. if the collection owner changes -the price **at the same time** you rent the protected data, you would end up -paying more than expected. Passing the `price` here allows the SDK to ensure -you're paying the right price. If prices don't match, the SDK will throw an -error. - -::: - -## Parameters - -```ts twoslash -import { type RentProtectedDataParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -Address or ENS of the protected data that you'd like rent. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const rentResult = await dataProtectorSharing.rentProtectedData({ - protectedData: '0x123abc...', // [!code focus] - price: 1, // 1 nRLC - duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days -}); -``` - -### price - -**Type:** `number` - -Price of the rental for the protected data that you expect to rent. This -parameter ensures that you will not be front-run by the owner of the protected -data. The unit is in nano RLC (nRLC). - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const rentResult = await dataProtectorSharing.rentProtectedData({ - protectedData: '0x123abc...', - price: 1, // 1 nRLC // [!code focus] - duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days -}); -``` - -::: tip - -To get the renting price of the given protected data, you can use -[getProtectedDataPricingParams](../read/getProtectedDataPricingParams.md). - -::: - -### duration - -**Type:** `number` - -Duration of the rental for the protected data that you expect to rent. This -parameter ensures that you will not be front-run by the owner of the protected -data. The unit is in seconds. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const rentResult = await dataProtectorSharing.rentProtectedData({ - protectedData: '0x123abc...', - price: 1, // 1 nRLC - duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days // [!code focus] -}); -``` - -::: tip - -To get the renting duration of the given protected data, you can use -[getProtectedDataPricingParams](../read/getProtectedDataPricingParams.md). - -::: - -## Return Value - -```ts twoslash -import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; -``` - -See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataRentingParams.md b/src/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataRentingParams.md deleted file mode 100644 index 52cffaad..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataRentingParams.md +++ /dev/null @@ -1,117 +0,0 @@ ---- -title: setProtectedDataRentingParams -description: - The setProtectedDataRentingParams method allows you to set or update the - renting parameters (price and duration) for a protected data item. If the data - isn't listed for rent yet, it will be set for renting with the provided terms. ---- - -# setProtectedDataRentingParams - -Method to update a protected data renting params. - -If the protected data is not yet available for renting, it will be set for -renting. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setForRentingResult = - await dataProtectorSharing.setProtectedDataRentingParams({ - protectedData: '0x123abc...', - price: 1, // 1 nRLC - duration: 60 * 60 * 24 * 30, // 30 days - }); -``` - -## Parameters - -```ts twoslash -import { type SetProtectedDataRentingParams } from '@iexec/dataprotector'; -``` - -### protectedData - -`AddressOrENS` - -Address of the protected data you'd like to set renting parameters. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setForRentingResult = - await dataProtectorSharing.setProtectedDataRentingParams({ - protectedData: '0x123abc...', // [!code focus] - price: 1, // 1 nRLC - duration: 60 * 60 * 24 * 30, // 30 days - }); -``` - -### price - -`number` - -The price in nano RLC (nRLC) you ask from someone who wants to rent the -protected data. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setForRentingResult = - await dataProtectorSharing.setProtectedDataRentingParams({ - protectedData: '0x123abc...', - price: 1, // 1 nRLC // [!code focus] - duration: 60 * 60 * 24 * 30, // 30 days - }); -``` - -### duration - -`number` - -The duration of the rental in seconds. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setForRentingResult = - await dataProtectorSharing.setProtectedDataRentingParams({ - protectedData: '0x123abc...', - price: 1, // 1 nRLC - duration: 60 * 60 * 24 * 30, // 30 days // [!code focus] - }); -``` - -## Return Value - -```ts twoslash -import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; -``` - -See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataToRenting.md b/src/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataToRenting.md deleted file mode 100644 index c430fbfb..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataToRenting.md +++ /dev/null @@ -1,118 +0,0 @@ ---- -title: setProtectedDataToRenting -description: - The setProtectedDataToRenting method allows a protected data item to be listed - for rent. This method sets the price and duration for future rentals. If the - data is already listed for rent, it updates the terms accordingly. ---- - -# setProtectedDataToRenting - -Method to allow a protected data to be rented. - -If you call this method on a protected data that is already set for renting, it -will update the `price` and `duration` parameters, and will apply to future -rentals. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setForRentingResult = - await dataProtectorSharing.setProtectedDataToRenting({ - protectedData: '0x123abc...', - price: 1, // 1 nRLC - duration: 60 * 60 * 24 * 30, // 30 days - }); -``` - -## Parameters - -```ts twoslash -import { type SetProtectedDataToRentingParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -Address of the protected data you'd like to set renting parameters for. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setForRentingResult = - await dataProtectorSharing.setProtectedDataToRenting({ - protectedData: '0x123abc...', // [!code focus] - price: 1, // 1 nRLC - duration: 60 * 60 * 24 * 30, // 30 days - }); -``` - -### price - -**Type:** `number` - -The price in nano RLC (nRLC) you ask from someone who wants to rent the -protected data. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setForRentingResult = - await dataProtectorSharing.setProtectedDataToRenting({ - protectedData: '0x123abc...', - price: 1, // 1 nRLC // [!code focus] - duration: 60 * 60 * 24 * 30, // 30 days - }); -``` - -### duration - -**Type:** `number` - -The duration of the rental in seconds. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setForRentingResult = - await dataProtectorSharing.setProtectedDataToRenting({ - protectedData: '0x123abc...', - price: 1, // 1 nRLC - duration: 60 * 60 * 24 * 30, // 30 days // [!code focus] - }); -``` - -## Return Value - -```ts twoslash -import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; -``` - -See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/manage-data/dataProtector/dataProtectorSharing/selling.md b/src/manage-data/dataProtector/dataProtectorSharing/selling.md deleted file mode 100644 index 63f377ec..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/selling.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: Data Sharing - Selling -description: - Learn how to list protected data for sale, transfer ownership permanently - through blockchain transactions, and manage data listing operations. ---- - -# Data Sharing - Selling - -The owner of protected data may list the data for sale. Upon completion of a -sale, ownership of the protected data transfers to the buyer. This is a -permanent operation enforced by the blockchain and executed by the Data Sharing -smart contract. The data owner may de-list the protected data at any point until -a sale is completed. The new owner of the protected data has full rights to -distribute the protected data through any channels they desire. diff --git a/src/manage-data/dataProtector/dataProtectorSharing/selling/buyProtectedData.md b/src/manage-data/dataProtector/dataProtectorSharing/selling/buyProtectedData.md deleted file mode 100644 index 7a8bd0c9..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/selling/buyProtectedData.md +++ /dev/null @@ -1,164 +0,0 @@ ---- -title: buyProtectedData -description: - Allows a user to purchase protected data that is listed for sale. Upon - successful purchase, the buyer gains full ownership and can distribute or keep - the data as desired. ---- - -# buyProtectedData - -Method to buy a protected data that is for sale. - -"Buying" here means to get ownership of the protected data. - -After buying a protected data, you'll be free to distribute it again at your own -terms, or to keep it for yourself. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.buyProtectedData({ - protectedData: '0x123abc...', - price: 1, -}); -``` - -## Parameters - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -import { type BuyProtectedDataParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -Address of the protected data you'd like to buy. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.buyProtectedData({ - protectedData: '0x123abc...', // [!code focus] - price: 1, -}); -``` - -### price - -**Type:** `number` - -Price of the protected data that you expect to buy. This parameter ensures that -you will not be front-run by the owner of the protected data. The unit is in -nano RLC (nRLC). - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.buyProtectedData({ - protectedData: '0x123abc...', - price: 1, // [!code focus] -}); -``` - -### addToCollectionId - -**Type:** `number` - -Collection ID to which you'd like to transfer the ownership of the protected -data. -The Data Sharing smart contract will still be the technical owner of the -protected data, but you'll fully own it as you own the collection to which it'll -transferred. If you use this param the `addOnlyAppWhitelist` is mandatory -because you must specify which `addOnlyAppWhitelist` will be able to consume -your protected data. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.buyProtectedData({ - protectedData: '0x123abc...', - price: 1, - addToCollectionId: 12, // [!code focus] - addOnlyAppWhitelist: '0xdef456...', -}); -``` - -### addOnlyAppWhitelist - -**Type:** `AddressOrENS` - -Address of the whitelist smart contract that contains applications allowed to -consume the protected data. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.buyProtectedData({ - protectedData: '0x123abc...', - price: 1, - addToCollectionId: 12, - addOnlyAppWhitelist: '0xdef456...', // [!code focus] -}); -``` - -::: tip - -For this `addOnlyAppWhitelist`, you are free to use -`0x256bcd881c33bdf9df952f2a0148f27d439f2e64` that contains apps created for the -purpose of Content Creator usecase-demo. This `addOnlyAppWhitelist` is managed -by iExec. - -For more details on how to create and manage appsWhitelist, see -[Apps whitelist](../../advanced/apps-whitelist). - -::: - -## Return Value - -```ts twoslash -import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; -``` - -See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/manage-data/dataProtector/dataProtectorSharing/selling/removeProtectedDataForSale.md b/src/manage-data/dataProtector/dataProtectorSharing/selling/removeProtectedDataForSale.md deleted file mode 100644 index 73b7b71a..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/selling/removeProtectedDataForSale.md +++ /dev/null @@ -1,62 +0,0 @@ ---- -title: removeProtectedDataForSale -description: - Method to remove a protected data from sale listing, preventing further - purchase transactions. ---- - -# removeProtectedDataForSale - -Method to remove a protected data for sale. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const notForSaleAnymoreResult = - await dataProtectorSharing.removeProtectedDataForSale({ - protectedData: '0x123abc...', - }); -``` - -## Parameters - -```ts twoslash -import { type RemoveProtectedDataForSaleParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -Address of the protected data that you'd like to remove for sale. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const notForSaleAnymoreResult = - await dataProtectorSharing.removeProtectedDataForSale({ - protectedData: '0x123abc...', // [!code focus] - }); -``` - -## Return Value - -```ts twoslash -import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; -``` - -See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/manage-data/dataProtector/dataProtectorSharing/selling/setProtectedDataForSale.md b/src/manage-data/dataProtector/dataProtectorSharing/selling/setProtectedDataForSale.md deleted file mode 100644 index 7d0081d9..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/selling/setProtectedDataForSale.md +++ /dev/null @@ -1,89 +0,0 @@ ---- -title: setProtectedDataForSale -description: - Allows a data owner to list their protected data for sale by setting a price. - Upon successful sale, ownership is transferred to the buyer, who can choose - their own pricing or retain the data for personal use." ---- - -# setProtectedDataForSale - -Method to set a protected data for sale. - -During a successful sale, **the ownership** of the protected data **is -transferred** to the buyer. The buyer will then be able to set their own pricing -parameters, or simply keep the protected data for their own use. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setForSaleResult = await dataProtectorSharing.setProtectedDataForSale({ - protectedData: '0x123abc...', - price: 2, // 2 nRLC -}); -``` - -## Parameters - -```ts twoslash -import { type SetProtectedDataForSaleParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -Address of the protected data that you'd like to set for sale. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setForSaleResult = await dataProtectorSharing.setProtectedDataForSale({ - protectedData: '0x123abc...', // [!code focus] - price: 2, // 2 nRLC -}); -``` - -### price - -**Type:** `number` - -The price in nano RLC (nRLC) you ask from someone who wants to buy the protected -data. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setForSaleResult = await dataProtectorSharing.setProtectedDataForSale({ - protectedData: '0x123abc...', - price: 2, // 2 nRLC // [!code focus] -}); -``` - -## Return Value - -```ts twoslash -import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; -``` - -See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/manage-data/dataProtector/dataProtectorSharing/subscription.md b/src/manage-data/dataProtector/dataProtectorSharing/subscription.md deleted file mode 100644 index 206420ff..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/subscription.md +++ /dev/null @@ -1,60 +0,0 @@ ---- -title: Data Sharing - Subscription -description: - Learn how to manage subscription agreements for protected data on the iExec - platform. Understand subscription pricing and duration, along with options for - modifying terms and protecting subscribers' access. ---- - -# Data Sharing - Subscription - -Subscription agreements are one of the options for distributing a collection -owner's protected data. Similar to the rental distribution terms, a subscription -agreement has the following attributes: - -**Subscription Price** - -The collection owner allows a subscriber to pay a set price to access the data. -The subscription fee is a one-time payment, not a recurring one. Subscriptions -do not auto-renew. - -**Subscription Duration** - -The collection owner allows the subscriber access to the data for a set period -of time. - -## The Subscription Bundle - -Where subscriptions differ from rental distribution terms is that a subscription -may cover more than one protected data. We use the term `subscription bundle` -for this grouping of data. The Data Sharing smart contract supports various -different methods for altering the contents of a subscription bundle. Once a -user purchases a subscription to the bundle, they retain access to all protected -data in the bundle until their subscription term expires. The owner may add new -data to the bundle but may not remove protected data from the bundle as long as -there remains at least one subscriber to it. - -## Subscriber Protection - -The Data Sharing smart contract ensures the subscriber maintains access for the -duration of their subscription term. Once the subscription expires, the consumer -loses access to all protected data. The subscriber has the option to pay the -subscription fee again to retain access. The data owner may update the -subscription fee and duration, but any data included in the subscription remains -available. This assurance is critical to the subscription paradigm to ensure -trust between the data owner and the subscriber. - -## Modifying Subscription Terms - -The collection owner has a few options to manage their subscription bundles: - -- The owner may remove the subscription terms and effectively de-list the - subscription bundle -- The owner may modify the subscription price or duration -- The owner may add additional protected data to the subscription -- The owner may remove protected data from the subscription only if there are no - active subscribers - -Making any of these changes is effective immediately. For any existing -subscribers, updates to the subscription terms take effect upon renewal of the -subscription. diff --git a/src/manage-data/dataProtector/dataProtectorSharing/subscription/removeProtectedDataFromSubscription.md b/src/manage-data/dataProtector/dataProtectorSharing/subscription/removeProtectedDataFromSubscription.md deleted file mode 100644 index a093cdb0..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/subscription/removeProtectedDataFromSubscription.md +++ /dev/null @@ -1,77 +0,0 @@ ---- -title: removeProtectedDataFromSubscription -description: - Remove a protected data from your subscription in iExec. Stop providing the - data to current and future subscribers, removing it from subscription access. ---- - -# removeProtectedDataFromSubscription - -Method to remove a protected data from your subscription. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = - await dataProtectorSharing.removeProtectedDataFromSubscription({ - protectedData: '0x123abc...', - }); -``` - -## Pre-conditions - -- You must be the owner of the collection of which the protected data is - currently part of. -- There should be no active subscriptions to this collection. -- The protected data should be part of your subscription. - -## Parameters - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -import { type RemoveProtectedDataFromSubscriptionParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -Address of the protected data you'd like to remove from subscription. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = - await dataProtectorSharing.removeProtectedDataFromSubscription({ - protectedData: '0x123abc...', // [!code focus] - }); -``` - -## Return Value - -```ts twoslash -import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; -``` - -See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/manage-data/dataProtector/dataProtectorSharing/subscription/setProtectedDataToSubscription.md b/src/manage-data/dataProtector/dataProtectorSharing/subscription/setProtectedDataToSubscription.md deleted file mode 100644 index 34673ef0..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/subscription/setProtectedDataToSubscription.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: setProtectedDataToSubscription -description: - Add your protected data to a subscription on the iExec platform. Allow active - subscribers to access your data easily by linking it to your subscribers to - access your data easily. ---- - -# setProtectedDataToSubscription - -Method to set a protected data as part of your subscription. - -Any user who has an active subscription to your collection will be able to -consume this protected data. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setToSubscriptionResult = - await dataProtectorSharing.setProtectedDataToSubscription({ - protectedData: '0x123abc...', - }); -``` - -## Parameters - -```ts twoslash -import { type SetProtectedDataToSubscriptionParams } from '@iexec/dataprotector'; -``` - -### protectedData - -**Type:** `AddressOrENS` - -Address of the protected data you'd like to be part of your subscription. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setToSubscriptionResult = - await dataProtectorSharing.setProtectedDataToSubscription({ - protectedData: '0x123abc...', // [!code focus] - }); -``` - -## Return Value - -```ts twoslash -import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; -``` - -See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/manage-data/dataProtector/dataProtectorSharing/subscription/setSubscriptionParams.md b/src/manage-data/dataProtector/dataProtectorSharing/subscription/setSubscriptionParams.md deleted file mode 100644 index 0d360eda..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/subscription/setSubscriptionParams.md +++ /dev/null @@ -1,114 +0,0 @@ ---- -title: setSubscriptionParams -description: - Set subscription parameters for your data collection on the iExec platform. - Define pricing, duration, and manage access to your protected data efficiently - using the Data Sharing smart contract. ---- - -# setSubscriptionParams - -Method to set subscription parameters for a given collection of yours. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setSubscriptionParamsResult = - await dataProtectorSharing.setSubscriptionParams({ - collectionId: 12, - price: 2, // 2 nRLC - duration: 60 * 60 * 24 * 30, // 30 days - }); -``` - -## Parameters - -```ts twoslash -import { type SetSubscriptionParams } from '@iexec/dataprotector'; -``` - -### collectionId - -**Type:** `number` - -Collection ID to which you'd like to set the subscription params. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setSubscriptionParamsResult = - await dataProtectorSharing.setSubscriptionParams({ - collectionId: 12, // [!code focus] - price: 2, // 2 nRLC - duration: 60 * 60 * 24 * 30, // 30 days - }); -``` - -### price - -**Type:** `number` - -The price in nano RLC (nRLC) it's going to cost a subscriber to access your -collection. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setSubscriptionParamsResult = - await dataProtectorSharing.setSubscriptionParams({ - collectionId: 12, - price: 2, // 2 nRLC // [!code focus] - duration: 60 * 60 * 24 * 30, // 30 days - }); -``` - -### duration - -**Type:** `number` - -The duration (in seconds) for a period of subscription. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const setSubscriptionParamsResult = - await dataProtectorSharing.setSubscriptionParams({ - collectionId: 12, - price: 2, // 2 nRLC - duration: 60 * 60 * 24 * 30, // 30 days // [!code focus] - }); -``` - -## Return Value - -```ts twoslash -import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; -``` - -See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/manage-data/dataProtector/dataProtectorSharing/subscription/subscribeToCollection.md b/src/manage-data/dataProtector/dataProtectorSharing/subscription/subscribeToCollection.md deleted file mode 100644 index 2ea2eac2..00000000 --- a/src/manage-data/dataProtector/dataProtectorSharing/subscription/subscribeToCollection.md +++ /dev/null @@ -1,135 +0,0 @@ ---- -title: subscribeToCollection -description: - Subscribe to a collection on iExec and gain access to both current and future - protected data. Manage your subscription with a fixed price and duration, with - no automatic renewal, using the Data Sharing smart contract. ---- - -# subscribeToCollection - -Method to subscribe to a collection. - -You subscribe for a certain price and duration. **The subscription will not -automatically renew.** - -With an active subscription, you'll have access to current **and future** -protected data. - -## Usage - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.subscribeToCollection({ - collectionId: 12, - price: 1, // 1 nRLC - duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days -}); -``` - -::: tip - -Technically, `price` and `duration` parameters could be avoided. It is mainly a -protection against front-running "attacks", ie. if the collection owner changes -the price **at the same time** you subscribe to the collection, you would end up -paying more than expected. Passing the `price` here allows the SDK to ensure -you're paying the right price. If prices don't match, the SDK will throw an -error. - -::: - -## Pre-conditions - -- The collection must be available for subscription, ie. the collection owner - must have set a price and a duration. - -## Parameters - -```ts twoslash -import { type SubscribeToCollectionParams } from '@iexec/dataprotector'; -``` - -### collectionId - -**Type:** `number` - -Collection ID to which you'd like to subscribe. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.subscribeToCollection({ - collectionId: 12, // [!code focus] - price: 1, // 1 nRLC - duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days -}); -``` - -### price - -**Type:** `number` - -Price of the rental for the protected data that you expect to rent. This -parameter ensures that you will not be front-run by the owner of the protected -data. The unit is in nano RLC (nRLC). - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.subscribeToCollection({ - collectionId: 12, - price: 1, // 1 nRLC // [!code focus] - duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days -}); -``` - -### duration - -**Type:** `number` - -Duration of the rental for the protected data that you expect to rent. This -parameter ensures that you will not be front-run by the owner of the protected -data. The unit is in seconds. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -const { txHash } = await dataProtectorSharing.subscribeToCollection({ - collectionId: 12, - price: 1, // 1 nRLC - duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days // [!code focus] -}); -``` - -## Return Value - -```ts twoslash -import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; -``` - -See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/manage-data/dataProtector/getting-started.md b/src/manage-data/dataProtector/getting-started.md deleted file mode 100644 index d4f77409..00000000 --- a/src/manage-data/dataProtector/getting-started.md +++ /dev/null @@ -1,249 +0,0 @@ ---- -title: Getting Started -description: DataProtector getting started ---- - -# Getting Started - -[![GitHub package.json version (branch)](https://img.shields.io/badge/npm-2.0.0--beta-green)](https://www.npmjs.com/package/@iexec/dataprotector) - -## Overview - -### Prerequisites - -Before getting started, ensure that you have the following installed on your -system: - -\- [**Node.js**](https://nodejs.org/en/) version 18 or higher - -\- [**NPM**](https://docs.npmjs.com/) (Node.js package manager) - -### Installation - -::: code-group - -```sh [npm] -npm install @iexec/dataprotector -``` - -```sh [yarn] -yarn add @iexec/dataprotector -``` - -```sh [pnpm] -pnpm add @iexec/dataprotector -``` - -```sh [bun] -bun add @iexec/dataprotector -``` - -::: - -**This package is an ESM package. Your project needs to use ESM too.** - [**Read more**](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) - -If you use it with **Webpack**, some polyfills will be needed. You can find a -minimal working project -[here](https://github.com/iExecBlockchainComputing/dataprotector-sdk/tree/115b797cf62dcff0f41e2ba783405d5083d78922/packages/demo/browser-webpack). - -### Instantiate SDK - -Depending on your project's requirements, you can instantiate the SDK using the -umbrella module for full functionality or opt for one of the submodules to -access specific sets of features. - -#### Instantiate Using the Umbrella Module - -For projects requiring the full functionality of the SDK, including both core -and sharing functions. - -::: code-group - -```ts twoslash [Browser] -declare global { - interface Window { - ethereum: any; - } -} -// ---cut--- -import { IExecDataProtector } from '@iexec/dataprotector'; - -const web3Provider = window.ethereum; -// Instantiate using the umbrella module for full functionality -const dataProtector = new IExecDataProtector(web3Provider); - -const dataProtectorCore = dataProtector.core; -const dataProtectorSharing = dataProtector.sharing; -``` - -```ts twoslash [NodeJS] -import { IExecDataProtector, getWeb3Provider } from '@iexec/dataprotector'; - -// Get Web3 provider from a private key -const web3Provider = getWeb3Provider('YOUR_PRIVATE_KEY'); - -// Instantiate using the umbrella module for full functionality -const dataProtector = new IExecDataProtector(web3Provider); - -const dataProtectorCore = dataProtector.core; // access to core methods -const dataProtectorSharing = dataProtector.sharing; // access to methods -``` - -::: - -#### Instantiate Only the `Core` Module - -For projects focusing solely on core data protection functions. - -::: code-group - -```ts twoslash [Browser] -declare global { - interface Window { - ethereum: any; - } -} -// ---cut--- -import { IExecDataProtectorCore } from '@iexec/dataprotector'; - -const web3Provider = window.ethereum; -// Instantiate only the Core module -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -``` - -```ts twoslash [NodeJS] -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -// Get Web3 provider from a private key -const web3Provider = getWeb3Provider('YOUR_PRIVATE_KEY'); - -// Instantiate only the Core module -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -``` - -::: - -#### Instantiate Only the `Sharing` Module - -For projects that need access management functions specifically. - -::: code-group - -```ts twoslash [Browser] -declare global { - interface Window { - ethereum: any; - } -} -// ---cut--- -import { IExecDataProtectorSharing } from '@iexec/dataprotector'; - -const web3Provider = window.ethereum; -// Instantiate only the Sharing module -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -``` - -```ts twoslash [NodeJS] -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -// Get Web3 provider from a private key -const web3Provider = getWeb3Provider('YOUR_PRIVATE_KEY'); - -// Instantiate only the Sharing module -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -``` - -::: - -#### Instantiate Without a Web3 Provider - -For projects that only require read functions, you can instantiate the SDK -without a Web3 provider. - -::: code-group - -```ts twoslash [Singleton Modules] -import { - IExecDataProtectorSharing, - IExecDataProtectorCore, -} from '@iexec/dataprotector'; - -// Instantiate only the Core module for read-only core methods -const dataProtectorCore = new IExecDataProtectorCore(); -// Instantiate only the Sharing module for read-only sharing methods -const dataProtectorSharing = new IExecDataProtectorSharing(); -``` - -```ts twoslash [Umbrella Module] -import { IExecDataProtector } from '@iexec/dataprotector'; - -// Instantiate using the umbrella module for read-only functions -const dataProtector = new IExecDataProtector(); - -// Access to read-only core methods -const dataProtectorCore = dataProtector.core; -// Access to read-only sharing methods -const dataProtectorSharing = dataProtector.sharing; -``` - -::: - -#### Advanced Configuration - -To add optional parameters, see -[advanced configuration](./advanced/advanced-configuration.md). - -::: info - -🧪 While protected data are processed in **TEE** by **intel SGX** technology by -default, `@iexec/dataprotector` can be configured to create and process -protected data in the experimental **intel TDX** environment. - -For more details see: - -- [configure DataProtector TDX](./advanced/advanced-configuration.md#iexecoptions) -- [create TDX protected data](./dataProtectorCore/protectData.md#usage) -- [process TDX protected data](./dataProtectorCore/processProtectedData.md#workerpool) - -⚠️ Keep in mind: TDX mode is experimental and can be subject to instabilities or -discontinuity. - -::: - -## Sandbox - - - - - - - diff --git a/src/manage-data/dataProtector/migrate-from-v1.md b/src/manage-data/dataProtector/migrate-from-v1.md deleted file mode 100644 index 721fc966..00000000 --- a/src/manage-data/dataProtector/migrate-from-v1.md +++ /dev/null @@ -1,146 +0,0 @@ ---- -title: Migrate from V1 to V2 -description: - Follow this guide to migrate your DataProtector project from v1 to v2beta with - the latest npm package ---- - -# Migrate from V1 to V2 - -::: tip - -This page concerns projects created with DataProtector prior or equal to version -1.0.0 - -::: - -```sh -npm install @iexec/dataprotector@latest -``` - -## Constructor - -The instantiation process has been updated to accommodate **new modular -architecture** introduced in version 2. This change allows for more flexibility -and enables the use of specific functionalities tailored to the developers' -needs. - -When instantiating the IExecDataProtector object, reference the _dataProtector_ -property to use core methods. Newer versions allow to use extended methods using -the _dataProtectorSharing_ property. - -```js -// 1.0.0 and before -const dataProtector = new IExecDataProtector(web3Provider); // [!code --] - -// AFTER 2.0.0 -// with Umbrella Module -const dataProtector = new IExecDataProtector(web3Provider).dataProtector; // [!code ++] -// Or with Core Module -const dataProtector = new IExecDataProtectorCore(web3Provider); // [!code ++] -``` - -## Methods - -Some methods were renamed in order to standardize the SDK, they still provide -the same functionalities as before. - -### Rename `fetchProtectedData` & Add New Filtering Param - -```js -await dataProtector.fetchProtectedData({ // [!code --] -await dataProtector.getProtectedData({ // [!code ++] - owner: '0xa0c15e...', - creationTimestampGte: 1707237580, // Feb 6th, 2024 16:39:40 GMT // [!code ++] -}); -``` - -### Rename `fetchGrantedAccess` - -```js -await dataProtector.fetchGrantedAccess({ // [!code --] -await dataProtector.getGrantedAccess({ // [!code ++] - protectedData: '0x123abc...', - authorizedApp: '0x456def...', - owner: '0xa0c15e...', -}); -``` - -### Removed `protectDataObservable` - -The `protectDataObservable` function has been removed and its functionality has -been integrated into the `protectData` method with a new optional parameter. -This parameter is a callback function that provides status updates during the -data protection process. This change simplifies the API and enhances flexibility -in handling the protection process status updates. - - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const protectedData = await dataProtectorCore.protectData({ - name: 'myEmail', - data: { - email: 'example@gmail.com', - }, - onStatusUpdate: ({ title, isDone }) => { // [!code ++] - console.log(title, isDone); // [!code ++] - }, // [!code ++] -}); -``` - - -### Removed `revokeAllAccessObservable` - -Similarly, the `revokeAllAccessObservable` method has been replaced for -`revokeAllAccess` which does the same thing as `revokeAllAccessObservable` but -includes an optional callback function parameter. This callback allows -developers to receive feedback about the revocation status of the process, -providing more control and better handling of the process. - - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const allAccessRevoked = await dataProtectorCore.revokeAllAccess({ - protectedData: '0x123abc...', - onStatusUpdate: ({ title, isDone }) => { // [!code ++] - console.log(title, isDone); // [!code ++] - }, // [!code ++] -}); - -``` - - -::: tip - -The introduction of callback functions in `protectData` and `revokeAllAccess` -methods allows for real-time status updates, making the data protection and -access revocation processes more interactive and manageable. - -::: - -## Protected Data Schema - -The serialization of the data protected by `protectData()` has been changed to -support a wider range of numbers, and extend the support for processing -protected data in non-JS-based applications. - -The new serialization mechanism is based on the [borsh](https://borsh.io/) -specification. - -Consequently, the data schemas associated with protected data have changed. - -| data type | v1 data schema | v2 data schema | -| --------- | ----------------------------------------------- | ---------------------------------------------- | -| boolean | `"boolean"` | `"bool"` | -| number | `"number"`
restricted to JS safe integers | `"f64"` | -| bigint | not supported | `"i128"`
restricted to 128 bits integers | -| string | `"string"` | `"string"` | -| binary | detected mime type | detected mime type | diff --git a/src/manage-data/dataProtector/types.md b/src/manage-data/dataProtector/types.md deleted file mode 100644 index db89799b..00000000 --- a/src/manage-data/dataProtector/types.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -title: Types -description: - Complete reference for DataProtector types including GrantedAccess, - ProtectedData, Collection, and other essential data structures. ---- - -# Types - -Types in DataProtector. - -## 🔑 GrantedAccess - -### dataset: `string` - -- Address of the `protectedData` containing user data - -### datasetprice: `string` - -- Price (iun nRLC) to charge the user specified in `requesterrestrict` for each - use of this `protectedData` - -### volume: `string` - -- Number of authorized uses of this `protectedData`; each use decrements this - counter - -### tag: `string` - -- Defines whether a `protectedData` is usable in a TEE environment; `0x00` is - TEE while `0x03` is non-TEE - -### apprestrict: `string` - -- Address of the authorized application; a value of 0x0 indicates any - application may access this data - -### requesterrestrict: `string` - -- Address of the requester authorized to use this `protectedData` in workloads; - a value of 0x0 indicates any requester may use this data - -### workerpoolrestrict: `string` - -- Address of the decentralized infrastructure (worker pool) authorized to - execute the application; a value of 0x0 indicates any worker pool may access - this data - -### salt: `string` - -- Random value to make an order unique and reusable as nonce in a blockchain - transaction - -### sign: `string` - -- Order signature of all the `grantedAccess` fields - -## 🔐 ProtectedData - -### name: `string` - -- Name specified when the protected data was created. This piece of information - is public and visible on-chain. - -### address: `Address` - -- Ethereum address of the protected data. - -### owner: `Address` - -- Ethereum address of the protected data owner. - -### schema: `DataSchema` - -- Data schema for the protected data as defined when the protected data was - created (see [protectedData](./dataProtectorCore/protectData.md)). `schema` - provides a structured representation of the protected data format and - attributes. This field plays a crucial role in understanding and interpreting - the underlying structure of the sensitive information. - -### creationTimestamp: `number` - -- Timestamp specifying when the protected data was created, expressed in - milliseconds since the epoch. This timestamp provides precise information - about the moment of creation and can be used for chronological ordering or - time-based operations. - -### multiaddr: `string` | `undefined` - -- The multiaddr field is the IPFS path of your encrypted data. - -## ❌ RevokedAccess - -### access: [`GrantedAccess`](#-grantedaccess) - -- The granted access that was revoked. - -### txHash: `string` - -- The ID of the transaction that happened on iExec's side chain. You may view - details on the transaction using the - [iExec explorer](https://explorer.iex.ec). - - -_Hash example:_ `0xc9c2d58fc01fe54149b7daf49a0026d4ab1fdd3d10fb7c76350790fff03fe24d` - - -You can read more about he iExec Explorer -[here](https://protocol.docs.iex.ec/for-developers/toolbox/iexec-explorer). - -## ✅ SuccessWithTransactionHash - -### txHash: `string` - -- The hash of the transaction that happened on iExec's side chain. You may view - details on the transaction using the - [iExec explorer](https://explorer.iex.ec). - - -_Hash example:_ `0xc9c2d58fc01fe54149b7daf49a0026d4ab1fdd3d10fb7c76350790fff03fe24d` - - -You can read more about he iExec Explorer -[here](https://protocol.docs.iex.ec/for-developers/toolbox/iexec-explorer). diff --git a/src/manage-data/guides/handle-schemas-dataset-types.md b/src/manage-data/guides/handle-schemas-dataset-types.md deleted file mode 100644 index 17d865cb..00000000 --- a/src/manage-data/guides/handle-schemas-dataset-types.md +++ /dev/null @@ -1,271 +0,0 @@ ---- -title: Handle Schemas and Dataset Types -description: - Learn how schemas work in DataProtector and how to use them in your iApps ---- - -# 🏷️ Handle Schemas and Dataset Types - -**Schemas are like content labels that describe what's inside your protected -data.** - -They define the structure and types of your data automatically when you protect -it, making it easy for iApps to know what they're working with. - -Think of schemas as **data fingerprints** - they tell iApps "this protected data -contains an email address and a phone number" without revealing the actual -values. - -## How Schemas Work - -When you protect data with DataProtector, the SDK automatically analyzes your -JSON object and generates a schema. **No manual schema definition needed** - -it's all handled for you. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const protectedData = await dataProtectorCore.protectData({ - name: 'User Contact', - data: { - email: 'alice@example.com', - phoneNumber: '+1234567890', - preferences: { - newsletter: true, - notifications: false, - }, - }, -}); - -console.log('✅ Protected data created!'); -console.log('📍 Address:', protectedData.address); -``` - -**🏷️ Generated Schema:** - -```json -{ - "email": "string", - "phoneNumber": "string", - "preferences": { - "newsletter": "bool", - "notifications": "bool" - } -} -``` - -::: info Schema Structure - -The schema automatically maps your data structure to types that iApps can -understand and validate. - -::: - -## Supported Data Types - -The schema automatically detects these types: - -| Type | Description | Example | -| ------------------------------- | -------------- | --------------------- | -| `string` | Text data | `"alice@example.com"` | -| `bool` | Boolean values | `true`, `false` | -| `f64` | Numbers | `42`, `3.14` | -| `i128` | Big integers | `BigInt(123456789)` | -| `application/octet-stream` | Binary data | File contents | -| `image/jpeg`, `image/png`, etc. | Media files | Images, videos | - -::: tip Auto-Detection - -The SDK automatically detects file types based on content. No need to specify -MIME types manually. - -::: - -## Why Schemas Matter - -- **Clarity**: Makes your data easier to understand and reuse -- **Safety**: Ensures iExec apps don’t process the wrong data -- **Structure**: Facilitates structured communication between **front-end and - iApp logic** - -### 🎯 **For iApp Development** - -Schemas let your iApps validate and process data safely: - -```ts twoslash -import { IExecDataProtectorDeserializer } from '@iexec/dataprotector-deserializer'; - -const deserializer = new IExecDataProtectorDeserializer(); -// ---cut--- -// Inside your iApp -const email = await deserializer.getValue('email', 'string'); -const preferences = await deserializer.getValue( - 'preferences.newsletter', - 'bool' -); -``` - -### 🛡️ **For Type Safety** - -Prevents your iApps from processing incompatible data types. - -### 🔍 **For Data Discovery** - -Users can find relevant protected data without seeing the actual content: - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const listProtectedData = await dataProtectorCore.getProtectedData({ - requiredSchema: { - email: 'string', - }, -}); -``` - -## Real Examples - -### Simple User Profile - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const userData = await dataProtectorCore.protectData({ - data: { - email: 'user@example.com', - age: 25, - isSubscribed: true, - }, -}); -``` - -**🏷️ Generated Schema:** - -```json -{ - "email": "string", - "age": "f64", - "isSubscribed": "bool" -} -``` - -### Nested Contact Information - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const contactData = await dataProtectorCore.protectData({ - data: { - personal: { - firstName: 'Alice', - lastName: 'Smith', - }, - contact: { - email: 'alice@example.com', - phone: '+1234567890', - }, - preferences: { - marketing: false, - notifications: true, - }, - }, -}); -``` - -**🏷️ Generated Schema:** - -```json -{ - "personal": { - "firstName": "string", - "lastName": "string" - }, - "contact": { - "email": "string", - "phone": "string" - }, - "preferences": { - "marketing": "bool", - "notifications": "bool" - } -} -``` - -### File Data - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -// Mock function for the example -function createArrayBufferFromFile(file: File): Promise { - return Promise.resolve(new ArrayBuffer(0)); -} - -// Get file from input element -const file = new File([''], 'example.jpg', { type: 'image/jpeg' }); - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const fileBuffer = await createArrayBufferFromFile(file); - -const fileData = await dataProtectorCore.protectData({ - data: { - fileName: file.name, - fileContent: fileBuffer, - uploadDate: Date.now(), - }, -}); -``` - -**🏷️ Schema for file upload:** - -```json -{ - "fileName": "string", - "fileContent": "image/jpeg", - "uploadDate": "f64" -} -``` - -## Using Schemas in iApps - -Once you have protected data with a schema, you'll want to process it inside an -iApp. - -::: warning Type Matching - -**Your iApp and frontend must use the same field names and types.** If they -don't match, you'll get runtime errors when processing the data. - -::: - -→ **Ready to build an iApp?** Check out our detailed -[Inputs and Outputs guide](/build-iapp/guides/inputs-and-outputs) to learn how -to access schema fields inside your iApp using the deserializer. - -## Next Steps - -**You now understand how schemas work with protected data.** Here's what to -explore next: - -- **Build an iApp**: Check out the - [iApp Generator guide](/build-iapp/iapp-generator) to create your first data - processor -- **Process data**: Learn about - [processProtectedData](/manage-data/dataProtector/dataProtectorCore/processProtectedData) - for running computations -- **See it in action**: Try our [Hello World tutorial](/overview/helloWorld) for - a complete example diff --git a/src/manage-data/guides/manage-access.md b/src/manage-data/guides/manage-access.md deleted file mode 100644 index 14d2d223..00000000 --- a/src/manage-data/guides/manage-access.md +++ /dev/null @@ -1,230 +0,0 @@ ---- -title: Manage Access to your ProtectedData -description: - Learn how to protect data and grant secure access for specific apps and users ---- - -# 🛡️ Manage Access - -**Want to keep your data private while still using it in confidential -applications?** - -DataProtector lets you encrypt data and control access through orders - -specifying who can use it, how many times, and at what price. Protected data is -only accessible in secure enclaves (TEEs) by authorized users and iApps. - -## Installation - -First, install DataProtector in your project: - -::: code-group - -```bash [npm] -npm install @iexec/dataprotector -``` - -```bash [yarn] -yarn add @iexec/dataprotector -``` - -```bash [pnpm] -pnpm add @iexec/dataprotector -``` - -```bash [bun] -bun add @iexec/dataprotector -``` - -::: - -## Protect your Data - -**Here's what happens:** Your data gets encrypted client-side and stored as an -NFT. Only you control who can decrypt and use it. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const protectedData = await dataProtectorCore.protectData({ - name: 'My Email Contact', - data: { - email: 'alice@example.com', - firstName: 'Alice', - lastName: 'Smith', - }, -}); - -console.log('Protected data address:', protectedData.address); -``` - -### What you Can Protect - -**Data**: Any kind of data you want to keep private and make available for -computations by authorized users and iApps. - -**Supported types**: Common data types like text, numbers, true/false values, -and files. See the [full list here](/manage-data/dataProtector/types). - -**Storage**: Store your data on IPFS or Arweave. For larger files, you can use -your own IPFS node. - -::: tip - -Need Help ? Check our -[Schema and Dataset Types guide](/manage-data/guides/handle-schemas-dataset-types) -for detailed formatting instructions. - -::: - -### Debug Mode Option - -Debug mode lets you test with debug iApps during development. As "debug" iApps -don't have the same security standards, we recommend using this mode only during -iApp development. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- - -const protectedData = await dataProtectorCore.protectData({ - data: { - email: 'test@example.com', - }, - allowDebug: true, // [!code focus] -}); -``` - -## Grant Access - -By default, your protected data is private. To let others use it, you need to -grant access to both: - -- An authorized user (who can trigger the processing) -- An authorized iApp (the application that will process the data in the private - environment) - -This ensures that only specific users can use specific applications to process -your data. Here's how to set it up: - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- - -const grantedAccess = await dataProtectorCore.grantAccess({ - protectedData: '0x123abc...', // Your protected data address - authorizedApp: '0x456def...', // iApp that can process the data - authorizedUser: '0x789cba...', // User who can trigger the processing - pricePerAccess: 0, // Cost per use (in nRLC) - numberOfAccess: 10, // Maximum number of uses -}); -``` - -### Parameters - -```ts twoslash -import { type GrantAccessParams } from '@iexec/dataprotector'; -``` - -#### protectedData - -**Type:** `AddressOrENS` - -The ethereum address of the protected data supplied by the user (returned when -you created it). **You must own this data** to grant access. - -#### authorizedApp - -**Type:** `AddressOrENS` - -**What it is**: The iApp address that's allowed to process your data inside the -secure enclave. - -**Why needed**: This ensures only specific, audited applications can access your -data. No random code can touch it. - -**Pro tip**: Use app whitelists for production. Instead of a single app address, -you can specify a whitelist contract that contains multiple app versions. Very -useful for when you need to upgrade your iApps, without losing all the granted -access. - -```ts -// Single app -authorizedApp: 'web3mail.apps.iexec.eth'; - -// Or use a whitelist (recommended for production) -authorizedApp: '0x781482C39CcE25546583EaC4957Fb7Bf04C277D2'; // Web3Mail whitelist -``` - -#### authorizedUser - -**Type:** `AddressOrENS` - -**What it is**: The wallet address of the user that is allowed to process this -data. - -**Why needed**: Even with an authorized app, only specific users can trigger the -computation. This gives you granular control over who uses your data. - -**Don't forget**: Even if you are the owner of the data, you need to authorize -yourself! - -**Special case**: Set to `0x0000000000000000000000000000000000000000` to allow -**any user** to trigger processing (useful for public datasets). - -#### pricePerAccess - -**Type:** `number` -**Default:** `0` - -**Quick explanation**: How much you charge per data usage (in nano RLC - nRLC). - -Set to `0` for free access, or specify a price to monetize your data -automatically. - -**Example**: `pricePerAccess: 1_000_000_000` = 1 RLC per access - -→ **Want to learn more monetization capabilities?** See our detailed -[Monetize Protected Data guide](/manage-data/guides/monetize-protected-data) - -#### numberOfAccess - -**Quick explanation**: Maximum number of times this authorization can be used. - -::: warning - -Important If someone tries to process your data more times than allowed, they'll -get a "no dataset orders" error. Set this high enough for your use case. - -::: - -**Example values**: - -- `1` - Single use (great for one-time data analysis) -- `100` - Limited campaign (email marketing with usage cap) -- `10000` - Effectively unlimited for most use cases - -## What's Next? - -**You now have protected data with controlled access.** Here are your next -steps: - -- **Process the data**: Use - [processProtectedData](/manage-data/dataProtector/dataProtectorCore/processProtectedData) - to run computations -- **Manage access**: - [Revoke](/manage-data/dataProtector/dataProtectorCore/revokeOneAccess) or - [modify permissions](/manage-data/dataProtector/dataProtectorCore/grantAccess) - anytime -- **Learn data types**: Deep dive into - [schemas and dataset types](/manage-data/guides/handle-schemas-dataset-types) -- **Monetize data**: Explore - [data monetization strategies](/manage-data/guides/monetize-protected-data) diff --git a/src/manage-data/guides/monetize-protected-data.md b/src/manage-data/guides/monetize-protected-data.md deleted file mode 100644 index df89ed41..00000000 --- a/src/manage-data/guides/monetize-protected-data.md +++ /dev/null @@ -1,256 +0,0 @@ ---- -title: Monetize Protected Data -description: - Explore different ways to monetize your protected data with signed orders - (usage-based) and time-based access (time-period payments) ---- - -# 💰 Monetize Protected Data - -**Your protected data can generate revenue automatically.** - -iExec offers two fundamental approaches for monetizing your data: - -- **DataProtector Core**: **Signed orders** with pay-per-use counting - specify - exact conditions, users pay for each individual data processing -- **DataProtector Sharing**: **Time-based access** with period payments - users - pay for unlimited access during specific time periods - -## 📝 Signed Orders (DataProtector Core) - -**How it works**: You create and publish signed orders that specify the exact -conditions for accessing your protected data. Each order defines: - -- **Authorized App**: Which iApp can process your data -- **Authorized User**: Who can access your data (specific address or any user) -- **Price per Access**: Cost for each individual use -- **Number of Access**: Maximum times the data can be used -- **Usage Counting**: Each data processing decrements the available access count - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -// Create a signed order with specific conditions -const grantedAccess = await dataProtectorCore.grantAccess({ - protectedData: '0x123abc...', // Your data address - authorizedApp: 'email-processor.apps.iexec.eth', // Specific iApp only - authorizedUser: '0x456def...', // Specific user only - pricePerAccess: 5000000000, // 5 RLC per individual use - numberOfAccess: 100, // Maximum 100 total uses -}); - -console.log('Signed order created:', grantedAccess); -``` - -**Perfect for**: - -- Direct partnerships with known clients -- Precise control over access conditions -- Simple setup with specific partners -- Exact counting of data usage - -## 📅 Time-Based Access Implementation (DataProtector Sharing) - -**How it works**: Instead of counting individual uses, DataProtector Sharing -provides **time-based access periods**. Users purchase access for specific -durations (hours, days, months) and can use your protected data unlimited times -during that period. Smart contracts handle all distribution automatically. - -**Key Innovation**: Shift from usage counting to time-based access - users buy -access time, not individual transactions. - -**Access Models Available**: - -- **Rental**: Pay once to access to individual protected data (not all the - collection) -- **Subscription**: Recurring payments for ongoing access -- **Sale**: Permanent ownership transfer - -::: tip See It Live - -The [Content Creator demo](/overview/use-case-demo/content-creator) shows -DataProtector Sharing in action with file monetization. While it uses the -content-delivery iApp for file streaming, the same patterns work for any iApp - -AI models, data processing, oracles, etc. - -::: - -### **Step 1: Create a Collection** - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -// Create a collection to group your data and provide -// a set of protectedData available for the subscription. -const collection = await dataProtectorSharing.createCollection(); -console.log('Collection address:', collection.collectionId); - -// Add your protected data to the collection -await dataProtectorSharing.addToCollection({ - protectedData: '0x123abc...', // Your protected data address - collectionId: collection.collectionId, - addOnlyAppWhitelist: '0x256bcd881c33bdf9df952f2a0148f27d439f2e64', // iExec apps whitelist -}); -``` - -### **Step 2: Choose your Distribution Model** - -**DataProtector Sharing offers three distribution models:** - -#### **Time-Based Rental Model** - -Users pay once for **unlimited access during a specific time period** (not per -use). For a specific protectedData of the created collection : - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -// DATA OWNER: Set up time-based rental terms -await dataProtectorSharing.setProtectedDataToRenting({ - protectedData: '0x123abc...', - price: 5000000000, // 5 RLC for the entire period - duration: 604800, // 7 days of unlimited access -}); - -// CLIENT/SUBSCRIBER: Rent access for the full time period -const rental = await dataProtectorSharing.rentProtectedData({ - protectedData: '0x123abc...', - price: 5000000000, // One payment for full period - duration: 604800, // Unlimited use for 7 days -}); -``` - -**Perfect for**: - -- Time-sensitive datasets (event data, seasonal trends) -- Expensive datasets where users need intensive short-term access -- Content that loses value over time -- Users who need to run multiple analyses during a period - -#### **Time-Based Subscription Model** - -Users pay for **recurring time-based access** to a bundle of data. Unlimited -usage during each subscription period. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); - -// Mock collection for the example -const collection = { collectionId: 123 }; -// ---cut--- -// DATA OWNER: Set time-based subscription parameters -await dataProtectorSharing.setSubscriptionParams({ - collectionId: collection.collectionId, - price: 20000000000, // 20 RLC per 30-day period - duration: 2592000, // 30 days unlimited access -}); - -// DATA OWNER: Add protected data to the time-based subscription bundle -await dataProtectorSharing.setProtectedDataToSubscription({ - protectedData: '0x123abc...', -}); - -// DATA OWNER: Add more data to the same subscription -await dataProtectorSharing.setProtectedDataToSubscription({ - protectedData: '0x456def...', // Additional dataset -}); - -// CLIENT/SUBSCRIBER: Get time-based subscription access -const subscription = await dataProtectorSharing.subscribeToCollection({ - collectionId: collection.collectionId, - price: 20000000000, // Pay for full period - duration: 2592000, // 30 days unlimited usage -}); -``` - -**Perfect for**: - -- Growing datasets (daily market data, news feeds) -- Educational content series -- Research datasets that expand over time -- SaaS-style data access - -#### **Sale Model** - -Transfer permanent ownership of your data to the buyer. - -```ts twoslash -import { - IExecDataProtectorSharing, - getWeb3Provider, -} from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); -// ---cut--- -// DATA OWNER: List data for sale -await dataProtectorSharing.setProtectedDataForSale({ - protectedData: '0x123abc...', - price: 100000000000, // 100 RLC purchase price -}); - -// CLIENT/BUYER: Purchase ownership -const purchase = await dataProtectorSharing.buyProtectedData({ - protectedData: '0x123abc...', - price: 100000000000, -}); -``` - -**Perfect for**: - -- Unique datasets or models -- Digital assets and NFT data -- One-time valuable insights -- When you want to exit data ownership - -## Which Approach to Choose? - -| **Signed Orders (Core)** | **Time-Based Access (Sharing)** | -| ---------------------------- | ------------------------------- | -| Usage counting & pay-per-use | Time periods & unlimited usage | -| Direct signed orders | Smart contract automation | -| High control, simple setup | Medium control, flexible models | - -**Choose Signed Orders when**: You need precise control, direct partnerships, -and usage-based billing. - -**Choose Time-Based Access when**: You want automated distribution, unlimited -usage periods, and flexible pricing models. - -## Next Steps - -**Ready to start monetizing your data?** Here are your next steps: - -- **See it in action**: Try the - [Content Creator demo](/overview/use-case-demo/content-creator) to understand - the full flow -- **Start simple**: Begin with - [pay-per-use via grantAccess](/manage-data/guides/create-and-share-access) -- **Explore sharing**: Try - [DataProtector Sharing](/manage-data/dataProtector/dataProtectorSharing) for - automated distribution -- **Build collections**: Learn about - [collection management](/manage-data/dataProtector/dataProtectorSharing/collection) -- **Set up subscriptions**: Implement - [recurring revenue models](/manage-data/dataProtector/dataProtectorSharing/subscription) diff --git a/src/manage-data/what-is-protected-data.md b/src/manage-data/what-is-protected-data.md deleted file mode 100644 index ea69bb91..00000000 --- a/src/manage-data/what-is-protected-data.md +++ /dev/null @@ -1,209 +0,0 @@ ---- -title: What is Protected Data? -description: Understanding iExec's data protection mechanisms ---- - -# ❓ What is Protected Data? - -Protected Data refers to any data encrypted using the **iExec Data Protector -tool**. This end-to-end encryption solution enables users to protect, manage and -monetize their data within the Web3 ecosystem. - -

-

Unlike standard datasets, Protected Data exposes its data types on-chain (for example, indicating that it contains an email address or a photo). This allows anyone to identify entries with the corresponding types.

-
- -## Key Concepts - -### 👑 Data Governance - -
- -
-
-
- -
- You own your data: The original data never leaves your control -
-
-
- -
- Granular permissions: Decide who can access and use your data -
-
-
- -
- Revocable access: Grant and revoke permissions at any time -
-
-
-
- -### 🔒 Privacy-preserving Computation - -
- -
-
-
- -
- Encrypted Processing: Maintains a complete chain of trust. -
-
-
- -
- TEE (Trusted Execution Environment): Secure enclaves protect data during processing -
-
-
- -
- Secret storage: Secrets stored in a TEE database. -
-
-
-
- -### 💰 Monetization - -
- -
-
-
- -
- Data marketplaces: Sell access to your protected data -
-
-
- -
- Usage tracking: Monitor how your data is being used -
-
-
- -
- Fair compensation: Get paid for data usage -
-
-
-
- -## How it Works - -
-
-
- 1. -
- Protect: Encrypt and register your data on the iExec network -
-
-
- 2. -
- Share: Authorize specific users and applications to access your data -
-
-
- 3. -
- Monitor: Track usage and maintain control -
-
-
- 4. -
- Monetize: Earn from your data while keeping it private -
-
-
- 5. -
- Compute: Authorized users can compute on your data with the authorized iApps -
-
-
-
- -## Use Cases - -
-
-
- 👤 -

Personal Data

-
-
    -
  • Health records
  • -
  • Financial data
  • -
  • Personal preferences
  • -
-
- -
-
- 🏢 -

Business Intelligence

-
-
    -
  • Market research
  • -
  • Customer analytics
  • -
  • Proprietary datasets
  • -
-
- -
-
- 🤖 -

AI Training

-
-
    -
  • Training models without exposing sensitive data
  • -
  • Federated learning
  • -
  • Privacy-preserving ML
  • -
-
- -
-
- 🔬 -

Research

-
-
    -
  • Collaborative research with privacy guarantees
  • -
  • Cross-institutional studies
  • -
  • Clinical trial data
  • -
-
-
- -
-

Ready to protect your data? Start with our DataProtector guides and learn how to secure your sensitive information while unlocking its value.

-
- -## Next Steps - -
-
-
- 📚 - -
-
- 🚀 -
- Getting Started: DataProtector Quick Start Guide -
-
-
-
diff --git a/src/modules/helloWorld/GrantAccess.vue b/src/modules/helloWorld/GrantAccess.vue index 0a765dfa..8305e195 100644 --- a/src/modules/helloWorld/GrantAccess.vue +++ b/src/modules/helloWorld/GrantAccess.vue @@ -107,7 +107,7 @@ import { ref } from 'vue'; import { Icon } from '@iconify/vue'; import { IExecDataProtectorCore } from '@iexec/dataprotector'; -import Button from '../../components/ui/Button.vue'; +import Button from '@/components/ui/Button.vue'; import ReownButton from './ReownButton.vue'; import { useAccount } from '@wagmi/vue'; import { useWalletConnection } from '../../hooks/useWalletConnection.vue'; diff --git a/src/modules/helloWorld/ProtectData.vue b/src/modules/helloWorld/ProtectData.vue index cb9d7d83..4bb2fa4e 100644 --- a/src/modules/helloWorld/ProtectData.vue +++ b/src/modules/helloWorld/ProtectData.vue @@ -107,7 +107,7 @@ import { ref } from 'vue'; import { Icon } from '@iconify/vue'; import { IExecDataProtectorCore } from '@iexec/dataprotector'; -import Button from '../../components/ui/Button.vue'; +import Button from '@/components/ui/Button.vue'; import ReownButton from './ReownButton.vue'; import { useAccount } from '@wagmi/vue'; import { useWalletConnection } from '../../hooks/useWalletConnection.vue'; diff --git a/src/modules/helloWorld/ReownButton.vue b/src/modules/helloWorld/ReownButton.vue index 26450b35..f6e2b682 100644 --- a/src/modules/helloWorld/ReownButton.vue +++ b/src/modules/helloWorld/ReownButton.vue @@ -40,8 +40,8 @@ import { watch } from 'vue'; import { useAppKit } from '@reown/appkit/vue'; import { useAccount, useDisconnect } from '@wagmi/vue'; -import AddressChip from '../../components/AddressChip.vue'; -import Button from '../../components/ui/Button.vue'; +import AddressChip from '@/components/AddressChip.vue'; +import Button from '@/components/ui/Button.vue'; const { open } = useAppKit(); const { disconnectAsync } = useDisconnect(); diff --git a/src/overview/develop-with-ai.md b/src/overview/develop-with-ai.md deleted file mode 100644 index faecb310..00000000 --- a/src/overview/develop-with-ai.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: Develop with AI -description: - Learn how to leverage AI tools like Vibe Coding and Context7 to build - privacy-first applications with iExec ---- - -# 🤖 Develop with AI - -Building privacy-first applications with iExec can be accelerated using -AI-powered development tools. This guide covers how to effectively use AI -assistants while maintaining security best practices. - -## 📚 Documentation for LLMs and AI Code Editors - -You can use some MCP (Model Control Protocol) servers like -[Context7](https://context7.com/iexecblockchaincomputing/documentation-tools) to -provide: - -- Code completion with iExec-specific knowledge -- Architecture suggestions for privacy-first apps -- Code explanation and best practices - -## 🎨 Vibe Coding Integration - -Vibe coding is a modern way to build applications by describing what you want in -plain language. An AI assistant (like Cursor or ChatGPT) then generates code -based on your description. - -It's fast, creative, and helps you prototype ideas quickly. Even if you're not a -technical expert you can: - -- Write a prompt like: "I want to create a form able to protect my data with - DataProtector" - -- The AI suggests code using iExec tools like DataProtector or iApp Generator - -- You review and adjust until it works - -## ⚠️ Security Considerations - -While vibe coding is powerful, it's important to keep privacy and security in -mind: - -- **Review the code**: Always check AI-generated code for bugs or - vulnerabilities - -- **Don't share secrets**: Never paste API keys, private keys, or sensitive - logic into the AI - -- **Validate privacy logic**: Make sure your confidential computing flow is - correctly implemented - -- **Test thoroughly**: Especially when handling protected data or smart contract - logic - -## Learn More - -- [iExec MCP Server](https://www.iex.ec/news/mcp-server-secure-interoperability-autonomous-ai-agents) -- [Agentic AI](https://www.iex.ec/academy/what-is-agentic-ai) -- [ElizaOS TDX use case](https://www.iex.ec/news/elizaos-ai-agents-iexec-intel-tdx) diff --git a/src/overview/helloWorld.md b/src/overview/helloWorld.md deleted file mode 100644 index 24fe8e42..00000000 --- a/src/overview/helloWorld.md +++ /dev/null @@ -1,122 +0,0 @@ ---- -title: Hello World Tutorial -description: - Kickstart your Web3 journey with iExec. In just 30 minutes, learn how to build - privacy-focused dApps, protect sensitive data, and manage data access. ---- - - - -# 👋 Welcome to iExec - -> Reading time 🕒 2 mins - -
-

Start Your Web3 Privacy Journey

-

And learn how to build Privacy-preserving decentralized applications (dApps) with iExec in this interactive guide.

-
- ☕ 30 minutes journey -

Perfect for hackathons 😊

-
-
- -## What you'll Learn and Build - - - -## Getting Started - -Before you begin, make sure you have: - -
-
-
- 🦊 Ethereum Wallet - -
- Metamask Download → -
-
-
- 📦 Node.js v20+ - -
- Download → -
-
-
- 🐳 Docker installed - -
- Download → -
- -
-
- 🐳 DockerHub Account - -
- Sign Up → -
-
- -
-

Need help setting up or got some questions? Join our Discord Community for support!

-
diff --git a/src/overview/helloWorld/1-overview.md b/src/overview/helloWorld/1-overview.md deleted file mode 100644 index 73eaaddc..00000000 --- a/src/overview/helloWorld/1-overview.md +++ /dev/null @@ -1,184 +0,0 @@ ---- -title: iExec Overview -description: - Explore how iExec enables developers to build privacy-preserving dApps using - confidential computing, blockchain, and secure data management. Learn how - tools like DataProtector and iApps empower users to control, protect, and - monetize sensitive data across Web3 applications. ---- - -# 🧐 iExec Overview - -> Reading time 🕒 8 mins - -
-

Let's start with the basics

-

and explore how iExec can help you build Privacy-preserving applications and securely manage sensitive data.

-
- -## 👨‍💻 Building Privacy-preserving dApps with iExec - -

Imagine you're building a decentralized application (dApp) that needs to handle sensitive user data, for example:

-
-
-
- 🤖 - An AI model training on sensitive data -
-
- 💰 - A financial app handling financial data -
-
- 🔬 - A research platform working with private datasets -
-
- 🏥 - A healthcare app processing confidential patient records -
-
- -
-

You'll need a way to:

-
- 🔒 - Keep the data confidential -
-
- 🎮 - Control who can access it -
-
- - Process it securely -
-
- 💎 - Potentially monetize it -
-
-
- -
-

This is where iExec comes in! We provide tools to easily add privacy and monetization features into your dApp.

-
- -## 👷 How do we Solve it? - -Unlike traditional security solutions, iExec protects your data throughout its -entire lifecycle, during storage, transfer, and even while **being processed by -applications.** - -This is made possible thanks to -Trusted -Execution Environment (TEE) and -Confidential -Computing technologies. - -
-

Our technology allows users to control the ownership, - confidentiality, and monetization of their data and digital assets within the Web3 ecosystem.

-
- -## 🔒 The Three Key Elements? - -iExec combines three fundamental elements that work together seamlessly: - -#### 1. Protect Data with our Devtool [DataProtector](../../manage-data/dataProtector/getting-started) - -- Encrypt your sensitive data and store it securely on Arweave or IPFS -- Only you control who can access it and when -- Perfect for private information like research data, business analytics, or - personal records - -#### 2. Compute Data with iExec Apps (iApps) Running in Secure Environment - -- Special applications that can work with protected data -- Run in secure environments (called TEEs) that keep your data private -- Process data without exposing sensitive information - -#### 3. Set the Rules with the Blockchain Layer - -- Enables tokenization of data -- Regain ownership of your data -- Provides transparent governance rules for data access - -
-

By merging blockchain technology with confidential computing, we've pioneered DeCC (Decentralized Confidential Computing) to take privacy and security to the next level in Web3 ecosystems.

-
- -### 🧸 DeCC Explained Like You're 5 - -Imagine a **secure room**, like a private bank vault for data, where everything -inside stays **safe** and **private**. - -With a tool called **DataProtector**, your data becomes **protected** and can -only be read and processed inside this secure room. - -Special applications called **iApps** (applications built to run in secure -environments) can enter the room to work with your data. You stay in **control** -by setting **access rules** that only you can modify about who can access it and -when. - -## 🔍 Building your First Privacy-preserving dApp - -Let's meet Bob and Alice to understand how iExec enables Privacy-preserving -applications: - -### 1. Meet Bob: the dApp Developer 👨‍💻 - -Bob is building a decentralized application that leverages iExec's technology. -His platform consists of: - -- A user-friendly interface for users. -- A DataProtector SDK that's easy to integrate into any application. -- Thanks to DataProtector, end users can protect their data, manage access, and - process it seamlessly. - -### 2. Meet Alice: the dApp User 👩‍💼 - -When using Bob's platform, Alice can: - -- Protect her data using the Bob's platform's DataProtector feature -- Maintain full control over who can access her protected data -- Authorize specific iExec Apps and persons like Bob to use her data in a secure - environment - -Depending on the dApp's use case, Alice could: - -- Apply algorithms to her data and get results confidentially -- Share her data with other users privately (and get paid for it) -- Ask questions to AI models about her data and get answers confidentially - -And many other use cases... - -## 🎯 Key Takeaways - -
-

In this chapter, we covered the core concepts of iExec:

-
- 🔒 -

Privacy-preserving Solution: iExec provides tools to protect sensitive data throughout its entire lifecycle - storage, transfer, and processing

-
-
- 💡 -

User Control: Data owners maintain full control over access, confidentiality, and monetization of their assets

-
-
- 📦 -

iApps (iExec App): Special applications that can process protected data

-
-
- ⛓️ -

Blockchain: Enables tokenization of data, regain ownership, and governance rules for data access

-
-
- 🔌 -

Wide Application: From AI to finance, enabling confidential data processing across various industries

-
-
- -
-

Now that you understand the fundamentals, let's dive into protecting your first piece of data! With Alice!

-
diff --git a/src/overview/helloWorld/2-protectData.md b/src/overview/helloWorld/2-protectData.md deleted file mode 100644 index af4b0ae8..00000000 --- a/src/overview/helloWorld/2-protectData.md +++ /dev/null @@ -1,192 +0,0 @@ ---- -title: Let's Protect Data -description: - Learn how to protect your data using iExec's DataProtector SDK in this - hands-on tutorial step. ---- - - - -# 🛡️ Let's Protect Data - -> Reading time: 6 minutes - -
-

Time to get practical

-

Let's follow Alice as she learns how to protect her data using DataProtector on Bob's dApp, our developer tool for protecting data creation and management.

-
- -
-

Protected data is encrypted data that remains confidential throughout its entire lifecycle - during storage, transfer and processing.

-
- -## 🧩 DataProtector, Key Features - -DataProtector is a developer tool built on top of our technology. It helps -developers easily add data protection, management, and monetization features to -their dApps with these key features: - -- 🔐 **Data Privacy and Security** - - Utilizes end-to-end encryption and decentralized storage - (IPFS or - AR.io) to ensure protection and - confidentiality, leveraging advanced confidential computing technology. - -- 🎮 **Dynamic Access Management** - - Allows users to manage access, enabling flexible control and monetization of - data assets. - -- 🔌 **Seamless dApp Integration** - - Features an SDK for easy integration into your DApp, enhancing functionality - and user experience. - -
-

DataProtector interacts with iExec's Bellecour sidechain, which is gasless, meaning you can use it completely free without needing any tokens!

-
- -## 🧩 Let's Create Protected Data - - - -## 🧩 What Happened Under the Hood - -
-

You won't believe how easy it is to protect your data with DataProtector. Just a few lines of code, and you're done!

-
- -To use it, simply call the `protectData` method from the **DataProtector SDK** -with two arguments. - -- The data object to be protected (can contain text, files, JSON data etc.) -- The name of the protected data - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const { address: protectedDataAddress } = await dataProtectorCore.protectData({ - name: 'myEmail', - data: { - email: 'example@gmail.com', - }, -}); -``` - -For this tutorial, you can try out the code directly in our interactive -CodeSandbox demo -[here](https://codesandbox.io/p/github/iExecBlockchainComputing/dataprotector-sandbox/main?file=%2Fsrc%2FApp.tsx&preventWorkspaceRedirect=true). -Here's a quick overview of what happened when you clicked the **Protect Data** -button: - -![alt](/assets/hello-world/dataprotector_light.png){.light-only} -![alt](/assets/hello-world/dataprotector_dark.png){.dark-only} - -
-
- 1 - The DataProtector SDK is called -
-
- 2 - The data is encrypted with a symmetric key -
-
- 3 - The encrypted data is stored on IPFS -
-
- 4 - The symmetric key is stored in a secure enclave (TEE) in the Secret Management Service -
-
- 5 - The DataProtector smart contract is used to establish data ownership as an NFT -
-
- 6 - The protected data address is returned to the user -
-
- -## 🧩 How to Install and Use DataProtector - -Decentralized confidential computing might sound complex, but we've made it -simple through our developer tools. - -
-
-

1. Install the Developer Tool

-

Run the install command:

- -::: code-group - -```sh [npm] -npm install @iexec/dataprotector -``` - -```sh [yarn] -yarn add @iexec/dataprotector -``` - -```sh [pnpm] -pnpm add @iexec/dataprotector -``` - -```sh [bun] -bun add @iexec/dataprotector -``` - -::: - -
-
-

2. Import and Initialize it in your project

- Import the tool - -```ts twoslash -import { Address, IExecDataProtector } from '@iexec/dataprotector'; -``` - -Create a new instance and call the methods you need - -
-
- -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const { address: protectedDataAddress } = await dataProtectorCore.protectData({ - name: 'myEmail', - data: { - email: 'example@gmail.com', - }, -}); -``` - -
-

Check out our code sandbox for ready-to-use examples!

-
- -## 🎯 Key Takeaways - -- 🔒 **DataProtector** ensures data protection, management, and confidentiality - -- 📦 **Protected Data** is encrypted and stored on decentralized storage - -- ⛓️ **Ownership** is stored on the blockchain and linked to your wallet - -- 🔌 **Integration** is simple with our developer tools - -
-

In the next chapter, we'll show you how to build, deploy, and run an iApp to process your protected data. Let's go! 🚀

-
diff --git a/src/overview/helloWorld/3-buildIApp.md b/src/overview/helloWorld/3-buildIApp.md deleted file mode 100644 index d64bb485..00000000 --- a/src/overview/helloWorld/3-buildIApp.md +++ /dev/null @@ -1,324 +0,0 @@ ---- -title: Build and Deploy Your First iApp -description: - Learn how to build and deploy your first iExec application (iApp) for - processing protected data in this comprehensive tutorial. ---- - -# 🛠️ Build and Deploy your First iApp - -> Reading time 🕒 10 mins - - - -
-

Time to build!

-

Let's build an iApp that can process protected data in a secure environment using the iExec iApp generator tool. This tool helps you create, test and deploy iApps with just a few commands.

-
- -If you wanna explore and deep dive in the CLI. You can check the -[iapp-cli](https://github.com/iExecBlockchainComputing/iapp/tree/main/cli) -github repository. Follow the instructions carefully for a smooth development -experience. - -## 📋 Prerequisites - -Before getting started, make sure you have: - -
-
-
- 📦 Node.js v20+ -
- Download → -
-
-
- 🐳 Docker installed -
- Download → -
- -
-
- 🐳 DockerHub Account -
- Sign Up → -
-
- -
-

Don't worry! All secrets used in this tutorial stay on your machine and aren’t shared with anyone. You’ll only need them to run the iapp run command.

-
- -## 🚀 Types of iApps you Can Build - -iExec enables you to build various types of Privacy-preserving applications. -Here are some popular use cases: - -### 📧 Web3 Mail - -Send privacy-preserving emails to registered Ethereum account holders without -knowing or storing their email addresses. -[Github](https://github.com/iExecBlockchainComputing/web3mail-sdk/tree/main/dapp) -| [Documentation](../../use-iapp/web3mail) - -### 💬 Web3 Telegram - -Send privacy-preserving Telegram messages without knowing or storing their -Telegram handles. -[Github](https://github.com/iExecBlockchainComputing/web3telegram-sdk/tree/main/dapp) -| [Documentation](../../use-iapp/web3telegram) - -### 🌐 Content Delivery - -Transfer, sell or rent protected content to authorized users. -[Github](https://github.com/iExecBlockchainComputing/dataprotector-sdk/tree/main/packages/protected-data-delivery-dapp) -| [Documentation](../../manage-data/dataProtector/dataProtectorSharing) - -
-

These are just a few examples, the possibilities are endless. Want to explore iApp Generator? Check out our documentation and see what you can build!

-
- -## 💾 Installation (Win / Mac / Linux) - -First, you need to install the `iapp` package. Open your terminal and run: - -::: code-group - -```sh [npm] -npm i -g @iexec/iapp -``` - -```sh [yarn] -yarn global add @iexec/iapp -``` - -```sh [pnpm] -pnpm add -g @iexec/iapp -``` - -```sh [bun] -bun add -g @iexec/iapp -``` - -::: - -You can check if the installation was successful by running: - -```sh -#checking the version -iapp --version - -#checking the available commands -iapp --help -``` - -## 🛠️ Bootstrap your iApp - -To initialize the working directory for developing your iApp, use the -`iapp init` command. This command sets up the necessary project structure and -files. - -```sh -mkdir iexec-test -cd iexec-test -iapp init -``` - -You will be prompted with the following message: - -```txt - ___ _ ____ ____ - |_ _| / \ | _ \| _ \ - | | / _ \ | |_) | |_) | - | | / ___ \| __/| __/ - |___/_/ \_\_| |_| - -✔ What's your project name? (A folder with this name will be created) … hello-world -✔ Which language do you want to use? › JavaScript -? What kind of project do you want to init? › - Use arrow-keys. Return to submit. -❯ Hello World - iapp quick start - advanced -``` - -
-
- 1 - Pick a name for your project -
-
- -```txt -? What's your project name? (A folder with this name will be created) ... -``` - -
-
- 2 - Select a programming language for your project -
-
- -```txt -? Which language do you want to use? › - Use arrow-keys. Return to submit. -❯ JavaScript - Python -``` - -
-
- 3 - Select the type of project you want to init -
-
- -```txt -? What kind of project do you want to init? › - Use arrow-keys. Return to submit. -❯ Hello World - iapp quick start - advanced -``` - -
-

We recommend selecting "Hello World" to quickly discover how iApp works! use advanced only if you are familiar with iExec.

-
- -```txt -✔ [Chosen language] app setup complete. -✔ Generated ethereum wallet (0xD4A28d.........................) - -``` - -- An iApp project is setup with the selected language -- An ethereum wallet has been created (we use it to sign the iApp creation - onchain) -- A new folder has been created, it contains a very simple application, with the - main code being located in `src/app.js` or `src/app.py` - -## 🧪 Test your iApp - -To test your iApp, run `iapp test` command - -```sh -iapp test -``` - -It uses your local docker to build and execute the app. - -
-

- If you have Error: Docker daemon is not accessible Make sure Docker is installed and running.

-
-

- If you have Error: Failed to locate iApp project root error: Ensure you are in your project folder before proceeding.

-
- -You can see the output of the computation by saying yes to the question: - -```txt -? Would you like to see the result? (View ./output/) (Y/n) -``` - -### 🧩 Using Arguments - -You can pass arguments to your iApp using the `--args` option. This allows you -to provide necessary inputs during runtime (you can use your name for example). - -```sh -iapp test --args your-name -``` - -### 🔒 Using Protected Data - -You can pass a protectedData that you are authorized to process to your iApp -using the `--protectedData` option. - -Since nothing is actually deployed during testing, we use Protected Data mocks -to test the app. Using `--protectedData` default will provide your app with the -default protectedData mock. - -```sh -iapp test --protectedData default -``` - -
-

You can check how args and protectedData are processed in src/app.js or src/app.py

-
- -## 🚀 Deploy your iApp - -Deploy your iApp on the iExec protocol. - -
- -
- 2 - Click "Personal access tokens" → "Generate new token" -
-
- 3 - Name it "Test iExec iApp CLI" (expiration date is optional) -
-
- 4 - Select "Read & Write" permissions -
-
- 5 - Save your token securely and your username -
-
- -Once you have your token, you can deploy your iApp using the following command: - -```sh -# You need your username and the access token (it can take a few minutes to deploy) -iapp deploy -``` - -
-

📝 Make sure to save your iApp address after deployment - you'll need it later!

-

You can find your iApp address in the iexec-app.json file in your project folder.

-
-

⚠️ If you encounter issues during deployment, make sure Docker's BuildKit feature is enabled and supports AMD64 architecture:

- -```sh -docker buildx inspect --bootstrap | grep -i platforms -``` - -

The output should include linux/amd64 in the list of supported platforms. If not, update to the latest Docker Desktop version which includes these requirements.

-
-

⚠️ If you set the wrong Docker username, you can change it by editing the iapp.config.json file

-
- -## 🏃 Run your iApp - -Now you can run your application: - -```sh -iapp run -``` - -To sum up the process, we take the **iApp** and wrap it in the iExec framework, -allowing it to run securely in a **Trusted Execution Environment (TEE)** for -**confidential computing**. If you want to explore further, you can check the -protocol documentation [here](https://protocol.docs.iex.ec/). - -
-

🎉 Congratulations! You've successfully deployed and run your first iApp on iExec. This is a significant milestone - your application is now ready to securely process confidential data in a trusted environment.

-
- -## 🎯 Key Takeaways - -- 🔒 **iApps:** Special applications that run in TEEs to process protected data -- 🛠️ **iApp CLI:** Command-line tool for building, testing, and deploying iApps -- 🔐 **Protected Data:** Can be integrated and processed securely in your iApp -- ⛓️ **Deployment:** Apps are deployed on iExec protocol to run in trusted - environments - -
-

Next up: Alice will learn how to authorize the iApp and Bob to access and use her protected data! 🚀

-
diff --git a/src/overview/helloWorld/4-manageDataAccess.md b/src/overview/helloWorld/4-manageDataAccess.md deleted file mode 100644 index 19e87dc8..00000000 --- a/src/overview/helloWorld/4-manageDataAccess.md +++ /dev/null @@ -1,123 +0,0 @@ ---- -title: Manage Data Access -description: - Learn how to grant and manage access to protected data using iExec's - DataProtector SDK in this tutorial step. ---- - - - -# 🔑 Manage Data Access - -> Reading time 🕒 6 mins - -
-

Control Your Data

-

Alice will learn how to grant access to her protected data and manage who can use it.

-
- -
-

When you protect your data, you can authorize specific users and applications to access it. This means an authorized user will be able to use an authorized iApp to compute your protected data.

-
- -## 🔐 The Authorization Flow - -Here is a simple diagram to explain the process: - -![alt](/assets/hello-world/process_light.png){.light-only} -![alt](/assets/hello-world/process_dark.png){.dark-only} - -
-
- 1 - Protect your data using DataProtector SDK -
-
- 2 - Authorize a user (wallet address) to access your data -
-
- 3 - Authorize the iApp to access your data -
-
- 4 - Authorized user can now run your iApp to process your protected data -
-
- -## 🔓 Grant the iApp Access to your Data - -
-

Remember the iApp address you saved from the previous chapter? You'll need it now to grant access to your protected data.

-
- - - -**Let's look at the code that makes this possible:** - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -// ---cut--- -const grantedAccess = await dataProtectorCore.grantAccess({ - protectedData: '0x123abc...', - authorizedApp: '0x456def...', - authorizedUser: '0x789cba...', -}); -``` - -- 📄 **protectedData**: The protected data address (local storage for the demo) -- 💻 **authorizedApp**: The iApp address you want to authorize -- 👤 **authorizedUser**: User's wallet address (0x... means all users) - -
-

As we don't have the Bob's wallet address, we'll use the zero address to grant access to all users.

-
- -## 🏃 Time to Run - -You're now ready to process your protected data in a trusted environment: - -```sh-vue -iapp run --protectedData {{ protectedDataAddress }} -``` - -
-

🎉 Congratulations! You've successfully completed the core workflow of protecting and processing data with iExec!

-
- -## What's Next: Data Monetization - -We've completed the first step of the journey! You can now integrate the -**DataProtector SDK** into your dApp, **secure your data**, **grant access** to -users and iApps, and **process it safely**. - -But here's where it gets even more exciting... **monetization!**. - -Our SDK offers flexible **monetization mechanisms**, allowing you to create -**protected data collections** and implement advanced models like -**subscriptions**, **rentals**, or **direct sales**. The choice is yours! - -Want to see it in action? Check out our -[Content Creator Demo](https://demo.iex.ec/content-creator/) where you can: - -- Create and protect your own content -- Set pricing and access rules -- Manage subscriptions and rentals -- Track your earnings - -For more technical details, see the -[DataProtector Sharing](../../manage-data/dataProtector/dataProtectorSharing) -documentation. - -
-

You have one more step to complete the journey, and it's the easy one. Let's go to the bonus chapter!

-
diff --git a/src/overview/helloWorld/5-bonusChapter.md b/src/overview/helloWorld/5-bonusChapter.md deleted file mode 100644 index 1a7d4929..00000000 --- a/src/overview/helloWorld/5-bonusChapter.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -title: Bonus Chapter -description: - Explore additional features and advanced concepts in this bonus chapter of the - Hello World tutorial series. ---- - - - -# 🎉 Bonus Chapter - -> Reading time 🕒 4 mins - -
-

Congratulations!

-

You've successfully completed the Hello World journey and learned how to protect data, deploy iApps, and manage data access. Now it's time to claim your rewards! 🏆

-
- -## 🏁 Any Questions? - -
-

- If you have any questions, please schedule an appointment with our DevRel team who will be happy to help! -
-
- 📅 Book a meeting -

-
- Funny waiting animation -
-
- -
-

Need help setting up or got some questions? Join our Discord Community for support!

-
- -## 🎁 Claim your Voucher - -
-

What is a Voucher?

-

A Voucher is your all-in-one solution for iExec development to use iExec's technology, access to premium support, technical guidance and mentorship to help you build and monetize your projects. 🚀

-

Claim your $20 voucher to kickstart your development journey. Want to learn more about Voucher ? 🎁

-
- -
-

Here's your unique coupon code based on your wallet address. You'll need to provide this code when claiming your voucher on Discord:

- - - -
- - - -
-

Thank you for being part of the iExec journey! We can't wait to see what you'll build next! 🚀

-
diff --git a/src/overview/quick-start.md b/src/overview/quick-start.md deleted file mode 100644 index fa2199bd..00000000 --- a/src/overview/quick-start.md +++ /dev/null @@ -1,116 +0,0 @@ ---- -title: Quick Start -description: - Get started quickly with iExec's privacy-preserving technologies and explore - our project starters and interactive sandboxes ---- - -# Quick Start - -Get started quickly with iExec's privacy-preserving technologies. Choose from -our pre-built starters or dive into interactive sandboxes to explore specific -features. - -## Project Starters - -Bootstrap your project with our framework-specific templates and start building -privacy-first applications. - - - - - - - - -## Interactive Sandboxes - -Explore and experiment with iExec features directly in your browser. Perfect for -learning and prototyping. - - - - - - - - - - - - -## Next StepsAfter Exploring our Starters and Sandboxes - -1. **Choose Your Framework**: Start with our Next.js template or wait for - React/Vue options -2. **Experiment**: Try the interactive sandboxes to understand core concepts -3. **Build**: Integrate the features you need into your application -4. **Deploy**: Use our deployment guides for production-ready applications - - diff --git a/src/overview/rlc.md b/src/overview/rlc.md deleted file mode 100644 index 978a34ac..00000000 --- a/src/overview/rlc.md +++ /dev/null @@ -1,134 +0,0 @@ ---- -title: RLC Token -description: - The RLC token is the cryptocurrency that powers the iExec decentralized - computing ecosystem ---- - -
- RLC Token Animation -

RLC Token

-

The native cryptocurrency that powers the entire iExec decentralized confidential computing ecosystem

-
- -**RLC** (**R**un on **L**ots of **C**omputers) serves as the primary medium of -exchange for all interactions within the protocol, enabling users to access -confidential computing services, and rewarding providers for their -contributions. - -## 🎯 What RLC Enables - -RLC is essential for interacting with the iExec protocol and accessing its -decentralized confidential computing services. - -## 💰 Transparent Payment Flow - -When you pay for a task execution with RLC, your payment is automatically and -transparently distributed to all iExec protocol participants: - -
-

🔍 How Your RLC Payment is Distributed

- - **1. App Provider** - Gets paid for providing the confidential application - - **2. Protected Data Provider** - Gets paid for providing access to protected confidential datasets - - **3. Worker** - Gets paid for running the confidential computation on their decentralized machine -
- -**💡 Transparent:** Payments are distributed based on pricing and conditions -defined by each provider (iApp, Protected Data, Worker) in their marketplace -orders. - -**🔒 RLC Staking:** To run a task on the protocol (executing an application with -protected data on a decentralized workerpool), you need to **lock RLC** in the -protocol during the task period. In exchange, you receive **sRLC (staked RLC)**. -Once the task is completed, you can recover the RLC that wasn't consumed for the -task payment. - -## 💰 Tokenomics & Ecosystem Metrics - -RLC operates on a utility-driven economic model where demand for confidential -computing services drives token value: - -**Fixed Supply**: RLC has a maximum supply of 87 million tokens, ensuring -scarcity and value preservation. - -**Network Effects**: As more users and providers join the iExec ecosystem, the -demand for RLC increases, driving token value through network effects. - - - -## 🔄 Getting RLC - -You can acquire RLC tokens through several methods: - -
- - - - - - - -
- -## 🚀 Ready to Get Started? - -Ready to dive into the iExec ecosystem? Here are the next steps: - -- **[Bridge RLC](./tooling-and-explorers/bridge.md)** - Transfer RLC between - networks -- **[Start Using iExec](./quick-start.md)** - Begin your confidential computing - journey -- **[Earn RLC](../manage-data/guides/create-and-share-access.md)** - Become a - provider and monetize your resources - - diff --git a/src/overview/tooling-and-explorers/blockchain-explorer.md b/src/overview/tooling-and-explorers/blockchain-explorer.md deleted file mode 100644 index 47310226..00000000 --- a/src/overview/tooling-and-explorers/blockchain-explorer.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Blockchain Explorers -description: - Explore iExec smart contracts on verified blockchain explorers for both - Arbitrum mainnet and Bellecour network. ---- - -# 🔍 Blockchain Explorers - -Monitor iExec protocol smart contracts on both supported networks through -verified blockchain explorers. All protocol contracts have been verified and are -publicly auditable. - -## 🌐 Supported Networks - -
- - - -
- -::: tip 💡 Dev Tip - -Use **Bellecour** for development and testing as it's a gasless blockchain, then -deploy to **Arbitrum** for production workloads. - -::: - - diff --git a/src/overview/tooling-and-explorers/bridge.md b/src/overview/tooling-and-explorers/bridge.md deleted file mode 100644 index 669f3256..00000000 --- a/src/overview/tooling-and-explorers/bridge.md +++ /dev/null @@ -1,119 +0,0 @@ ---- -title: iExec RLC Bridge -description: - Bridge RLC tokens between networks to interact with the iExec protocol. - Transfer RLC to Bellecour (xRLC) and Arbitrum networks using dedicated bridges ---- - -# RLC Bridge - -**RLC** (RLC Token) is the essential cryptocurrency for interacting with the -iExec protocol. Whether you are executing tasks, accessing protected data, or -participating in the iExec decentralized confidential computing network, you -will need RLC tokens on the appropriate network. - -This guide helps you bridge RLC tokens to **Bellecour** (becoming xRLC) and -**Arbitrum** networks using dedicated bridge solutions. - -## 🗂️ Available Bridges - -iExec provides officially supported bridges for seamless token transfer across -networks: - - - - - - - -## 🔄 Bellecour Bridge - -The **Bellecour Bridge** enables seamless transfer of RLC tokens between -Ethereum mainnet and the Bellecour sidechain in both directions. When bridged to -Bellecour, RLC becomes xRLC, the native asset of the Bellecour network. - -### Ethereum <> Bellecour (RLC <> xRLC) - -1. **Connect Wallet**: Visit - [Bellecour Bridge UI](https://bridge-bellecour.iex.ec/) and connect your - wallet -2. **Select Source Network**: The bridge automatically detects your current - network and available tokens (RLC on Ethereum or xRLC on Bellecour) -3. **Choose Destination**: The bridge will show the opposite network as - destination automatically -4. **Select Amount**: Choose the amount of tokens you want to bridge -5. **Confirm Transaction**: Approve the bridge transaction and wait for - confirmation -6. **Receive Tokens**: Your tokens will be available on the destination network - - - -
-

🔄 Bidirectional Bridge

-

The bridge interface automatically detects your wallet's network and available tokens. The process is similar in both directions - simply switch to the appropriate network (source chain) in your wallet and refresh the page to update the bridge direction, then the bridge will handle the conversion between RLC and xRLC seamlessly.

-
- -## ⚡ Stargate Bridge - -The **Stargate Bridge** powered by LayerZero enables cross-chain transfers of -RLC tokens between Ethereum and Arbitrum mainnet in both directions. - -### Ethereum <> Arbitrum (RLC <> RLC) - -1. **Visit Stargate**: Go to [Stargate UI](https://stargate.finance/bridge) -2. **Connect Wallet**: Connect your wallet to the Stargate interface -3. **Select Networks**: Choose your source network (Ethereum or Arbitrum) and - destination network -4. **Select Token**: Choose RLC as the token to bridge -5. **Enter Amount**: Specify the amount of RLC to transfer -6. **Confirm Transaction**: Approve the bridge transaction and wait for - confirmation - - - -
-

🔄 Bidirectional Bridge

-

The Stargate bridge interface automatically detects your wallet's network and available RLC tokens. The process is similar in both directions - simply select the appropriate source and destination networks to transfer RLC between Ethereum and Arbitrum seamlessly.

-
- - diff --git a/src/overview/tooling-and-explorers/builder-dashboard.md b/src/overview/tooling-and-explorers/builder-dashboard.md deleted file mode 100644 index 993df2db..00000000 --- a/src/overview/tooling-and-explorers/builder-dashboard.md +++ /dev/null @@ -1,170 +0,0 @@ ---- -title: Builder Dashboard -description: - Monitor iExec applications with the powerful Builder Dashboard. Manage your - iApps and your manage vouchers ---- - -# 🏗️ Builder Dashboard - -The **Builder Dashboard** is your comprehensive development hub for iExec -protocol. Monitor voucher usage, track your remaining compute capacity for TEE -iApp runs, and view execution history—all in one place. This powerful interface -streamlines your development workflow and provides deep insights on your -confidential iApps deployed on the protocol. - - - -## 🎯 Key Features - - - - - - - -## 📊 Voucher Consumption & Task History - -The first screen of the Builder Dashboard provides comprehensive voucher -monitoring with detailed task execution history and real-time balance tracking. - - - -### Current Voucher Balance & Management - -
-

💰 Voucher Balance Overview

-
    -
  • Current Balance: View your remaining voucher credits and compute capacity
  • -
  • Claim New Vouchers: Request additional vouchers directly from the dashboard
  • -
  • Expiration Alerts: Get notified before vouchers expire
  • -
-
- -### Task Execution History - - - - - - - - - - - -## 📱 Confidential iApp Management - -The second screen provides comprehensive management and analytics for your -deployed confidential iApps with detailed statistics and user insights. - - - -### My Confidential iApps Overview - -
-

📱 iApp Portfolio Metrics

-
    -
  • Deployed Applications: Complete list of all your confidential iApps currently deployed
  • -
  • Execution Statistics: See exactly how many times each iApp has been executed
  • -
  • Unique Users: Track total unique users who have run each application
  • -
  • Revenue Insights: Track earnings and profitability per application
  • -
-
- -### Application Statistics Dashboard - - - - - - - - - - - -::: tip 💡 Dev Tip - -Use the **Builder Dashboard** to monitor your iExec applications and optimize -your development workflow. The comprehensive analytics help you make data-driven -decisions for better application performance. - -::: - - diff --git a/src/overview/tooling-and-explorers/iexec-explorer.md b/src/overview/tooling-and-explorers/iexec-explorer.md deleted file mode 100644 index 19b6f38a..00000000 --- a/src/overview/tooling-and-explorers/iexec-explorer.md +++ /dev/null @@ -1,211 +0,0 @@ ---- -title: iExec Explorer -description: - Explore and analyze transactions on the iExec decentralized computing - platform. Monitor deals, tasks, apps, and datasets in real-time. ---- - -# 🔍 iExec Explorer - -The **iExec Explorer** is your real-time window into the iExec confidential -decentralized computing protocol. Track deals, monitor task execution, and -explore apps and protectedData—all in one powerful dashboard. - - - -## 🎯 What you Can Explore - - - - - - - - - - - - -
-

🏗️ Understanding iExec Architecture

-

Deals are the fundamental orchestration unit - each deal coordinates a set of different stakeholders that share resources and execution parameters to execute a confidential computation task.

-

Each Deal brings together:

-
    -
  • Requester: The entity requesting the computation
  • -
  • iApp: The confidential computation logic
  • -
  • Dataset: The data that will be processed by the iApp
  • -
  • Workerpool: The decentralized network of workers providing computation power
  • -
-
- -## 💼 Deals & Tasks - - - -> **💡 Understanding Deals**: A deal is a coordinated set of stakeholders that -> brings together all the necessary components (app, dataset, workerpool) to -> execute confidential computation requests. - -### Deal Management Dashboard - -- **Deal Architecture**: Visualize how deals orchestrate multiple stakeholders - with shared resources and parameters -- **Stakeholder Composition**: See which stakeholders belong to each deal and - their execution dependencies -- **Asset Coordination**: Monitor how apps, datasets, and workerpools are - allocated across deal tasks -- **Deal Lifecycle**: Track deals from initiation through task execution to - final completion -- **Pricing Analysis**: View payment distributions across the different assets - of a deal (workerpool, dataset, app) - -### Tasks Overview - - - -Browse and analyze all tasks across the iExec network: - -- **Task Listing**: View all tasks independently of their parent deals -- **Task Status**: Monitor task states across the entire network -- **Performance Analytics**: Analyze execution success rates -- **Historical Analysis**: Access task execution history for trend analysis - -### Task Execution Monitoring - - - - - -> **🔗 Task Assets**: Each task involves four key assets working together: the -> requester, the **iApp** (application logic), the **Dataset** (protected data), -> and the **Workerpool** (execution infrastructure). The Explorer shows how -> these components interact. - -- **Asset Relationships**: Visualize the connections between iApp, dataset, and - workerpool for each task -- **Real-time Progress**: Monitor task status from queued → running → completed - with detailed state transitions -- **Execution Environment**: See which workerpool nodes are processing your - tasks and their TEE capabilities (TDX, SGX) -- **Data Flow**: Track which protected datasets are securely accessed by - authorized iApps -- **Result Management**: - - **Download Results**: Access completed task outputs directly from the - Explorer interface - - **Encryption Status**: Understand if results are encrypted based on - requester specifications - - **Result Verification**: Validate computation correctness and integrity - -## 📱 iApps Listing - - - -Explore the iExec application marketplace: - -- **iApp Directory**: Browse available applications -- **Usage Analytics**: View execution counts, and user adoption -- **Performance Metrics**: Analyze execution success rates - -## 🗄️ Protected Data Listing - - - -Navigate the protected data landscape: - -- **Data Catalog**: Discover available datasets with their metadata and asset - type -- **Usage Patterns**: Analyze dataset popularity and user adoption -- **Schema Validation**: Ensure data structure compatibility with your - applications - -## ⚡ Workerpools - - - -Explore the decentralized computing infrastructure: - -- **Infrastructure Overview**: Monitor the distributed network of computing - nodes -- **Worker Metrics**: Analyze execution success rates -- **Resource Availability**: Check computational resources and capacity -- **Usage Statistics**: Analyze workerpool utilization - - diff --git a/src/overview/tooling-and-explorers/subgraph-explorer.md b/src/overview/tooling-and-explorers/subgraph-explorer.md deleted file mode 100644 index 344300f1..00000000 --- a/src/overview/tooling-and-explorers/subgraph-explorer.md +++ /dev/null @@ -1,204 +0,0 @@ ---- -title: The Graph Explorer -description: - Explore and query blockchain data using The Graph's decentralized indexing - protocol. Access iExec subgraphs on Arbitrum and Bellecour networks. ---- - -# 🔍 The Graph Explorer - -The **iExec protocol** uses **The Graph** as a decentralized protocol for -indexing and querying blockchain data across multiple networks. This powerful -tool allows developers and users to efficiently retrieve and analyze on-chain -data through GraphQL queries. - - - -## 🎯 What is the Graph? - -The Graph is a decentralized protocol for indexing and querying blockchain data. -It enables developers to build and publish open APIs called **subgraphs** that -applications can query using GraphQL. This makes it easy to access blockchain -data without having to run your own indexing infrastructure. - - - -### Key Benefits for iExec - -- **Decentralized Indexing**: Data is indexed by a network of decentralized - indexers -- **Real-time Queries**: Get up-to-date information about deals, tasks, apps, - and protected data -- **GraphQL Interface**: Powerful query language for flexible data retrieval -- **Multi-network Support**: Access data across different blockchain networks - -## 🗂️ Available Subgraphs - -iExec has deployed several subgraphs across different networks to provide -comprehensive data access. Each subgraph indexes specific aspects of the iExec -protocol. - - - - - - - - - - - - -## 🔍 GraphQL Explorer Interface - -The Graph provides an interactive GraphQL explorer that allows you to build and -test queries directly in your browser. This powerful interface makes it easy to -explore the available data and construct complex queries. - - - -### How to Use the GraphQL Explorer - -1. **Navigate to a Subgraph**: Click on any of the subgraph links above to - access the GraphQL explorer -2. **Explore the Schema**: Use the "Schema" tab to browse available entities and - their fields -3. **Build Queries**: Use the "Query" tab to construct and test GraphQL queries -4. **View Results**: Execute queries to see real-time data from the blockchain -5. **Copy Queries**: Use the generated queries in your applications - -### Example Queries - -Here are some example queries you can try in the GraphQL explorer: - -#### Query iApps - -```graphql -query { - apps { - id - name - owner { - id - } - appType - appUri - } -} -``` - -#### Query Protected Data - -```graphql -query { - protectedDatas { - id - name - owner { - id - } - dataType - dataUri - } -} -``` - -#### Query Deals and Tasks - -```graphql -query { - deals { - id - requester { - id - } - app { - name - } - dataset { - name - } - workerpool { - id - } - tasks { - id - status - } - } -} -``` - -
-

💡 Pro Tip

-

Use the GraphQL explorer's auto-completion feature to discover available fields and build complex queries. The schema documentation is always up-to-date with the latest protocol changes.

-
- - diff --git a/src/overview/use-cases.md b/src/overview/use-cases.md deleted file mode 100644 index 31197eae..00000000 --- a/src/overview/use-cases.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -title: Use Cases -description: - Explore real-world applications and demo showcases of iExec's - privacy-preserving technologies in action ---- - -# Use Cases Showcase - -Explore our demo applications showcasing iExec's privacy-preserving technologies -in action. Each use case demonstrates real-world applications of confidential -computing and decentralized data protection. - -
- - - - - - -
- - diff --git a/src/overview/welcome.md b/src/overview/welcome.md deleted file mode 100644 index 351f64ed..00000000 --- a/src/overview/welcome.md +++ /dev/null @@ -1,188 +0,0 @@ ---- -title: About iExec -description: - Discover how iExec empowers developers to build privacy-first applications ---- - -# 💡 Privacy Made Easy - -
-

Welcome to iExec

-

Your complete toolkit for building privacy-first Web3 applications that protect and use sensitive data

-
- -## 🤔 Why iExec? - -In a world where data privacy is more critical than ever, developers face a -tough challenge: how to build innovative applications without compromising -sensitive user data. - -
-

iExec solves this by providing a privacy-first toolkit that makes it simple to protect, manage, and compute data securely, even in untrusted environments. We believe privacy should be a built-in feature, not an afterthought.

-
- -## 🛠️ How we Make it Happen - -iExec provides a complete toolkit that makes privacy simple and actionable: - -
-
-
- 🔌 -
- Privacy: Integrate confidentiality features without managing complex infrastructure -
-
-
- 🎮 -
- Governance and Ownership: Control your assets through orders, choose who can access your data, with which privacy applications, and at what price -
-
-
- ⛓️ -
- Multi-Chain Interoperability: Build privacy-first applications on supported networks -
-
-
- 🔧 -
- Plug & Play tools: Fluide experience with comprehensive toolkit, SDKs, and ready-to-use components for seamless development -
-
-
-
- -## 🔧 What we Provide - -Ready-to-use components to protect sensitive data and computation: - -
-
-
- 🔒 -
- DataProtector: Secure and control access to sensitive information -
-
-
- -
- iApp Generator: Bootstrap your Privacy Apps on TEEs. -Confidential Computing made easy -
-
-
- 🛡️ -
- Decentralized Confidential Computing: Process data securely in secure and confidential environments while controlling access and monetization through blockchain -
-
-
-
- -## 💡 Real-world Use Cases - -
-
-
- 💰 -

Finance

-
-
    -
  • Analyze sensitive financial data
  • -
  • Process credit scores without exposing personal information
  • -
-
- -
-
- 🤖 -

AI/ML

-
-
    -
  • Train models on private datasets
  • -
  • Perform confidential predictions
  • -
-
- -
-
- 🔬 -

Research

-
-
    -
  • Share and analyze research data securely
  • -
  • Collaborate while protecting intellectual property
  • -
-
- -
-
- 📊 -

Business Analytics

-
-
    -
  • Process competitive market data
  • -
  • Analyze business metrics confidentially
  • -
-
- -
-
- 🎮 -

Gaming

-
-
    -
  • Protect player data and game assets
  • -
  • Process in-game transactions securely
  • -
-
-
-
- 🏥 -

Healthcare

-
-
    -
  • Process patient records privately
  • -
  • Run medical analyses while preserving patient confidentiality
  • -
-
-
- -
-

In the next chapters, we'll guide you through our Hello World journey, a 30 minutes start that will teach you everything about iExec, from protecting sensitive data to building and deploying confidential apps.

-
- -## 🚀 Start Building - -Ready to build privacy-first applications? Choose your path: - -
-
-
- -
- Quick Start: Follow our Hello World guide to build your first confidential app in minutes -
-
-
- 🔍 -
- Explore Use Cases: Browse real-world examples to see what you can build with iExec -
-
-
- 💬 -
- Join the Community: Connect with other builders and get support on our Discord -
-
-
-
- ---- - -_Ready to transform how you handle sensitive data? Let's build something amazing -together._ diff --git a/src/protocol/glossary.md b/src/protocol/glossary.md deleted file mode 100644 index 7d1374e3..00000000 --- a/src/protocol/glossary.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: Glossary -description: iExec terms glossary ---- - -# 📖 Glossary - -This page is under development. - - diff --git a/src/protocol/sdk.md b/src/protocol/sdk.md deleted file mode 100644 index 374e09f8..00000000 --- a/src/protocol/sdk.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: iExec SDK -description: iExec SDK ---- - -# 🔧 iExec SDK - -This page is under development. - - diff --git a/src/protocol/workers.md b/src/protocol/workers.md deleted file mode 100644 index c9ea366c..00000000 --- a/src/protocol/workers.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Workers and Workerpools -description: - Understanding the compute infrastructure of iExec and how workers and - workerpools function ---- - -# ⚙️ Workers & Workerpools - -This page is under development. - - diff --git a/src/use-iapp/getting-started.md b/src/use-iapp/getting-started.md deleted file mode 100644 index f0604249..00000000 --- a/src/use-iapp/getting-started.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -title: Getting Started with iApps -description: - Your first steps to discover and execute existing iApps on the iExec platform ---- - -# 🚀 Getting Started - -This page is under development. - - diff --git a/src/use-iapp/guides/add-inputs-to-execution.md b/src/use-iapp/guides/add-inputs-to-execution.md deleted file mode 100644 index 0965928e..00000000 --- a/src/use-iapp/guides/add-inputs-to-execution.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: Add Inputs to the Execution -description: Add inputs to the execution ---- - -# Add Inputs to the Execution - -This page is under development. - - diff --git a/src/use-iapp/guides/different-ways-to-execute.md b/src/use-iapp/guides/different-ways-to-execute.md deleted file mode 100644 index a2efbf89..00000000 --- a/src/use-iapp/guides/different-ways-to-execute.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: Different Ways to Execute an iApp -description: Different ways to execute an iApp ---- - -# Different Ways to Execute an iApp - -This page is under development. - - diff --git a/src/use-iapp/guides/find-iapps.md b/src/use-iapp/guides/find-iapps.md deleted file mode 100644 index 2353eed1..00000000 --- a/src/use-iapp/guides/find-iapps.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: Find iApps to Use -description: Find iApps to Use ---- - -# Find iApps to Use - -This page is under development. - - diff --git a/src/use-iapp/guides/how-to-pay-executions.md b/src/use-iapp/guides/how-to-pay-executions.md deleted file mode 100644 index 68a42d82..00000000 --- a/src/use-iapp/guides/how-to-pay-executions.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: How to Pay the Executions -description: How to pay for executions ---- - -# How to Pay the Executions - -This page is under development. - - diff --git a/src/use-iapp/guides/use-iapp-with-protected-data.md b/src/use-iapp/guides/use-iapp-with-protected-data.md deleted file mode 100644 index 74467330..00000000 --- a/src/use-iapp/guides/use-iapp-with-protected-data.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: Use iApp with Protected Data -description: Use iApp with Protected Data ---- - -# Use iApp with Protected Data - -This page is under development. - - diff --git a/src/use-iapp/how-to-pay/how-to-pay-for-web3mail.md b/src/use-iapp/how-to-pay/how-to-pay-for-web3mail.md deleted file mode 100644 index 8eec8cdc..00000000 --- a/src/use-iapp/how-to-pay/how-to-pay-for-web3mail.md +++ /dev/null @@ -1,165 +0,0 @@ ---- -title: How to Pay for Web3Mail -description: - Learn how to pay for Web3Mail's confidential computing power using vouchers - and xLC for secure, blockchain-based email communication. ---- - -# How to Pay for Web3Mail - -[Web3Mail](../web3mail) dev tool offers secure, blockchain-based communication -by encrypting emails and protecting user privacy. - -The `sendEmail` function uses confidential computing power to encrypt and send -messages, ensuring secure and decentralized email exchanges. - -This guide explains how to pay for Web3Mail's computing power using **vouchers** -and **xRLC**, detailing the steps for each method. - -## Using Vouchers for Web3Mail - -### Step 1: Obtain a Voucher - -- **Acquire Vouchers**: Obtain vouchers through the - [iExec Builder Dashboard](https://builder.iex.ec/). Note that the number of - Web3Mail executions and the expiration time of each voucher is restricted - based on its validity period. Refer to - [pricing documentation](https://www.iex.ec/voucher) for more information. -- **Support**: For specific limitations related to your voucher, please contact - iExec Support. - -### Step 2: Use the Builder Dashboard - - - Builder dashboard screenshot - - -The iExec Builder Dashboard is a comprehensive tool for managing vouchers and -resources, providing an intuitive interface for: - -- **Claiming Vouchers**: Builders can claim vouchers during the BUILD and EARN - stages. -- **Top-Up Vouchers**: Future updates will allow direct top-ups via the - dashboard. Currently, builders are redirected to Discord. -- **Checking Voucher Balance**: Track your voucher balance and usage history. - -🧙🏼 [Go here](https://builder.iex.ec/) - -### Step 3: Grant Allowance (If Necessary) - -Use `iexec.account.approve(voucherAddress)` to authorize the voucher smart -contract to debit your account if the voucher balance is insufficient. This -ensures that if the voucher alone doesn't cover the execution cost, the -remaining balance is automatically deducted from your account. - -For additional information on using xRLC for fallback payment in Web3Mail, refer -to the **Using xRLC with Web3Mail** section. - -### Step 4: Execute Web3Mail's SendEmail Function - -When using a voucher for payment, set the `useVoucher` parameter to `true`: - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const sendEmail = await web3mail.sendEmail({ - protectedData: '0x123abc...', - emailSubject: 'My email subject', - emailContent: 'My email content', - useVoucher: true, // [!code focus] -}); -``` - -## Using xRLC for Web3Mail - -If you choose to use xRLC to cover the computational cost of Web3Mail (or if you -need to cover data access costs such as retrieving the recipient's email -address), follow these steps: - -### Install the iExec SDK - -To manage RLC tokens, developers must use the iExec SDK, which offers all the -necessary tools for interacting with the iExec platform. This includes -depositing, withdrawing, and checking balances of RLC and xRLC - -- In your JS/TS project npm install iexec -- Instantiate the iExec SDK (see the - [doc](https://github.com/iExecBlockchainComputing/iexec-sdk/blob/master/docs/README.md#quick-start)) - -```javascript -import { IExec } from 'iexec'; -// connect injected provider -const iexec = new IExec({ ethProvider: window.ethereum }); -``` - -### Purchase RLC - -Obtain RLC tokens from a supported cryptocurrency exchange. - -### Convert to xRLC - -Use the iExec Bridge to convert your RLC into xRLC for use on iExec's sidechain. -The bridging operation follows the lock & mint / burn & unlock principle. When -sending tokens from Mainnet to Bellecour, the bridge locks the initial amount on -the source chain and mints the equivalent on the destination chain. When going -in the other direction, it burns tokens on the Sidechain and unlocks the same -amount on Mainnet. The bridged asset is called xRLC on Bellecour. - -Users can send tokens from the Ethereum Mainnet to the iExec Sidechain or -vice-versa using the Account Manager -([iExec Explorer](https://explorer.iex.ec/bellecour) or -[POA Bridge UI](https://bridge-bellecour.iex.ec/)) available across all iExec -products. - -### Deposit xRLC - -Deposit the xRLC into your iExec account using the command: - -```javascript -iexec.account.deposit(xRLC_amount); -``` - -This converts xRLC into sRLC, used as proof of funds for task execution. - -### Check sRLC Balance - -Use the command below to check your balance: - -```javascript -iexec.account.show(); -``` - -### Execute sendEmail - -Set the `useVoucher` parameter to `false` when using Web3Mail's sendEmail -function to pay with xRLC: - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const sendEmail = await web3mail.sendEmail({ - protectedData: '0x123abc...', - emailSubject: 'My email subject', - emailContent: 'My email content', - useVoucher: false, // [!code focus] -}); -``` - -### Withdraw sRLC (If Desired) - -Convert sRLC back to xRLC and withdraw to your wallet using: - -```javascript -iexec.account.withdraw(RLC_amount); -``` - - diff --git a/src/use-iapp/how-to-pay/how-to-pay-for-web3telegram.md b/src/use-iapp/how-to-pay/how-to-pay-for-web3telegram.md deleted file mode 100644 index ea3a5b43..00000000 --- a/src/use-iapp/how-to-pay/how-to-pay-for-web3telegram.md +++ /dev/null @@ -1,178 +0,0 @@ ---- -title: How to Pay for Web3Telegram -description: - Learn how to pay for Web3Telegram using vouchers or xRLC. This guide walks you - through obtaining vouchers, managing RLC to xRLC conversion, and using both - methods for secure Telegram communication. ---- - -# How to Pay for Web3Telegram - -[Web3Telegram](../web3telegram) dev tool offers secure, blockchain-based -communication by encrypting emails and protecting user privacy. - -The `sendTelegram` function uses confidential computing power to encrypt and -send messages, ensuring secure and decentralized email exchanges. - -This guide explains how to pay for Web3Telegram's computing power using -**vouchers** and **xRLC**, detailing the steps for each method. - -## Using Vouchers for Web3Telegram - -### Step 1: Obtain a Voucher - -- **Acquire Vouchers**: Obtain vouchers through the - [iExec Builder Dashboard](https://builder.iex.ec/). Note that the number of - Web3Telegram executions and the expiration time of each voucher is restricted - based on its validity period. Refer to - [pricing documentation](https://www.iex.ec/voucher) for more information. -- **Support**: For specific limitations related to your voucher, please contact - iExec Support. - -### Step 2: Use the Builder Dashboard - - - Builder dashboard screenshot - - -The iExec Builder Dashboard is a comprehensive tool for managing vouchers and -resources, providing an intuitive interface for: - -- **Claiming Vouchers**: Builders can claim vouchers during the BUILD and EARN - stages. -- **Top-Up Vouchers**: Future updates will allow direct top-ups via the - dashboard. Currently, builders are redirected to Discord. -- **Checking Voucher Balance**: Track your voucher balance and usage history. - -🧙🏼 [Go here](https://builder.iex.ec/) - -### Step 3: Grant Allowance (If Necessary) - -Use `iexec.account.approve(voucherAddress)` to authorize the voucher smart -contract to debit your account if the voucher balance is insufficient. This -ensures that if the voucher alone doesn't cover the execution cost, the -remaining balance is automatically deducted from your account. - -For additional information on using xRLC for fallback payment in Web3Telegram, -refer to the **Using xRLC with Web3Telegram** section. - -### Step 4: Execute Web3Telegram's `sendTelegram` Function - -When using a voucher for payment, set the `useVoucher` parameter to `true`: - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- - -const sendTelegram = await web3telegram.sendTelegram({ - protectedData: '0x123abc...', - telegramContent: 'My telegram message content', - senderName: 'Awesome project team', - label: 'some-cutom-id', - workerpoolAddressOrEns: 'prod-v8-bellecour.main.pools.iexec.eth', - dataMaxPrice: 42, - appMaxPrice: 42, - workerpoolMaxPrice: 42, - useVoucher: true, // [!code focus] -}); -``` - -## Using xRLC for Web3Telegram - -If you choose to use xRLC to cover the computational cost of Web3Telegram (or if -you need to cover data access costs such as retrieving the recipient's Chat Id), -follow these steps: - -### Install the iExec SDK - -To manage RLC tokens, developers must use the iExec SDK, which offers all the -necessary tools for interacting with the iExec platform. This includes -depositing, withdrawing, and checking balances of RLC and xRLC - -- In your JS/TS project npm install iexec -- Instantiate the iExec SDK (see the - [doc](https://github.com/iExecBlockchainComputing/iexec-sdk/blob/master/docs/README.md#quick-start)) - -```javascript -import { IExec } from 'iexec'; -// connect injected provider -const iexec = new IExec({ ethProvider: window.ethereum }); -``` - -### Purchase RLC - -Obtain RLC tokens from a supported cryptocurrency exchange. - -### Convert to xRLC - -Use the iExec Bridge to convert your RLC into xRLC for use on iExec's sidechain. -The bridging operation follows the lock & mint / burn & unlock principle. When -sending tokens from Mainnet to Bellecour, the bridge locks the initial amount on -the source chain and mints the equivalent on the destination chain. When going -in the other direction, it burns tokens on the Sidechain and unlocks the same -amount on Mainnet. The bridged asset is called xRLC on Bellecour. - -Users can send tokens from the Ethereum Mainnet to the iExec Sidechain or -vice-versa using the Account Manager -([iExec Explorer](https://explorer.iex.ec/bellecour) or -[POA Bridge UI](https://bridge-bellecour.iex.ec/)) available across all iExec -products. - -### Deposit xRLC - -Deposit the xRLC into your iExec account using the command: - -```javascript -iexec.account.deposit(xRLC_amount); -``` - -This converts xRLC into sRLC, used as proof of funds for task execution. - -### Check sRLC Balance - -Use the command below to check your balance: - -```javascript -iexec.account.show(); -``` - -### Execute `sendTelegram` - -Set the `useVoucher` parameter to `false` when using Web3Telegram's sendTelegram -function to pay with xRLC: - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- - -const sendTelegram = await web3telegram.sendTelegram({ - protectedData: '0x123abc...', - telegramContent: 'My telegram message content', - senderName: 'Awesome project team', - label: 'some-cutom-id', - workerpoolAddressOrEns: 'prod-v8-bellecour.main.pools.iexec.eth', - dataMaxPrice: 42, - appMaxPrice: 42, - workerpoolMaxPrice: 42, - useVoucher: false, // [!code focus] -}); -``` - -### Withdraw sRLC (If Desired) - -Convert sRLC back to xRLC and withdraw to your wallet using: - -```javascript -iexec.account.withdraw(RLC_amount); -``` - - diff --git a/src/use-iapp/how-to-pay/pricing-considerations.md b/src/use-iapp/how-to-pay/pricing-considerations.md deleted file mode 100644 index 09587a20..00000000 --- a/src/use-iapp/how-to-pay/pricing-considerations.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: Pricing Considerations -description: Pricing considerations ---- - -# Pricing Considerations - -This page is under development. - - diff --git a/src/use-iapp/how-to-pay/voucher.md b/src/use-iapp/how-to-pay/voucher.md deleted file mode 100644 index cf1c7822..00000000 --- a/src/use-iapp/how-to-pay/voucher.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -title: Voucher Guide -description: Voucher Guide ---- - -# Voucher Guide - -This page is under development. - - diff --git a/src/use-iapp/introduction.md b/src/use-iapp/introduction.md deleted file mode 100644 index 3434e4f6..00000000 --- a/src/use-iapp/introduction.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Introduction to Using iApps -description: - Learn how to discover, execute, and interact with iExec applications for - privacy-preserving computation ---- - -# 📝 Introduction - -This page is under development. - - diff --git a/src/use-iapp/web3mail.md b/src/use-iapp/web3mail.md deleted file mode 100644 index 769bb9af..00000000 --- a/src/use-iapp/web3mail.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: Web3Mail -description: - Web3Mail enables secure, private email communication on the blockchain using - Ethereum addresses. Manage contact permissions and send emails without - revealing personal information. ---- - - - -# ✉ Web3Mail - -The Web3Mail tool offers a secure method to manage email-based communications -via the blockchain. This mechanism helps protect the personal information of the -email recipients through use of Ethereum addresses. - -The email address is stored as a `protectedData` entity using the -[iExec Data Protector tool](./dataProtector.md). Through this mechanism, users -have complete control over which applications may use their email address for -sending communications. Sending a user a message, therefore, requires knowledge -of the Ethereum address of their `protectedData` as well positive authorization -for your account to contact them. Your account may be bound to either an -application or an individual. At any time a user may revoke permissions and this -revocation is immediate, giving users complete control over the privacy and -security of their information. - -Apps using the Web3Mail tool can: - -- enable an entity (such as an application provider or an end-user) to email an - Ethereum account holder without knowing their email address -- grant users complete control over which entities are authorized to use their - email address to send them communications - -The Web3Mail tool currently supports the following methods: - -- **fetchMyContacts** — retrieve a list of Ethereum addresses whose owners have - authorized you to email them -- **fetchUserContacts** — retrieve a list of Ethereum addresses whose owners - have authorized a given entity to email them -- **sendEmail** — send an email message to a user knowing only the Ethereum - address for the `protectedData` containing their email address - -**Try the demo:** - - - Web3Messaging Demo - diff --git a/src/use-iapp/web3mail/advanced-configuration.md b/src/use-iapp/web3mail/advanced-configuration.md deleted file mode 100644 index 1eccb10f..00000000 --- a/src/use-iapp/web3mail/advanced-configuration.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -title: Advanced Configuration -description: - Customize iExec Web3Mail with advanced options like custom iApp address, iApp - whitelist, IPFS node, and subgraph URL for tailored blockchain email - communication. ---- - -# Advanced Configuration - -The `IExecWeb3mail` constructor accepts configuration options object. As these -options are very specific, you won't need to use them for a standard usage of -`@iexec/web3mail`. - -## Parameters - -```ts twoslash -import { type Web3MailConfigOptions } from '@iexec/web3mail'; -``` - -### dappAddressOrENS - -The Ethereum contract address or ENS (Ethereum Name Service) for the web3mail -dApp. - -If not provided, the default ENS `web3mail.apps.iexec.eth` pointing to the -latest version of the web3mail dApp provided by iExec will be used. - -You can find the corresponding dApp address with Bellecour explorer: -[https://explorer.iex.ec/bellecour/search/web3mail.apps.iexec.eth](https://explorer.iex.ec/bellecour/search/web3mail.apps.iexec.eth). - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const web3mail = new IExecWeb3mail(web3Provider, { - dappAddressOrENS: 'web3mail.apps.iexec.eth', // [!code focus] -}); -``` - -### dappWhitelistAddress - -The Ethereum contract address for the web3mail dApps whitelist. By granting -access to a whitelist, email address owners ensure their email is still -available to consumers even after a new version of web3mail dApp gets released. - -If not provided, the default whitelist smart contract address provided by iExec -will be used. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const web3mail = new IExecWeb3mail(web3Provider, { - dappWhitelistAddress: '0x781482C39CcE25546583EaC4957Fb7Bf04C277D2', // [!code focus] -}); -``` - -See it in -[https://blockscout-bellecour.iex.ec/](https://blockscout-bellecour.iex.ec/address/0x781482C39CcE25546583EaC4957Fb7Bf04C277D2) - -### dataProtectorSubgraph - -The subgraph URL for querying data. - -If not provided, the default data protector subgraph provided by iExec will be -used. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const web3mail = new IExecWeb3mail(web3Provider, { - dataProtectorSubgraph: - 'https://thegraph-product.iex.ec/subgraphs/name/bellecour/dataprotector', // [!code focus] -}); -``` - -### ipfsNode - -The IPFS node URL for content uploads. Use this option if you want to use your -own IPFS node to upload content. - -If not provided, the default IPFS node provided by iExec will be used. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const web3mail = new IExecWeb3mail(web3Provider, { - ipfsNode: 'https://ipfs-upload.v8-bellecour.iex.ec', // [!code focus] -}); -``` - -### ipfsGateway - -The IPFS gateway URL used for content downloads. Mainly used for checking -content uploaded on the IPFS network. Use this option if you want to use your -own IPFS node for content downloads. - -If not provided, the default IPFS gateway provided by iExec will be used. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const web3mail = new IExecWeb3mail(web3Provider, { - ipfsGateway: 'https://ipfs-gateway.v8-bellecour.iex.ec', // [!code focus] -}); -``` - -### iexecOptions - -Low level configuration options for `iexec` SDK, see -[iexec SDK documentation IExecConfigOptions](https://github.com/iExecBlockchainComputing/iexec-sdk/blob/master/docs/interfaces/IExecConfigOptions.md) -for more details. diff --git a/src/use-iapp/web3mail/getting-started.md b/src/use-iapp/web3mail/getting-started.md deleted file mode 100644 index 70a2390f..00000000 --- a/src/use-iapp/web3mail/getting-started.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -title: Getting Started -description: - Get started with the iExec Web3Mail SDK. Learn how to install, configure, and - instantiate it with or without a Web3 provider to enable blockchain-based - email communication. ---- - -# Getting Started - -[![GitHub package.json version (branch)](https://img.shields.io/github/package-json/v/iExecBlockchainComputing/web3mail-sdk?color=green)](https://github.com/iExecBlockchainComputing/web3mail-sdk) - -## Overview - -### Prerequisites - -Before getting started, ensure that you have the following installed on your -system: - -\- [**Node.js**](https://nodejs.org/en/) version 18 or higher - -\- [**NPM**](https://docs.npmjs.com/) (Node.js package manager) - -### Installation - -::: code-group - -```sh [npm] -npm install @iexec/web3mail -``` - -```sh [yarn] -yarn add @iexec/web3mail -``` - -```sh [pnpm] -pnpm add @iexec/web3mail -``` - -```sh [bun] -bun add @iexec/web3mail -``` - -::: - -**This package is an ESM package. Your project needs to use ESM too.** - [**Read more**](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) - -If you use it with **Webpack**, some polyfills will be needed. You can find a -working project -[here](https://github.com/iExecBlockchainComputing/web3mail-sdk/tree/main/demo/browser-webpack). - -### Instantiate with a Web3 Provider - -::: code-group - -```ts twoslash [Browser] -declare global { - interface Window { - ethereum: any; - } -} -// ---cut--- -import { IExecWeb3mail } from '@iexec/web3mail'; - -const web3Provider = window.ethereum; -// instantiate -const web3mail = new IExecWeb3mail(web3Provider); -``` - -```ts twoslash [NodeJS] -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -// get web3 provider from a private key -const web3Provider = getWeb3Provider('YOUR_PRIVATE_KEY'); -// instantiate -const web3mail = new IExecWeb3mail(web3Provider); -``` - -::: - -### Instantiate Without a Web3 Provider - -For projects that only require read functions, you can instantiate the SDK -without a Web3 provider. - -::: code-group - -```ts twoslash [Browser] -import { IExecWeb3mail } from '@iexec/web3mail'; - -// instantiate -const web3mail = new IExecWeb3mail(); -``` - -```ts twoslash [NodeJS] -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -// instantiate -const web3mail = new IExecWeb3mail(); -``` - -::: - -## Sandbox - - - ⚡  Code Sandbox - - -Corresponding GitHub repository: - - - 🔎  GitHub repository sandbox - diff --git a/src/use-iapp/web3mail/methods/fetchMyContacts.md b/src/use-iapp/web3mail/methods/fetchMyContacts.md deleted file mode 100644 index b7e6952a..00000000 --- a/src/use-iapp/web3mail/methods/fetchMyContacts.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: fetchMyContacts -description: - Use the fetchMyContacts method from iExec Web3Mail to retrieve contact infos - of users who authorized you to email them. ---- - -# fetchMyContacts - -This method provides a list of `contact` objects identifying all users who -previously granted authorization to send them email messages. Each contact -contains the contact's ETH address as well as the ETH address for the -`protectedData` containing their email address. - -## Usage - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const contactsList = await web3mail.fetchMyContacts(); -``` - -## Parameters - -```ts twoslash -import { type FetchMyContactsParams } from '@iexec/web3mail'; -``` - -### isUserStrict - -**Type:** `boolean` - -This parameter enables fetching contacts who granted access exclusively to the -user and no one else. - -:::tip - -When you grant access to someone, you can choose to grant access to a specific -user (a wallet) or to any user (`0x0000000000000000000000000000000000000000`). - -::: - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const contactsList = await web3mail.fetchMyContacts({ - isUserStrict: true, // [!code focus] -}); -``` - -## Return Value - -The result object contains a list of `contact` objects. Each `contact` -represents one user who previously granted you authorization to send them -messages. - -```ts twoslash -import { type Contact } from '@iexec/web3mail'; -``` - -[`Contact[]`](../../types.md#contact) diff --git a/src/use-iapp/web3mail/methods/fetchUserContacts.md b/src/use-iapp/web3mail/methods/fetchUserContacts.md deleted file mode 100644 index 42bad9d3..00000000 --- a/src/use-iapp/web3mail/methods/fetchUserContacts.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: fetchUserContacts -description: - Use fetchUserContacts from iExec Web3Mail to get users who authorized a - specific address to email them. ---- - -# fetchUserContacts - -This method provides a list of `contact` objects identifying all entities who -previously granted authorization to a specified entity to send them email -messages. Each contact contains the contact's ETH address as well as the ETH -address for the `protectedData` containing their email address. - -## Usage - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const contactsList = await web3mail.fetchUserContacts({ - userAddress: '0x789cba...', -}); -``` - -## Parameters - -```ts twoslash -import { type FetchUserContactsParams } from '@iexec/web3mail'; -``` - -### userAddress - -**Type:** `Address` - -The user for which you wish to obtain the list of contacts. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const contactsList = await web3mail.fetchUserContacts({ - userAddress: '0x789cba...', // [!code focus] -}); -``` - -### isUserStrict - -**Type:** `boolean` - -This parameter enables fetching contacts who granted access exclusively to the -user and no one else. - -:::tip - -When someone grants access, you can choose to grant access to a specific user (a -wallet) or to any user (`0x0000000000000000000000000000000000000000`). - -::: - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const contactsList = await web3mail.fetchUserContacts({ - userAddress: '0x789cba...', - isUserStrict: true, // [!code focus] -}); -``` - -## Return Value - -The result object contains a list of `contact` objects. Each `contact` -represents one user who previously granted authorization for the user identified -with `userAddress` to send them messages. - -```ts twoslash -import { type Contact } from '@iexec/web3mail'; -``` - -[`Contract[]`](../../types.md#contact) diff --git a/src/use-iapp/web3mail/methods/sendEmail.md b/src/use-iapp/web3mail/methods/sendEmail.md deleted file mode 100644 index a5f74ffd..00000000 --- a/src/use-iapp/web3mail/methods/sendEmail.md +++ /dev/null @@ -1,392 +0,0 @@ ---- -title: sendEmail -description: - Send secure, permissioned emails using iExec Web3Mail's sendEmail method—no - need to know the user's email, just their authorized protectedData address. ---- - -# sendEmail - -This method allows an authorized entity to send an email message to a user -without needing to know their email address. - -The recipient email address is stored in a `protectedData` entity. The user -receiving the email must explicitly authorize you to send them email -communications and permission must be granted for the `Web3Mail` tool to use the -`protectedData` entity containing their email address. This is best done by -granting authorization to the Web3Mail app whitelist -`0x781482C39CcE25546583EaC4957Fb7Bf04C277D2` as `authorizedApp`. Refer to the -[Data Protector `grantAccess`](../../dataProtector/dataProtectorCore/grantAccess.md) -documentation for more details. - -::: tip - -For executing the `sendEmail` method with a voucher or xRLC, refer to the -dedicated section in the documentation under -"[How to Pay for web3mail](../../../overview/how-to-pay-for-web3mail)". - -::: - -## Usage - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const sendEmail = await web3mail.sendEmail({ - protectedData: '0x123abc...', - emailSubject: 'My email subject', - emailContent: 'My email content', -}); -``` - -## Parameters - -```ts twoslash -import { type SendEmailParams } from '@iexec/web3mail'; -``` - -### protectedData - -**Type:** `Address` - -The address of the `protectedData` holding the contact's email address. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const sendEmail = await web3mail.sendEmail({ - protectedData: '0x123abc...', // [!code focus] - emailSubject: 'My email subject', - emailContent: 'My email content', -}); -``` - -### emailSubject - -**Type:** `string` -**Max**: 78 characters - -The subject line for the email you are sending. This field is limited to 78 -characters. Any characters beyond that limited are truncated. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const sendEmail = await web3mail.sendEmail({ - protectedData: '0x123abc...', - emailSubject: 'My email subject', // [!code focus] - emailContent: 'My email content', -}); -``` - -### emailContent - -**Type:** `string` - -optionally HTML encoded - -_maximum size_: 512 kb - -The email content that needs to be sent. The content is limited to 512 kb in -size. Email content will be encrypted and stored in IPFS. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const sendEmail = await web3mail.sendEmail({ - protectedData: '0x123abc...', - emailSubject: 'My email subject', - emailContent: 'My email content', // [!code focus] -}); -``` - -### useVoucher - -**Type:** `boolean` -**Default:** `false` - -This optional param allows you to pay for the deal using your voucher. Make sure -that your voucher is held by your connected wallet. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const sendEmail = await web3mail.sendEmail({ - protectedData: '0x123abc...', - emailSubject: 'My email subject', - emailContent: 'My email content', - useVoucher: true, // [!code focus] -}); -``` - -::: tip - -If your voucher doesn't have enough xRLC to cover the deal, the SDK will -automatically get the required amount to your iExec account. Ensure that your -voucher is authorized to access your iExec account and that your account has -sufficient funds for this transfer to proceed. - -::: - -### contentType - -**Type:** `text/plain` or `text/html` -**Default:** `text/plain` - -This is used by the mail client to properly render the delivered text. Set this -to `text/html` to enable rich HTML content in your email. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const sendEmail = await web3mail.sendEmail({ - protectedData: '0x123abc...', - emailSubject: 'My email subject', - emailContent: '

Hello world!

', - contentType: 'text/html', // [!code focus] -}); -``` - -### senderName - -**Type:** `string` -**Default:** `Web3Mail` -**Min:** 3 characters -**Max:** 20 characters - -Allows specifying a sender name for the email. This is used by the mail client -in rendering the email to the user. The Web3Mail tool appends `via Web3Mail` to -the supplied name. Setting this to `Tom`, for example, will result in a sender -name of, `Tom via Web3Mail`, in the delivered email. If no name is specified, -the Web3Mail tool sets this to a value of `Web3Mail`. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const sendEmail = await web3mail.sendEmail({ - protectedData: '0x123abc...', - emailSubject: 'My email subject', - emailContent: 'My email content', - senderName: 'Awesome project team', // [!code focus] -}); -``` - -### label - -**Type:** `string` - -Allows adding a custom public label. The Web3Mail tool writes this onchain as -`iexec_args` in the deal params. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const sendEmail = await web3mail.sendEmail({ - protectedData: '0x123abc...', - emailSubject: 'My email subject', - emailContent: 'My email content', - label: 'some-cutom-id', // [!code focus] -}); -``` - -### workerpoolAddressOrEns - -**Type:** `workerpoolAddressOrEns` -**Default:** iExec's production workerpool - -Allows specifying the workerpool that will run the Web3Mail application. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const sendEmail = await web3mail.sendEmail({ - protectedData: '0x123abc...', - emailSubject: 'My email subject', - emailContent: 'My email content', - workerpoolAddressOrEns: 'prod-v8-bellecour.main.pools.iexec.eth', // [!code focus] -}); -``` - -::: tip - -iExec currently offers a production workerpool located at the Ethereum Name -Service (ENS) address `prod-v8-bellecour.main.pools.iexec.eth`. This is the -default workerpool for running confidential computations on the iExec platform. - -::: - -### dataMaxPrice - -**Type:** `number` -**Default:** `0` - -Allows specifying the maximum amount (in nRLC) you are willing to pay the email -address owner for using their data. The owner of the protected email address -receives this as a payment for sharing their data. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const sendEmail = await web3mail.sendEmail({ - protectedData: '0x123abc...', - emailSubject: 'My email subject', - emailContent: 'My email content', - dataMaxPrice: 42, // [!code focus] -}); -``` - -### appMaxPrice - -**Type:** `number` -**Default:** `0` - -Allows specifying the maximum amount (in nRLC) you are willing to pay the -Web3Mail app provider (iExec) for using the Web3Mail application. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const sendEmail = await web3mail.sendEmail({ - protectedData: '0x123abc...', - emailSubject: 'My email subject', - emailContent: 'My email content', - appMaxPrice: 42, // [!code focus] -}); -``` - -### workerpoolMaxPrice - -**Type:** `number` -**Default:** `0` - -Allows specifying the maximum amount you want to pay the workerpool provider for -using their infrastructure to run the web3mail app in nRLC. - -```ts twoslash -import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3mail = new IExecWeb3mail(web3Provider); -// ---cut--- -const sendEmail = await web3mail.sendEmail({ - protectedData: '0x123abc...', - emailSubject: 'My email subject', - emailContent: 'My email content', - workerpoolMaxPrice: 42, // [!code focus] -}); -``` - -## Return Value - -```ts twoslash -import { type SendEmailResponse } from '@iexec/web3mail'; -``` - -### taskId - -**Type:** `Addess` - -This uniquely identifies the email task on the iExec side chain. You can view -the status of the `sendEmail` method by monitoring the task on the -[iExec Explorer](https://explorer.iex.ec/bellecour). - -## Error Handling - -### Validation Errors - -We use [yup](https://github.com/jquense/yup) to validate input parameters. - -In case one is not valid, you'll get **a yup ValidationError**. - -Example to check received Validation errors: - -```ts -import { ValidationError } from '@iexec/web3mail'; - -try { - await web3mail.sendEmail({ - protectedData, - senderName: 'ab', // Bad input - emailSubject, - emailContent, - }); -} catch (err) { - console.error(err.message); // "senderName must be at least 3 characters" - - // Or list all validation errors: - if (err instanceof ValidationError) { - console.error('Validation errors:', (err as ValidationError).errors); - } -} -``` - -### Email Schema Error - -To be able to send an email to a protected data, it needs to contain, well, an -email address. - -If not, you'll get a `WorkflowError` in the form of: - -```json5 -{ - message: 'Failed to sendEmail', - errorCause: Error('This protected data does not contain "email:string" in its schema.') -} -``` - -### iExec Protocol Errors - -In case the iExec stack is to blame, we'll make it clear and you'll get a -specific `WorkflowError`: - -```json5 -{ - message: "A service in the iExec protocol appears to be unavailable. You can retry later or contact iExec's technical support for help.", - errorCause: , - isProtocolError: true -} -``` - -### Workflow Errors - -For any other errors, you'll get a `WorkflowError` error in the form of: - -```json5 -{ - message: 'Failed to sendEmail', - errorCause: -} -``` diff --git a/src/use-iapp/web3telegram.md b/src/use-iapp/web3telegram.md deleted file mode 100644 index 95d5e3d9..00000000 --- a/src/use-iapp/web3telegram.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -title: Web3Telegram -description: - Web3Telegram enables private, blockchain-based Telegram messaging using - Ethereum addresses. Users retain full control over who can contact - them—without sharing their chat ID. ---- - - - -# :speech_balloon: Web3Telegram - -Web3Telegram offers a secure method to manage telegram communications via the -blockchain. This mechanism helps protect the personal information of the -telegram chat ID recipients through use of Ethereum addresses. - -The telegram chat ID address is stored as a `protectedData` entity using -[iExec Data Protector](./dataProtector.md). Through this mechanism, users have -complete control over which applications may use their -[chat ID](./web3telegram/integration-guide.md#_1-get-your-users-to-retrieve-their-chat-id) -for sending communications. - -Sending a user a message, therefore, requires knowledge of the Ethereum address -of their `protectedData` as well as an explicit authorization for your account -to contact them. But also requires the receiver to send a telegram message to -the bot first. - -Your account may be bound to either an application or an individual. At any time -a user may revoke permissions and this revocation is immediate, giving users -complete control over the privacy and security of their information. - -Apps using Web3Telegram can: - -- enable an entity (such as an application provider or an end-user) to message - an Ethereum account holder with telegram without knowing their chat ID or - username -- grant users complete control over which entities are authorized to use their - chat ID to send them communications - -**Try the demo:** - - - Web3Messaging Demo - diff --git a/src/use-iapp/web3telegram/advanced-configuration.md b/src/use-iapp/web3telegram/advanced-configuration.md deleted file mode 100644 index c18a4a38..00000000 --- a/src/use-iapp/web3telegram/advanced-configuration.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -title: Advanced Configuration -description: - Explore the advanced configuration options for integrating Web3Telegram. Learn - how to customize the dApp address, whitelist, subgraph, IPFS node, and more - for secure Telegram communication on the iExec blockchain. ---- - -# Advanced Configuration - -The `IExecWeb3Telegram` constructor accepts configuration options object. As -these options are very specific, you won't need to use them for a standard usage -of `@iexec/web3telegram`. - -## Parameters - -```ts twoslash -import { type Web3TelegramConfigOptions } from '@iexec/web3telegram'; -``` - -### dappAddressOrENS - -The Ethereum contract address or ENS (Ethereum Name Service) for the -web3telegram dApp. - -If not provided, the default ENS `web3telegram.apps.iexec.eth` pointing to the -latest version of the web3telegram dApp provided by iExec will be used. - -You can find the corresponding dApp address with Bellecour explorer: -[https://explorer.iex.ec/bellecour/search/web3telegram.apps.iexec.eth](https://explorer.iex.ec/bellecour/search/web3telegram.apps.iexec.eth). - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const web3telegram = new IExecWeb3telegram(web3Provider, { - dappAddressOrENS: 'web3telegram.apps.iexec.eth', // [!code focus] -}); -``` - -### dappWhitelistAddress - -The Ethereum contract address for the web3telegram dApps whitelist. By granting -access to a whitelist, Chat Id owners ensure their Chat Id is still available to -consumers even after a new version of web3telegram dApp gets released. - -If not provided, the default whitelist smart contract address provided by iExec -will be used. - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const web3telegram = new IExecWeb3telegram(web3Provider, { - dappWhitelistAddress: '0x192C6f5AccE52c81Fcc2670f10611a3665AAA98F', // [!code focus] -}); -``` - -See it in -[https://blockscout-bellecour.iex.ec/](https://blockscout-bellecour.iex.ec/address/0x192C6f5AccE52c81Fcc2670f10611a3665AAA98F) - -### dataProtectorSubgraph - -The subgraph URL for querying data. - -If not provided, the default data protector subgraph provided by iExec will be -used. - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const web3telegram = new IExecWeb3telegram(web3Provider, { - dataProtectorSubgraph: - 'https://thegraph-product.iex.ec/subgraphs/name/bellecour/dataprotector', // [!code focus] -}); -``` - -### ipfsNode - -The IPFS node URL for content uploads. Use this option if you want to use your -own IPFS node to upload content. - -If not provided, the default IPFS node provided by iExec will be used. - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const web3telegram = new IExecWeb3telegram(web3Provider, { - ipfsNode: 'https://ipfs-upload.v8-bellecour.iex.ec', // [!code focus] -}); -``` - -### ipfsGateway - -The IPFS gateway URL used for content downloads. Mainly used for checking -content uploaded on the IPFS network. Use this option if you want to use your -own IPFS node for content downloads. - -If not provided, the default IPFS gateway provided by iExec will be used. - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -// ---cut--- -const web3telegram = new IExecWeb3telegram(web3Provider, { - ipfsGateway: 'https://ipfs-gateway.v8-bellecour.iex.ec', // [!code focus] -}); -``` - -### iexecOptions - -Low level configuration options for `iexec` SDK, see -[iexec SDK documentation IExecConfigOptions](https://github.com/iExecBlockchainComputing/iexec-sdk/blob/master/docs/interfaces/IExecConfigOptions.md) -for more details. diff --git a/src/use-iapp/web3telegram/getting-started.md b/src/use-iapp/web3telegram/getting-started.md deleted file mode 100644 index a6825d72..00000000 --- a/src/use-iapp/web3telegram/getting-started.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: Getting Started -description: - Get started with Web3Telegram, a secure blockchain-based tool for sending - Telegram messages. Install the SDK and integrate it with your Web3 project. ---- - -# Getting Started - -[![GitHub package.json version (branch)](https://img.shields.io/github/package-json/v/iExecBlockchainComputing/web3telegram-sdk?color=green)](https://github.com/iExecBlockchainComputing/web3telegram-sdk) - -## Overview - -### Prerequisites - -Before getting started, ensure that you have the following installed on your -system: - -\- [**Node.js**](https://nodejs.org/en/) version 18 or higher - -\- [**NPM**](https://docs.npmjs.com/) (Node.js package manager) - -### Installation - -::: code-group - -```sh [npm] -npm install @iexec/web3telegram -``` - -```sh [yarn] -yarn add @iexec/web3telegram -``` - -```sh [pnpm] -pnpm add @iexec/web3telegram -``` - -```sh [bun] -bun add @iexec/web3telegram -``` - -::: - -**This package is an ESM package. Your project needs to use ESM too.** - [**Read more**](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) - -If you use it with **Webpack**, some polyfills will be needed. You will find -later a working project - -### Instantiate SDK - -::: code-group - -```ts twoslash [Browser] -declare global { - interface Window { - ethereum: any; - } -} -// ---cut--- -import { IExecWeb3telegram } from '@iexec/web3telegram'; - -const web3Provider = window.ethereum; -// instantiate -const web3telegram = new IExecWeb3telegram(web3Provider); -``` - -```ts twoslash [NodeJS] -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -// get web3 provider from a private key -const web3Provider = getWeb3Provider('YOUR_PRIVATE_KEY'); -// instantiate -const web3telegram = new IExecWeb3telegram(web3Provider); -``` - -::: - -## Sandbox - - - ⚡  Code Sandbox - - -Corresponding GitHub repository: - - - 🔎  GitHub repository sandbox - diff --git a/src/use-iapp/web3telegram/integration-guide.md b/src/use-iapp/web3telegram/integration-guide.md deleted file mode 100644 index 898cc0f1..00000000 --- a/src/use-iapp/web3telegram/integration-guide.md +++ /dev/null @@ -1,112 +0,0 @@ ---- -title: iExec Web3Telegram Integration Guide -description: - Integrate iExec Web3Telegram to enable secure and private Telegram messaging - via blockchain-based access control, ensuring user privacy and decentralized - messaging control ---- - -# iExec Web3Telegram Integration Guide - -## Overview - -Integrating **iExec Web3Telegram** enables secure and private messaging on -Telegram using blockchain-based access control. This allows users to send and -receive messages while maintaining full control over their data. - -The integration process consists of the following steps: - -1. **Get your user to retrieve their Chat ID from the iExec Web3Telegram bot.** -2. **Create the protected data via the iExec Data Protector SDK.** -3. **Grant access via the Data Protector SDK to authorize users to receive - messages.** -4. **Send messages securely using the Web3Telegram SDK.** - -## 1. Get your Users to Retrieve their Chat ID - -To enable messaging via Web3Telegram, you need to retrieve the recipient's Chat -ID. - -A **Chat ID** is a unique identifier assigned to your Telegram account. It -allows applications to send messages to you **without revealing your actual -Telegram username or phone number**. By **protecting your Chat ID with iExec**, -you ensure that it remains **encrypted and private**, so only **authorized -senders** can contact you. - -### Steps: - -- Ask the recipient to open Telegram and start a conversation with - [**@IExecWeb3Telegrambot**](https://t.me/IExecWeb3TelegramBot). -- The bot will reply with their unique Chat ID. -- Save this Chat ID as you will need it for the next steps. - -::: tip - -- Once the Chat ID is protected, all messages will arrive within this bot - conversation. -- The recipient can leave the conversation at any time to stop receiving - messages. - -::: - -## 2. Create the Protected Data with Data Protector SDK - -After obtaining your user's Chat ID, you need to protect it using iExec’s Data -Protector to ensure privacy and security. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -const protectedData = await dataProtectorCore.protectData({ - data: { - telegram_chatId: '12345678', // Recipient's Chat ID - }, -}); -``` - -## 3. Grant Access via Data Protector SDK - -To allow users to send messages, you must explicitly grant access to specific -users. - -```ts twoslash -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); -const grantedAccess = await dataProtectorCore.grantAccess({ - protectedData: '0x123abc...', // Protected Chat ID data address - authorizedApp: '0x456def...', // Web3Telegram app address - authorizedUser: '0x789cba...', // Ethereum address of the authorized sender - pricePerAccess: 3, // Cost per message (in iExec tokens) - numberOfAccess: 10, // Allowed message count - onStatusUpdate: ({ title, isDone }) => { - console.log(title, isDone); - }, -}); -``` - -## 4. Send Messages via Web3Telegram SDK - -Once authorized, a user can send messages via Web3Telegram SDK. - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -const sendTelegram = await web3telegram.sendTelegram({ - protectedData: '0x123abc...', // Protected Chat ID data address - senderName: 'Arthur', - telegramContent: 'My telegram message content', -}); -``` - -## Conclusion - -By integrating **iExec Web3Telegram**, you ensure privacy, security, and -decentralized control over your Telegram messaging. Your users decide who can -send them messages and set a cost for access while keeping their Telegram handle -hidden. - -For further support, join the iExec community on -[Discord](https://discord.com/invite/pbt9m98wnU). diff --git a/src/use-iapp/web3telegram/methods/fetchMyContacts.md b/src/use-iapp/web3telegram/methods/fetchMyContacts.md deleted file mode 100644 index 26cf1aba..00000000 --- a/src/use-iapp/web3telegram/methods/fetchMyContacts.md +++ /dev/null @@ -1,67 +0,0 @@ ---- -title: fetchMyContacts -description: - Use the fetchMyContacts method from iExec web3telegram to retrieve contact - infos of users who authorized you to message them ---- - -# fetchMyContacts - -This method provides a list of `contact` objects identifying all users who -previously granted authorization to send them telegram messages. Each contact -contains the contact's ETH address as well as the ETH address for the -`protectedData` containing their telegram chat ID. - -## Usage - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- -const contactsList = await web3telegram.fetchMyContacts(); -``` - -## Parameters - -```ts twoslash -import { type FetchMyContactsParams } from '@iexec/web3telegram'; -``` - -### isUserStrict - -**Type:** `boolean` - -This parameter enables fetching contacts who granted access exclusively to the -user and no one else. - -:::tip - -When you grant access to someone, you can choose to grant access to a specific -user (a wallet) or to any user (`0x0000000000000000000000000000000000000000`). - -::: - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- -const contactsList = await web3telegram.fetchMyContacts({ - isUserStrict: true, // [!code focus] -}); -``` - -## Return Value - -The result object contains a list of `contact` objects. Each `contact` -represents one user who previously granted you authorization to send them -messages. - -```ts twoslash -import { type Contact } from '@iexec/web3telegram'; -``` - -[`Contact[]`](../../types.md#contact) diff --git a/src/use-iapp/web3telegram/methods/fetchUserContacts.md b/src/use-iapp/web3telegram/methods/fetchUserContacts.md deleted file mode 100644 index 2eeb855b..00000000 --- a/src/use-iapp/web3telegram/methods/fetchUserContacts.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: fetchUserContacts -description: - Use fetchUserContacts method from iExec web3telegram to get users who - authorized a specific Ethereum address to message them ---- - -# fetchUserContacts - -This method provides a list of `contact` objects identifying all entities who -previously granted authorization to a specified entity to send them telegram -messages. Each contact contains the contact's ETH address as well as the ETH -address for the `protectedData` containing their telegram chat ID. - -## Usage - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- -const contactsList = await web3telegram.fetchUserContacts({ - userAddress: '0xF048eF3d7E3B33A465E0599E641BB29421f7Df92', -}); -``` - -## Parameters - -```ts twoslash -import { type FetchUserContactsParams } from '@iexec/web3telegram'; -``` - -### userAddress - -`Address` - -The entity for which you wish to obtain the list of contacts. - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- -const contactsList = await web3telegram.fetchUserContacts({ - userAddress: '0xF048eF3d7E3B33A465E0599E641BB29421f7Df92', // [!code focus] -}); -``` - -### isUserStrict - -**Type:** `boolean` - -This parameter enables fetching contacts who granted access exclusively to the -user and no one else. - -:::tip - -When someone grants access, you can choose to grant access to a specific user (a -wallet) or to any user (`0x0000000000000000000000000000000000000000`). - -::: - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- -const contactsList = await web3telegram.fetchUserContacts({ - userAddress: '0x789cba...', - isUserStrict: true, // [!code focus] -}); -``` - -## Return Value - -The result object contains a list of `contact` objects. Each `contact` -represents one user who previously granted authorization for the user identified -with `userAddress` to send them messages. - -```ts twoslash -import { type Contact } from '@iexec/web3telegram'; -``` - -[`Contract[]`](../../types.md#contact) diff --git a/src/use-iapp/web3telegram/methods/sendTelegram.md b/src/use-iapp/web3telegram/methods/sendTelegram.md deleted file mode 100644 index bf63172b..00000000 --- a/src/use-iapp/web3telegram/methods/sendTelegram.md +++ /dev/null @@ -1,295 +0,0 @@ ---- -title: sendTelegram -description: - Use the sendTelegram method from Web3Telegram to send secure Telegram messages - without knowing the recipient's username or chat ID. ---- - -# sendTelegram - -This method allows an authorized entity to send a telegram message to a User -without needing to know their username or Chat ID. - -The recipient Chat ID is stored in a `protectedData` entity. The user receiving -message must explicitly authorize you to send them telegram communications and -permission must be granted for the `Web3Telegram` tool to use the -`protectedData` entity containing their chat ID. This is best done by granting -authorization to the Web3Telegram app whitelist -`0x192C6f5AccE52c81Fcc2670f10611a3665AAA98F` as `authorizedApp`. Refer to the -[Data Protector `grantAccess`](../../dataProtector/dataProtectorCore/grantAccess.md) -documentation for more details. - -::: tip - -For executing the `sendTelegram` method with a voucher or xRLC, refer to the -dedicated section in the documentation under -"[How to Pay for web3telegram](/tools/web3telegram/how-to-pay-for-web3telegram)". - -::: - -## Usage - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- - -const sendTelegram = await web3telegram.sendTelegram({ - protectedData: '0x123abc...', - telegramContent: 'My telegram message content', - senderName: 'Awesome project team', - label: 'some-cutom-id', - workerpoolAddressOrEns: 'prod-v8-bellecour.main.pools.iexec.eth', - dataMaxPrice: 42, - appMaxPrice: 42, - workerpoolMaxPrice: 42, -}); -``` - -## Parameters - -```ts twoslash -import { type SendTelegramParams } from '@iexec/web3telegram'; -``` - -### protectedData - -`Address` - -The address of the `protectedData` holding the contact's telegram chat ID. - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- - -const sendTelegram = await web3telegram.sendTelegram({ - protectedData: '0x123abc...', // [!code focus] - senderName: 'Arthur', - telegramContent: 'My telegram message content', -}); -``` - -### senderName - -`string` - -The name of the telegram message sender. - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- - -const sendTelegram = await web3telegram.sendTelegram({ - protectedData: '0x123abc...', - senderName: 'Arthur', // [!code focus] - telegramContent: 'My telegram message content', -}); -``` - -### telegramContent - -`string` - -_maximum size_: 512 kb - -The telegram message content that needs to be sent. The content is limited to -512 kb in size. Telegram content is encrypted and stored in IPFS. - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- - -const sendTelegram = await web3telegram.sendTelegram({ - protectedData: '0x123abc...', - senderName: 'Arthur', - telegramContent: 'My telegram message content', // [!code focus] -}); -``` - -### useVoucher - -**Type:** `boolean` -**Default:** `false` - -This optional param allows you to pay for the deal using your voucher. Make sure -that your voucher is held by your connected wallet. - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- - -const sendTelegram = await web3telegram.sendTelegram({ - protectedData: '0x123abc...', - telegramContent: 'My telegram message content', - senderName: 'Awesome project team', - label: 'some-cutom-id', - workerpoolAddressOrEns: 'prod-v8-bellecour.main.pools.iexec.eth', - dataMaxPrice: 42, - appMaxPrice: 42, - workerpoolMaxPrice: 42, - useVoucher: true, // [!code focus] -}); -``` - -::: tip - -If your voucher doesn't have enough xRLC to cover the deal, the SDK will -automatically get the required amount to your iExec account. Ensure that your -voucher is authorized to access your iExec account and that your account has -sufficient funds for this transfer to proceed. - -::: - -### label - -`string | undefined` - -Allows adding a custom public label. The Web3telegram tool writes this onchain -as `iexec_args` in the deal params. - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- - -const sendTelegram = await web3telegram.sendTelegram({ - protectedData: '0x123abc...', - senderName: 'Arthur', - telegramContent: 'My telegram message content', - label: 'some-cutom-id', // [!code focus] -}); -``` - -### workerpoolAddressOrEns - -`workerpoolAddressOrEns | undefined` - -_default_: iExec's production workerpool - -Allows specifying the workerpool that will run the Web3Telegram application. - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- - -const sendTelegram = await web3telegram.sendTelegram({ - protectedData: '0x123abc...', - senderName: 'Arthur', - telegramContent: 'My telegram message content', - workerpoolAddressOrEns: 'prod-v8-bellecour.main.pools.iexec.eth', // [!code focus] -}); -``` - -::: tip - -iExec currently offers a production workerpool located at the Ethereum Name -Service (ENS) address `prod-v8-bellecour.main.pools.iexec.eth`. This is the -default workerpool for running confidential computations on the iExec platform. - -::: - -### dataMaxPrice - -`number | undefined` - -_default_: `0` - -Allows specifying the maximum amount (in nRLC) you are willing to pay the -telegram chat ID owner for using their data. The owner of the protected chat ID -receives this as a payment for sharing their data. - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- - -const sendTelegram = await web3telegram.sendTelegram({ - protectedData: '0x123abc...', - senderName: 'Arthur', - telegramContent: 'My telegram message content', - dataMaxPrice: 42, // [!code focus] -}); -``` - -### appMaxPrice - -`number | undefined` - -_default_: `0` - -Allows specifying the maximum amount (in nRLC) you are willing to pay the -Web3telegram app provider (iExec) for using the Web3telegram application. - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- - -const sendTelegram = await web3telegram.sendTelegram({ - protectedData: '0x123abc...', - senderName: 'Arthur', - telegramContent: 'My telegram message content', - appMaxPrice: 42, // [!code focus] -}); -``` - -### workerpoolMaxPrice - -`number | undefined` - -_default_: `0` - -Allows specifying the maximum amount you want to pay the workerpool provider for -using their infrastructure to run the web3telegram app in nRLC. - -```ts twoslash -import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; - -const web3Provider = getWeb3Provider('PRIVATE_KEY'); -const web3telegram = new IExecWeb3telegram(web3Provider); -// ---cut--- - -const sendTelegram = await web3telegram.sendTelegram({ - protectedData: '0x123abc...', - senderName: 'Arthur', - telegramContent: 'My telegram message content', - workerpoolMaxPrice: 42, // [!code focus] -}); -``` - -## Return Value - -```ts twoslash -import { type SendTelegramResponse } from '@iexec/web3telegram'; -``` - -### taskId - -`Addess` - -This uniquely identifies the telegram task on the iExec side chain. You can view -the status of the `sendTelegram` method by monitoring the task on the -[iExec Explorer](https://explorer.iex.ec/bellecour). From d7a13bafccacdcc87216a3583e92113ec8815cea Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Mon, 11 Aug 2025 21:24:30 +0200 Subject: [PATCH 2/8] Add Web3Telegram documentation and integration guides - Created new documentation for Web3Telegram including: - Overview and description of Web3Telegram functionality - Advanced configuration options for the SDK - Getting started guide for installation and setup - Integration guide detailing steps for using Web3Telegram - Methods documentation for fetchMyContacts, fetchUserContacts, and sendTelegram - Welcome page introducing iExec and its privacy-first toolkit --- .../build-iapp/guides/build-&-deploy.md | 362 ++++++++ .../build-iapp/guides/debugging.md | 175 ++++ .../guides/how-to-get-and-decrypt-results.md | 314 +++++++ src/documentation/build-iapp/guides/index.md | 32 + .../build-iapp/guides/inputs-and-outputs.md | 641 ++++++++++++++ .../build-iapp/guides/manage-access.md | 303 +++++++ .../build-iapp/guides/using-tdx.md | 192 +++++ .../build-iapp/iapp-generator.md | 119 +++ .../iapp-generator/building-your-iexec-app.md | 204 +++++ .../build-iapp/iapp-generator/deserializer.md | 83 ++ .../iapp-generator/deserializer/getValue.md | 68 ++ .../iapp-generator/getting-started.md | 57 ++ src/documentation/build-iapp/what-is-iapp.md | 202 +++++ src/documentation/develop-with-ai.md | 61 ++ src/documentation/helloWorld.md | 122 +++ src/documentation/helloWorld/1-overview.md | 184 ++++ src/documentation/helloWorld/2-protectData.md | 192 +++++ src/documentation/helloWorld/3-buildIApp.md | 324 +++++++ .../helloWorld/4-manageDataAccess.md | 123 +++ .../helloWorld/5-bonusChapter.md | 61 ++ .../manage-data/dataProtector.md | 48 ++ .../advanced/advanced-configuration.md | 166 ++++ .../dataProtector/advanced/apps-whitelist.md | 79 ++ .../addAppToAddOnlyAppWhitelist.md | 98 +++ .../createAddOnlyAppWhitelist.md | 33 + .../getUserAddOnlyAppWhitelist.md | 61 ++ .../advanced/dps-smart-contract.md | 122 +++ .../dataProtector/dataProtectorCore.md | 42 + .../dataProtectorCore/getGrantedAccess.md | 219 +++++ .../dataProtectorCore/getProtectedData.md | 193 +++++ .../getResultFromCompletedTask.md | 136 +++ .../dataProtectorCore/grantAccess.md | 252 ++++++ .../dataProtectorCore/processProtectedData.md | 497 +++++++++++ .../dataProtectorCore/protectData.md | 442 ++++++++++ .../dataProtectorCore/revokeAllAccess.md | 143 ++++ .../dataProtectorCore/revokeOneAccess.md | 91 ++ .../dataProtectorCore/transferOwnership.md | 105 +++ .../dataProtector/dataProtectorSharing.md | 65 ++ .../dataProtectorSharing/collection.md | 70 ++ .../collection/addToCollection.md | 166 ++++ .../collection/createCollection.md | 36 + .../collection/removeCollection.md | 70 ++ .../removeProtectedDataFromCollection.md | 76 ++ .../consume/consumeProtectedData.md | 341 ++++++++ .../dataProtectorSharing/data-sharing-sc.png | Bin 0 -> 93263 bytes .../inside-a-collection.png | Bin 0 -> 113538 bytes .../read/getCollectionOwners.md | 70 ++ .../read/getCollectionSubscriptions.md | 106 +++ .../read/getCollectionsByOwner.md | 89 ++ .../read/getProtectedDataInCollections.md | 229 +++++ .../read/getProtectedDataPricingParams.md | 65 ++ .../dataProtectorSharing/read/getRentals.md | 103 +++ .../dataProtectorSharing/renting.md | 42 + .../renting/removeProtectedDataFromRenting.md | 66 ++ .../renting/rentProtectedData.md | 139 +++ .../renting/setProtectedDataRentingParams.md | 117 +++ .../renting/setProtectedDataToRenting.md | 118 +++ .../dataProtectorSharing/selling.md | 15 + .../selling/buyProtectedData.md | 164 ++++ .../selling/removeProtectedDataForSale.md | 62 ++ .../selling/setProtectedDataForSale.md | 89 ++ .../dataProtectorSharing/subscription.md | 60 ++ .../removeProtectedDataFromSubscription.md | 77 ++ .../setProtectedDataToSubscription.md | 66 ++ .../subscription/setSubscriptionParams.md | 114 +++ .../subscription/subscribeToCollection.md | 135 +++ .../dataProtector/getting-started.md | 249 ++++++ .../dataProtector/migrate-from-v1.md | 146 ++++ .../manage-data/dataProtector/types.md | 124 +++ .../guides/handle-schemas-dataset-types.md | 271 ++++++ .../manage-data/guides/manage-access.md | 230 +++++ .../guides/monetize-protected-data.md | 256 ++++++ .../manage-data/what-is-protected-data.md | 209 +++++ src/documentation/protocol/glossary.md | 10 + src/documentation/protocol/sdk.md | 10 + src/documentation/protocol/workers.md | 12 + src/documentation/quick-start.md | 116 +++ src/documentation/rlc.md | 134 +++ .../blockchain-explorer.md | 53 ++ .../tooling-and-explorers/bridge.md | 119 +++ .../builder-dashboard.md | 170 ++++ .../tooling-and-explorers/iexec-explorer.md | 211 +++++ .../subgraph-explorer.md | 204 +++++ src/documentation/use-cases.md | 56 ++ src/documentation/use-iapp/getting-started.md | 150 ++++ .../guides/add-inputs-to-execution.md | 799 ++++++++++++++++++ .../guides/different-ways-to-execute.md | 120 +++ .../use-iapp/guides/find-iapps.md | 128 +++ .../use-iapp/guides/how-to-pay-executions.md | 396 +++++++++ src/documentation/use-iapp/guides/index.md | 39 + .../guides/use-iapp-with-protected-data.md | 473 +++++++++++ .../how-to-pay/how-to-pay-for-web3mail.md | 165 ++++ .../how-to-pay/how-to-pay-for-web3telegram.md | 178 ++++ .../how-to-pay/pricing-considerations.md | 10 + .../use-iapp/how-to-pay/voucher.md | 10 + src/documentation/use-iapp/introduction.md | 53 ++ src/documentation/use-iapp/web3mail.md | 49 ++ .../web3mail/advanced-configuration.md | 121 +++ .../use-iapp/web3mail/getting-started.md | 115 +++ .../web3mail/methods/fetchMyContacts.md | 67 ++ .../web3mail/methods/fetchUserContacts.md | 87 ++ .../use-iapp/web3mail/methods/sendEmail.md | 392 +++++++++ src/documentation/use-iapp/web3telegram.md | 46 + .../web3telegram/advanced-configuration.md | 121 +++ .../use-iapp/web3telegram/getting-started.md | 90 ++ .../web3telegram/integration-guide.md | 112 +++ .../web3telegram/methods/fetchMyContacts.md | 67 ++ .../web3telegram/methods/fetchUserContacts.md | 87 ++ .../web3telegram/methods/sendTelegram.md | 295 +++++++ src/documentation/welcome.md | 188 +++++ 110 files changed, 16234 insertions(+) create mode 100644 src/documentation/build-iapp/guides/build-&-deploy.md create mode 100644 src/documentation/build-iapp/guides/debugging.md create mode 100644 src/documentation/build-iapp/guides/how-to-get-and-decrypt-results.md create mode 100644 src/documentation/build-iapp/guides/index.md create mode 100644 src/documentation/build-iapp/guides/inputs-and-outputs.md create mode 100644 src/documentation/build-iapp/guides/manage-access.md create mode 100644 src/documentation/build-iapp/guides/using-tdx.md create mode 100644 src/documentation/build-iapp/iapp-generator.md create mode 100644 src/documentation/build-iapp/iapp-generator/building-your-iexec-app.md create mode 100644 src/documentation/build-iapp/iapp-generator/deserializer.md create mode 100644 src/documentation/build-iapp/iapp-generator/deserializer/getValue.md create mode 100644 src/documentation/build-iapp/iapp-generator/getting-started.md create mode 100644 src/documentation/build-iapp/what-is-iapp.md create mode 100644 src/documentation/develop-with-ai.md create mode 100644 src/documentation/helloWorld.md create mode 100644 src/documentation/helloWorld/1-overview.md create mode 100644 src/documentation/helloWorld/2-protectData.md create mode 100644 src/documentation/helloWorld/3-buildIApp.md create mode 100644 src/documentation/helloWorld/4-manageDataAccess.md create mode 100644 src/documentation/helloWorld/5-bonusChapter.md create mode 100644 src/documentation/manage-data/dataProtector.md create mode 100644 src/documentation/manage-data/dataProtector/advanced/advanced-configuration.md create mode 100644 src/documentation/manage-data/dataProtector/advanced/apps-whitelist.md create mode 100644 src/documentation/manage-data/dataProtector/advanced/apps-whitelist/addAppToAddOnlyAppWhitelist.md create mode 100644 src/documentation/manage-data/dataProtector/advanced/apps-whitelist/createAddOnlyAppWhitelist.md create mode 100644 src/documentation/manage-data/dataProtector/advanced/apps-whitelist/getUserAddOnlyAppWhitelist.md create mode 100644 src/documentation/manage-data/dataProtector/advanced/dps-smart-contract.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorCore.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorCore/getGrantedAccess.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorCore/getProtectedData.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorCore/getResultFromCompletedTask.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorCore/grantAccess.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorCore/processProtectedData.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorCore/protectData.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorCore/revokeAllAccess.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorCore/revokeOneAccess.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorCore/transferOwnership.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/collection.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/addToCollection.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/createCollection.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/removeCollection.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/removeProtectedDataFromCollection.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/consume/consumeProtectedData.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/data-sharing-sc.png create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/inside-a-collection.png create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionOwners.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionSubscriptions.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionsByOwner.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataInCollections.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataPricingParams.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getRentals.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/renting.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/removeProtectedDataFromRenting.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/rentProtectedData.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataRentingParams.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataToRenting.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/selling.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/selling/buyProtectedData.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/selling/removeProtectedDataForSale.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/selling/setProtectedDataForSale.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/removeProtectedDataFromSubscription.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/setProtectedDataToSubscription.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/setSubscriptionParams.md create mode 100644 src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/subscribeToCollection.md create mode 100644 src/documentation/manage-data/dataProtector/getting-started.md create mode 100644 src/documentation/manage-data/dataProtector/migrate-from-v1.md create mode 100644 src/documentation/manage-data/dataProtector/types.md create mode 100644 src/documentation/manage-data/guides/handle-schemas-dataset-types.md create mode 100644 src/documentation/manage-data/guides/manage-access.md create mode 100644 src/documentation/manage-data/guides/monetize-protected-data.md create mode 100644 src/documentation/manage-data/what-is-protected-data.md create mode 100644 src/documentation/protocol/glossary.md create mode 100644 src/documentation/protocol/sdk.md create mode 100644 src/documentation/protocol/workers.md create mode 100644 src/documentation/quick-start.md create mode 100644 src/documentation/rlc.md create mode 100644 src/documentation/tooling-and-explorers/blockchain-explorer.md create mode 100644 src/documentation/tooling-and-explorers/bridge.md create mode 100644 src/documentation/tooling-and-explorers/builder-dashboard.md create mode 100644 src/documentation/tooling-and-explorers/iexec-explorer.md create mode 100644 src/documentation/tooling-and-explorers/subgraph-explorer.md create mode 100644 src/documentation/use-cases.md create mode 100644 src/documentation/use-iapp/getting-started.md create mode 100644 src/documentation/use-iapp/guides/add-inputs-to-execution.md create mode 100644 src/documentation/use-iapp/guides/different-ways-to-execute.md create mode 100644 src/documentation/use-iapp/guides/find-iapps.md create mode 100644 src/documentation/use-iapp/guides/how-to-pay-executions.md create mode 100644 src/documentation/use-iapp/guides/index.md create mode 100644 src/documentation/use-iapp/guides/use-iapp-with-protected-data.md create mode 100644 src/documentation/use-iapp/how-to-pay/how-to-pay-for-web3mail.md create mode 100644 src/documentation/use-iapp/how-to-pay/how-to-pay-for-web3telegram.md create mode 100644 src/documentation/use-iapp/how-to-pay/pricing-considerations.md create mode 100644 src/documentation/use-iapp/how-to-pay/voucher.md create mode 100644 src/documentation/use-iapp/introduction.md create mode 100644 src/documentation/use-iapp/web3mail.md create mode 100644 src/documentation/use-iapp/web3mail/advanced-configuration.md create mode 100644 src/documentation/use-iapp/web3mail/getting-started.md create mode 100644 src/documentation/use-iapp/web3mail/methods/fetchMyContacts.md create mode 100644 src/documentation/use-iapp/web3mail/methods/fetchUserContacts.md create mode 100644 src/documentation/use-iapp/web3mail/methods/sendEmail.md create mode 100644 src/documentation/use-iapp/web3telegram.md create mode 100644 src/documentation/use-iapp/web3telegram/advanced-configuration.md create mode 100644 src/documentation/use-iapp/web3telegram/getting-started.md create mode 100644 src/documentation/use-iapp/web3telegram/integration-guide.md create mode 100644 src/documentation/use-iapp/web3telegram/methods/fetchMyContacts.md create mode 100644 src/documentation/use-iapp/web3telegram/methods/fetchUserContacts.md create mode 100644 src/documentation/use-iapp/web3telegram/methods/sendTelegram.md create mode 100644 src/documentation/welcome.md diff --git a/src/documentation/build-iapp/guides/build-&-deploy.md b/src/documentation/build-iapp/guides/build-&-deploy.md new file mode 100644 index 00000000..a655b1ad --- /dev/null +++ b/src/documentation/build-iapp/guides/build-&-deploy.md @@ -0,0 +1,362 @@ +--- +title: Create and Deploy an iApp +description: + How to create a confidential iExec application and deploy it on iExec protocol +--- + +# Create and Deploy an iApp + +iApps (iExec Applications) are decentralized applications that run on the iExec +network. They leverage confidential computing to ensure data privacy and +security while providing scalable off-chain computation. + +## About iApp Generator + +Bootstrap TEE-compatible applications in minutes without any hardcoding skills, +iApp Generator handles all the low-level complexity for you. + +- **Select your project mode & language** - Get started with either a basic or + advanced setup, depending on your experience with the iExec framework. You can + use Python or JavaScript—whichever you prefer! +- **Develop your iApp effortlessly** - Write your application logic using + familiar programming languages while the generator handles all TEE-specific + configurations. +- **Access to TEEs easily** - No need to dive into low-level requirements, + create iApps that connect to TEEs in minutes. +- **Check and deploy iApps quickly** - iApp Generator checks that your iApp + complies with the iExec Framework and streamlines its deployment. + +## Prerequisites + +Before getting started, make sure you have the following installed: + +- **Node.js** (version 18 or higher) - [Download here](https://nodejs.org/) +- **Docker** - [Download here](https://www.docker.com/get-started) +- **Docker Hub account** - [Sign up here](https://hub.docker.com/) (required for + deployment) + +## Installation + +First, install the iApp Generator CLI tool using your preferred package manager: + +::: code-group + +```sh [npm] +npm install -g @iexec/iapp +``` + +```sh [yarn] +yarn global add @iexec/iapp +``` + +```sh [pnpm] +pnpm add -g @iexec/iapp +``` + +```sh [bun] +bun add -g @iexec/iapp +``` + +::: + +## Quick Start + +Once installed, you can create and deploy your first iApp. The CLI will guide +you through an interactive setup process to configure your project name, +programming language, and template: + + + +After the interactive setup, continue with development and deployment: + +## Development and Testing + +Navigate to your project and run tests locally to simulate the TEE environment. +The CLI will build a Docker image, run your app, and show you the results: + + + +## Deployment + +After your tests pass and the package is built, you can deploy your iApp to a +supported network. During deployment, you'll enter your DockerHub credentials, +specify your app version, and push both standard and TEE-compatible images: + + + +## Real Examples + +Here are some real-world examples of iApps to help you understand how they work +in practice. + +### Email Notification iApp + +This iApp lets you send updates to your contacts without ever seeing their email +addresses, privacy is preserved by design. + +::: code-group + +```js [Node.js] +/* User runs: "Send updates to my contacts about my project" */ +const contacts = loadProtectedData(); // User's protected contact list +contacts.forEach((contact) => { + sendEmail(contact, projectUpdateMessage); +}); +// → Emails sent directly, you never see the addresses +``` + +```python [Python] +# User runs: "Send updates to my contacts about my project" +contacts = load_protecteddata() # User's protected contact list +for contact in contacts: + send_email(contact, project_update_message) +# → Emails sent directly, you never see the addresses +``` + +::: + +### Oracle Update iApp + +This iApp securely updates a price oracle using private trading data, ensuring +sensitive information stays confidential. + +::: code-group + +```js [Node.js] +// User runs: "Update price oracle with my private trading data" +const tradingData = loadProtectedData(); // User's protected trading history +const averagePrice = calculateWeightedAverage(tradingData); +updateOracleContract(averagePrice); +// → Oracle updated with real data, trading history stays private +``` + +```python [Python] +# User runs: "Update price oracle with my private trading data" +trading_data = load_protecteddata() # User's protected trading history +average_price = calculate_weighted_average(trading_data) +update_oracle_contract(average_price) +# → Oracle updated with real data, trading history stays private +``` + +::: + +### Automated Transactions iApp + +This iApp automates monthly payments using protected payment details, so +financial information remains private. + +::: code-group + +```js [Node.js] +// User runs: "Automate payments every month" +const paymentInfo = loadProtectedData(); // User's payment details +for (let month = 0; month < 12; month++) { + processPayment(paymentInfo); +} +// → Payments processed, payment details stay private +``` + +```python [Python] +# User runs: "Automate payments every month" +payment_info = load_protecteddata() # User's payment details +for month in range(12): + process_payment(payment_info) +# → Payments processed, payment details stay private +``` + +::: + + diff --git a/src/documentation/build-iapp/guides/debugging.md b/src/documentation/build-iapp/guides/debugging.md new file mode 100644 index 00000000..769df047 --- /dev/null +++ b/src/documentation/build-iapp/guides/debugging.md @@ -0,0 +1,175 @@ +--- +title: Debugging your iApp +description: + Troubleshoot and optimize your iApp execution in the TEE environment +--- + +# 🐛 Debugging + +**When your iApp doesn't work as expected, debugging in the TEE environment +requires specific techniques.** This guide helps you identify issues and +optimize your iApp's performance. + +## Task Execution Lifecycle + +Understanding how your task progresses through the iExec network: + +### Key Stages + +1. **Deal Creation** - Orders matched, funds locked +2. **Task Initialization** - Workers selected for execution +3. **iApp Execution** - Your code runs inside TEE +4. **Result Processing** - Results encrypted and uploaded +5. **Task Completion** - Results available for download + +**Most failures happen during stages 2-4** + +## Monitoring your Tasks + +### iExec Explorer + +Track your tasks at [explorer.iex.ec](https://explorer.iex.ec): + +- Search by `taskId` or deal ID +- Check status: `PENDING` → `ACTIVE` → `COMPLETED/FAILED` +- View error messages if execution fails + +### Status in Code + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const response = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + onStatusUpdate: ({ title, isDone }) => { + console.log(`Status: ${title} - Done: ${isDone}`); + }, +}); +``` + +## Debug Commands + +### Local Testing + +```bash +# Test your iApp locally +iapp test --args "model=bert threshold=0.8" +iapp test --secrets "key1=value1,key2=value2" + +# Mock protected data for testing +iapp mock protectedData +iapp test --protectedData "mock_name" +``` + +### Remote Debugging + +```bash +# Deploy and run +iapp deploy +iapp run + +# Debug failed executions +iapp debug +``` + +### Task Information + +```bash +# View task details +iexec task show + +# Download results (if completed) +iexec task show --download +``` + +## Common Issues + +### ⏱️ **Task Timeout** + +- **Cause**: Code takes too long to execute +- **Solution**: Optimize algorithms, reduce input sizes, use appropriate task + category + +### 💾 **Memory Issues** + +- **Cause**: Loading large files, memory leaks, TEE constraints +- **Solution**: Process data in chunks, use streaming, optimize memory usage + +### 📁 **Input/Output Problems** + +- **Cause**: Wrong file paths, missing `computed.json` +- **Solution**: Always create `computed.json`, verify environment variables + +```python +# Always create computed.json +import json, os +computed = {"deterministic-output-path": f"{os.environ['IEXEC_OUT']}/result.json"} +with open(f"{os.environ['IEXEC_OUT']}/computed.json", 'w') as f: + json.dump(computed, f) +``` + +### ⚠️ **Dataset Type Unmatching** + +- **Cause**: The dataset type specified in the frontend (protectData) does not + match with the dataset type specified in the iApp +- **Solution**: Check both dataset types + +## Best Practices + +### 🔍 **Input Validation** + +```python +import os, sys + +# Check required environment variables +if not os.environ.get('IEXEC_IN') or not os.environ.get('IEXEC_OUT'): + print("ERROR: Missing IEXEC_IN or IEXEC_OUT") + sys.exit(1) + +# Validate arguments +if len(sys.argv) < 2: + print("ERROR: Missing required arguments") + sys.exit(1) +``` + +### 📝 **Clear Error Messages** + +```python +try: + # Your processing logic + result = process_data(data) +except Exception as e: + print(f"ERROR: Processing failed: {str(e)}") + sys.exit(1) +``` + +### 🔒 **Safe File Operations** + +```python +import os, json + +# Always ensure output directory exists +iexec_out = os.environ['IEXEC_OUT'] +os.makedirs(iexec_out, exist_ok=True) + +# Write results safely +try: + with open(f"{iexec_out}/result.json", 'w') as f: + json.dump(result_data, f) +except Exception as e: + print(f"ERROR: Failed to write results: {e}") + sys.exit(1) +``` + +## What's Next? + +Continue improving your iApps: + +- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - Handle data + in TEE +- **[How to Get and Decrypt Results](/documentation/build-iapp/guides/how-to-get-and-decrypt-results)** - + Retrieve results diff --git a/src/documentation/build-iapp/guides/how-to-get-and-decrypt-results.md b/src/documentation/build-iapp/guides/how-to-get-and-decrypt-results.md new file mode 100644 index 00000000..1b99c031 --- /dev/null +++ b/src/documentation/build-iapp/guides/how-to-get-and-decrypt-results.md @@ -0,0 +1,314 @@ +--- +title: How to Get and Decrypt Results +description: Download and decrypt iApp execution results from completed tasks +--- + +# 📦 How to Get and Decrypt Results + +**When an iApp execution completes, you need to retrieve and decrypt the +results.** This guide shows you how to download task results and decrypt them to +access the actual output files. + +Understanding the result retrieval process is essential for building +user-friendly applications with iExec. + +## Understanding Results Structure + +### Deal → Task → Result Flow + +**Every execution follows this hierarchy**: + +``` +Deal (agreement between parties) +├── Task 1 (individual execution instance) +│ └── Result (encrypted output files) +├── Task 2 +│ └── Result +└── ... +``` + +- **Deal**: Contains one or more tasks from your execution request +- **Task**: Individual computation instance with unique `taskId` +- **Result**: Encrypted ZIP file containing your iApp's output files + +### Result Accessibility + +**Results are publicly downloadable** but may be encrypted: + +- ✅ **Anyone can download** the result file from IPFS +- 🔒 **Only authorized parties can decrypt** the contents +- 📁 **Results contain** all files from `IEXEC_OUT` directory +- ⚡ **Available immediately** after task completion + +## Downloading Results + +### Using iExec SDK CLI + +**Get task information and download**: + +```bash +# Check task status and get result info +iexec task show + +# Download encrypted result +iexec task show --download my-result + +# Extract downloaded files +unzip my-result.zip -d my-result/ +ls my-result/ +``` + +**Get task ID from deal**: + +```bash +# If you only have the deal ID +iexec deal show + +# Lists all tasks in the deal with their IDs +``` + +### Using DataProtector SDK + +**Integrated download and decryption**: + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +// Get result from completed task +const result = await dataProtectorCore.getResultFromCompletedTask({ + taskId: '0x123abc...', // Your task ID +}); + +console.log('Result downloaded and decrypted:', result); +``` + +## Decrypting Results + +### Automatic Decryption with DataProtector + +**The easiest way** - decryption happens automatically: + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +// Execute and get results in one flow +const processResponse = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', +}); + +console.log('Task ID:', processResponse.taskId); + +// Get decrypted result +const result = await dataProtectorCore.getResultFromCompletedTask({ + taskId: processResponse.taskId, +}); + +// Result is automatically decrypted ArrayBuffer +const resultText = new TextDecoder().decode(result.result); +console.log('Decrypted result:', resultText); +``` + +### Manual Decryption with CLI + +**If you downloaded manually**: + +```bash +# Download the encrypted result +iexec task show --download my-result + +# Decrypt using your wallet (must be the beneficiary) +iexec result decrypt my-result.zip --force + +# Extract decrypted files +unzip decrypted-result.zip -d final-result/ +cat final-result/result.txt +``` + +## Result File Structure + +### What's Inside a Result + +**Typical result contents**: + +``` +result.zip +├── computed.json # Mandatory metadata file +├── result.txt # Your main output +├── analysis.json # Additional outputs +├── logs.txt # Optional logs +└── metadata.json # Optional metadata +``` + +### `computed.json` Structure + +**Always present in every result**: + +```json +{ + "deterministic-output-path": "/iexec_out/result.txt", + "execution-timestamp": "2024-01-15T10:30:00Z", + "app-version": "1.0.0" +} +``` + +**Key fields**: + +- `deterministic-output-path`: Main result file path +- `execution-timestamp`: When the computation completed +- Custom fields added by your iApp + +## Common Patterns + +### React Application Example + +**Integrate result retrieval in your frontend**: + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +async function downloadResult(taskId: string) { + try { + const resultResponse = await dataProtectorCore.getResultFromCompletedTask({ + taskId, + }); + + // Convert to text or JSON based on your result format + const resultText = new TextDecoder().decode(resultResponse.result); + const resultJson = JSON.parse(resultText); + + return resultJson; + } catch (error) { + console.error('Failed to download result:', error); + throw error; + } +} + +// Usage example +const taskId = '0x123abc...'; +const result = await downloadResult(taskId); +console.log('Analysis Result:', result); +``` + +### Node.js Backend Example + +**Server-side result processing**: + +```javascript +const { + IExecDataProtectorCore, + getWeb3Provider, +} = require('@iexec/dataprotector'); + +async function processTaskResult(taskId) { + const web3Provider = getWeb3Provider(process.env.PRIVATE_KEY); + const dataProtectorCore = new IExecDataProtectorCore(web3Provider); + + try { + // Get the result + const resultBuffer = await dataProtectorCore.getResultFromCompletedTask({ + taskId, + }); + + // Parse the result based on your format + const resultText = new TextDecoder().decode(resultBuffer); + + // If your result is JSON + const analysisResult = JSON.parse(resultText); + + // Store in database, send notifications, etc. + await saveToDatabase(taskId, analysisResult); + await notifyUser(analysisResult); + + return analysisResult; + } catch (error) { + console.error('Result processing failed:', error); + throw error; + } +} +``` + +## Troubleshooting + +### Common Issues + +**❌ "Task not completed"** + +``` +Error: Task is still running +``` + +**Solution**: Wait for task completion or check status with +`iexec task show ` + +**❌ "Decryption failed"** + +``` +Error: Failed to decrypt result +``` + +**Solutions**: + +- Ensure you're using the correct wallet (beneficiary) +- Check if result was actually encrypted +- Verify task completed successfully + +**❌ "Result not found"** + +``` +Error: Result not available +``` + +**Solutions**: + +- Check task status - it might have failed +- Verify the task ID is correct +- Wait for result upload to complete + +### Checking Task Status + +**Before downloading, verify completion**: + +```bash +# Check if task is completed +iexec task show + +# Look for status: "COMPLETED" +# And result information in the output +``` + +### Result Encryption Status + +**Not all results are encrypted**: + +- 🔒 **Encrypted**: When `beneficiary` is set in the request +- 📂 **Plain**: When no beneficiary specified (public results) +- ✅ **DataProtector handles both** automatically + +## What's Next? + +**You can now retrieve and decrypt iApp results!** + +Integrate result handling into your applications: + +- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - Understand + what your iApp can output +- **[Debugging Your iApp](/documentation/build-iapp/guides/debugging)** - + Troubleshoot execution issues +- **[App Access Control and Pricing](/documentation/build-iapp/guides/manage-access)** - Control who + can run your iApp + +### Advanced Topics + +- **[DataProtector SDK](/documentation/manage-data/dataProtector)** - Complete SDK + documentation +- **[SDK Deep Dive](/documentation/protocol/sdk)** - Advanced result handling techniques diff --git a/src/documentation/build-iapp/guides/index.md b/src/documentation/build-iapp/guides/index.md new file mode 100644 index 00000000..3769d00e --- /dev/null +++ b/src/documentation/build-iapp/guides/index.md @@ -0,0 +1,32 @@ +--- +title: Build iApp Guides +description: Complete guide collection for building privacy-first iApps on iExec +--- + +# 🛠️ Build iApp Guides + +Welcome to the complete collection of guides for building privacy-first applications on iExec. These guides will walk you through every aspect of creating, testing, and deploying confidential iApps. + +## 🚀 Getting Started + +- **[Build & Deploy](/documentation/build-iapp/guides/build-&-deploy)** - Your first iApp in 15 minutes +- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - Handle data flow in TEE environment +- **[Debugging](/documentation/build-iapp/guides/debugging)** - Troubleshoot execution issues + +## 🔐 Access Control & Management + +- **[Manage Access](/documentation/build-iapp/guides/manage-access)** - Control who can use your iApp +- **[How to Get and Decrypt Results](/documentation/build-iapp/guides/how-to-get-and-decrypt-results)** - Retrieve and use outputs + +## 🧪 Advanced Features + +- **[Using TDX (Experimental)](/documentation/build-iapp/guides/using-tdx)** - Next-gen TEE technology + +## 📚 What's Next? + +After mastering these guides, explore: + +- **[iApp Generator](/documentation/build-iapp/iapp-generator)** - Complete development toolkit +- **[What is an iApp?](/documentation/build-iapp/what-is-iapp)** - Core concepts and TEE overview + +Ready to build your first privacy-preserving application? Start with the [Build & Deploy](/documentation/build-iapp/guides/build-&-deploy) guide! diff --git a/src/documentation/build-iapp/guides/inputs-and-outputs.md b/src/documentation/build-iapp/guides/inputs-and-outputs.md new file mode 100644 index 00000000..781ac9d2 --- /dev/null +++ b/src/documentation/build-iapp/guides/inputs-and-outputs.md @@ -0,0 +1,641 @@ +--- +title: Inputs and Outputs +description: + Understand the different input types and output formats for iApps in the TEE + environment +--- + +# 📥📤 Inputs and Outputs + +**Your iApp runs inside a secure TEE environment with access to different types +of inputs.** Understanding what data you can access, how to access it, and when +to use each type is crucial for building effective privacy-preserving +applications. + +This guide covers all input types available to your iApp and how to generate +proper outputs that users can retrieve and decrypt. + +## Development vs User Execution + +**Two perspectives on inputs:** + +- 🔧 **As a developer** (using iApp Generator): You write code to access inputs + from the TEE environment +- 👤 **As a user** (using DataProtector): You provide inputs when executing the + iApp via `processProtectedData()` + +This guide shows both perspectives for each input type. + +## Input Types Overview + +When your iApp executes in the TEE, it can access four different types of +inputs: + +| Input Type | Visibility | Use Case | Access Method | +| --------------------- | ----------- | ------------------------ | ---------------------- | +| **Args** | Public | Configuration parameters | Command line arguments | +| **Input Files** | Public URLs | Large datasets, models | Download from URLs | +| **Requester Secrets** | Private | API keys, credentials | Environment variables | +| **Protected Data** | Encrypted | User's sensitive data | File system in TEE | + +## 1. Arguments (Args) + +**What they are:** Public parameters passed to your iApp during execution. + +**When to use:** Configuration settings, model parameters, processing options - +anything that doesn't need to be secret. + +::: danger + +Security Warning Args are **completely public** and visible on the blockchain +explorer. Never pass sensitive information through args. + +::: + +### How to Access Args + +In your iApp Generator project, args are passed as command-line arguments: + +::: code-group + +```python [Python] +import sys + +# Access args from command line +args = sys.argv[1:] # Skip first arg (script name) + +# Example: iapp run myapp --args "model=bert threshold=0.8" +if len(args) >= 2: + model_name = args[0] # "model=bert" + threshold = args[1] # "threshold=0.8" +``` + +```javascript [JavaScript] +// Access args from command line +const args = process.argv.slice(2); // Skip node and script name + +// Example: iapp run myapp --args "model=bert threshold=0.8" +if (args.length >= 2) { + const modelName = args[0]; // "model=bert" + const threshold = args[1]; // "threshold=0.8" +} +``` + +::: + +### How Users Provide Args + +Users pass args through the DataProtector `processProtectedData()` call: + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +// User provides args when executing your iApp +const response = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + args: 'model=sentiment-bert temperature=0.7 format=json', // Public arguments +}); +``` + +### Example Use Cases + +- Model configuration: `"model=sentiment-bert temperature=0.7"` +- Processing options: `"format=json output_size=small"` +- Analysis parameters: `"start_date=2024-01-01 end_date=2024-12-31"` + +## 2. Input Files + +**What they are:** Files downloaded from public URLs during iApp execution. + +**When to use:** Large datasets, ML models, reference files that don't contain +sensitive information. + +### How to Access Input Files + +Files are downloaded to the `IEXEC_INPUT_FILES_FOLDER` directory: + +::: code-group + +```python [Python] +import os + +# Get the input files directory +input_dir = os.environ.get('IEXEC_INPUT_FILES_FOLDER', './input') + +# List all downloaded files +for filename in os.listdir(input_dir): + file_path = os.path.join(input_dir, filename) + + # Process your file + with open(file_path, 'r') as f: + content = f.read() + print(f"Loaded file: {filename}") +``` + +```javascript [JavaScript] +const fs = require('fs'); +const path = require('path'); + +// Get the input files directory +const inputDir = process.env.IEXEC_INPUT_FILES_FOLDER || './input'; + +// List all downloaded files +fs.readdirSync(inputDir).forEach((filename) => { + const filePath = path.join(inputDir, filename); + + // Process your file + const content = fs.readFileSync(filePath, 'utf8'); + console.log(`Loaded file: ${filename}`); +}); +``` + +::: + +### How Users Provide Input Files + +Users specify input files when executing your iApp: + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +// User provides input files via DataProtector +const response = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + inputFiles: [ + 'https://example.com/sentiment-model.pkl', + 'https://myapp.com/config.json', + ], +}); +``` + +### Example Use Cases + +- ML model files: `"https://example.com/sentiment-model.pkl"` +- Reference datasets: `"https://data.gov/reference-corpus.csv"` +- Configuration files: `"https://myapp.com/config.json"` + +### Limits and Best Practices + +- **File size**: Limited by TEE enclave memory (typically several GB max) +- **Memory constraint**: Files are loaded into enclave memory - large files may + cause out-of-memory errors +- **Format**: Any format (binary, text, compressed) +- **URLs**: Must be direct download links (not web pages) +- **Security**: Files are public - don't use for sensitive data +- **Best practice**: Keep input files under 1-2GB for reliable execution + +## 3. Requester Secrets + +**What they are:** Confidential credentials provided by the user running your +iApp. + +**When to use:** API keys, database credentials, authentication tokens that the +user needs to provide. + +### How to Access Requester Secrets + +Secrets are available as environment variables with the pattern +`IEXEC_REQUESTER_SECRET_`: + +::: code-group + +```python [Python] +import os + +# Access requester secrets by index +api_key = os.environ.get('IEXEC_REQUESTER_SECRET_1') +db_password = os.environ.get('IEXEC_REQUESTER_SECRET_2') + +if api_key: + # Use the API key for external service calls + headers = {'Authorization': f'Bearer {api_key}'} + # Make API calls... +else: + print("No API key provided") +``` + +```javascript [JavaScript] +// Access requester secrets by index +const apiKey = process.env.IEXEC_REQUESTER_SECRET_1; +const dbPassword = process.env.IEXEC_REQUESTER_SECRET_2; + +if (apiKey) { + // Use the API key for external service calls + const headers = { Authorization: `Bearer ${apiKey}` }; + // Make API calls... +} else { + console.log('No API key provided'); +} +``` + +::: + +### How Users Provide Inputs + +Users provide all inputs when executing your iApp via DataProtector: + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +// Example: User executes your iApp with all input types +const processProtectedDataResponse = + await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', // Protected data address + app: '0x456def...', // Your iApp address + args: 'model=bert threshold=0.8', // Public arguments + inputFiles: [ + // Public input files + 'https://example.com/model.pkl', + 'https://example.com/config.json', + ], + secrets: { + // Requester secrets + 1: 'sk-1234567890abcdef', // API key + 2: 'mydbpassword123', // DB password + }, + }); +``` + +## 4. Protected Data + +**What it is:** Encrypted user data that's only decrypted inside your TEE +environment. + +**When to use:** Processing user's sensitive information like personal data, +financial records, health data. + +### How to Access Protected Data + +Protected data is available in the `IEXEC_IN` directory as decrypted files: + +::: code-group + +```python [Python] +import os +import json + +# Get the input directory +iexec_in = os.environ['IEXEC_IN'] + +# Protected data is decrypted and available as files +try: + # For single protected data + with open(f"{iexec_in}/protectedData", 'r') as f: + data = json.load(f) + + # Access user's sensitive data + user_email = data.get('email') + user_preferences = data.get('preferences') + + print(f"Processing data for user: {user_email}") + +except FileNotFoundError: + print("No protected data provided") +``` + +```javascript [JavaScript] +const fs = require('fs'); +const path = require('path'); + +// Get the input directory +const iexecIn = process.env.IEXEC_IN; + +try { + // Protected data is decrypted and available as files + const dataPath = path.join(iexecIn, 'protectedData'); + const data = JSON.parse(fs.readFileSync(dataPath, 'utf8')); + + // Access user's sensitive data + const userEmail = data.email; + const userPreferences = data.preferences; + + console.log(`Processing data for user: ${userEmail}`); +} catch (error) { + console.log('No protected data provided'); +} +``` + +::: + +### How Users Provide Protected Data + +Users specify the protected data address when executing your iApp: + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +// User provides their protected data for processing +const response = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', // Address of their protected data + app: '0x456def...', // Your iApp address +}); +``` + +### Working with Multiple Protected Datasets + +When multiple datasets are provided, they're available as separate files: + +::: code-group + +```python [Python] +import os + +iexec_in = os.environ['IEXEC_IN'] + +# List all available protected datasets +for filename in os.listdir(iexec_in): + if filename.startswith('dataset_'): + with open(f"{iexec_in}/{filename}", 'r') as f: + dataset = json.load(f) + print(f"Processing dataset: {filename}") +``` + +::: + +### Memory Limitations + +::: warning + +TEE Memory Constraints Protected data is decrypted and loaded into TEE enclave +memory. Very large datasets (>1-2GB) may cause out-of-memory errors. Consider +data preprocessing or chunking for large datasets. + +::: + +## Creating Outputs + +Your iApp must generate outputs in the `IEXEC_OUT` directory. **Every iApp must +create a `computed.json` file** with metadata about the computation. + +### Basic Output Structure + +::: code-group + +```python [Python] +import os +import json + +# Get output directory +iexec_out = os.environ['IEXEC_OUT'] + +# Create your result file +result_data = { + "analysis": "positive sentiment", + "confidence": 0.92, + "processed_at": "2024-01-15T10:30:00Z" +} + +# Save main result +with open(f"{iexec_out}/result.json", 'w') as f: + json.dump(result_data, f) + +# REQUIRED: Create `computed.json` metadata +computed_metadata = { + "deterministic-output-path": f"{iexec_out}/result.json", + "execution-timestamp": "2024-01-15T10:30:00Z", + "app-version": "1.0.0" +} + +with open(f"{iexec_out}/computed.json", 'w') as f: + json.dump(computed_metadata, f) +``` + +```javascript [JavaScript] +const fs = require('fs'); +const path = require('path'); + +// Get output directory +const iexecOut = process.env.IEXEC_OUT; + +// Create your result file +const resultData = { + analysis: 'positive sentiment', + confidence: 0.92, + processed_at: '2024-01-15T10:30:00Z', +}; + +// Save main result +fs.writeFileSync( + path.join(iexecOut, 'result.json'), + JSON.stringify(resultData, null, 2) +); + +// REQUIRED: Create computed.json metadata +const computedMetadata = { + 'deterministic-output-path': path.join(iexecOut, 'result.json'), + 'execution-timestamp': '2024-01-15T10:30:00Z', + 'app-version': '1.0.0', +}; + +fs.writeFileSync( + path.join(iexecOut, 'computed.json'), + JSON.stringify(computedMetadata, null, 2) +); +``` + +::: + +### Output Best Practices + +1. **Always create `computed.json`** - This is mandatory +2. **Use descriptive filenames** - `analysis_result.json` vs `output.txt` +3. **Include metadata** - Timestamps, versions, parameters used +4. **Structure your data** - Use JSON for structured results +5. **Keep files reasonable** - Large outputs increase retrieval time and may hit + memory limits +6. **Memory awareness** - TEE enclave memory is limited, avoid generating + multi-GB outputs + +### Example: Multi-file Output + +```python +import os +import json + +iexec_out = os.environ['IEXEC_OUT'] + +# Create multiple output files +summary = {"total_processed": 1000, "success_rate": 0.95} +with open(f"{iexec_out}/summary.json", 'w') as f: + json.dump(summary, f) + +# Create a detailed report +with open(f"{iexec_out}/detailed_report.txt", 'w') as f: + f.write("Detailed analysis results...\n") + +# Create visualization data +chart_data = {"labels": ["A", "B", "C"], "values": [10, 20, 30]} +with open(f"{iexec_out}/chart_data.json", 'w') as f: + json.dump(chart_data, f) + +# Required metadata file +computed = { + "deterministic-output-path": f"{iexec_out}/summary.json", + "additional-files": [ + f"{iexec_out}/detailed_report.txt", + f"{iexec_out}/chart_data.json" + ] +} +with open(f"{iexec_out}/computed.json", 'w') as f: + json.dump(computed, f) +``` + +## Testing Inputs Locally + +Use iApp Generator to test different input types: + +```bash +# Test with different input types +iapp test --args "model=bert threshold=0.8" # Test with arguments +iapp test --inputFiles "https://example.com/data.json" # Test with input files +iapp test --secrets "key1=value1,key2=value2" # Test with secrets + +# Mock protected data for testing +iapp mock protectedData # Generate sample protected data + +# Test your iApp locally with mocked protected data +iapp test --protectedData "mock_name" +``` + +## Common Patterns + +### 🔍 **Data Analysis iApp** + +**User execution (DataProtector):** + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +// User runs your data analysis iApp +const response = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', // Their business data + app: '0x456def...', // Your analysis iApp + args: 'analysis_type=sentiment period=monthly', + secrets: { 1: 'api-key-for-external-service' }, +}); +``` + +**Your iApp code (Python):** + +```python +# Access inputs in your iApp +args = sys.argv[1:] # Processing parameters +api_key = os.environ.get('IEXEC_REQUESTER_SECRET_1') # User's API access +protected_data = load_protected_data() # User's sensitive data + +# Process and output results +results = analyze_data(protected_data, args, api_key) +save_results(results) +``` + +### 🤖 **AI Model iApp** + +**User execution (DataProtector):** + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +// User runs your AI model with their data +const response = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', // Their personal data + app: '0x456def...', // Your AI model iApp + inputFiles: ['https://example.com/model-weights.pkl'], + args: 'model_type=classification confidence_threshold=0.8', +}); +``` + +**Your iApp code (Python):** + +```python +# Load model from input files +model = load_model_from_inputs() + +# Get user data to process +user_data = load_protected_data() + +# Run inference +predictions = model.predict(user_data) + +# Return encrypted results +save_encrypted_results(predictions) +``` + +### 📊 **Report Generator iApp** + +**User execution (DataProtector):** + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +// User generates a report from their business data +const response = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', // Their business data + app: '0x456def...', // Your report generator iApp + args: 'report_type=quarterly format=pdf include_charts=true', + inputFiles: ['https://example.com/company-template.xlsx'], +}); +``` + +**Your iApp code (Python):** + +```python +# Get configuration from args +report_type = get_arg('type', default='summary') + +# Access user's business data +business_data = load_protected_data() + +# Generate report +report = generate_report(business_data, report_type) +save_report(report) +``` + +## Output Retrieval + +Once your iApp completes execution, users can retrieve and decrypt the results: + +→ **Learn how users get results**: Check our +[How to Get and Decrypt Results](/documentation/build-iapp/guides/how-to-get-and-decrypt-results) +guide for the complete user workflow. + +## What's Next? + +**You now understand all input types and output requirements!** + +Continue building with these guides: + +- **[App Access Control and Pricing](/documentation/build-iapp/guides/manage-access)** - Control who + can use your iApp +- **[Debugging Your iApp](/documentation/build-iapp/guides/debugging)** - + Troubleshoot execution issues +- **[How to Get and Decrypt Results](/documentation/build-iapp/guides/how-to-get-and-decrypt-results)** - + User-side result handling + +### Technical Deep Dive + +- **[SDK Deep Dive](/documentation/protocol/sdk)** - Advanced SDK concepts +- **[Application I/O Protocol Docs](https://protocol.docs.iex.ec/for-developers/application-io)** - + Low-level protocol details diff --git a/src/documentation/build-iapp/guides/manage-access.md b/src/documentation/build-iapp/guides/manage-access.md new file mode 100644 index 00000000..fad6bb19 --- /dev/null +++ b/src/documentation/build-iapp/guides/manage-access.md @@ -0,0 +1,303 @@ +--- +title: App Access Control and Pricing +description: Control who can use your iApp and set pricing with app orders +--- + +# 💰 Manage Access + +**Orders control who can use your iApp and under what conditions.** Once your +iApp is deployed with iApp Generator, you need to create app orders to make it +accessible to users and define your governance rules. + +Think of orders as **usage contracts** - they define pricing, access +restrictions, and execution conditions for your application. + +## What is an Order? + +An **app order** is a signed contract that defines the usage conditions for your +iApp: + +- **Price per execution** (in nRLC) +- **Number of authorized uses** +- **Access restrictions** (specific users, workerpools) +- **TEE configuration** (for confidential applications) + +::: tip + +Currently, order management is not yet available in iApp Generator. This guide +shows you how to use the iExec SDK CLI to create and manage your app orders. + +For complete SDK documentation, check the +[iExec SDK GitHub repository](https://github.com/iExecBlockchainComputing/iexec-sdk). + +::: + +## How Orders Work + +Here's the simplified process: + +1. **You create an app order** with your conditions (price, restrictions, etc.) +2. **You sign the order** with your wallet +3. **You publish the order** on the iExec marketplace +4. **Users can discover** and execute your iApp according to your conditions +5. **You automatically receive** payment in RLC for each execution + +``` +Deployed iApp + Signed App Order = Application accessible on iExec +``` + +## App Order Example + +Here's an example app order for a sentiment analysis iApp: + +```json +{ + "app": "0x123abc...", // Your iApp address + "appprice": "1000000000", // 1 RLC per execution + "volume": "100", // 100 authorized uses + "tag": "0x0000000000000000000000000000000000000000000000000000000000000003", // TEE required + "datasetrestrict": "0x0000000000000000000000000000000000000000", + "workerpoolrestrict": "0x0000000000000000000000000000000000000000", + "requesterrestrict": "0x0000000000000000000000000000000000000000" +} +``` + +## Creating an App Order + +### Step 1: Install the iExec SDK + +Since iApp Generator doesn't handle orders yet, you need to use the iExec SDK +CLI: + +::: code-group + +```bash [npm] +npm install -g iexec +``` + +```bash [yarn] +yarn global add iexec +``` + +::: + +Verify the installation: + +```bash +iexec --version +iexec --help +``` + +### Step 2: Configure your iExec Project + +In your iApp Generator project folder, initialize the iExec configuration: + +```bash +# In your iApp Generator project folder +iexec init --skip-wallet +``` + +This creates the necessary configuration files: + +- `iexec.json` - Project configuration +- `chain.json` - Blockchain configuration + +### Step 3: Configure your Wallet + +If you don't have an iExec wallet yet: + +```bash +iexec wallet create +``` + +Or import an existing wallet: + +```bash +iexec wallet import +``` + +::: tip iApp Generator Users + +If you used iApp Generator, you already have an `iexecconfig.json` file with a +generated private key. You can use this existing private key to initialize your +wallet: + +```bash +# Extract the private key from your `iexecconfig.json` +iexec wallet import +``` + +::: + +Check your wallet: + +```bash +iexec wallet show +``` + +### Step 4: Create the App Order + +Initialize the app order: + +```bash +iexec order init --app +``` + +This adds an `apporder` section to your `iexec.json`. Edit the parameters +according to your needs: + +```json +{ + "apporder": { + "app": "0xYourAppAddress", + "appprice": "1000000000", + "volume": "100", + "tag": "0x0000000000000000000000000000000000000000000000000000000000000003", + "datasetrestrict": "0x0000000000000000000000000000000000000000", + "workerpoolrestrict": "0x0000000000000000000000000000000000000000", + "requesterrestrict": "0x0000000000000000000000000000000000000000" + } +} +``` + +### Step 5: Sign and Publish the Order + +Sign your app order with your wallet: + +```bash +iexec order sign --app +``` + +Publish the order on the marketplace: + +```bash +iexec order publish --app +``` + +Your iApp is now accessible according to the conditions you defined! + +## Managing Orders + +### View Published Orders + +Check active orders for your app: + +```bash +iexec orderbook app +``` + +### Modify an Order + +To change conditions, create a new order with new parameters. + +### Cancel an Order + +Remove an order from the marketplace: + +```bash +iexec order unpublish --app +``` + +Completely invalidate an order: + +```bash +iexec order cancel --app +``` + +### 🛡️ **Confidential App (TEE Required)** + +```json +{ + "appprice": "2000000000", + "volume": "500", + "tag": "0x0000000000000000000000000000000000000000000000000000000000000003" +} +``` + +## App Order Parameters + +Here's the detailed description of each parameter: + +### `app` + +**Description:** Ethereum address of your deployed iApp + +**Example:** `"0x123abc456def..."` + +### `appprice` + +**Description:** Price to charge per execution (in nano RLC - nRLC) + +**Common values:** + +- `"0"` - Free +- `"1000000000"` - 1 RLC per execution +- `"500000000"` - 0.5 RLC per execution + +::: tip + +1 RLC = 1,000,000,000 nRLC (10^9) + +::: + +### `volume` + +**Description:** Number of authorized executions (decrements with each use) + +**Examples:** + +- `"1"` - Single use +- `"100"` - Limited campaign +- `"10000"` - Virtually unlimited usage + +### `tag` + +**Description:** Specifies the required execution environment + +**Supported values:** + +| Value | Description | +| -------------------------------------------------------------------- | -------------------- | +| `0x0000000000000000000000000000000000000000000000000000000000000000` | Standard execution | +| `0x0000000000000000000000000000000000000000000000000000000000000003` | TEE required (Scone) | + +### Access Restrictions + +All restrictions use `0x0000000000000000000000000000000000000000` to indicate +"no restriction". + +#### `datasetrestrict` + +**Description:** Restrict usage to a specific dataset + +**Typical usage:** `"0x0000000000000000000000000000000000000000"` (no +restriction) + +#### `workerpoolrestrict` + +**Description:** Restrict execution to a specific workerpool + +**Example:** `"prod-v8-bellecour.main.pools.iexec.eth"` for the main workerpool + +#### `requesterrestrict` + +**Description:** Restrict usage to a specific user + +**Typical usage:** `"0x0000000000000000000000000000000000000000"` (open to all) + +## What's Next? + +**Your iApp is now accessible with custom conditions!** + +Next steps: + +- **Monitor executions**: Track usage with `iexec task show` +- **Adjust pricing**: Create new orders based on demand +- **Manage revenue**: Check your earnings with `iexec account show` + +### Technical Deep Dive + +- **[iExec SDK Documentation](https://github.com/iExecBlockchainComputing/iexec-sdk)** - + Complete CLI reference +- **[Official Orders Documentation](https://protocol.docs.iex.ec/for-developers/advanced/manage-your-apporders)** - + Protocol-level order management diff --git a/src/documentation/build-iapp/guides/using-tdx.md b/src/documentation/build-iapp/guides/using-tdx.md new file mode 100644 index 00000000..bb1bef73 --- /dev/null +++ b/src/documentation/build-iapp/guides/using-tdx.md @@ -0,0 +1,192 @@ +--- +title: Using TDX (Experimental) +description: + Enable Intel TDX for enhanced TEE security in iApps - experimental feature +--- + +# 🛡️ Using TDX (Experimental) + +:::danger ⚠️ EXPERIMENTAL FEATURE + +**TDX support is currently experimental and should NOT be used in production.** +This feature is provided for testing and development purposes only. Expect +instabilities, limited compatibility, and potential outages. + +::: + +**Intel TDX (Trust Domain Extensions) is the next generation of TEE +technology.** This guide shows you how to enable TDX in your iApps and +understand the differences from the default SGX implementation. + +## What is TDX? + +**TDX (Trust Domain Extensions)** is Intel's newer confidential computing +technology, different from the default SGX implementation. + +### SGX vs TDX Differences + +**SGX (Current Default)**: + +- ✅ **Production ready** and stable +- ✅ **Widely supported** by iExec workers +- ❌ **Memory limitations** in TEE environment + +**TDX (Experimental)**: + +- ✅ **Potentially better** for memory-intensive workloads +- ❌ **Experimental** and unstable +- ❌ **Limited worker availability** +- ❌ **Not production ready** + +| Feature | Intel SGX | Intel TDX | +| ------------------------ | ----------------------------------------------------------------------------------- | -------------------------------------------- | +| Release Year | 2015 | 2023 | +| Enclave Scope | Application level | Virtual machine level | +| Code Adaptation Required | Yes - needs redesign of app's logic | No - supports lift-and-shift of full systems | +| Memory Size | Limited | Extensive (multi-GB+) | +| Integration Complexity | Higher (more dev work) | Lower (VM legacy code) | +| Best Fit For | Lightweight, high-assurance modules (e.g. wallets, crypto key ops, small AI models) | Heavier AI workloads, legacy apps, databases | + +## Enabling TDX in iApp Generator + +### Environment Variable Method + +**Enable TDX for deployment and execution**: + +```bash +# Set the experimental flag +export EXPERIMENTAL_TDX_APP=true + +# Deploy and run with TDX +iapp deploy +iapp run +``` + +:::warning Environment Variable Declaration + +The syntax for setting environment variables differs between operating systems: + +- **Mac/Linux**: `export EXPERIMENTAL_TDX_APP=true` +- **Windows**: `set EXPERIMENTAL_TDX_APP=true` + +::: + +### Per-Command Method + +**Enable TDX for specific commands**: + +```bash +# Deploy TDX-enabled iApp +EXPERIMENTAL_TDX_APP=true iapp deploy + +# Run with TDX +EXPERIMENTAL_TDX_APP=true iapp run + +# Debug TDX execution +EXPERIMENTAL_TDX_APP=true iapp debug +``` + +### Verification + +**Check if TDX is enabled**: + +```bash +# Your deployed iApp should show TDX-related tags +iexec app show +``` + +### + +⚠️ **To use** the iExec DataProtector SDK with TDX support, you must configure +the SDK with the right SMS endpoint. + +```jsx +const dataProtector = new IExecDataProtector(web3Provider, { + iexecOptions: { + smsURL: 'https://sms.labs.iex.ec', + }, +}); +``` + +⚠️**You need** to change the default worker pool in your protected Data +declaration + +```jsx +await dataProtector.core.processProtectedData({ + protectedData: protectedData.address, + workerpool: 'tdx-labs.pools.iexec.eth', + app: '0x1919ceb0c6e60f3B497936308B58F9a6aDf071eC', +}); +``` + +## Protected Data Compatibility + +:::warning Protected Data Requirements + +**TDX iApps may require TDX-compatible protected data.** Check compatibility +before using protected data with TDX iApps. + +::: + +**Important**: The exact process for creating TDX-compatible protected data may +differ from standard protected data creation. Consult the latest DataProtector +documentation for TDX-specific requirements. + +## Development Workflow + +### 1. **Local Testing** + +```bash +# Test locally (same as regular iApps) +iapp test --protectedData "mock_name" + +# TDX only affects remote deployment/execution +``` + +### 2. **Deployment** + +```bash +# Deploy TDX iApp +EXPERIMENTAL_TDX_APP=true iapp deploy +``` + +### 3. **Execution** + +```bash +# Run with TDX +EXPERIMENTAL_TDX_APP=true iapp run +``` + +## Current Limitations + +:::danger Production Warnings + +- **🚫 NOT for production use** +- **🚫 Limited worker availability** +- **🚫 Unstable execution** environment +- **🚫 Breaking changes** without notice + +::: + +## When to Use TDX + +**Consider TDX only for**: + +- 🔬 **Research/development** purposes +- 🧪 **Testing future capabilities** + +**Use SGX for**: + +- 🚀 **All production applications** +- ⚡ **Reliable execution** requirements + +## What's Next? + +**For production applications, use the standard SGX guides**: + +- **[Debugging Your iApp](/documentation/build-iapp/guides/debugging)** - + Troubleshoot execution issues +- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - Handle data + in TEE environment +- **[App Access Control and Pricing](/documentation/build-iapp/guides/manage-access)** - Deploy + production-ready iApps diff --git a/src/documentation/build-iapp/iapp-generator.md b/src/documentation/build-iapp/iapp-generator.md new file mode 100644 index 00000000..e51a918e --- /dev/null +++ b/src/documentation/build-iapp/iapp-generator.md @@ -0,0 +1,119 @@ +--- +title: iApp Generator +description: + Build privacy-first applications that run in secure TEE environments. Your + complete toolkit for creating, testing, and deploying confidential iApps on + the iExec network. +--- + +# 🤖 iApp Generator + +**Build privacy-first applications that run in secure TEE environments.** iApp +Generator is your complete toolkit for creating, testing, and deploying +confidential iApps on the iExec network. + +Transform your ideas into production-ready privacy-preserving applications in +minutes, not months. + +## What is iApp Generator? + +**iApp Generator** is a CLI tool that simplifies building **iExec Applications +(iApps)** - applications that run inside **Trusted Execution Environments +(TEE)** for maximum privacy and security. + +### What you Can Build + +- **AI models** that process sensitive data privately +- **Data analysis** tools that protect user information +- **Custom algorithms** with confidential inputs and outputs +- **Privacy-preserving services** for Web3 applications + +### What iApp Generator Provides + +- ✅ **Project scaffolding** - Complete iApp structure ready to deploy +- ✅ **Local testing** - Debug and iterate quickly in simulation mode +- ✅ **One-click deployment** - Deploy to TEE workers with a single command +- ✅ **Input/output handling** - Seamless integration with protected data + +## Quick Start Path + +### 1. **Learn the Concepts** + +Start here to understand what iApps are and how they work: + +- **[What Is an iApp?](/documentation/build-iapp/what-is-iapp)** - Core + concepts and TEE overview +- **[Getting Started](/documentation/build-iapp/iapp-generator/getting-started)** - Your first + iApp in 15 minutes +- **[Building Your iApp](/documentation/build-iapp/iapp-generator/building-your-iexec-app)** - + Complete development guide + +### 2. **Master the Development Workflow** + +Once you've built your first iApp, level up with these practical guides: + +- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - Handle data + flow in TEE environment +- **[Debugging Your iApp](/documentation/build-iapp/guides/debugging)** - + Troubleshoot execution issues +- **[App Access Control and Pricing](/documentation/build-iapp/guides/manage-access)** - Control who + can use your iApp +- **[How to Get and Decrypt Results](/documentation/build-iapp/guides/how-to-get-and-decrypt-results)** - + Retrieve and use outputs + +### 3. **Explore Advanced Features** + +Ready for production? Dive into specialized topics: + +- **[Using TDX (Experimental)](/documentation/build-iapp/guides/using-tdx)** - + Next-gen TEE technology +- **[Complete Guides Overview](/documentation/build-iapp/guides/)** - All development guides in + one place + +## Why Choose iApp Generator? + +### 🔒 **Privacy by Design** + +Your applications run in hardware-secured enclaves where even the infrastructure +provider can't access your data or code. + +### ⚡ **Developer-Friendly** + +Focus on your application logic while iApp Generator handles the complex TEE +setup, deployment, and execution infrastructure. + +### 🌍 **Decentralized Infrastructure** + +Deploy on a global network of TEE-enabled workers without managing servers or +cloud infrastructure. + +### 🔧 **Complete Toolkit** + +From local development to production deployment, everything you need is included +in one CLI tool. + +## Ready to Build? + +**Start with the basics** and work your way up to advanced privacy-preserving +applications: + +::: tip Quick Path + +1. **[Getting Started](/documentation/build-iapp/iapp-generator/getting-started)** - Build + your first iApp (15 minutes) +2. **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - Handle data + properly +3. **[Debugging](/documentation/build-iapp/guides/debugging)** - Fix issues quickly +4. **[App Access Control](/documentation/build-iapp/guides/manage-access)** - Go to production ::: + +### Need Help? + +- **[Complete Guides](/documentation/build-iapp/guides/)** - All development guides +- **[iExec Discord](https://discord.com/invite/pbt9m98wnU)** - Community support +- **[Protocol Documentation](https://protocol.docs.iex.ec)** - Technical deep + dive + +--- + +**Ready to revolutionize privacy in computing?** Your first privacy-preserving +application is just a few commands away! 🚀 diff --git a/src/documentation/build-iapp/iapp-generator/building-your-iexec-app.md b/src/documentation/build-iapp/iapp-generator/building-your-iexec-app.md new file mode 100644 index 00000000..41994eb6 --- /dev/null +++ b/src/documentation/build-iapp/iapp-generator/building-your-iexec-app.md @@ -0,0 +1,204 @@ +--- +title: Build Your iApp +description: + Learn how to initialize, configure, and build your iExec application using the + iApp Generator CLI with step-by-step guidance. +--- + +# 🧑‍🏭 Build your iApp + +## 🧰 Initialize your iApp + +The iApp (iExec Application) Generator CLI simplifies the setup of your iApp by +guiding you through a step-by-step initialization process. This ensures your +iApp is correctly configured and compatible with iExec’s confidential computing +environment. + +### 🏗 Define your Project + + + +Follow the prompts to specify: + +- **Project name** – Creates a folder for your project files. +- **Language** – Choose between JavaScript, Python, etc. +- **Project mode** – Choose Basic (Hello-World setup) or Advanced (full debug + capabilities). + +### ⚙ Configure + +::: info + +We are going to create and test our iApp locally. In **debug mode**, you can +quickly iterate and troubleshoot your code. Logs and output files are available +for debugging, helping you refine your app before moving to production. + +::: + +You'll set up: + +- **Arguments (Args)** – Public parameters for your iApp. +- **Input Files** – Files dynamically downloaded during execution. These can + come from **a specific URL**. +- **Requester Secrets** – Confidential authentication strings. +- **Protected Data** – Encrypted data accessible only inside the TEE. +- **App Secret** – Immutable secret provisioned by the iApp owner. + +::: warning 💡 + +The Secret Management Service (SMS) securely stores application developer +secrets. Once set, the App Secret is immutable and cannot be updated. Use with +caution. + +For more information on **App Secrets**, refer to +[Access confidential assets from your app](https://protocol.docs.iex.ec/for-developers/confidential-computing/access-confidential-assets) + +::: + +For more details and to learn how to use them in your application, refer here +[Application I/O](https://protocol.docs.iex.ec/for-developers/application-io) + +## 🚀 Launch your iApp + +After initialization, the following essential files and directories are +generated: + +- `iapp.config.json` +- `src/app.js` _(JavaScript)_ or `src/app.py` _(Python)_ +- `Dockerfile` +- Directories: + - `input/` + - `output/` + - `cache/` + +### 📝 Update your iApp + +To modify your main application logic open: + +```sh +src/app.js # For JavaScript +src/app.py # For Python +``` + +::: info + +💡 The `src/` directory contains the core logic of your iApp. Implement your +algorithms and data processing here. + +::: + +### 🧪 Test and Deploy your iApp + +Use the following CLI commands to **validate**, **deploy**, and **execute** your +iApp: + +```sh +iapp test # Runs a basic test locally. +iapp deploy # Turns your code into a TEE app and registers the iApp on iExec. + +iapp run # Executes the deployed iApp on a worker node. +iapp debug # Retrieve detailed execution logs from worker nodes for a specific task + +iapp mock # Creates a mocked input for testing. +iapp --help # Displays available commands. +``` + +::: info + +use `iapp debug ` if execution exceeds the timeout (default: 5 min). + +::: + +Once deployed, your iApp will run **securely in a TEE-enabled workerpool** +within the iExec network. + +::: info + +💡 A **workerpool** is a decentralized network of nodes that execute iApps +securely within a **Trusted Execution Environment (TEE)**. + +::: + +::: info + +🧪 While **TEE** iApp are base on **intel SGX** technology by default, iApp has +an experimental support for **intel TDX** applications. + +TDX mode is enabled by setting the environment variable +`EXPERIMENTAL_TDX_APP=true`. + +examples: + +- `EXPERIMENTAL_TDX_APP=true iapp test` +- `EXPERIMENTAL_TDX_APP=true iapp deploy` +- `EXPERIMENTAL_TDX_APP=true iapp run ` + +⚠️ Keep in mind: TDX mode is experimental and can be subject to instabilities or +discontinuity. + +::: + +### 🚀 Next Steps + +Your iApp is now running in **debug mode** on iExec! + +Once your application is **stable** and **functional**, you can: + +- Contact **iExec** to move to **production mode** (Full Privacy). +- Learn how to **manage orders** and integrate with the **iExec protocol**. + +#### 📚 Recommended Resources + +- 🔗 + [Order Management](https://protocol.docs.iex.ec/for-developers/advanced/manage-your-apporders) +- 🔗 [iExec Protocol Documentation](https://protocol.docs.iex.ec/) + + diff --git a/src/documentation/build-iapp/iapp-generator/deserializer.md b/src/documentation/build-iapp/iapp-generator/deserializer.md new file mode 100644 index 00000000..11c61f32 --- /dev/null +++ b/src/documentation/build-iapp/iapp-generator/deserializer.md @@ -0,0 +1,83 @@ +--- +title: Deserialize a ProtectedData +description: + Learn how to deserialize protected data in your iApp using the + @iexec/dataprotector-deserializer utility package for accessing authorized + data. +--- + +# Deserialize a ProtectedData + +If you want to build your own iApp (iExec TEE Dapp), you may need to access +protected data that your wallet and iApp are authorized to use. To achieve this, +you must deserialize the content of the protected data with the expected data +schema. + +To simplify this process, you can use our lightweight utility package, +`@iexec/dataprotector-deserializer`, in your iApp. This package streamlines the +deserialization of protected data, making it easy for you to access and utilize +the information securely. + +## Overview + +This deserializer is built on the +[Borsh technical specification](https://borsh.io/). We developed this JavaScript +library to simplify deserialization in your iApp built with JavaScript. + +::: warning + +If you want to build your iApp in another language, you need to know how to +deserialize a protected data. + +Under the hood, protected data are **zip files** replicating the tree structure +of the original data object. Each value is stored in a dedicated file, binary +values are stored as is while boolean, numbers, and strings are serialized with +borsh. + +To access a value from a protected data, your app will need to unzip the iExec +dataset file at `$IEXEC_IN/$IEXEC_DATASET_FILENAME`. Then for `'bool'`, `'f64'`, +`'i128'` or `'string'` types, use the Borsh deserialization specification to +recover the original value. Borsh has +[implementations in various languages](https://github.com/near/borsh#implementations), +check your favorite one. + +::: + +### Prerequisites + +Before getting started, ensure that you have the following installed on your +system: + +\- [**Node.js**](https://nodejs.org/en/) version 14 or higher + +\- [**NPM**](https://docs.npmjs.com/) (Node.js package manager) + +### Installation + +::: code-group + +```sh [npm] +npm install @iexec/dataprotector-deserializer +``` + +```sh [yarn] +yarn add @iexec/dataprotector-deserializer +``` + +```sh [pnpm] +pnpm add @iexec/dataprotector-deserializer +``` + +```sh [bun] +bun add @iexec/dataprotector-deserializer +``` + +::: + +### Instantiate SDK + +```ts twoslash [NodeJS] +import { IExecDataProtectorDeserializer } from '@iexec/dataprotector-deserializer'; + +const deserializer = new IExecDataProtectorDeserializer(); +``` diff --git a/src/documentation/build-iapp/iapp-generator/deserializer/getValue.md b/src/documentation/build-iapp/iapp-generator/deserializer/getValue.md new file mode 100644 index 00000000..4fbafa27 --- /dev/null +++ b/src/documentation/build-iapp/iapp-generator/deserializer/getValue.md @@ -0,0 +1,68 @@ +--- +title: getValue +description: + Method to deserialize a value given a provided type using the + IExecDataProtectorDeserializer. +--- + +# getValue + +Method to deserialize a value given a provided type. + +## Usage + +```ts twoslash [NodeJS] +import { IExecDataProtectorDeserializer } from '@iexec/dataprotector-deserializer'; + +const deserializer = new IExecDataProtectorDeserializer(); +// ---cut--- +const value1 = await deserializer.getValue('path.to.value1', 'bool'); +const value2 = await deserializer.getValue('path.to.value2', 'string'); +``` + +## Parameters + +```ts twoslash +import { IExecDataProtectorDeserializer } from '@iexec/dataprotector-deserializer'; +``` + +### path + +`string` + +The path of the value inside the protected data that you want to deserialize. + +```ts twoslash [NodeJS] +import { IExecDataProtectorDeserializer } from '@iexec/dataprotector-deserializer'; + +const deserializer = new IExecDataProtectorDeserializer(); +// ---cut--- +const value1 = await deserializer.getValue( + 'path.to.value1', // [!code focus] + 'bool' +); +``` + +### type + +`string` + +Type of the desired data. The supported types are: + +- `bool` | `f64` | `i128` | `bigint` | `string` | `Uint8Array` | `boolean` + (legacy schema) | `number` (legacy schema) + +```ts twoslash [NodeJS] +import { IExecDataProtectorDeserializer } from '@iexec/dataprotector-deserializer'; + +const deserializer = new IExecDataProtectorDeserializer(); +// ---cut--- +const value1 = await deserializer.getValue( + 'path.to.value1', + 'bool' // [!code focus] +); +``` + +## Return Value + +The recovered original value. diff --git a/src/documentation/build-iapp/iapp-generator/getting-started.md b/src/documentation/build-iapp/iapp-generator/getting-started.md new file mode 100644 index 00000000..6d381e56 --- /dev/null +++ b/src/documentation/build-iapp/iapp-generator/getting-started.md @@ -0,0 +1,57 @@ +--- +title: Getting Started +description: + Learn how to set up and start using the iApp Generator with prerequisites, + installation, and first steps. +--- + +# 🛠 Getting Started + +## 🕕 Prerequisites + +Before using the iApp Generator, make sure you have: + +\- [**Node.js**](https://nodejs.org/en/) version 20 or higher + +\- **Docker / Docker hub account** + +\- **Docker Buildx** _(for macOS users, check AMD64 compatibility)_ + +::: tip 🔍 Verify Docker Compatibility + +```bash +docker buildx inspect --bootstrap | grep -i platforms +``` + +If `linux/amd64` is not listed, **update your Docker installation.** + +::: + +### 📦 Installation + +::: code-group + +```sh [npm] +npm install -g @iexec/iapp +``` + +```sh [yarn] +yarn global add @iexec/iapp +``` + +```sh [pnpm] +pnpm add -g @iexec/iapp +``` + +```sh [bun] +bun add -g @iexec/iapp +``` + +::: + +Once installed, generate the auto-completion script and add it to your shell by +following the instructions: + +```bash +iapp completion +``` diff --git a/src/documentation/build-iapp/what-is-iapp.md b/src/documentation/build-iapp/what-is-iapp.md new file mode 100644 index 00000000..cb7181e7 --- /dev/null +++ b/src/documentation/build-iapp/what-is-iapp.md @@ -0,0 +1,202 @@ +--- +title: What is an iApp? +description: Privacy-first applications that run on decentralized infrastructure +--- + +# 🚀 What is an iApp? + +An iExec Application (iApp) is your regular application code (Python script, AI +model, data processor, ...) that can securely process protected data (created by +[DataProtector](/documentation/manage-data/dataProtector)) inside a confidential computing +environment called TEE (a Trusted Execution Environment). + +## Why iApps Matter ? + +iApps let you process sensitive data while keeping it private and secure. + +Imagine you want to build: + +
+
+
+ 🤖 + An AI that analyzes personal health data +
+
+ 📧 + An email tool that needs access to contact lists +
+
+ 💰 + A financial advisor that processes bank statements +
+
+ 🛡️ + A content filter that reads private messages +
+
+
+ +Users have this data, but they won't give it to your regular app. **With iApps, +they will.** + +## Key Concepts + +
+
+ +

True Privacy: Users never expose their raw data. Your app processes it privately inside secure enclaves.

+
+
+ +

Trusted Execution: iExec ensures that your code runs inside a Trusted Execution Environment (TEE), which guarantees that only the specified Docker image is executed in a secure and isolated environment.

+
+
+ +

Decentralized Infrastructure: No single point of failure. Your app runs across a distributed network of workers.

+
+
+ +

Zero Trust Architecture: User data is protected by hardware-based TEEs, which keep data confidential and inaccessible to the host, cloud provider, or operating system during execution.

+
+
+ +## How it Works + +Your code runs in a Trusted Execution Environment (TEE), a secure area inside +specific processors (Intel SGX/TDX chipset). Everything that happens there stays +private and protected, even from the operating system. + +An authorized user can trigger an iApp that processes someone's protected data +inside this private environment. The data is used, but never exposed, not even +to the person running the app. + +
+
+
+ 1 + User provides private data +
+
+ 2 + Data is protected with DataProtector +
+
+ 3 + User builds and deploys a confidential iApp that processes protected data +
+
+ 4 + Run the iApp with the corresponding protected data, performing confidential computing +
+
+
+ +Your iApp can send emails, update contracts, make transactions, trigger +notifications - anything your code needs to do with the protected data. This +isn't about trust - it's about **cryptographic and hardware-enforced +guarantees** that privacy is preserved within the TEE execution environment. + +## Use Cases + +
+
+
+ 📧 +

Private Communication

+
+

Users send emails, notifications, or messages using their protected contact lists without exposing recipient information.

+
+ +
+
+ 🔮 +

Trustworthy Oracles

+
+

Users contribute real data to oracles while keeping their private information confidential.

+
+ +
+
+ 🤖 +

Personal AI Assistants

+
+

Users let AI models perform actions based on their private data - trading, scheduling, recommendations...

+
+ +
+
+ +

Automated Actions

+
+

Users let AI models perform actions based on their private data - trading, scheduling, recommendations...

+
+
+ +## ❓ Frequently Asked Questions + +::: details 📦 What can I build with iApps? + +Anything that runs in Docker! AI models, data processing scripts, web scrapers, +image processing, financial calculations, etc. If it runs in a container, it can +be an iApp. + +::: + +::: details ⚡How fast are iApps? + +Initial task scheduling takes a few seconds (depending on the resources the +worker download, congestion etc), then your code runs at normal speed depending +on complexity. + +::: + +::: details 🛡️ Are iApps really secure? + +Yes! Code runs in Intel SGX or TDX secure enclaves. Even the worker running your +iApp can't see what's happening inside the enclave. + +::: + +::: details 🚀 How do I deploy my first iApp? + +Try our [Hello World](/documentation/helloWorld) for a quick start, or check the +[iApp Generator](/documentation/build-iapp/iapp-generator) section for detailed instructions. + +::: + +::: details 🔧 What programming languages are supported? + +iApps can be built in any language that runs in Docker (Python, JavaScript, R, +Java, Go, etc.). However, **iApp Generator** currently supports only Python and +Node.js for simplified development. + +::: + +## Next Steps + +
+ +
+
+
📚
+
+ Learn More - iApp Generator: + Complete DataProtector Documentation +
+
+
+
🚀
+
+ Getting Started - deploy your first iApp: + DataProtector Quick Start Guide +
+
+
+ +
+ +--- + +**TL;DR**: iApps = Your code + Secure execution + User privacy + Verifiable +results. Cloud computing, but nobody can spy on your stuff. 🔒 diff --git a/src/documentation/develop-with-ai.md b/src/documentation/develop-with-ai.md new file mode 100644 index 00000000..faecb310 --- /dev/null +++ b/src/documentation/develop-with-ai.md @@ -0,0 +1,61 @@ +--- +title: Develop with AI +description: + Learn how to leverage AI tools like Vibe Coding and Context7 to build + privacy-first applications with iExec +--- + +# 🤖 Develop with AI + +Building privacy-first applications with iExec can be accelerated using +AI-powered development tools. This guide covers how to effectively use AI +assistants while maintaining security best practices. + +## 📚 Documentation for LLMs and AI Code Editors + +You can use some MCP (Model Control Protocol) servers like +[Context7](https://context7.com/iexecblockchaincomputing/documentation-tools) to +provide: + +- Code completion with iExec-specific knowledge +- Architecture suggestions for privacy-first apps +- Code explanation and best practices + +## 🎨 Vibe Coding Integration + +Vibe coding is a modern way to build applications by describing what you want in +plain language. An AI assistant (like Cursor or ChatGPT) then generates code +based on your description. + +It's fast, creative, and helps you prototype ideas quickly. Even if you're not a +technical expert you can: + +- Write a prompt like: "I want to create a form able to protect my data with + DataProtector" + +- The AI suggests code using iExec tools like DataProtector or iApp Generator + +- You review and adjust until it works + +## ⚠️ Security Considerations + +While vibe coding is powerful, it's important to keep privacy and security in +mind: + +- **Review the code**: Always check AI-generated code for bugs or + vulnerabilities + +- **Don't share secrets**: Never paste API keys, private keys, or sensitive + logic into the AI + +- **Validate privacy logic**: Make sure your confidential computing flow is + correctly implemented + +- **Test thoroughly**: Especially when handling protected data or smart contract + logic + +## Learn More + +- [iExec MCP Server](https://www.iex.ec/news/mcp-server-secure-interoperability-autonomous-ai-agents) +- [Agentic AI](https://www.iex.ec/academy/what-is-agentic-ai) +- [ElizaOS TDX use case](https://www.iex.ec/news/elizaos-ai-agents-iexec-intel-tdx) diff --git a/src/documentation/helloWorld.md b/src/documentation/helloWorld.md new file mode 100644 index 00000000..eb12705c --- /dev/null +++ b/src/documentation/helloWorld.md @@ -0,0 +1,122 @@ +--- +title: Hello World Tutorial +description: + Kickstart your Web3 journey with iExec. In just 30 minutes, learn how to build + privacy-focused dApps, protect sensitive data, and manage data access. +--- + + + +# 👋 Welcome to iExec + +> Reading time 🕒 2 mins + +
+

Start Your Web3 Privacy Journey

+

And learn how to build Privacy-preserving decentralized applications (dApps) with iExec in this interactive guide.

+
+ ☕ 30 minutes journey +

Perfect for hackathons 😊

+
+
+ +## What you'll Learn and Build + + + +## Getting Started + +Before you begin, make sure you have: + +
+
+
+ 🦊 Ethereum Wallet + +
+ Metamask Download → +
+
+
+ 📦 Node.js v20+ + +
+ Download → +
+
+
+ 🐳 Docker installed + +
+ Download → +
+ +
+
+ 🐳 DockerHub Account + +
+ Sign Up → +
+
+ +
+

Need help setting up or got some questions? Join our Discord Community for support!

+
diff --git a/src/documentation/helloWorld/1-overview.md b/src/documentation/helloWorld/1-overview.md new file mode 100644 index 00000000..c603ca4d --- /dev/null +++ b/src/documentation/helloWorld/1-overview.md @@ -0,0 +1,184 @@ +--- +title: iExec Overview +description: + Explore how iExec enables developers to build privacy-preserving dApps using + confidential computing, blockchain, and secure data management. Learn how + tools like DataProtector and iApps empower users to control, protect, and + monetize sensitive data across Web3 applications. +--- + +# 🧐 iExec Overview + +> Reading time 🕒 8 mins + +
+

Let's start with the basics

+

and explore how iExec can help you build Privacy-preserving applications and securely manage sensitive data.

+
+ +## 👨‍💻 Building Privacy-preserving dApps with iExec + +

Imagine you're building a decentralized application (dApp) that needs to handle sensitive user data, for example:

+
+
+
+ 🤖 + An AI model training on sensitive data +
+
+ 💰 + A financial app handling financial data +
+
+ 🔬 + A research platform working with private datasets +
+
+ 🏥 + A healthcare app processing confidential patient records +
+
+ +
+

You'll need a way to:

+
+ 🔒 + Keep the data confidential +
+
+ 🎮 + Control who can access it +
+
+ + Process it securely +
+
+ 💎 + Potentially monetize it +
+
+
+ +
+

This is where iExec comes in! We provide tools to easily add privacy and monetization features into your dApp.

+
+ +## 👷 How do we Solve it? + +Unlike traditional security solutions, iExec protects your data throughout its +entire lifecycle, during storage, transfer, and even while **being processed by +applications.** + +This is made possible thanks to +Trusted +Execution Environment (TEE) and +Confidential +Computing technologies. + +
+

Our technology allows users to control the ownership, + confidentiality, and monetization of their data and digital assets within the Web3 ecosystem.

+
+ +## 🔒 The Three Key Elements? + +iExec combines three fundamental elements that work together seamlessly: + +#### 1. Protect Data with our Devtool [DataProtector](/documentation/manage-data/dataProtector/getting-started) + +- Encrypt your sensitive data and store it securely on Arweave or IPFS +- Only you control who can access it and when +- Perfect for private information like research data, business analytics, or + personal records + +#### 2. Compute Data with iExec Apps (iApps) Running in Secure Environment + +- Special applications that can work with protected data +- Run in secure environments (called TEEs) that keep your data private +- Process data without exposing sensitive information + +#### 3. Set the Rules with the Blockchain Layer + +- Enables tokenization of data +- Regain ownership of your data +- Provides transparent governance rules for data access + +
+

By merging blockchain technology with confidential computing, we've pioneered DeCC (Decentralized Confidential Computing) to take privacy and security to the next level in Web3 ecosystems.

+
+ +### 🧸 DeCC Explained Like You're 5 + +Imagine a **secure room**, like a private bank vault for data, where everything +inside stays **safe** and **private**. + +With a tool called **DataProtector**, your data becomes **protected** and can +only be read and processed inside this secure room. + +Special applications called **iApps** (applications built to run in secure +environments) can enter the room to work with your data. You stay in **control** +by setting **access rules** that only you can modify about who can access it and +when. + +## 🔍 Building your First Privacy-preserving dApp + +Let's meet Bob and Alice to understand how iExec enables Privacy-preserving +applications: + +### 1. Meet Bob: the dApp Developer 👨‍💻 + +Bob is building a decentralized application that leverages iExec's technology. +His platform consists of: + +- A user-friendly interface for users. +- A DataProtector SDK that's easy to integrate into any application. +- Thanks to DataProtector, end users can protect their data, manage access, and + process it seamlessly. + +### 2. Meet Alice: the dApp User 👩‍💼 + +When using Bob's platform, Alice can: + +- Protect her data using the Bob's platform's DataProtector feature +- Maintain full control over who can access her protected data +- Authorize specific iExec Apps and persons like Bob to use her data in a secure + environment + +Depending on the dApp's use case, Alice could: + +- Apply algorithms to her data and get results confidentially +- Share her data with other users privately (and get paid for it) +- Ask questions to AI models about her data and get answers confidentially + +And many other use cases... + +## 🎯 Key Takeaways + +
+

In this chapter, we covered the core concepts of iExec:

+
+ 🔒 +

Privacy-preserving Solution: iExec provides tools to protect sensitive data throughout its entire lifecycle - storage, transfer, and processing

+
+
+ 💡 +

User Control: Data owners maintain full control over access, confidentiality, and monetization of their assets

+
+
+ 📦 +

iApps (iExec App): Special applications that can process protected data

+
+
+ ⛓️ +

Blockchain: Enables tokenization of data, regain ownership, and governance rules for data access

+
+
+ 🔌 +

Wide Application: From AI to finance, enabling confidential data processing across various industries

+
+
+ +
+

Now that you understand the fundamentals, let's dive into protecting your first piece of data! With Alice!

+
diff --git a/src/documentation/helloWorld/2-protectData.md b/src/documentation/helloWorld/2-protectData.md new file mode 100644 index 00000000..af4b0ae8 --- /dev/null +++ b/src/documentation/helloWorld/2-protectData.md @@ -0,0 +1,192 @@ +--- +title: Let's Protect Data +description: + Learn how to protect your data using iExec's DataProtector SDK in this + hands-on tutorial step. +--- + + + +# 🛡️ Let's Protect Data + +> Reading time: 6 minutes + +
+

Time to get practical

+

Let's follow Alice as she learns how to protect her data using DataProtector on Bob's dApp, our developer tool for protecting data creation and management.

+
+ +
+

Protected data is encrypted data that remains confidential throughout its entire lifecycle - during storage, transfer and processing.

+
+ +## 🧩 DataProtector, Key Features + +DataProtector is a developer tool built on top of our technology. It helps +developers easily add data protection, management, and monetization features to +their dApps with these key features: + +- 🔐 **Data Privacy and Security** + + Utilizes end-to-end encryption and decentralized storage + (IPFS or + AR.io) to ensure protection and + confidentiality, leveraging advanced confidential computing technology. + +- 🎮 **Dynamic Access Management** + + Allows users to manage access, enabling flexible control and monetization of + data assets. + +- 🔌 **Seamless dApp Integration** + + Features an SDK for easy integration into your DApp, enhancing functionality + and user experience. + +
+

DataProtector interacts with iExec's Bellecour sidechain, which is gasless, meaning you can use it completely free without needing any tokens!

+
+ +## 🧩 Let's Create Protected Data + + + +## 🧩 What Happened Under the Hood + +
+

You won't believe how easy it is to protect your data with DataProtector. Just a few lines of code, and you're done!

+
+ +To use it, simply call the `protectData` method from the **DataProtector SDK** +with two arguments. + +- The data object to be protected (can contain text, files, JSON data etc.) +- The name of the protected data + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const { address: protectedDataAddress } = await dataProtectorCore.protectData({ + name: 'myEmail', + data: { + email: 'example@gmail.com', + }, +}); +``` + +For this tutorial, you can try out the code directly in our interactive +CodeSandbox demo +[here](https://codesandbox.io/p/github/iExecBlockchainComputing/dataprotector-sandbox/main?file=%2Fsrc%2FApp.tsx&preventWorkspaceRedirect=true). +Here's a quick overview of what happened when you clicked the **Protect Data** +button: + +![alt](/assets/hello-world/dataprotector_light.png){.light-only} +![alt](/assets/hello-world/dataprotector_dark.png){.dark-only} + +
+
+ 1 + The DataProtector SDK is called +
+
+ 2 + The data is encrypted with a symmetric key +
+
+ 3 + The encrypted data is stored on IPFS +
+
+ 4 + The symmetric key is stored in a secure enclave (TEE) in the Secret Management Service +
+
+ 5 + The DataProtector smart contract is used to establish data ownership as an NFT +
+
+ 6 + The protected data address is returned to the user +
+
+ +## 🧩 How to Install and Use DataProtector + +Decentralized confidential computing might sound complex, but we've made it +simple through our developer tools. + +
+
+

1. Install the Developer Tool

+

Run the install command:

+ +::: code-group + +```sh [npm] +npm install @iexec/dataprotector +``` + +```sh [yarn] +yarn add @iexec/dataprotector +``` + +```sh [pnpm] +pnpm add @iexec/dataprotector +``` + +```sh [bun] +bun add @iexec/dataprotector +``` + +::: + +
+
+

2. Import and Initialize it in your project

+ Import the tool + +```ts twoslash +import { Address, IExecDataProtector } from '@iexec/dataprotector'; +``` + +Create a new instance and call the methods you need + +
+
+ +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const { address: protectedDataAddress } = await dataProtectorCore.protectData({ + name: 'myEmail', + data: { + email: 'example@gmail.com', + }, +}); +``` + +
+

Check out our code sandbox for ready-to-use examples!

+
+ +## 🎯 Key Takeaways + +- 🔒 **DataProtector** ensures data protection, management, and confidentiality + +- 📦 **Protected Data** is encrypted and stored on decentralized storage + +- ⛓️ **Ownership** is stored on the blockchain and linked to your wallet + +- 🔌 **Integration** is simple with our developer tools + +
+

In the next chapter, we'll show you how to build, deploy, and run an iApp to process your protected data. Let's go! 🚀

+
diff --git a/src/documentation/helloWorld/3-buildIApp.md b/src/documentation/helloWorld/3-buildIApp.md new file mode 100644 index 00000000..31538fe8 --- /dev/null +++ b/src/documentation/helloWorld/3-buildIApp.md @@ -0,0 +1,324 @@ +--- +title: Build and Deploy Your First iApp +description: + Learn how to build and deploy your first iExec application (iApp) for + processing protected data in this comprehensive tutorial. +--- + +# 🛠️ Build and Deploy your First iApp + +> Reading time 🕒 10 mins + + + +
+

Time to build!

+

Let's build an iApp that can process protected data in a secure environment using the iExec iApp generator tool. This tool helps you create, test and deploy iApps with just a few commands.

+
+ +If you wanna explore and deep dive in the CLI. You can check the +[iapp-cli](https://github.com/iExecBlockchainComputing/iapp/tree/main/cli) +github repository. Follow the instructions carefully for a smooth development +experience. + +## 📋 Prerequisites + +Before getting started, make sure you have: + +
+
+
+ 📦 Node.js v20+ +
+ Download → +
+
+
+ 🐳 Docker installed +
+ Download → +
+ +
+
+ 🐳 DockerHub Account +
+ Sign Up → +
+
+ +
+

Don't worry! All secrets used in this tutorial stay on your machine and aren’t shared with anyone. You’ll only need them to run the iapp run command.

+
+ +## 🚀 Types of iApps you Can Build + +iExec enables you to build various types of Privacy-preserving applications. +Here are some popular use cases: + +### 📧 Web3 Mail + +Send privacy-preserving emails to registered Ethereum account holders without +knowing or storing their email addresses. +[Github](https://github.com/iExecBlockchainComputing/web3mail-sdk/tree/main/dapp) +| [Documentation](../use-iapp/web3mail) + +### 💬 Web3 Telegram + +Send privacy-preserving Telegram messages without knowing or storing their +Telegram handles. +[Github](https://github.com/iExecBlockchainComputing/web3telegram-sdk/tree/main/dapp) +| [Documentation](../use-iapp/web3telegram) + +### 🌐 Content Delivery + +Transfer, sell or rent protected content to authorized users. +[Github](https://github.com/iExecBlockchainComputing/dataprotector-sdk/tree/main/packages/protected-data-delivery-dapp) +| [Documentation](/documentation/manage-data/dataProtector/dataProtectorSharing) + +
+

These are just a few examples, the possibilities are endless. Want to explore iApp Generator? Check out our documentation and see what you can build!

+
+ +## 💾 Installation (Win / Mac / Linux) + +First, you need to install the `iapp` package. Open your terminal and run: + +::: code-group + +```sh [npm] +npm i -g @iexec/iapp +``` + +```sh [yarn] +yarn global add @iexec/iapp +``` + +```sh [pnpm] +pnpm add -g @iexec/iapp +``` + +```sh [bun] +bun add -g @iexec/iapp +``` + +::: + +You can check if the installation was successful by running: + +```sh +#checking the version +iapp --version + +#checking the available commands +iapp --help +``` + +## 🛠️ Bootstrap your iApp + +To initialize the working directory for developing your iApp, use the +`iapp init` command. This command sets up the necessary project structure and +files. + +```sh +mkdir iexec-test +cd iexec-test +iapp init +``` + +You will be prompted with the following message: + +```txt + ___ _ ____ ____ + |_ _| / \ | _ \| _ \ + | | / _ \ | |_) | |_) | + | | / ___ \| __/| __/ + |___/_/ \_\_| |_| + +✔ What's your project name? (A folder with this name will be created) … hello-world +✔ Which language do you want to use? › JavaScript +? What kind of project do you want to init? › - Use arrow-keys. Return to submit. +❯ Hello World - iapp quick start + advanced +``` + +
+
+ 1 + Pick a name for your project +
+
+ +```txt +? What's your project name? (A folder with this name will be created) ... +``` + +
+
+ 2 + Select a programming language for your project +
+
+ +```txt +? Which language do you want to use? › - Use arrow-keys. Return to submit. +❯ JavaScript + Python +``` + +
+
+ 3 + Select the type of project you want to init +
+
+ +```txt +? What kind of project do you want to init? › - Use arrow-keys. Return to submit. +❯ Hello World - iapp quick start + advanced +``` + +
+

We recommend selecting "Hello World" to quickly discover how iApp works! use advanced only if you are familiar with iExec.

+
+ +```txt +✔ [Chosen language] app setup complete. +✔ Generated ethereum wallet (0xD4A28d.........................) + +``` + +- An iApp project is setup with the selected language +- An ethereum wallet has been created (we use it to sign the iApp creation + onchain) +- A new folder has been created, it contains a very simple application, with the + main code being located in `src/app.js` or `src/app.py` + +## 🧪 Test your iApp + +To test your iApp, run `iapp test` command + +```sh +iapp test +``` + +It uses your local docker to build and execute the app. + +
+

- If you have Error: Docker daemon is not accessible Make sure Docker is installed and running.

+
+

- If you have Error: Failed to locate iApp project root error: Ensure you are in your project folder before proceeding.

+
+ +You can see the output of the computation by saying yes to the question: + +```txt +? Would you like to see the result? (View ./output/) (Y/n) +``` + +### 🧩 Using Arguments + +You can pass arguments to your iApp using the `--args` option. This allows you +to provide necessary inputs during runtime (you can use your name for example). + +```sh +iapp test --args your-name +``` + +### 🔒 Using Protected Data + +You can pass a protectedData that you are authorized to process to your iApp +using the `--protectedData` option. + +Since nothing is actually deployed during testing, we use Protected Data mocks +to test the app. Using `--protectedData` default will provide your app with the +default protectedData mock. + +```sh +iapp test --protectedData default +``` + +
+

You can check how args and protectedData are processed in src/app.js or src/app.py

+
+ +## 🚀 Deploy your iApp + +Deploy your iApp on the iExec protocol. + +
+ +
+ 2 + Click "Personal access tokens" → "Generate new token" +
+
+ 3 + Name it "Test iExec iApp CLI" (expiration date is optional) +
+
+ 4 + Select "Read & Write" permissions +
+
+ 5 + Save your token securely and your username +
+
+ +Once you have your token, you can deploy your iApp using the following command: + +```sh +# You need your username and the access token (it can take a few minutes to deploy) +iapp deploy +``` + +
+

📝 Make sure to save your iApp address after deployment - you'll need it later!

+

You can find your iApp address in the iexec-app.json file in your project folder.

+
+

⚠️ If you encounter issues during deployment, make sure Docker's BuildKit feature is enabled and supports AMD64 architecture:

+ +```sh +docker buildx inspect --bootstrap | grep -i platforms +``` + +

The output should include linux/amd64 in the list of supported platforms. If not, update to the latest Docker Desktop version which includes these requirements.

+
+

⚠️ If you set the wrong Docker username, you can change it by editing the iapp.config.json file

+
+ +## 🏃 Run your iApp + +Now you can run your application: + +```sh +iapp run +``` + +To sum up the process, we take the **iApp** and wrap it in the iExec framework, +allowing it to run securely in a **Trusted Execution Environment (TEE)** for +**confidential computing**. If you want to explore further, you can check the +protocol documentation [here](https://protocol.docs.iex.ec/). + +
+

🎉 Congratulations! You've successfully deployed and run your first iApp on iExec. This is a significant milestone - your application is now ready to securely process confidential data in a trusted environment.

+
+ +## 🎯 Key Takeaways + +- 🔒 **iApps:** Special applications that run in TEEs to process protected data +- 🛠️ **iApp CLI:** Command-line tool for building, testing, and deploying iApps +- 🔐 **Protected Data:** Can be integrated and processed securely in your iApp +- ⛓️ **Deployment:** Apps are deployed on iExec protocol to run in trusted + environments + +
+

Next up: Alice will learn how to authorize the iApp and Bob to access and use her protected data! 🚀

+
diff --git a/src/documentation/helloWorld/4-manageDataAccess.md b/src/documentation/helloWorld/4-manageDataAccess.md new file mode 100644 index 00000000..6ec51501 --- /dev/null +++ b/src/documentation/helloWorld/4-manageDataAccess.md @@ -0,0 +1,123 @@ +--- +title: Manage Data Access +description: + Learn how to grant and manage access to protected data using iExec's + DataProtector SDK in this tutorial step. +--- + + + +# 🔑 Manage Data Access + +> Reading time 🕒 6 mins + +
+

Control Your Data

+

Alice will learn how to grant access to her protected data and manage who can use it.

+
+ +
+

When you protect your data, you can authorize specific users and applications to access it. This means an authorized user will be able to use an authorized iApp to compute your protected data.

+
+ +## 🔐 The Authorization Flow + +Here is a simple diagram to explain the process: + +![alt](/assets/hello-world/process_light.png){.light-only} +![alt](/assets/hello-world/process_dark.png){.dark-only} + +
+
+ 1 + Protect your data using DataProtector SDK +
+
+ 2 + Authorize a user (wallet address) to access your data +
+
+ 3 + Authorize the iApp to access your data +
+
+ 4 + Authorized user can now run your iApp to process your protected data +
+
+ +## 🔓 Grant the iApp Access to your Data + +
+

Remember the iApp address you saved from the previous chapter? You'll need it now to grant access to your protected data.

+
+ + + +**Let's look at the code that makes this possible:** + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const grantedAccess = await dataProtectorCore.grantAccess({ + protectedData: '0x123abc...', + authorizedApp: '0x456def...', + authorizedUser: '0x789cba...', +}); +``` + +- 📄 **protectedData**: The protected data address (local storage for the demo) +- 💻 **authorizedApp**: The iApp address you want to authorize +- 👤 **authorizedUser**: User's wallet address (0x... means all users) + +
+

As we don't have the Bob's wallet address, we'll use the zero address to grant access to all users.

+
+ +## 🏃 Time to Run + +You're now ready to process your protected data in a trusted environment: + +```sh-vue +iapp run --protectedData {{ protectedDataAddress }} +``` + +
+

🎉 Congratulations! You've successfully completed the core workflow of protecting and processing data with iExec!

+
+ +## What's Next: Data Monetization + +We've completed the first step of the journey! You can now integrate the +**DataProtector SDK** into your dApp, **secure your data**, **grant access** to +users and iApps, and **process it safely**. + +But here's where it gets even more exciting... **monetization!**. + +Our SDK offers flexible **monetization mechanisms**, allowing you to create +**protected data collections** and implement advanced models like +**subscriptions**, **rentals**, or **direct sales**. The choice is yours! + +Want to see it in action? Check out our +[Content Creator Demo](https://demo.iex.ec/content-creator/) where you can: + +- Create and protect your own content +- Set pricing and access rules +- Manage subscriptions and rentals +- Track your earnings + +For more technical details, see the +[DataProtector Sharing](/documentation/manage-data/dataProtector/dataProtectorSharing) +documentation. + +
+

You have one more step to complete the journey, and it's the easy one. Let's go to the bonus chapter!

+
diff --git a/src/documentation/helloWorld/5-bonusChapter.md b/src/documentation/helloWorld/5-bonusChapter.md new file mode 100644 index 00000000..fba85313 --- /dev/null +++ b/src/documentation/helloWorld/5-bonusChapter.md @@ -0,0 +1,61 @@ +--- +title: Bonus Chapter +description: + Explore additional features and advanced concepts in this bonus chapter of the + Hello World tutorial series. +--- + + + +# 🎉 Bonus Chapter + +> Reading time 🕒 4 mins + +
+

Congratulations!

+

You've successfully completed the Hello World journey and learned how to protect data, deploy iApps, and manage data access. Now it's time to claim your rewards! 🏆

+
+ +## 🏁 Any Questions? + +
+

+ If you have any questions, please schedule an appointment with our DevRel team who will be happy to help! +
+
+ 📅 Book a meeting +

+
+ Funny waiting animation +
+
+ +
+

Need help setting up or got some questions? Join our Discord Community for support!

+
+ +## 🎁 Claim your Voucher + +
+

What is a Voucher?

+

A Voucher is your all-in-one solution for iExec development to use iExec's technology, access to premium support, technical guidance and mentorship to help you build and monetize your projects. 🚀

+

Claim your $20 voucher to kickstart your development journey. Want to learn more about Voucher ? 🎁

+
+ +
+

Here's your unique coupon code based on your wallet address. You'll need to provide this code when claiming your voucher on Discord:

+ + + +
+ + + +
+

Thank you for being part of the iExec journey! We can't wait to see what you'll build next! 🚀

+
diff --git a/src/documentation/manage-data/dataProtector.md b/src/documentation/manage-data/dataProtector.md new file mode 100644 index 00000000..e352f572 --- /dev/null +++ b/src/documentation/manage-data/dataProtector.md @@ -0,0 +1,48 @@ +--- +title: DataProtector +description: + Discover DataProtector, iExec's secure data management solution. Encrypt, + share, and monetize your data with DataProtector Core and Sharing modules, all + powered by blockchain technology. +--- + +# 🔐 DataProtector + +DataProtector **simplifies secure data management**, offering users essential +tools for protecting, managing, and sharing their data effectively. + +## DataProtector Core + +As the foundational component of DataProtector, DataProtector Core provides +essential functionalities for data protection. Users can **encrypt their data** +and **record ownership on a smart contract**, ensuring confidentiality and +traceability. Granting access to authorized applications is streamlined, +facilitating secure data management. + +## DataProtector Sharing + +Building upon DataProtector Core, DataProtector Sharing introduces **advanced +features for data sharing** and ownership transfer. Users can securely transfer +ownership of protected data, enabling collaboration and potential **monetization +opportunities**. This module empowers users to share and manage their data +securely, fostering innovation and collaboration in the digital realm. + +## Which One to Use? + +With `DataProtector Core`, you can **grant access** to your protected data **to +a specific user**. + +- You define the number of times the user can access the data. +- You should choose an iApp (iExec TEE Dapp) that will be able to process your + protected data. +- You'll have to sign a transaction at the moment you grant access to the user + and the iApp (iExec TEE Dapp). + +With `DataProtector Sharing`, you can **distribute** your protected data to **a +wider audience**. + +- You don't need to know the user's Ethereum address. +- You define a period of time and a price for which the user can access the + data. +- Any user can access your content as long as they comply with your distribution + and monetization choices. diff --git a/src/documentation/manage-data/dataProtector/advanced/advanced-configuration.md b/src/documentation/manage-data/dataProtector/advanced/advanced-configuration.md new file mode 100644 index 00000000..bbb23d20 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/advanced/advanced-configuration.md @@ -0,0 +1,166 @@ +--- +title: Advanced Configuration +description: + Explore advanced configuration options for the IExecDataProtector constructor + in the iExec DataProtector SDK. Customize Ethereum contract addresses, + subgraph URLs, IPFS nodes, and more for specific needs. +--- + +# Advanced Configuration + +The `IExecDataProtector` constructor accepts additional configuration +parameters. As these parameters are very specific, they are not needed for a +standard usage of `@iexec/dataprotector`. + +Similarly, not all functionalities need to be instantiated at once in the SDK, +as described in the [getting started](../getting-started.md#instantiate-sdk) +section. + +## Parameters + +```ts twoslash +import { type DataProtectorConfigOptions } from '@iexec/dataprotector'; +``` + +### dataprotectorContractAddress + +`AddressOrENS` + +The Ethereum contract address or ENS (Ethereum Name Service) for dataProtector +smart contract. If not provided, the default dataProtector contract address will +be used. + +```ts twoslash +import { IExecDataProtector, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const dataProtector = new IExecDataProtector(web3Provider, { + dataprotectorContractAddress: '0x123abc...', // [!code focus] +}); +``` + +### sharingContractAddress + +`AddressOrENS` + +The Ethereum contract address or ENS (Ethereum Name Service) for dataProtector +sharing smart contract. If not provided, the default dataProtector sharing +contract address will be used. + +```ts twoslash +import { IExecDataProtector, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const dataProtector = new IExecDataProtector(web3Provider, { + sharingContractAddress: '0x123abc...', // [!code focus] +}); +``` + +### subgraphUrl + +`string` + +The subgraph URL for querying data. + +If not provided, the default data protector subgraph provided by iExec will be +used. + +```ts twoslash +import { IExecDataProtector, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const dataProtector = new IExecDataProtector(web3Provider, { + subgraphUrl: + 'https://thegraph-product.iex.ec/subgraphs/name/bellecour/dataprotector', // [!code focus] +}); +``` + +### ipfsNode + +`string` + +The IPFS node URL for content uploads. Use this option if you want to use your +own IPFS node to upload content. + +If not provided, the default IPFS node provided by iExec will be used. + +```ts twoslash +import { IExecDataProtector, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const dataProtector = new IExecDataProtector(web3Provider, { + ipfsNode: 'https://ipfs-upload.v8-bellecour.iex.ec', // [!code focus] +}); +``` + +### ipfsGateway + +`string` + +The IPFS gateway URL used for content downloads. Mainly used for checking +content uploaded on the IPFS network. Use this option if you want to use your +own IPFS node for content downloads. + +If not provided, the default IPFS gateway provided by iExec will be used. + +```ts twoslash +import { IExecDataProtector, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const dataProtector = new IExecDataProtector(web3Provider, { + ipfsGateway: 'https://ipfs-gateway.v8-bellecour.iex.ec', // [!code focus] +}); +``` + +### iexecOptions + +Low level configuration options for `iexec` SDK, see +[iexec SDK documentation IExecConfigOptions](https://github.com/iExecBlockchainComputing/iexec-sdk/blob/master/docs/interfaces/IExecConfigOptions.md) +for more details. + +```ts twoslash +import { IExecDataProtector, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const dataProtector = new IExecDataProtector(web3Provider, { + iexecOptions: { smsURL: 'https://sms.scone-prod.v8-bellecour.iex.ec' }, // [!code focus] +}); +``` + +::: info + +🧪 While protected data are processed in **TEE** by **intel SGX** technology by +default, `@iexec/dataprotector` can be configured to create and process +protected data in the experimental **intel TDX** environment. + +TDX mode is enabled by setting connecting the TDX SMS and using the TDX +workerpool. + +```ts twoslash [Browser] +declare global { + interface Window { + ethereum: any; + } +} +// ---cut--- +import { IExecDataProtector } from '@iexec/dataprotector'; + +const web3Provider = window.ethereum; +// Instantiate dataProtector connected to the TDX SMS +const dataProtector = new IExecDataProtector(web3Provider, { + iexecOptions: { + smsURL: 'https://sms.labs.iex.ec', + }, +}); +``` + +⚠️ Keep in mind: TDX mode is experimental and can be subject to instabilities or +discontinuity. + +::: diff --git a/src/documentation/manage-data/dataProtector/advanced/apps-whitelist.md b/src/documentation/manage-data/dataProtector/advanced/apps-whitelist.md new file mode 100644 index 00000000..026a8e08 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/advanced/apps-whitelist.md @@ -0,0 +1,79 @@ +--- +title: Apps Whitelist +description: + Understand the Apps Whitelist mechanism for consuming protected data in the + iExec DataProtector Sharing module. Learn about the Trusted Execution + Environment (TEE) dApp and whitelist usage for secure data delivery. +--- + +# Apps Whitelist + +In order to consume a protected data, an iExec TEE dApp needs to be provided. + +::: tip + +**TEE** stands for Trusted Execution Environment. Find more details [here](https://protocol.docs.iex.ec/help/glossary#trusted-execution-environment-tee) + +::: + +The story goes as follow: + +1. The collection owner adds a protected data to a collection. When doing so, + they need to set an `addOnlyAppWhitelist` parameter (see + [here](../dataProtectorSharing/collection/addToCollection.md#addonlyappwhitelist)). + This parameter is the address of a whitelist smart contract that contains + applications allowed to consume the protected data. + +2. When a user wants to consume the protected data, they need to provide the + address of the application they want to use to consume the data (See + [consumeProtectedData](../dataProtectorSharing/consume/consumeProtectedData.md#app-param) +  `app` parameter). This chosen application must be in the whitelist + defined by the collection owner. + +## Protected Data Delivery iApp + +Built for the needs of +[Content Creator use case](https://demo.iex.ec/content-creator/), +this iExec TEE dApp is simple: + +1. Download the protected data from IPFS. It expects to find a property named + `file` in the protected data. + +```json +{ + "file": "" +} +``` + +2. Encrypt the protected data with the beneficiary public key. + +3. Re-upload the encrypted data to IPFS and return the URL. + +::: warning + +Please note: This application and its whitelist can only be used **within the +dataProtectorSharing module**, as it is owned by the DataProtector Sharing smart +contract. + +::: + +### Whitelist + +**Whitelist address:** `0x256bcd881c33bdf9df952f2a0148f27d439f2e64` + +This whitelist contains current and past versions of the "Protected data +delivery dApp" + +See it in +[https://blockscout-bellecour.iex.ec/](https://blockscout-bellecour.iex.ec/address/0x256bcd881c33bdf9df952f2a0148f27d439f2e64). + +### dApp + +**Most recent dApp from this whitelist:** +`0x1cb7D4F3FFa203F211e57357D759321C6CE49921` + +See it in +[https://explorer.iex.ec/bellecour](https://explorer.iex.ec/bellecour/app/0x1cb7d4f3ffa203f211e57357d759321c6ce49921) + +See it in +[https://blockscout-bellecour.iex.ec/](https://blockscout-bellecour.iex.ec/address/0x1cb7D4F3FFa203F211e57357D759321C6CE49921) diff --git a/src/documentation/manage-data/dataProtector/advanced/apps-whitelist/addAppToAddOnlyAppWhitelist.md b/src/documentation/manage-data/dataProtector/advanced/apps-whitelist/addAppToAddOnlyAppWhitelist.md new file mode 100644 index 00000000..e56acb39 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/advanced/apps-whitelist/addAppToAddOnlyAppWhitelist.md @@ -0,0 +1,98 @@ +--- +title: addAppToAddOnlyAppWhitelist +description: + Method to add an app (iExec TEE dApp) into the AddOnlyAppWhitelist for secure + data access control. +--- + +# addAppToAddOnlyAppWhitelist + +Method to add an app (iExec TEE dApp) into the `AddOnlyAppWhitelist`. + +::: warning + +Once added, you can't remove an app from the whitelist. + +_Why?_ + +This is mainly **to protect users** who have paid for protected data. Imagine +the collection owner could remove all apps from the initial whitelist, users +having rented the protected data could no longer consume it. + +::: + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const isAddedToAddAppToAddOnlyAppWhitelist = + await dataProtectorSharing.addAppToAddOnlyAppWhitelist({ + addOnlyAppWhitelist: '0x123abc...', + app: '0x127ahs...', + }); +``` + +## Parameters + +```ts twoslash +import { type AddAppToAppWhitelistParams } from '@iexec/dataprotector'; +``` + +### addOnlyAppWhitelist + +**Type:** `Address` + +Address of the `addOnlyAppWhitelist` in which you want to add an app. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const isAddedToAddAppToAddOnlyAppWhitelist = + await dataProtectorSharing.addAppToAddOnlyAppWhitelist({ + addOnlyAppWhitelist: '0x123abc...', // [!code focus] + app: '0x127ahs...', + }); +``` + +### app {#app-param} + +**Type:** `AddressOrENS` + +Address of app that you want to add to the `addOnlyAppWhitelist`. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const isAddedToAddAppToAddOnlyAppWhitelist = + await dataProtectorSharing.addAppToAddOnlyAppWhitelist({ + addOnlyAppWhitelist: '0x123abc...', + app: '0x127ahs...', // [!code focus] + }); +``` + +## Return Value + +```ts twoslash +import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; +``` + +See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/documentation/manage-data/dataProtector/advanced/apps-whitelist/createAddOnlyAppWhitelist.md b/src/documentation/manage-data/dataProtector/advanced/apps-whitelist/createAddOnlyAppWhitelist.md new file mode 100644 index 00000000..567f287a --- /dev/null +++ b/src/documentation/manage-data/dataProtector/advanced/apps-whitelist/createAddOnlyAppWhitelist.md @@ -0,0 +1,33 @@ +--- +title: createAddOnlyAppWhitelist +description: + Method to create an AddOnlyAppWhitelist for controlling app access. The caller + becomes the owner by default, with transferable ownership as an ERC721. +--- + +# createAddOnlyAppWhitelist + +Method to create an `AddOnlyAppWhitelist`. By default, the owner will be the +caller of the `createAddOnlyAppWhitelist` method, but as the +`AddOnlyAppWhitelist` is an ERC721, you can transfer ownership to someone else. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const isAddedToAddAppToAddOnlyAppWhitelist = + await dataProtectorSharing.createAddOnlyAppWhitelist(); +``` + +## Return Value + +```ts twoslash +import { type CreateAppWhitelistResponse } from '@iexec/dataprotector'; +``` diff --git a/src/documentation/manage-data/dataProtector/advanced/apps-whitelist/getUserAddOnlyAppWhitelist.md b/src/documentation/manage-data/dataProtector/advanced/apps-whitelist/getUserAddOnlyAppWhitelist.md new file mode 100644 index 00000000..9472a2b3 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/advanced/apps-whitelist/getUserAddOnlyAppWhitelist.md @@ -0,0 +1,61 @@ +--- +title: getUserAddOnlyAppWhitelist +description: + Method to get AddOnlyAppWhitelist with filtering by user ethereum address for + app access control management. +--- + +# getUserAddOnlyAppWhitelist + +Method to get `AddOnlyAppWhitelist`, you can filter by user ethereum address. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const allAppOnlyAppWhitelistAvailable = + await dataProtectorSharing.getUserAddOnlyAppWhitelist(); +``` + +## Parameters + +```ts twoslash +import { type GetUserAppWhitelistParams } from '@iexec/dataprotector'; +``` + +### user + +**Type:** `AddressOrENS` + +Address or ENS of the user that manages the `AddAppToAddOnlyAppWhitelist` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const allUserAddOnlyAppWhitelist = + await dataProtectorSharing.getUserAddOnlyAppWhitelist({ + user: '0x123abc...', // [!code focus] + }); +``` + +## Return Value + +```ts twoslash +import { type GetUserAppWhitelistResponse } from '@iexec/dataprotector'; + +// Child types +import { type AddOnlyAppWhitelist } from '@iexec/dataprotector'; +``` diff --git a/src/documentation/manage-data/dataProtector/advanced/dps-smart-contract.md b/src/documentation/manage-data/dataProtector/advanced/dps-smart-contract.md new file mode 100644 index 00000000..762f3304 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/advanced/dps-smart-contract.md @@ -0,0 +1,122 @@ +--- +title: DataProtector Sharing Smart Contracts +description: + Learn about the DataProtector Sharing smart contract for managing and sharing + protected data via collections, subscriptions, rentals, and sales. Explore the + Solidity code and features in iExec's implementation. +--- + +# DataProtector Sharing Smart Contracts + +A specific smart contract has been developed to support all of the "Sharing" +module features. + +It mainly serves as **a storage for collections**, their associated protected +data, and their owners. + +## Code + +You can find the Solidity code [here](https://github.com/iExecBlockchainComputing/dataprotector-sdk/tree/main/packages/sharing-smart-contract) + +## DataProtectorSharing + +This is a contract that provides a mechanism for managing and sharing protected +data through collections, subscriptions, rentals, and sales. This contract +extends several functionalities from OpenZeppelin libraries and incorporates +access control, token handling, and order management. + +::: tip FUNCTIONS + +--- + +#### General + +- `consumeProtectedData(protectedData, workerpoolOrder, app)` +- `getProtectedDataRenter(protectedData, renterAddress)` +- `getCollectionSubscriber(collectionTokenId, subscriberAddress)` +- `createCollection(to)` +- `addProtectedDataToCollection(collectionTokenId, protectedData, appWhitelist)` +- `removeProtectedDataFromCollection(protectedData)` + +#### Subscription + +- `subscribeToCollection(collectionTokenId, subscriptionParams)` +- `setProtectedDataToSubscription(protectedData)` +- `removeProtectedDataFromSubscription(protectedData)` +- `setSubscriptionParams(collectionTokenId, subscriptionParams)` + +#### Renting + +- `rentProtectedData(protectedData, rentingParams)` +- `setProtectedDataToRenting(protectedData, rentingParams)` +- `removeProtectedDataFromRenting(protectedData)` + +#### Sale + +- `buyProtectedData(protectedData, to, price)` +- `setProtectedDataForSale(protectedData, price)` +- `removeProtectedDataForSale(protectedData)` + +::: + +:::tip EVENTS + +--- + +#### General + +- `OwnershipTransferred(previousOwner, newOwner)` +- `ProtectedDataTransfer(protectedData, fromCollection, toCollection, appWhitelist)` + +#### Subscription + +- `NewSubscription(collectionTokenId, subscriber, endDate)` +- `ProtectedDataAddedForSubscription(collectionTokenId, protectedData)` +- `ProtectedDataRemovedFromSubscription(collectionTokenId, protectedData)` +- `NewSubscriptionParams(collectionTokenId, subscriptionParams)` + +#### Renting + +- `NewRental(collectionTokenId, protectedData, renter, endDate)` +- `ProtectedDataAddedForRenting(collectionTokenId, protectedData, rentingParams)` +- `ProtectedDataRemovedFromRenting(collectionTokenId, protectedData)` + +#### Sale + +- `ProtectedDataSold(collectionTokenId, newOwner, protectedData)` +- `ProtectedDataAddedForSale(collectionTokenId, protectedData, price)` +- `ProtectedDataRemovedFromSale(collectionTokenId, protectedData)` +- `ProtectedDataConsumed(dealid, protectedData, mode)` + +::: + +:::tip ERRORS + +--- + +#### General + +- `OnlyPocoCallerAuthorized(account)` +- `EmptyCallData()` + +#### Subscription + +- `InvalidSubscriptionParams(collectionTokenId, subscriptionParams)` +- `OnGoingCollectionSubscriptions(collectionTokenId)` +- `ProtectedDataAvailableInSubscription(collectionTokenId, protectedData)` + +#### Renting + +- `ProtectedDataCurrentlyBeingRented(protectedData)` +- `ProtectedDataNotAvailableForRenting(protectedData)` +- `DurationInvalid(duration)` +- `ProtectedDataAvailableForRenting(collectionTokenId, protectedData)` + +#### Sale + +- `ProtectedDataNotForSale(protectedData)` +- `InvalidPriceForPurchase(protectedData, price)` +- `WorkerpoolOrderNotFree(workerpoolOrder)` +- `NoValidRentalOrSubscription(collectionTokenId, protectedData)` + +::: diff --git a/src/documentation/manage-data/dataProtector/dataProtectorCore.md b/src/documentation/manage-data/dataProtector/dataProtectorCore.md new file mode 100644 index 00000000..f87f0f49 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorCore.md @@ -0,0 +1,42 @@ +--- +title: DataProtector Core +description: + Learn how DataProtector Core gives developers powerful tools to encrypt data, + manage ownership with NFTs, and control access using smart contracts and + confidential computing. +--- + +# DataProtector Core + +The DataProtector tool allows application developers to provide users with +unparalleled ownership over their data. End users gain the ability to invoke +iExec apps without ever exposing their data to any other party. They gain +complete privacy of personally identifiable information (PII) or other sensitive +classes of data. + +This approach to data management relies on: + +- end-to-end encryption of data with access controlled entirely by the owner of + the data +- confidential computing technology that ensures only authorized apps are + permitted access to a user's data +- smart contracts to manage an iExec application's permissions for a user's + encrypted data + +DataProtector Core module contains the following set of methods: + +- **protectData** — safeguard data by encrypting it and recording ownership as + an NFT +- **getProtectedData** — retrieve a list of all protected data for one owner + and/or data schema +- **transferOwnership** — transfer a protected data to a new owner +- **grantAccess** — authorize an application to process a user's data without + exposing the data to any external system or user review +- **getGrantedAccess** — retrieve a list of all authorized users and + applications for a protected data +- **revokeOneAccess** — remove a specific access previously granted on a + protected data +- **revokeAllAccess** — remove all access granted to any iExec applications or + user for a protected data +- **processProtectedData** — process a protected data with a specified iExec + application diff --git a/src/documentation/manage-data/dataProtector/dataProtectorCore/getGrantedAccess.md b/src/documentation/manage-data/dataProtector/dataProtectorCore/getGrantedAccess.md new file mode 100644 index 00000000..2ee47134 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorCore/getGrantedAccess.md @@ -0,0 +1,219 @@ +--- +title: getGrantedAccess +description: + Retrieve all granted access details for a protected data object with iExec's + getGrantedAccess method. Filter access by user, application, or both, and + manage access with pagination. +--- + +# getGrantedAccess + +This method provides a listing of all access grants given for the specified +protected data object. Options for filtering include specifying an authorized +user, an authorized app, or both. + +## Usage + +The request object is a JSON `GetGrantedAccessParams` object. Each address in +the object is a string representation of an ethereum address or ENS name +(Ethereum Name Service) reference. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const listGrantedAccess = await dataProtectorCore.getGrantedAccess({ + protectedData: '0x123abc...', + authorizedApp: '0x456def...', + authorizedUser: '0x789cba...', + page: 1, + pageSize: 100, +}); +``` + +## Parameters + +```ts twoslash +import { type GetGrantedAccessParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +Address of the protected data object for which you are querying access +authorization grants. It's a representation of ethereum address or ENS name +(Ethereum Name Service). If no address is specified, it will return all granted +access for any protected data. + +**Usage example:** + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const listGrantedAccess = await dataProtectorCore.getGrantedAccess({ + protectedData: '0x123abc...', // [!code focus] +}); +``` + +### authorizedApp + +**Type:** `AddressOrENS` + +Optional filter to restrict the results to include only authorizations for the +specified application. It's a representation of ethereum address or ENS name +(Ethereum Name Service). If no address is specified, it will return all granted +access for any application. + +**Usage example:** + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const listGrantedAccess = await dataProtectorCore.getGrantedAccess({ + authorizedApp: '0x456def...', // [!code focus] +}); +``` + +::: tip + +If you specified an application whitelist when using +[`grantAccess`](./grantAccess.md), you must specify that same whitelist address +when using this filtering option. The `getGrantedAccess` method does not check +against whitelist smart contracts when aggregating results. If you granted +authorization to a whitelist but specify an application address for the +`authorizedApp` parameter, you will not receive any results unless you _also_ +explicitly granted access to that application address. + +::: + +### authorizedUser + +**Type:** `AddressOrENS` + +Optional filter to restrict the results to include only authorizations for the +specified user. It's a string representation of ethereum address or ENS name +(Ethereum Name Service). If no address is specified, it will return all granted +access for any user. + +**Usage example:** + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const listGrantedAccess = await dataProtectorCore.getGrantedAccess({ + authorizedUser: '0x789cba...', // [!code focus] +}); +``` + +### isUserStrict + +**Type:** `boolean` +**Default:** `false` + +Optional filter to restrict the results to include only authorizations for the +specified user. Authorizations made for `any` user are not returned. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- + +const listGrantedAccess = await dataProtectorCore.getGrantedAccess({ + protectedData: '0x123abc...', + authorizedApp: '0x456def...', + authorizedUser: '0x789cba...', + isUserStrict: true, // [!code focus] +}); +``` + +### page + +**Type:** `number` +**Default:** `0` + +Specifies the page number of the result set to return. Pages are zero-based +indexed, with the default value being `0`, indicating the first page. If used, +you can also specify the `pageSize` parameter to control the number of records +per page. By default, when no page number is specified, the system returns the +first page (page 0) containing `20` elements. + +**Usage example:** + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const listGrantedAccess = await dataProtectorCore.getGrantedAccess({ + protectedData: '0x123abc...', + page: 1, // [!code focus] + pageSize: 100, +}); +``` + +### pageSize + +**Type:** `number` +**Default:** `20` +**Range:** `[10...1000]` + +Specifies the number of records to include in each page of the result set. This +is used in conjunction with the optional `page` parameter to limit the size of +each page. + +**Usage example:** + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const listGrantedAccess = await dataProtectorCore.getGrantedAccess({ + protectedData: '0x123abc...', + page: 1, + pageSize: 100, // [!code focus] +}); +``` + +## Return value + +```ts twoslash +import { type GrantedAccessResponse } from '@iexec/dataprotector'; +``` + +The return value for this method has two fields: a `count` parameter indicating +the number of results, and an array of `GrantedAccess` objects containing all +access data. When using the optional paging parameters, the `count` will be +limited by the selected `pageSize` parameter. You may use these result objects +in conjunction with the [revokeOneAccess](revokeOneAccess.md) method to revoke a +previously granted authorization for access. + +### count + +**Type:** `number` + +An integer value indicating the number of results returned by this method. This +is of particular note when using paging as the number of records returned may be +smaller than the page size. + +### grantedAccess + +**Type:** GrantedAccess + +See [`GrantedAccess`](../types.md#grantedaccess) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorCore/getProtectedData.md b/src/documentation/manage-data/dataProtector/dataProtectorCore/getProtectedData.md new file mode 100644 index 00000000..a25f42ae --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorCore/getProtectedData.md @@ -0,0 +1,193 @@ +--- +title: getProtectedData +description: + Retrieve all protected data for a specific owner or schema with the + getProtectedData method in iExec DataProtector. Easily access encrypted data + and metadata, sorted by creation date. +--- + +# getProtectedData + +This method allows the user to retrieve all protected data for a given owner, +data schema, or both. + +Results are ordered by `creationTimestamp` desc. + +::: tip + +A data schema is the metadata describing the contents of the protected data +object. The schema is returned as part of the [protectData](protectData.md) +method invocation. + +::: + +## Usage + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const listProtectedData = await dataProtectorCore.getProtectedData({ + owner: '0xa0c15e...', + requiredSchema: { + email: 'string', + }, +}); +``` + +## Parameters + +```ts twoslash +import { type GetProtectedDataParams } from '@iexec/dataprotector'; +``` + +### protectedDataAddress + +**Type:** `AddressOrENS` + +Returns the protected data associated with this address. +Returns an empty array if the protected data is not found. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const oneProtectedData = await dataProtectorCore.getProtectedData({ + protectedDataAddress: '0x123abc...', // [!code focus] +}); +``` + +### requiredSchema + +**Type:** `SearchableDataSchema` + +Provides a list of protected data objects matching this schema. + + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const listProtectedData = await dataProtectorCore.getProtectedData({ + requiredSchema: { // [!code focus] + email: 'string', // [!code focus] + }, // [!code focus] +}); +``` + + +It's also possible to provide a list of accepted types for one schema field: + + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const listProtectedData = await dataProtectorCore.getProtectedData({ + requiredSchema: { // [!code focus] + picture: ['image/png', 'image/jpeg'], // [!code focus] + }, // [!code focus] +}); +``` + + +Available types are listed [here](./protectData#schema). + +### owner + +**Type:** `AddressOrENS` + +Provides a list of protected data objects owned by the user with this ETH +address. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const listProtectedData = await dataProtectorCore.getProtectedData({ + owner: '0xa0c15e...', // [!code focus] +}); +``` + +### createdAfterTimestamp + +**Type:** `number` + +Provides a list of protected data objects created after this timestamp value. +The provided value should be in seconds. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const listProtectedData = await dataProtectorCore.getProtectedData({ + owner: '0xa0c15e...', + createdAfterTimestamp: 1710257612, // March 12, 2024 15:33:32 GMT // [!code focus] +}); +``` + +### page + +**Type:** `number` +**Default:** `0` + +Specifies the results page to return. Pages are indexed starting at page 0. If +using this field you may also specify a `pageSize` to control the size of the +results. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const listProtectedData = await dataProtectorCore.getProtectedData({ + owner: '0xa0c15e...', + createdAfterTimestamp: 1710257612, // March 12, 2024 15:33:32 GMT + page: 1, // [!code focus] +}); +``` + +### pageSize + +**Type:** `number` +**Default:** `1000` +**Range:** `[10...1000]` + +Specifies the number of records in each page of the result set. This is used in +conjunction with the optional `page` parameter to constrain the size of the +result. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const listProtectedData = await dataProtectorCore.getProtectedData({ + owner: '0xa0c15e...', + createdAfterTimestamp: 1710257612, // March 12, 2024 15:33:32 GMT + page: 1, + pageSize: 100, // [!code focus] +}); +``` + +## Return Value + +```ts twoslash +import { type ProtectedData } from '@iexec/dataprotector'; +``` + +See [`ProtectedData`](../types.md#protecteddata) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorCore/getResultFromCompletedTask.md b/src/documentation/manage-data/dataProtector/dataProtectorCore/getResultFromCompletedTask.md new file mode 100644 index 00000000..121b5152 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorCore/getResultFromCompletedTask.md @@ -0,0 +1,136 @@ +--- +title: getResultFromCompletedTask +description: + Retrieve the result of a completed task with iExec's + getResultFromCompletedTask method. Easily access task outcomes by providing + the task ID. +--- + +# getResultFromCompletedTask + +Method to get the result of a completed task. + +## Usage + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const completedTaskResult = await dataProtectorCore.getResultFromCompletedTask({ + taskId: '0x7ac398...', +}); +``` + +## Parameters + +```ts twoslash +import { type GetResultFromCompletedTaskParams } from '@iexec/dataprotector'; +``` + +### taskId + +**Type:** `Address` + +Address of the task ID data you'd like to get the result from. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const completedTaskResult = await dataProtectorCore.getResultFromCompletedTask({ + taskId: '0x7ac398...', // [!code focus] +}); +``` + +### path + +**Type:** `string` + +Under the hood, a protected data is a zip file. With this `path` parameter, you +can specify the file you're interested in. The zip file will be uncompressed for +you, and only the desired file will be given as the `result`. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const completedTaskResult = await dataProtectorCore.getResultFromCompletedTask({ + taskId: '0x7ac398...', + path: 'content', // [!code focus] +}); +``` + +### pemPrivateKey + +**Type:** `string` + +If you have previously saved or generated a RSA keypair, you can reuse it in +further calls. + +It needs to be the private key corresponding to the public key initially used to +encrypt the protected data. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const completedTaskResult = await dataProtectorCore.getResultFromCompletedTask({ + taskId: '0x7ac398...', + pemPrivateKey: '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----', // [!code focus] +}); +``` + +### onStatusUpdate + +**Type:** `OnStatusUpdateFn` + +Callback function to be notified at intermediate steps. + + +```ts twoslash +import { + IExecDataProtectorCore, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const completedTaskResult = + await dataProtectorCore.getResultFromCompletedTask({ + taskId: '0x7ac398...', + onStatusUpdate: ({ title, isDone }) => { // [!code focus] + console.log(title, isDone); // [!code focus] + }, // [!code focus] + }); +``` + + +You can expect this callback function to be called with the following titles: + +```ts +'CONSUME_RESULT_DOWNLOAD'; +'CONSUME_RESULT_DECRYPT'; +``` + +Once with `isDone: false`, and then with `isDone: true` + +## Return Value + +```ts twoslash +import { type GetResultFromCompletedTaskResponse } from '@iexec/dataprotector'; +``` + +### result + +`ArrayBuffer` + +The actual content of the protected file. diff --git a/src/documentation/manage-data/dataProtector/dataProtectorCore/grantAccess.md b/src/documentation/manage-data/dataProtector/dataProtectorCore/grantAccess.md new file mode 100644 index 00000000..cfa8e79f --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorCore/grantAccess.md @@ -0,0 +1,252 @@ +--- +title: grantAccess +description: + Grant secure access to encrypted data with iExec's grantAccess method. + Authorize specific applications or users to process protected data, with + customizable access limits and pricing. +--- + +# grantAccess + +Data encrypted through the Data Protector tool requires explicit authorization +for runtime access. A newly created `protectedData` object has no inherent +authorizations. This method grants permission to securely access the specified +`protectedData` for processing using the `processProtectedData` method. +Authorization to use the `protectedData` is given to a user in the context of an +application (or a designated list of applications). + +## Usage + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const grantedAccess = await dataProtectorCore.grantAccess({ + protectedData: '0x123abc...', + authorizedApp: '0x456def...', + authorizedUser: '0x789cba...', + pricePerAccess: 3, + numberOfAccess: 10, + onStatusUpdate: ({ title, isDone }) => { + console.log(title, isDone); + }, +}); +``` + +## Parameters + +```ts twoslash +import { type GrantAccessParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +The ethereum address of the protected data supplied by the user (returned when +you created it). **You must own this data** to grant access. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const grantedAccess = await dataProtectorCore.grantAccess({ + protectedData: '0x123abc...', // [!code focus] + authorizedApp: '0x456def...', + authorizedUser: '0x789cba...', +}); +``` + +### authorizedApp + +**Type:** `AddressOrENS` + +The address of the application you wish to authorize to process the +`protectedData` within a secure execution environment. You may specify either a +single application or an application whitelist. To specify a whitelist, you +provide the ETH address of an +[iExec Whitelist Smart Contract](https://github.com/iExecBlockchainComputing/whitelist-smart-contract/tree/main). +This smart contract should aggregates multiple application versions. This allows +you to introduce new versions of your application without needing to grant +access for the `protectedData` each time you do so. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const grantedAccess = await dataProtectorCore.grantAccess({ + protectedData: '0x123abc...', + authorizedApp: '0x456def...', // [!code focus] + authorizedUser: '0x789cba...', +}); +``` + +::: tip + +You may authorize a specific app or a whitelist of apps to use the protected +data. + +iExec uses the ENS `web3mail.apps.iexec.eth` for the latest version of the +Web3Mail decentralized application. + +iExec also maintains a whitelist for current and past versions of Web3Mail +dApps. Granting access to this whitelist allows use of an email `protectedData` +with all versions of the Web3Mail application, ensuring you only have to grant +this access once. The ETH address for this whitelist is +**0x781482C39CcE25546583EaC4957Fb7Bf04C277D2**. + +::: + +### authorizedUser + +**Type:** `AddressOrENS` + +The address of the user you wish to authorize to use the `protectedData`. Note +that these users may not view or manipulate the data. This only grants +permission for the user to submit the data to an iExec application. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const grantedAccess = await dataProtectorCore.grantAccess({ + protectedData: '0x123abc...', + authorizedApp: '0x456def...', + authorizedUser: '0x789cba...', // [!code focus] +}); +``` + +::: tip + +You may authorize all users to use the protected data by setting this to +**0x0000000000000000000000000000000000000000**. + +::: + +### pricePerAccess + +**Type:** `number` +**Default:** `0` + +Specifies the usage fee in nano RLC (nRLC) associated with each access of the +data. It represents the cost incurred for each individual interaction with +application. + +By invoking the grantAccess method with a specific `pricePerAccess` you define +the fee that the specified user (`authorizedUser` parameter) must pay for each +access to the data when used with the specified application (`authorizedApp` +parameter). + +The fee is paid to the owner of the protected data. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const grantedAccess = await dataProtectorCore.grantAccess({ + protectedData: '0x123abc...', + authorizedApp: '0x456def...', + authorizedUser: '0x789cba...', + pricePerAccess: 3, // [!code focus] + numberOfAccess: 10, +}); +``` + +::: tip + +`pricePerAccess` is expressed in nano RLC (nRLC). nRLC is the smallest +subdivision of the RLC token, 1 RLC equals to 10^9 nRLC. + +When provided, `pricePerAccess` must be a non-negative integer value. + +::: + +### numberOfAccess + +**Type:** `number` +**Default:** `1` + +Allows restricting the number of times the protected data may be processed and +used. + +It is not technically possible to set an unlimited number of accesses, but you +can set `numberOfAccess` to `10000` for example. + +::: info + +If you attempt to process the protected data more times than specified in +`numberOfAccess`, you will encounter a **"no dataset orders"** error. + +To prevent this error, ensure the `numberOfAccess` is properly set when calling +the `grantAccess` method. + +::: + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const grantedAccess = await dataProtectorCore.grantAccess({ + protectedData: '0x123abc...', + authorizedApp: '0x456def...', + authorizedUser: '0x789cba...', + pricePerAccess: 3, + numberOfAccess: 10, // [!code focus] +}); +``` + +### onStatusUpdate + +**Type:** `OnStatusUpdateFn` + +Callback function to be notified at intermediate steps. + + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const grantedAccess = await dataProtectorCore.grantAccess({ + protectedData: '0x123abc...', + authorizedApp: '0x456def...', + authorizedUser: '0x789cba...', + onStatusUpdate: ({ title, isDone }) => { // [!code focus] + console.log(title, isDone); // [!code focus] + }, // [!code focus] +}); +``` + + +You can expect this callback function to be called with the following titles: + +```ts +'CREATE_DATASET_ORDER'; +'PUBLISH_DATASET_ORDER'; +``` + +Once with `isDone: false`, and then with `isDone: true` + +## Return Value + +```ts twoslash +import { type GrantedAccess } from '@iexec/dataprotector'; +``` + +The result of this method confirms the new access grant. It consists of a JSON +`grantedAccess` object. + +[`GrantedAccess`](../types.md#grantedaccess) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorCore/processProtectedData.md b/src/documentation/manage-data/dataProtector/dataProtectorCore/processProtectedData.md new file mode 100644 index 00000000..de1ca5b0 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorCore/processProtectedData.md @@ -0,0 +1,497 @@ +--- +title: processProtectedData +description: + Process encrypted data securely with iExec's processProtectedData method. Use + authorized applications to process protected datasets while maintaining data + privacy and security. +--- + +# processProtectedData + +Allows processing a protected dataset through use of a specified iExec +application. + +> [!IMPORTANT] +> +> You must ensure this application has authorization to use the `protectedData`. +> You may grant this permission using the [`grantAccess`](./grantAccess.md) +> method. + +## Usage + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const processProtectedDataResponse = + await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + args: 'arg1 arg2', + inputFiles: ['https://example.com/file1', 'https://example.com/file2'], + secrets: { + 1: 'secret1', + 2: 'secret2', + }, + }); +``` + +## Parameters + +```ts twoslash +import { type ProcessProtectedDataParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +The ETH address or Ethereum Name Service (ENS) reference for the protected data +you wish the `app` to process. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const processProtectedDataResponse = + await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', // [!code focus] + app: '0x456def...', + }); +``` + +### app {#app-param} + +**Type:** `AddressOrENS` + +The ETH address or Ethereum Name Service (ENS) address for the iExec application +to process the protected data. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const processProtectedDataResponse = + await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', // [!code focus] + }); +``` + +### path + +**Type:** `string` + +Under the hood, a protected data is a zip file. With this `path` parameter, you +can specify the file you're interested in. The zip file will be uncompressed for +you, and only the desired file will be given as the `result`. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const processProtectedDataResponse = + await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + path: 'my-content', // [!code focus] + }); +``` + +### userWhitelist + +**Type:** `Address` + +If access to the protected data is granted to a group of users via a whitelist +contract, you must use this `userWhitelist` parameter. The value should be the +whitelist contract address that has access to the protected data. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const processProtectedDataResponse = + await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + userWhitelist: '0x656def...', // [!code focus] + }); +``` + +### useVoucher + +**Type:** `boolean` +**Default:** `false` + +This optional parameter allows you to pay for the task using a voucher. By +default, it uses the voucher associated with your connected wallet, but you can +override this by specifying a voucherAddress. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const processProtectedDataResponse = + await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + useVoucher: true, // [!code focus] + }); +``` + +::: tip + +If your voucher doesn't have enough xRLC to cover the deal, the SDK will +automatically get the required amount to your iExec account. Ensure that your +voucher is authorized to access your iExec account and that your account has +sufficient funds for this transfer to proceed. + +::: + +### voucherOwner + +**Type:** `Address` + +This optional parameter allows you to pay for the task using someone else’s +voucher. Make sure the voucher's owner has authorized you to use it. This +parameter must be used in combination with `useVoucher: true`. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const processProtectedDataResponse = + await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + useVoucher: true, // [!code focus] + voucherOwner: '0x5714eB...', // [!code focus] + }); +``` + +### args + +**Type:** `string` + +Set of execution arguments for the application. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const processProtectedDataResponse = + await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + args: 'arg1 arg2', // [!code focus] + }); +``` + +::: danger + +Do not use this to provide any sensitive information to the application. All +arguments passed this way are visible in plain text using the +[iExec blockchain explorer](https://explorer.iex.ec). + +::: + +### inputFiles + +**Type:** `string[]` + +A set of URLs representing the input files required for application execution. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const processProtectedDataResponse = + await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + inputFiles: ['https://example.com/file1', 'https://example.com/file2'], // [!code focus] + }); +``` + +### secrets + +**Type:** `Record` + +A set of requester secrets necessary for the application's execution. This is +represented as a mapping of numerical identifiers to corresponding secrets +stored in the secrets manager needed for the application's execution. + +Secrets are accessible during the application's execution as environment +variables. For more details, see +[Access requester secrets](https://protocol.docs.iex.ec/for-developers/confidential-computing/access-confidential-assets/requester-secrets). + + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const processProtectedDataResponse = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + secrets: { // [!code focus] + 1: 'secret1', // [!code focus] + 2: 'secret2', // [!code focus] + }, // [!code focus] +}); +``` + + +### workerpool + +**Type:** `AddressOrENS | 'any'` +**Default:** `prod-v8-bellecour.main.pools.iexec.eth` + +The ETH address or Ethereum Name Service (ENS) address for the iExec workerpool. +It's the confidential computer on which the iExec application will run. + +::: tip + +iExec currently offers a production workerpool located at the Ethereum Name +Service (ENS) address `prod-v8-bellecour.main.pools.iexec.eth`. This is the +default workerpool for running confidential computations on the iExec platform. + +If you don't specify a workerpool preference, +0x0000000000000000000000000000000000000000 represents any randomly available +workerpool. + +::: + +::: info + +🧪 While protected data are processed in **TEE** by **intel SGX** technology by +default, `@iexec/dataprotector` can be configured to create and process +protected data in the experimental **intel TDX** environment. + +TDX mode is enabled by setting connecting the **TDX SMS** and using the **TDX +workerpool**. + +```ts twoslash [Browser] +declare global { + interface Window { + ethereum: any; + } +} +// ---cut--- +import { IExecDataProtectorCore } from '@iexec/dataprotector'; + +const web3Provider = window.ethereum; +// Instantiate dataProtector connected to the TDX SMS +const dataProtectorCore = new IExecDataProtectorCore(web3Provider, { + iexecOptions: { + smsURL: 'https://sms.labs.iex.ec', // [!code focus] + }, +}); + +const processProtectedDataResponse = + await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + workerpool: 'tdx-labs.pools.iexec.eth', // [!code focus] + }); +``` + +⚠️ Keep in mind: TDX mode is experimental and can be subject to instabilities or +discontinuity. + +::: + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const processProtectedDataResponse = + await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + workerpool: '0xa5de76...', // [!code focus] + }); +``` + +### dataMaxPrice + +**Type:** `number` +**Default:** `0` + +Allows specifying the maximum amount (in nRLC) you are willing to pay the +protected data owner for using their data. The owner of the protected data +receives this as a payment for sharing their data. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const processProtectedDataResponse = + await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + dataMaxPrice: 42, // [!code focus] + }); +``` + +### appMaxPrice + +**Type:** `number` +**Default:** `0` + +Allows specifying the maximum amount (in nRLC) you are willing to pay the iApp +provider for using the deployed application. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const processProtectedDataResponse = + await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + appMaxPrice: 42, // [!code focus] + }); +``` + +### workerpoolMaxPrice + +**Type:** `number` +**Default:** `0` + +Allows specifying the maximum amount you want to pay the workerpool provider for +using their infrastructure to run the iApp in nRLC. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const processProtectedDataResponse = + await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + workerpoolMaxPrice: 42, // [!code focus] + }); +``` + +### onStatusUpdate + +**Type:** `OnStatusUpdateFn` + +Callback function to be notified at intermediate steps. + + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const processProtectedDataResponse = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + onStatusUpdate: ({ title, isDone }) => { // [!code focus] + console.log(title, isDone); // [!code focus] + }, // [!code focus] +}); +``` + + +You can expect this callback function to be called with the following titles: + +```ts +'FETCH_PROTECTED_DATA_ORDERBOOK'; +'FETCH_APP_ORDERBOOK'; +'FETCH_WORKERPOOL_ORDERBOOK'; +'PUSH_REQUESTER_SECRET'; +'REQUEST_TO_PROCESS_PROTECTED_DATA'; +'CONSUME_TASK'; +'CONSUME_RESULT_DOWNLOAD'; +'CONSUME_RESULT_DECRYPT'; +``` + +Once with `isDone: false`, and then with `isDone: true` + +## Return Value + +```ts twoslash +import { type ProcessProtectedDataResponse } from '@iexec/dataprotector'; +``` + +### txHash + +`string` + +The ID of the transaction that happened on iExec's side chain. You may view +details on the transaction using the [iExec explorer](https://explorer.iex.ec). + +### dealId + +`string` + +Identifies the specific deal associated with this transaction. + +### taskId + +`string` + +A unique identifier associated with a task currently running on the iExec +Bellecour side chain. You can monitor task execution using the +[iExec blockchain explorer](https://explorer.iex.ec). + +::: tip + +The +[getResultFromCompletedTask()](../dataProtectorCore/getResultFromCompletedTask.md) +function allows you to retrieve the result of a completed task using its +`taskId`. + +Additionally, you can specify a **file path** within the ZIP archive to extract +a specific file when required. + +::: + +### result + +`ArrayBuffer` + +The result is a ZIP file containing at least one mandatory file: + +- **computed.json**: This file contains metadata about the computation performed + by the application. +- additional files may be included depending on the dapp used. + +::: info + +In the case of the **Content Creator Delivery DApp**, the ZIP file will also +include a file named **content**, which corresponds to the protected data +processed during the task. + +::: diff --git a/src/documentation/manage-data/dataProtector/dataProtectorCore/protectData.md b/src/documentation/manage-data/dataProtector/dataProtectorCore/protectData.md new file mode 100644 index 00000000..36fd05bb --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorCore/protectData.md @@ -0,0 +1,442 @@ +--- +title: protectData +description: + Use the protectData method from iExec DataProtector to encrypt user data + client-side and ensure full privacy. Easily secure emails, credentials, or + custom data structures in your iApp. +--- + +# protectData + +The iExec tool suite supports deployment of applications where the user of the +application has complete and total control over access to their data. This +ensures privacy and security when invoking these applications. + +Through use of the `protectData` method, a user may encrypt and secure any type +of data. Encryption occurs on the client side, supporting the user's control +over their data. + +## Usage + +The method accepts a JSON object containing the data to encrypt and an optional +name to identify the data. + +An email address, for example, may be submitted as: + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const protectedData = await dataProtectorCore.protectData({ + data: { + email: 'example@gmail.com', + }, +}); +``` + +Your object may contain any number of custom keys. The following example +illustrates protection of multiple categories of data within one object: + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const protectedData = await dataProtectorCore.protectData({ + data: { + email: 'example@gmail.com', + SMTPserver: { + port: 5000, + smtp_server: 'smtp.gmail.com', + }, + }, +}); +``` + +::: info + +🧪 While protected data are processed in **TEE** by **intel SGX** technology by +default, `@iexec/dataprotector` can be configured to create and process +protected data in the experimental **intel TDX** environment. + +TDX mode is enabled by setting connecting the **TDX SMS** and using the **TDX +workerpool**. + +```ts twoslash [Browser] +declare global { + interface Window { + ethereum: any; + } +} +// ---cut--- +import { IExecDataProtectorCore } from '@iexec/dataprotector'; + +const web3Provider = window.ethereum; +// Instantiate dataProtector connected to the TDX SMS +const dataProtectorCore = new IExecDataProtectorCore(web3Provider, { + iexecOptions: { + smsURL: 'https://sms.labs.iex.ec', // [!code focus] + }, +}); +// create a protected data +const protectedData = await dataProtectorCore.protectData({ + data: { + email: 'example@gmail.com', + SMTPserver: { + port: 5000, + smtp_server: 'smtp.gmail.com', + }, + }, +}); +``` + +⚠️ Keep in mind: TDX mode is experimental and can be subject to instabilities or +discontinuity. + +::: + +## Parameters + +```ts twoslash +import { type ProtectDataParams } from '@iexec/dataprotector'; +``` + +### data + +**Type:** `DataObject` + +This is the actual data the user is protecting, provided as a JSON object with +any number of custom keys. The data is encrypted and stored as an NFT. + + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const protectedData = await dataProtectorCore.protectData({ + data: { // [!code focus] + email: 'example@gmail.com', // [!code focus] + }, // [!code focus] +}); +``` + + +::: tip + +If you'd like to **protect a file**, you first need to convert it to some kind +of buffer. To do so, you can use `createArrayBufferFromFile`. + +```ts twoslash +const file: File = new File([], 'emptyFile.txt'); +// ---cut--- +import { createArrayBufferFromFile } from '@iexec/dataprotector'; + +const fileAsArrayBuffer = await createArrayBufferFromFile(file); +``` + +::: + +::: tip + +If you want to **protect an `Array`**, you must represent it as a +`Record`. To do so, you can use the `reduceArray` method +implemented in this example. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const reduceArray = (array: Array): Record => + array.reduce((accumulator, current, i) => { + accumulator[i] = current; + return accumulator; + }, {}); + +const emailsArray = [ + 'example@gmail.com', + 'example@my-company.com', + 'example@example.com', +]; + +const protectedData = await dataProtectorCore.protectData({ + data: { + emails: reduceArray(emailsArray), + }, +}); + +/** + * protectedData.schema: + * { + * emails: { + * 0: 'string', + * 1: 'string', + * 2: 'string' + * } + * } + */ +``` + +::: + +### name + +**Type:** `string` +**Default:** `''` + +Allows providing a descriptive name for the protected data. This is considered +public metadata, describing the protected data. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const protectedData = await dataProtectorCore.protectData({ + name: 'myEmail', // [!code focus] + data: { + email: 'example@gmail.com', + }, +}); +``` + +::: tip + +The name is public and not encrypted. + +::: + +### uploadMode + +**Type:** `"ipfs" | "arweave"` +**Default:** `"ipfs"` + +Specify the storage solution to use for the protected data encrypted payload +hosting. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const protectedData = await dataProtectorCore.protectData({ + name: 'myEmail', + data: { + email: 'example@gmail.com', + }, + uploadMode: 'arweave', // [!code focus] +}); +``` + +### allowDebug + +**Type:** `boolean` +**Default:** `false` + +Allow using the protected data in TEE debug apps. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const protectedData = await dataProtectorCore.protectData({ + name: 'myEmail', + data: { + email: 'example@gmail.com', + }, + allowDebug: true, // [!code focus] +}); +``` + +::: tip + +This param is for development only. + +::: + +### onStatusUpdate + +**Type:** `OnStatusUpdateFn` + +Callback function to be notified at intermediate steps. + + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const protectedData = await dataProtectorCore.protectData({ + name: 'myEmail', + data: { + email: 'example@gmail.com', + }, + onStatusUpdate: ({ title, isDone }) => {// [!code focus] + console.log(title, isDone); // [!code focus] + }, // [!code focus] +}); +``` + + +You can expect this callback function to be called with the following titles: + +```ts +'EXTRACT_DATA_SCHEMA'; +'CREATE_ZIP_FILE'; +'CREATE_ENCRYPTION_KEY'; +'ENCRYPT_FILE'; +'UPLOAD_ENCRYPTED_FILE'; +'DEPLOY_PROTECTED_DATA'; +'PUSH_SECRET_TO_SMS'; +``` + +Once with `isDone: false`, and then with `isDone: true` + +## Return Value + +```ts twoslash +import type { + ProtectedDataWithSecretProps, + ProtectedData, +} from '@iexec/dataprotector'; +``` + +The `protectData` method returns the following fields, as a JSON object. + +### name + +`string` + +The optional name provided during invocation of the method. If no name is +specified this value defaults to `Untitled`. + +### address + +`Address` + +The ETH address of the newly created `protectedData`. + +### owner + +`Address` + +The ETH address of the creator and owner of this `protectedData`. + +### schema + +`DataSchema` + +Metadata describing the fields provided in the `data` parameter. The data types +are automatically detected and listed in the schema. + +::: tip + +The following data types are automatically detected: + +- Scalars + - `bool` + - `f64` (JavaScript `number`) + - `i128` (JavaScript `bigint` up to 128 bits) + - `string` +- Binary: + - `application/octet-stream` + - `application/pdf` + - `application/xml` + - `application/zip` + - `audio/midi` + - `audio/mpeg` + - `audio/x-wav` + - `image/bmp` + - `image/gif` + - `image/jpeg` + - `image/png` + - `image/webp` + - `video/mp4` + - `video/mpeg` + - `video/x-msvideo` + +Any undetected binary data type is categorized as `application/octet-stream` + +::: + +### creationTimestamp + +`number` + +A unix-style timestamp indicating the creation time of this `protectedData`. + +### transactionHash + +`string` + +The ID of the transaction that happened on iExec's side chain. You may view +details on the transaction using the [iExec explorer](https://explorer.iex.ec). + +### zipFile + +`Uint8Array` + +Under the hood, your protected data will be **compressed as a zip file**. In +this zip file, you'll find back all of your protected fields, each field being +serialized with a tool called `borsh`. You can find more details here: +[deserializer](/documentation/build-iapp/iapp-generator/deserializer). + +This is mainly returned for debug purpose. + +### encryptionKey + +`Uint8Array` + +The encryption key generated by the client to encrypt the data. This key is for +your own usage. You will not have to share it in the context of the iExec +protocol or developer tools. + +Under the hood, it is a symmetric AES-256 key. + +::: tip + +The zip file generated is a `Uint8Array`, so if you want to handle the binary +data or download it consider adding a zip extension to it. + +::: + +### multiaddr + +`string` | `undefined` + +The multiaddr field is the IPFS path of your encrypted data. + +::: tip + +You can access your encrypted IPFS data with the link: + +`https://ipfs-gateway.v8-bellecour.iex.ec/ipfs/abc123...` + +`abc123...` is the second part of the returned string `/p2p/abc123...` + +::: + +## Created Protected Data + +To further check your data was correctly created, you can inspect it on the +[iExec explorer](https://explorer.iex.ec/). + + + iExec explorer - Dataset example + + + diff --git a/src/documentation/manage-data/dataProtector/dataProtectorCore/revokeAllAccess.md b/src/documentation/manage-data/dataProtector/dataProtectorCore/revokeAllAccess.md new file mode 100644 index 00000000..becf31c3 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorCore/revokeAllAccess.md @@ -0,0 +1,143 @@ +--- +title: revokeAllAccess +description: + Revoke all or specific access permissions to protected data with iExec's + revokeAllAccess method. Efficiently manage data security by removing access + from users or smart contract. +--- + +# revokeAllAccess + +This method allows revoking authorizations granted to a `protectedData` entity. +You may optionally specify application or user addresses for revocation. If you +do not specify either of these optional values, this method will revoke all +access for all users and applications. + +You must be the owner of the protected data. + +Under the hood, all granted access will be retrieved and be revoked one by one. +If by any chance there were **more than 20 granted access** to be revoked, you +would need to call this `revokeAllAccess()` method more than once for all +granted access to be actually revoked. Use `getGrantedAccess()` to ensure it is +all done. + +## Usage + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const revokeAllAccessResult = await dataProtectorCore.revokeAllAccess({ + protectedData: '0x123abc...', + authorizedApp: '0x456def...', + authorizedUser: '0x789cba...', +}); +``` + +## Parameters + +```ts twoslash +import { type RevokeAllAccessParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +The address of the `protectedData` subject to access revocation. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const revokeAllAccessResult = await dataProtectorCore.revokeAllAccess({ + protectedData: '0x123abc...', // [!code focus] +}); +``` + +### authorizedApp + +**Type:** `AddressOrENS` + +The application address to be removed from the authorization list for the +specified `protectedData`. If no address is specified, it will revoke all access +from the protected data, regardless of the app. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const revokeAllAccessResult = await dataProtectorCore.revokeAllAccess({ + protectedData: '0x123abc...', + authorizedApp: '0x456def...', // [!code focus] + authorizedUser: '0x789cba...', +}); +``` + +### authorizedUser + +**Type:** `AddressOrENS` + +The user address to be removed from the authorization list for the specified +`protectedData`. If no address is specified, it will revoke all access from the +protected data, regardless of the authorized user. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const revokeAllAccessResult = await dataProtectorCore.revokeAllAccess({ + protectedData: '0x123abc...', + authorizedApp: '0x456def...', + authorizedUser: '0x789cba...', // [!code focus] +}); +``` + +### onStatusUpdate + +**Type:** `OnStatusUpdateFn` + +Callback function to be notified at intermediate steps. + + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const revokeAllAccessResult = await dataProtectorCore.revokeAllAccess({ + protectedData: '0x123abc...', + authorizedApp: '0x456def...', + authorizedUser: '0x789cba...', + onStatusUpdate: ({ title, isDone }) => { // [!code focus] + console.log(title, isDone); // [!code focus] + }, // [!code focus] +}); +``` + + +You can expect this callback function to be called with the following titles: + +```ts +'RETRIEVE_ALL_GRANTED_ACCESS'; +'REVOKE_ONE_ACCESS'; +``` + +Once with `isDone: false`, and then with `isDone: true` + +## Return Value + +```ts twoslash +import { type RevokedAccess } from '@iexec/dataprotector'; +``` + +[`RevokedAccess[]`](../types.md#revokedaccess) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorCore/revokeOneAccess.md b/src/documentation/manage-data/dataProtector/dataProtectorCore/revokeOneAccess.md new file mode 100644 index 00000000..866de9fe --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorCore/revokeOneAccess.md @@ -0,0 +1,91 @@ +--- +title: revokeOneAccess +description: + Revoke specific access permissions to protected data with iExec's + revokeOneAccess method. Manage and remove access granted to users or + applications through blockchain transactions. +--- + +# revokeOneAccess + +This method allows revoking a specific access authorization from a +`protectedData` entity. The input parameter for this method is sourced from the +[getGrantedAccess](getGrantedAccess.md) method, which provides a list of all +authorizations on single `protectedData` entity. + +As this will generate a blockchain transaction, expect it to take a least 5sec +(a block time). + +## Usage + +The `revokeOneAccess` method requires a `grantedAccess` object as an input +parameter. This object is retrieved from the +[`getGrantedAccess`](./getGrantedAccess.md) method. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const revokeAccess = await dataProtectorCore.revokeOneAccess({ + apprestrict: '0xea...', + dataset: '0xA0C...', + datasetprice: '0', + requesterrestrict: '0xecb..', + salt: '0x0147...', + sign: '0xc22c1...', + tag: '0x0000000000000000000000000000000000000000000000000000000000000003', + volume: '1', + workerpoolrestrict: '0x000...', +}); +``` + +## Parameters + +```ts twoslash +import { type GrantedAccess } from '@iexec/dataprotector'; +``` + +### grantedAccess + +**Type:** `GrantedAccess` + +This is the complete `granted access` object retrieved from an invocation of +`getGrantedAccess`. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const revokeAccess = await dataProtectorCore.revokeOneAccess({ + apprestrict: '0xea...', // [!code focus] + dataset: '0xA0C...', // [!code focus] + datasetprice: '0', // [!code focus] + requesterrestrict: '0xecb..', // [!code focus] + salt: '0x0147...', // [!code focus] + sign: '0xc22c1...', // [!code focus] + tag: '0x0000000000000000000000000000000000000000000000000000000000000003', // [!code focus] + volume: '1', // [!code focus] + workerpoolrestrict: '0x000...', // [!code focus] +}); +``` + +::: warning + +The tag must always be set to +`0x0000000000000000000000000000000000000000000000000000000000000003`. This +specific value indicates that the order is for a confidential asset (a protected +data). + +::: + +## Result Value + +```ts twoslash +import { type RevokedAccess } from '@iexec/dataprotector'; +``` + +[`RevokedAccess`](../types.md#revokedaccess) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorCore/transferOwnership.md b/src/documentation/manage-data/dataProtector/dataProtectorCore/transferOwnership.md new file mode 100644 index 00000000..9ce7f33b --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorCore/transferOwnership.md @@ -0,0 +1,105 @@ +--- +title: transferOwnership +description: + Transfer ownership of protected data to a new owner with iExec's + transferOwnership method. Securely update data ownership and automatically + revoke previous access permissions. +--- + +# transferOwnership + +Allows transferring ownership of a `protectedData` entity to a new owner, +identified by their ETH address. The return value provides a transaction hash +and confirmation of the new owner of the `protectedData`. Only the current owner +of the `protectedData` may invoke this method. + +When transferring the `protectedData`, the grantedAccess created by the previous +owner are revoked automatically. + +Ownership of the `protectedData` can be renounced by transferring it to the burn +address `0x000000000000000000000000000000000000dEaD`. + +## Usage + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const transferResponse = await dataProtectorCore.transferOwnership({ + protectedData: '0x123abc...', + newOwner: '0xc5e9f4...', +}); +``` + +## Parameters + +```ts twoslash +import { type TransferParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +ETH address of the `protectedData` owned by you which is to be transferred to a +new owner. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const transferResponse = await dataProtectorCore.transferOwnership({ + protectedData: '0x123abc...', // [!code focus] + newOwner: '0xc5e9f4...', +}); +``` + +### newOwner + +**Type:** `AddressOrENS` + +ETH address for the new owner for the `protectedData`. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const transferResponse = await dataProtectorCore.transferOwnership({ + protectedData: '0x123abc...', + newOwner: '0xc5e9f4...', // [!code focus] +}); +``` + +## Return Value + +```ts twoslash +import { type TransferResponse } from '@iexec/dataprotector'; +``` + +The result of this method is an array of objects identifying the new owner. The +objects contain the three fields: + +### address + +`Address` + +The ETH address of the `protectedData` you transferred. + +### to + +`AddressOrENS` + +The ETH address of the new owner of the `protectedData`. + +### txHash + +`string` + +The ID of the transaction that happened on iExec's side chain. You may view +details on the transaction using the [iExec explorer](https://explorer.iex.ec). diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing.md new file mode 100644 index 00000000..086fd6a6 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing.md @@ -0,0 +1,65 @@ +--- +title: DataProtector Sharing +description: + Distribute and monetize your protected data effortlessly with DataProtector + Sharing. Use smart contracts to manage access, rent, or sell data while + maintaining control and privacy. +--- + +# DataProtector Sharing + +The DataProtector Sharing module includes a set of methods for distributing and +monetizing your protected data. These work seamlessly in your own applications +in a way that's transparent to end users. The Sharing module supports the +following options for distributing a user's protected data: + +- Sharing freely with other users +- Renting data for a pre-determined period of time +- Providing access for a recurring subscription fee +- Selling ownership of the data + +Once access is obtained through one of these distribution methods, the `consume` +methods allow interaction with the protected data. The application designer uses +these methods to securely retrieve data from IPFS and decrypt it on the client +side. This ensures only the consumer gains access to the data. + +## How Does it Work? + +The user's protected data is managed by a special Data Sharing smart contract. +This smart contract enforces the user's desired sharing paradigm. Protected data +is bundled by the user into one or more collections. Within the collection, the +user may specify different sharing options for different pieces of data. This +illustration shows the hierarchy of a Data Sharing smart contract: + +![Data Sharing smart contract](./dataProtectorSharing/data-sharing-sc.png) + +The Data Sharing smart contract enforces governance over the user's protected +data based on their desired distribution choices. The user has complete control +over the mode of sharing for each Collection and each Protected Data contained +in their collections. + +The smart contract also protects the consumer of a user's data. If a consumer +rents access to a video clip, for example, even if the owner of the video clip +removes the rental option, the renter maintains their access for the duration of +the rental period. + +## How Does this Differ from `DataProtector Core`? + +With `DataProtector Core` you must grant each consumer individual access to your +protected data. This has several prerequisites: + +- The data owner must know the consumer's Ethereum address +- The data owner must sign a transaction at the moment you grant the access +- The data owner must define the number of times the consumer can access the + data up front + +With `DataProtector Sharing` a user can easily distribute their protected data +to a wider audience. The user authorizes distribution channels up front and +consumers of the data trigger smart contracts on their own, requiring no +additional activity from the data owner. This has several key benefits + +- The data owner doesn't need to know the consumer's Ethereum address +- The data owner doesn't need to sign a transaction at the moment of + distribution, only the consumer of the data signs their transaction +- The data owner doesn't manage access, the Data Sharing smart contract + automatically enforces the distribution and monetization choices diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/collection.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/collection.md new file mode 100644 index 00000000..55921734 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/collection.md @@ -0,0 +1,70 @@ +--- +title: Data Sharing - Collection Methods +description: + Organize your protected data into collections with iExec's Data Sharing + module. Choose distribution options like renting, free access, subscription, + or selling to control how your data is shared and monetized. +--- + +# Data Sharing - Collection Methods + +A collection is a way to group protected data for sharing by the Data Sharing +module. The Data Sharing smart contract operates on data contained within a +collection. You must group data within a collection before choosing a method of +distribution. The data owner is not restricted to only a single collection. This +diagram illustrates all the options for protected data within a collection: + +![Data Sharing collection](inside-a-collection.png) + +## Distribution Options + +The distribution choice impacts the visibility of the protected data when a +potential consumer is browsing the collection. Inside a collection, a data owner +may specify one of the following states: + +**Not distributed** + +Only the collection's owner can see it. This is useful for things in +development, or when the owner wishes to release things for a limited time. + +**For rent** + +The collection's owner allows a consumer to pay a set price to access the data +for a predetermined period of time. The smart contract ensures the consumer +maintains access. Even if the collection owner de-lists the data or changes the +rental terms, the consumer maintains their access for the duration of their +rental term. Once the rental term is up, the consumer loses access to the +protected data. If the user wishes to re-up their rental term, they are bound to +any new price or duration changes the data owner makes. + +**Free** + +This functions the same as a rental but with no price for the transaction. The +data owner may still set a duration for the free access. This supports scenarios +like a free giveaway, or a timed promotion. + +**Included in Subscription** + +The collection's owner may create one or more subscription models for +distribution of their data. A subscription bundle is a subset of the protected +data within the collection. Subscribers pay a set fee at a set cadence to +maintain access to all protected data within the subscription. The collection +owner may assign protected data to more than one subscription bundles at the +same time. + +The collection owner may add additional protected data to the subscription at +any time. They may not, however, remove protected data from the subscription +bundle if there are any active subscribers. A subscriber maintains access to all +protected data within the subscription as long as they continue paying the +subscription fee. The collection owner may de-list a subscription bundle by +setting either the price, the duration, or both, to zero. + +**Both For Rent and Included in Subscription** + +The collection owner may set any combination of rental and subscription terms +for any protected data in the collection. + +**For sale** + +The collection owner may list any of their protected data for sale. This is +especially useful for dealers of digital assets like NFTs. diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/addToCollection.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/addToCollection.md new file mode 100644 index 00000000..05ccde21 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/addToCollection.md @@ -0,0 +1,166 @@ +--- +title: addToCollection +description: + Transfer a protected data to one of your collections in the Data Sharing smart + contract. The method approves the contract to manage the data and adds it to + the specified collection. +--- + +# addToCollection + +Method to transfer one of your protected data to a collection of yours in the +Data Sharing smart contract. + +Under the hood, this method performs two actions: + +- Approve the Data Sharing smart contract to manage your protected data. +- Add the protected data to your collection. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.addToCollection({ + protectedData: '0x123abc...', + collectionId: 12, + addOnlyAppWhitelist: '0x541abc...', +}); +``` + +## Parameters + +```ts twoslash +import { type AddToCollectionParams } from '@iexec/dataprotector'; +``` + +### collectionId + +**Type:** `number` + +Collection ID to which you'd like to add the protected data. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.addToCollection({ + collectionId: 12, // [!code focus] + protectedData: '0x123abc...', + addOnlyAppWhitelist: '0x541abc...', +}); +``` + +### protectedData + +**Type:** `AddressOrENS` + +Address of the protected data you'd like to add to your collection. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.addToCollection({ + collectionId: 12, + protectedData: '0x123abc...', // [!code focus] + addOnlyAppWhitelist: '0x541abc...', +}); +``` + +Before any smart contract interaction, the existence of the protected data will +be checked, as well as the ownership: it should be the wallet address you used +to instantiate DataProtector SDK. + +### addOnlyAppWhitelist + +**Type:** `AddressOrENS` + +Address of the whitelist smart contract that contains applications allowed to +consume the protected data. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.addToCollection({ + collectionId: 12, + protectedData: '0x123abc...', + addOnlyAppWhitelist: '0xba46d6...', // [!code focus] +}); +``` + +::: tip + +For this `addOnlyAppWhitelist`, you are free to use +`0x256bcd881c33bdf9df952f2a0148f27d439f2e64`. + +For more details on how to create and manage appsWhitelist, see +[Apps whitelist](../../advanced/apps-whitelist). + +::: + +### onStatusUpdate + +**Type:** `OnStatusUpdateFn` + +Callback function to be notified at intermediate steps. + + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.addToCollection({ + protectedData: '0x123abc...', + collectionId: 12, + addOnlyAppWhitelist: '0xba46d6...', + onStatusUpdate: ({ title, isDone }) => { // [!code focus] + console.log(title, isDone); // [!code focus] + }, // [!code focus] +}); +``` + + +You can expect this callback function to be called with the following titles: + +```ts +'APPROVE_COLLECTION_CONTRACT'; +'ADD_PROTECTED_DATA_TO_COLLECTION'; +``` + +Once with `isDone: false`, and then with `isDone: true` + +## Return Value + +```ts twoslash +import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; +``` + +See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/createCollection.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/createCollection.md new file mode 100644 index 00000000..9f48e64e --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/createCollection.md @@ -0,0 +1,36 @@ +--- +title: createCollection +description: + Create a new NFT collection with iExec's createCollection method. Organize and + manage your protected data for seamless distribution and monetization through + DataProtector Sharing. +--- + +# createCollection + +Method to create a new collection in the Data Sharing smart contract. + +Having a collection is a required step before choosing how to distribute your +protected data. + +A wallet address may own multiple collections. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const createCollectionResult = await dataProtectorSharing.createCollection(); +``` + +## Return Value + +```ts twoslash +import { type CreateCollectionResponse } from '@iexec/dataprotector'; +``` diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/removeCollection.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/removeCollection.md new file mode 100644 index 00000000..64a24b3f --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/removeCollection.md @@ -0,0 +1,70 @@ +--- +title: removeCollection +description: + Remove a collection from the Data Sharing smart contract by burning its + associated NFT. Transfer the NFT to the zero address and permanently remove + the collection. +--- + +# removeCollection + +Method to remove one of your collections in the Data Sharing smart contract. + +By removing a collection, we mean to burn the associated NFT, ie. to **transfer +it to the zero address**. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.removeCollection({ + collectionId: 15, +}); +``` + +## Pre-conditions + +- You must be the owner of the collection. +- There should be no protected data in the collection. See + [`removeProtectedDataFromCollection`](./removeProtectedDataFromCollection.md). + +## Parameters + +```ts twoslash +import { type RemoveCollectionParams } from '@iexec/dataprotector'; +``` + +### collectionId + +**Type:** `number` + +The collection ID of the collection you want to remove. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.removeCollection({ + collectionId: 15, // [!code focus] +}); +``` + +## Return Value + +```ts twoslash +import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; +``` + +See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/removeProtectedDataFromCollection.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/removeProtectedDataFromCollection.md new file mode 100644 index 00000000..fde983da --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/collection/removeProtectedDataFromCollection.md @@ -0,0 +1,76 @@ +--- +title: removeProtectedDataFromCollection +description: + Remove a protected data from one of your collections in the Data Sharing smart + contract. This method transfers the ownership of the protected data back to + the owner. +--- + +# removeProtectedDataFromCollection + +Method to remove one of your protected data from a collection of yours in the +Data Sharing smart contract. + +To put it differently, this method will transfer the ownership of the protected +data back to you. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.removeProtectedDataFromCollection( + { + protectedData: '0x123abc...', + } +); +``` + +## Pre-conditions + +- You must be the owner of the collection of which the protected data is + currently part of. +- There should be no active subscriptions to this collection. +- There should be no active rentals of this protected data. + +## Parameters + +```ts twoslash +import { type RemoveFromCollectionParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +Address of the protected data you'd like to remove from your collection. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.removeProtectedDataFromCollection( + { + protectedData: '0x123abc...', // [!code focus] + } +); +``` + +## Return Value + +```ts twoslash +import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; +``` + +See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/consume/consumeProtectedData.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/consume/consumeProtectedData.md new file mode 100644 index 00000000..944a7198 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/consume/consumeProtectedData.md @@ -0,0 +1,341 @@ +--- +title: consumeProtectedData +description: + Consume protected data in iExec by visualizing or downloading it. This method + involves generating RSA keys, interacting with iExec's Secret Management + Service, and securely retrieving encrypted data from IPFS. +--- + + + +# consumeProtectedData + +Method to consume a protected data, ie. visualize it or download it. + +This method does a few things under the hood: + +- Generate an RSA key pair and save it to indexedDB (if available) +- Push the public key to iExec SMS (Secret Management Service) (For more info, + see + [iExec Protocol documentation](https://protocol.docs.iex.ec/for-developers/confidential-computing/access-confidential-assets#secret-management-service-sms)) +- Wait for the consuming task to be executed by a worker. The iExec TEE dApp + being executed is the one given with the `app` parameter. The iExec TEE dApp + will get the protected data from IPFS, encrypt it with the public key + generated in the first step, and re-upload it to IPFS. +- Retrieve the encrypted data from IPFS and decrypt it with the private key + generated in the first step. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const consumeProtectedDataResult = + await dataProtectorSharing.consumeProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + }); +``` + +## Pre-conditions + +You need to either have: + +- an active rental for the protected data, +- an active subscription to the corresponding collection if the protected data + is part of the collection subscription bundle. + +## Parameters + +```ts twoslash +import { type ConsumeProtectedDataParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +Address of the protected data you'd like to visualize. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const consumeProtectedDataResult = + await dataProtectorSharing.consumeProtectedData({ + protectedData: '0x123abc...', // [!code focus] + app: '0x456def...', + }); +``` + +### app {#app-param} + +**Type:** `AddressOrENS` + +Address or ENS of the iExec TEE dApp that will be used to consume the protected +data. This iExec TEE dApp is the one that runs within an iExec worker. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const consumeProtectedDataResult = + await dataProtectorSharing.consumeProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', // [!code focus] + }); +``` + +::: tip + +For this `app` parameter you can use the "Protected data delivery TEE dApp": + +``` +0x1cb7D4F3FFa203F211e57357D759321C6CE49921 +``` + +
+ +
Please note: This application can only be used within the +dataProtectorSharing module, as it is owned by the DataProtector Sharing smart contract. + +For more details, see [Apps whitelist](../../advanced/apps-whitelist). + +::: + +::: tip + +If you want to provide **your own TEE dApp**, you will need to create a +whitelist that contains your app. + +For more details, see [Apps whitelist](../../advanced/apps-whitelist). + +::: + +### path + +**Type:** `string` + +Under the hood, a protected data is a zip file. With this `path` parameter, you +can specify the file you're interested in. The zip file will be uncompressed for +you, and only the desired file will be given as the `result`. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const consumeProtectedDataResult = + await dataProtectorSharing.consumeProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + path: 'my-content', // [!code focus] + }); +``` + +### workerpool + +**Type:** `AddressOrENS` +**Default:** `prod-v8-bellecour.main.pools.iexec.eth` + +Address or ENS of the workerpool. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const consumeProtectedDataResult = + await dataProtectorSharing.consumeProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + workerpool: 'prod-v8-bellecour.main.pools.iexec.eth', // [!code focus] + }); +``` + +::: tip + +iExec currently offers a production workerpool located at the Ethereum Name +Service (ENS) address `prod-v8-bellecour.main.pools.iexec.eth`. This is the +default workerpool for running confidential computations on the iExec platform. + +::: + +### maxPrice + +**Type:** `number` +**Default:** `0` + +The maximum price (in nRLC) that a requester is willing to pay for each task +related to consuming the protected data. It is the sum of the application price, +dataset price, and workerpool price per task. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const consumeProtectedDataResult = + await dataProtectorSharing.consumeProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + maxPrice: 10, // [!code focus] + }); +``` + +### pemPublicKey + +**Type:** `string` + +If you have previously called `consumeProtectedData()` and saved the returned +public key, you can reuse it in further calls. + +Alternatively, you can generate a RSA keypair on your own. + +If a public key is provided, its corresponding private key needs also to be +provided. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const consumeProtectedDataResult = + await dataProtectorSharing.consumeProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + pemPublicKey: '-----BEGIN PUBLIC KEY-----\n...\n-----END PUBLIC KEY-----', // [!code focus] + }); +``` + +### pemPrivateKey + +**Type:** `string` + +If you have previously called `consumeProtectedData()` and saved the returned +private key, you can reuse it in further calls. + +Alternatively, you can generate a RSA keypair on your own. + +If a private key is provided, its corresponding public key needs also to be +provided. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const consumeProtectedDataResult = + await dataProtectorSharing.consumeProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + pemPrivateKey: + '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----', // [!code focus] + }); +``` + +### onStatusUpdate + +**Type:** `OnStatusUpdateFn` + +Callback function to be notified at intermediate steps. + + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const consumeProtectedDataResult = + await dataProtectorSharing.consumeProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + onStatusUpdate: ({ title, isDone }) => { // [!code focus] + console.log(title, isDone); // [!code focus] + }, // [!code focus] + }); +``` + + +You can expect this callback function to be called with the following titles: + +```ts +'FETCH_WORKERPOOL_ORDERBOOK'; +'PUSH_ENCRYPTION_KEY'; +'CONSUME_ORDER_REQUESTED'; +'CONSUME_TASK'; +'CONSUME_RESULT_DOWNLOAD'; +'CONSUME_RESULT_DECRYPT'; +``` + +## Return Value + +```ts twoslash +import { type ConsumeProtectedDataResponse } from '@iexec/dataprotector'; +``` + +### txHash + +`string` + +The transaction hash corresponding to the execution of the function. + +### dealId + +`string` + +Identifies the specific deal associated with this transaction. + +### taskId + +`string` + +Identifies the specific task associated with the deal. + +### result + +`ArrayBuffer` + +The actual content of the protected file. diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/data-sharing-sc.png b/src/documentation/manage-data/dataProtector/dataProtectorSharing/data-sharing-sc.png new file mode 100644 index 0000000000000000000000000000000000000000..189c41087644a4fea1142a0e18d39a6bde0a751d GIT binary patch literal 93263 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

-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

0@7GQASvsf)WEB8EZx9i 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 literal 0 HcmV?d00001 diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/inside-a-collection.png b/src/documentation/manage-data/dataProtector/dataProtectorSharing/inside-a-collection.png new file mode 100644 index 0000000000000000000000000000000000000000..aac8c11a574e6cbad5f426f19f3813a0382d9edd GIT binary patch literal 113538 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 z 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 TvldJR{?t{pE)<@-cISTpV+`6% literal 0 HcmV?d00001 diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionOwners.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionOwners.md new file mode 100644 index 00000000..f8bc117b --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionOwners.md @@ -0,0 +1,70 @@ +--- +title: getCollectionOwners +description: + Method to get all collection owners with results ordered by creation timestamp + in descending order. +--- + +# getCollectionOwners + +Method to get all collection owners. + +Results of `CollectionOwner.collections` are ordered by +`collections.creationTimestamp` desc. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const collectionOwners = await dataProtectorSharing.getCollectionOwners(); +``` + +## Parameters + +```ts twoslash +import { type GetCollectionOwnersParams } from '@iexec/dataprotector'; +``` + +### limit + +**Type:** `number` +**Default:** `100` +**Range:** `[1...1000]` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const collectionOwners = await dataProtectorSharing.getCollectionOwners({ + limit: 100, // [!code focus] +}); +``` + +## Return Value + +```ts twoslash +import type { GetCollectionOwnersResponse } from '@iexec/dataprotector'; + +// Child types +import type { CollectionOwner, SubscriptionParams } from '@iexec/dataprotector'; +``` + +See +Type ↗️ + +### hasActiveSubscription + +`true` if you (logged-in user) have an active subscription to one of the +collections. diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionSubscriptions.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionSubscriptions.md new file mode 100644 index 00000000..40f15f26 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionSubscriptions.md @@ -0,0 +1,106 @@ +--- +title: getCollectionSubscriptions +description: + Fetch all subscriptions for a specific collection or user in iExec. Get + detailed information about subscription activity based on collection ID. +--- + +# getCollectionSubscriptions + +Method to get all subscriptions for: + +- a specific collection +- a specific user + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const collectionActiveSubscriptions = + await dataProtectorSharing.getCollectionSubscriptions({ + collectionId: 9, + }); +``` + +## Parameters + +```ts twoslash +import { type GetCollectionSubscriptionsParams } from '@iexec/dataprotector'; +``` + +### subscriberAddress + +**Type:** `AddressOrENS` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const userActiveSubscriptions = + await dataProtectorSharing.getCollectionSubscriptions({ + subscriberAddress: '0x246bdf...', // [!code focus] + }); +``` + +### collectionId + +**Type:** `number` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const collectionActiveSubscriptions = + await dataProtectorSharing.getCollectionSubscriptions({ + collectionId: 9, // [!code focus] + }); +``` + +### includePastSubscriptions + +**Type:** `boolean` +**Default:** `false` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const userRentals = await dataProtectorSharing.getCollectionSubscriptions({ + subscriberAddress: '0x246bdf...', + includePastSubscriptions: true, // [!code focus] +}); +``` + +## Return Value + +```ts twoslash +import { type GetCollectionSubscriptionsResponse } from '@iexec/dataprotector'; + +// Child types +import { type CollectionSubscription } from '@iexec/dataprotector'; +``` + +See +Type ↗️ diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionsByOwner.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionsByOwner.md new file mode 100644 index 00000000..06970e70 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionsByOwner.md @@ -0,0 +1,89 @@ +--- +title: getCollectionsByOwner +description: + Method to get all collections for a specific user with filtering and + pagination options. +--- + +# getCollectionsByOwner + +Method to get all collections for a specific user. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const userCollections = await dataProtectorSharing.getCollectionsByOwner({ + owner: '0xa0c15e...', +}); +``` + +## Parameters + +```ts twoslash +import { type GetCollectionsByOwnerParams } from '@iexec/dataprotector'; +``` + +### owner + +**Type:** `AddressOrENS` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const userCollections = await dataProtectorSharing.getCollectionsByOwner({ + owner: '0xa0c15e...', // [!code focus] +}); +``` + +### includeHiddenProtectedDatas + +**Type:** `boolean` +**Default:** `false` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const userCollectionsWithAllProtectedData = + await dataProtectorSharing.getCollectionsByOwner({ + owner: '0xa0c15e...', + includeHiddenProtectedDatas: true, // [!code focus] + }); +``` + +## Return Value + +```ts twoslash +import type { GetCollectionsByOwnerResponse } from '@iexec/dataprotector'; + +// Child types +import type { + CollectionWithProtectedDatas, + ProtectedDataInCollection, + SubscriptionParams, + RentingParams, + SellingParams, +} from '@iexec/dataprotector'; +``` + +See +Type ↗️ diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataInCollections.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataInCollections.md new file mode 100644 index 00000000..1e77e275 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataInCollections.md @@ -0,0 +1,229 @@ +--- +title: getProtectedDataInCollections +description: + Retrieve protected data from collections in iExec. Each protected data can + belong to only one collection at a time, with results ordered by creation + timestamp in descending order. +--- + +# getProtectedDataInCollections + +Method to get protected data that are in collections. + +A protected data can only be in one collection at a time. + +Results are ordered by `creationTimestamp` desc. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const protectedData = + await dataProtectorSharing.getProtectedDataInCollections(); +``` + +## Parameters + +```ts twoslash +import { type GetProtectedDataInCollectionsParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const oneProtectedData = + await dataProtectorSharing.getProtectedDataInCollections({ + protectedData: '0x123abc...', // [!code focus] + }); +``` + +### collectionId + +**Type:** `number` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const protectedDataByCollection = + await dataProtectorSharing.getProtectedDataInCollections({ + collectionId: 12, // [!code focus] + }); +``` + +### collectionOwner + +**Type:** `AddressOrENS` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const protectedDataByOwner = + await dataProtectorSharing.getProtectedDataInCollections({ + collectionOwner: '0x123...', // [!code focus] + }); +``` + +### createdAfterTimestamp + +**Type:** `number` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const latestProtectedData = + await dataProtectorSharing.getProtectedDataInCollections({ + createdAfterTimestamp: 1707237580, // Feb 6th, 2024 16:39:40 GMT // [!code focus] + }); +``` + +### isRentable + +**Type:** `boolean` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const rentableProtectedData = + await dataProtectorSharing.getProtectedDataInCollections({ + isRentable: true, // [!code focus] + }); +``` + +### isForSale + +**Type:** `boolean` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const protectedDataForSale = + await dataProtectorSharing.getProtectedDataInCollections({ + isForSale: true, // [!code focus] + }); +``` + +### isDistributed + +**Type:** `boolean` + +Used to filter protected data that are either for sale, renting or part of a +subscription. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const protectedDataForSale = + await dataProtectorSharing.getProtectedDataInCollections({ + isDistributed: true, // [!code focus] + }); +``` + +### page + +**Type:** `number` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const protectedData = await dataProtectorSharing.getProtectedDataInCollections({ + collectionId: 12, + page: 3, // [!code focus] + pageSize: 25, +}); +``` + +### pageSize + +**Type:** `number` +**Range:** `[10...1000]` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const protectedData = await dataProtectorSharing.getProtectedDataInCollections({ + collectionId: 12, + page: 3, + pageSize: 25, // [!code focus] +}); +``` + +## Return Value + +```ts twoslash +import type { GetProtectedDataInCollectionsResponse } from '@iexec/dataprotector'; + +// Child types +import type { + ProtectedDataInCollection, + RentingParams, + SellingParams, +} from '@iexec/dataprotector'; +``` + +See +Type ↗️ diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataPricingParams.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataPricingParams.md new file mode 100644 index 00000000..68e53d18 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataPricingParams.md @@ -0,0 +1,65 @@ +--- +title: getProtectedDataPricingParams +description: + Get pricing parameters for renting a specific protected data in iExec. + Retrieve rental price and duration to determine the cost and terms for data + access. +--- + +# getProtectedDataPricingParams + +Method to get all distribution params for a protected data. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const pricingParams = await dataProtectorSharing.getProtectedDataPricingParams({ + protectedData: '0x123abc...', +}); +``` + +## Parameters + +```ts twoslash +import { type GetProtectedDataPricingParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +Address of the protected data you'd like to get the pricing params for. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const pricingParams = await dataProtectorSharing.getProtectedDataPricingParams({ + protectedData: '0x123abc...', // [!code focus] +}); +``` + +## Return Value + +```ts twoslash +import type { GetProtectedDataPricingParamsResponse } from '@iexec/dataprotector'; + +// Child types +import type { SubscriptionParams, RentingParams } from '@iexec/dataprotector'; +``` + +See +Type ↗️ diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getRentals.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getRentals.md new file mode 100644 index 00000000..7fe042ce --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/read/getRentals.md @@ -0,0 +1,103 @@ +--- +title: getRentals +description: + Retrieve all rentals for a specific protected data or user in iExec. Access + detailed rental information based on the protected data address. +--- + +# getRentals + +Method to get all rentals for: + +- a specific protected data +- a specific user + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const protectedDataActiveRentals = await dataProtectorSharing.getRentals({ + protectedData: '0x123abc...', +}); +``` + +## Parameters + +```ts twoslash +import { type GetRentalsParams } from '@iexec/dataprotector'; +``` + +### renterAddress + +**Type:** `AddressOrENS` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const userActiveRentals = await dataProtectorSharing.getRentals({ + renterAddress: '0x246bdf...', // [!code focus] +}); +``` + +### protectedData + +**Type:** `AddressOrENS` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const protectedDataActiveRentals = await dataProtectorSharing.getRentals({ + protectedData: '0x123abc...', // [!code focus] +}); +``` + +### includePastRentals + +**Type:** `boolean` +**Default:** `false` + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const userRentals = await dataProtectorSharing.getRentals({ + renterAddress: '0x246bdf...', + includePastRentals: true, // [!code focus] +}); +``` + +## Return Value + +```ts twoslash +import { type GetRentalsResponse } from '@iexec/dataprotector'; + +// Child types +import type { ProtectedDataRental } from '@iexec/dataprotector'; +``` + +See +Type ↗️ diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/renting.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/renting.md new file mode 100644 index 00000000..c6597a68 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/renting.md @@ -0,0 +1,42 @@ +--- +title: Data Sharing - Renting +description: + Renting options for distributing protected data allow consumers to access data + for a set price and duration. The Data Sharing smart contract ensures renter + protection, guaranteeing access for the agreed term and enforcing rental + terms, even if modified by the owner. +--- + +# Data Sharing - Renting + +Rental agreements are one of the options given for distributing a collection +owner's protected data. A rental agreement has the following attributes: + +**Rental Price** + +The collection owner allows a consumer to pay a set price to access the data. +This is a one-time up-front cost. + +**Rental Duration** + +The collection owner allows the renter access to the data for a set period of +time. + +## Renter Protection + +The Data Sharing smart contract ensures the renter maintains access for the +duration of their rental term. Once the rental term is up, the renter loses +access to the protected data. This assurance is critical to the renting paradigm +to ensure trust between the data owner and the renter. + +## Modifying Rental Terms + +The collection owner has a few options once they list protected data for rent: + +- The owner may remove the rental terms and effectively de-list the protected + data +- The owner may also modify the rental price or duration + +Making either of these chances is effective immediately but only for future +rentals. The Data Sharing smart contract enforces any ongoing rental agreements +until the terms expire. diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/removeProtectedDataFromRenting.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/removeProtectedDataFromRenting.md new file mode 100644 index 00000000..7bc1b923 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/removeProtectedDataFromRenting.md @@ -0,0 +1,66 @@ +--- +title: removeProtectedDataFromRenting +description: + The removeProtectedDataFromRenting method allows the collection owner to + remove a protected data item from being rented. Active rentals will still be + honored until their rental period ends. +--- + +# removeProtectedDataFromRenting + +Method to remove a protected data from renting. + +If there are still active rentals to the protected data, these rentals will be +honored until the end of their rental period. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const notForRentingAnymoreResult = + await dataProtectorSharing.removeProtectedDataFromRenting({ + protectedData: '0x123abc...', + }); +``` + +## Parameters + +```ts twoslash +import { type RemoveProtectedDataFromRentingParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +Address of the protected data you'd like to remove from renting. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const notForRentingAnymoreResult = + await dataProtectorSharing.removeProtectedDataFromRenting({ + protectedData: '0x123abc...', // [!code focus] + }); +``` + +## Return Value + +```ts twoslash +import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; +``` + +See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/rentProtectedData.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/rentProtectedData.md new file mode 100644 index 00000000..d717bfbb --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/rentProtectedData.md @@ -0,0 +1,139 @@ +--- +title: rentProtectedData +description: + The rentProtectedData method allows you to rent a protected data item by + specifying the price and duration. If the parameters don't match the current + listing, the SDK will not submit the transaction to avoid paying gas fees for + a transaction that would revert. +--- + +# rentProtectedData + +Method to rent a protected data. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const rentResult = await dataProtectorSharing.rentProtectedData({ + protectedData: '0x123abc...', + price: 1, // 1 nRLC + duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days +}); +``` + +::: tip + +Technically, `price` and `duration` parameters could be avoided. It is mainly a +protection against front-running "attacks", ie. if the collection owner changes +the price **at the same time** you rent the protected data, you would end up +paying more than expected. Passing the `price` here allows the SDK to ensure +you're paying the right price. If prices don't match, the SDK will throw an +error. + +::: + +## Parameters + +```ts twoslash +import { type RentProtectedDataParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +Address or ENS of the protected data that you'd like rent. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const rentResult = await dataProtectorSharing.rentProtectedData({ + protectedData: '0x123abc...', // [!code focus] + price: 1, // 1 nRLC + duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days +}); +``` + +### price + +**Type:** `number` + +Price of the rental for the protected data that you expect to rent. This +parameter ensures that you will not be front-run by the owner of the protected +data. The unit is in nano RLC (nRLC). + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const rentResult = await dataProtectorSharing.rentProtectedData({ + protectedData: '0x123abc...', + price: 1, // 1 nRLC // [!code focus] + duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days +}); +``` + +::: tip + +To get the renting price of the given protected data, you can use +[getProtectedDataPricingParams](../read/getProtectedDataPricingParams.md). + +::: + +### duration + +**Type:** `number` + +Duration of the rental for the protected data that you expect to rent. This +parameter ensures that you will not be front-run by the owner of the protected +data. The unit is in seconds. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const rentResult = await dataProtectorSharing.rentProtectedData({ + protectedData: '0x123abc...', + price: 1, // 1 nRLC + duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days // [!code focus] +}); +``` + +::: tip + +To get the renting duration of the given protected data, you can use +[getProtectedDataPricingParams](../read/getProtectedDataPricingParams.md). + +::: + +## Return Value + +```ts twoslash +import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; +``` + +See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataRentingParams.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataRentingParams.md new file mode 100644 index 00000000..52cffaad --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataRentingParams.md @@ -0,0 +1,117 @@ +--- +title: setProtectedDataRentingParams +description: + The setProtectedDataRentingParams method allows you to set or update the + renting parameters (price and duration) for a protected data item. If the data + isn't listed for rent yet, it will be set for renting with the provided terms. +--- + +# setProtectedDataRentingParams + +Method to update a protected data renting params. + +If the protected data is not yet available for renting, it will be set for +renting. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setForRentingResult = + await dataProtectorSharing.setProtectedDataRentingParams({ + protectedData: '0x123abc...', + price: 1, // 1 nRLC + duration: 60 * 60 * 24 * 30, // 30 days + }); +``` + +## Parameters + +```ts twoslash +import { type SetProtectedDataRentingParams } from '@iexec/dataprotector'; +``` + +### protectedData + +`AddressOrENS` + +Address of the protected data you'd like to set renting parameters. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setForRentingResult = + await dataProtectorSharing.setProtectedDataRentingParams({ + protectedData: '0x123abc...', // [!code focus] + price: 1, // 1 nRLC + duration: 60 * 60 * 24 * 30, // 30 days + }); +``` + +### price + +`number` + +The price in nano RLC (nRLC) you ask from someone who wants to rent the +protected data. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setForRentingResult = + await dataProtectorSharing.setProtectedDataRentingParams({ + protectedData: '0x123abc...', + price: 1, // 1 nRLC // [!code focus] + duration: 60 * 60 * 24 * 30, // 30 days + }); +``` + +### duration + +`number` + +The duration of the rental in seconds. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setForRentingResult = + await dataProtectorSharing.setProtectedDataRentingParams({ + protectedData: '0x123abc...', + price: 1, // 1 nRLC + duration: 60 * 60 * 24 * 30, // 30 days // [!code focus] + }); +``` + +## Return Value + +```ts twoslash +import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; +``` + +See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataToRenting.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataToRenting.md new file mode 100644 index 00000000..c430fbfb --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataToRenting.md @@ -0,0 +1,118 @@ +--- +title: setProtectedDataToRenting +description: + The setProtectedDataToRenting method allows a protected data item to be listed + for rent. This method sets the price and duration for future rentals. If the + data is already listed for rent, it updates the terms accordingly. +--- + +# setProtectedDataToRenting + +Method to allow a protected data to be rented. + +If you call this method on a protected data that is already set for renting, it +will update the `price` and `duration` parameters, and will apply to future +rentals. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setForRentingResult = + await dataProtectorSharing.setProtectedDataToRenting({ + protectedData: '0x123abc...', + price: 1, // 1 nRLC + duration: 60 * 60 * 24 * 30, // 30 days + }); +``` + +## Parameters + +```ts twoslash +import { type SetProtectedDataToRentingParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +Address of the protected data you'd like to set renting parameters for. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setForRentingResult = + await dataProtectorSharing.setProtectedDataToRenting({ + protectedData: '0x123abc...', // [!code focus] + price: 1, // 1 nRLC + duration: 60 * 60 * 24 * 30, // 30 days + }); +``` + +### price + +**Type:** `number` + +The price in nano RLC (nRLC) you ask from someone who wants to rent the +protected data. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setForRentingResult = + await dataProtectorSharing.setProtectedDataToRenting({ + protectedData: '0x123abc...', + price: 1, // 1 nRLC // [!code focus] + duration: 60 * 60 * 24 * 30, // 30 days + }); +``` + +### duration + +**Type:** `number` + +The duration of the rental in seconds. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setForRentingResult = + await dataProtectorSharing.setProtectedDataToRenting({ + protectedData: '0x123abc...', + price: 1, // 1 nRLC + duration: 60 * 60 * 24 * 30, // 30 days // [!code focus] + }); +``` + +## Return Value + +```ts twoslash +import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; +``` + +See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/selling.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/selling.md new file mode 100644 index 00000000..63f377ec --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/selling.md @@ -0,0 +1,15 @@ +--- +title: Data Sharing - Selling +description: + Learn how to list protected data for sale, transfer ownership permanently + through blockchain transactions, and manage data listing operations. +--- + +# Data Sharing - Selling + +The owner of protected data may list the data for sale. Upon completion of a +sale, ownership of the protected data transfers to the buyer. This is a +permanent operation enforced by the blockchain and executed by the Data Sharing +smart contract. The data owner may de-list the protected data at any point until +a sale is completed. The new owner of the protected data has full rights to +distribute the protected data through any channels they desire. diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/selling/buyProtectedData.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/selling/buyProtectedData.md new file mode 100644 index 00000000..7a8bd0c9 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/selling/buyProtectedData.md @@ -0,0 +1,164 @@ +--- +title: buyProtectedData +description: + Allows a user to purchase protected data that is listed for sale. Upon + successful purchase, the buyer gains full ownership and can distribute or keep + the data as desired. +--- + +# buyProtectedData + +Method to buy a protected data that is for sale. + +"Buying" here means to get ownership of the protected data. + +After buying a protected data, you'll be free to distribute it again at your own +terms, or to keep it for yourself. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.buyProtectedData({ + protectedData: '0x123abc...', + price: 1, +}); +``` + +## Parameters + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +import { type BuyProtectedDataParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +Address of the protected data you'd like to buy. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.buyProtectedData({ + protectedData: '0x123abc...', // [!code focus] + price: 1, +}); +``` + +### price + +**Type:** `number` + +Price of the protected data that you expect to buy. This parameter ensures that +you will not be front-run by the owner of the protected data. The unit is in +nano RLC (nRLC). + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.buyProtectedData({ + protectedData: '0x123abc...', + price: 1, // [!code focus] +}); +``` + +### addToCollectionId + +**Type:** `number` + +Collection ID to which you'd like to transfer the ownership of the protected +data. +The Data Sharing smart contract will still be the technical owner of the +protected data, but you'll fully own it as you own the collection to which it'll +transferred. If you use this param the `addOnlyAppWhitelist` is mandatory +because you must specify which `addOnlyAppWhitelist` will be able to consume +your protected data. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.buyProtectedData({ + protectedData: '0x123abc...', + price: 1, + addToCollectionId: 12, // [!code focus] + addOnlyAppWhitelist: '0xdef456...', +}); +``` + +### addOnlyAppWhitelist + +**Type:** `AddressOrENS` + +Address of the whitelist smart contract that contains applications allowed to +consume the protected data. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.buyProtectedData({ + protectedData: '0x123abc...', + price: 1, + addToCollectionId: 12, + addOnlyAppWhitelist: '0xdef456...', // [!code focus] +}); +``` + +::: tip + +For this `addOnlyAppWhitelist`, you are free to use +`0x256bcd881c33bdf9df952f2a0148f27d439f2e64` that contains apps created for the +purpose of Content Creator usecase-demo. This `addOnlyAppWhitelist` is managed +by iExec. + +For more details on how to create and manage appsWhitelist, see +[Apps whitelist](../../advanced/apps-whitelist). + +::: + +## Return Value + +```ts twoslash +import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; +``` + +See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/selling/removeProtectedDataForSale.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/selling/removeProtectedDataForSale.md new file mode 100644 index 00000000..73b7b71a --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/selling/removeProtectedDataForSale.md @@ -0,0 +1,62 @@ +--- +title: removeProtectedDataForSale +description: + Method to remove a protected data from sale listing, preventing further + purchase transactions. +--- + +# removeProtectedDataForSale + +Method to remove a protected data for sale. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const notForSaleAnymoreResult = + await dataProtectorSharing.removeProtectedDataForSale({ + protectedData: '0x123abc...', + }); +``` + +## Parameters + +```ts twoslash +import { type RemoveProtectedDataForSaleParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +Address of the protected data that you'd like to remove for sale. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const notForSaleAnymoreResult = + await dataProtectorSharing.removeProtectedDataForSale({ + protectedData: '0x123abc...', // [!code focus] + }); +``` + +## Return Value + +```ts twoslash +import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; +``` + +See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/selling/setProtectedDataForSale.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/selling/setProtectedDataForSale.md new file mode 100644 index 00000000..7d0081d9 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/selling/setProtectedDataForSale.md @@ -0,0 +1,89 @@ +--- +title: setProtectedDataForSale +description: + Allows a data owner to list their protected data for sale by setting a price. + Upon successful sale, ownership is transferred to the buyer, who can choose + their own pricing or retain the data for personal use." +--- + +# setProtectedDataForSale + +Method to set a protected data for sale. + +During a successful sale, **the ownership** of the protected data **is +transferred** to the buyer. The buyer will then be able to set their own pricing +parameters, or simply keep the protected data for their own use. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setForSaleResult = await dataProtectorSharing.setProtectedDataForSale({ + protectedData: '0x123abc...', + price: 2, // 2 nRLC +}); +``` + +## Parameters + +```ts twoslash +import { type SetProtectedDataForSaleParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +Address of the protected data that you'd like to set for sale. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setForSaleResult = await dataProtectorSharing.setProtectedDataForSale({ + protectedData: '0x123abc...', // [!code focus] + price: 2, // 2 nRLC +}); +``` + +### price + +**Type:** `number` + +The price in nano RLC (nRLC) you ask from someone who wants to buy the protected +data. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setForSaleResult = await dataProtectorSharing.setProtectedDataForSale({ + protectedData: '0x123abc...', + price: 2, // 2 nRLC // [!code focus] +}); +``` + +## Return Value + +```ts twoslash +import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; +``` + +See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription.md new file mode 100644 index 00000000..206420ff --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription.md @@ -0,0 +1,60 @@ +--- +title: Data Sharing - Subscription +description: + Learn how to manage subscription agreements for protected data on the iExec + platform. Understand subscription pricing and duration, along with options for + modifying terms and protecting subscribers' access. +--- + +# Data Sharing - Subscription + +Subscription agreements are one of the options for distributing a collection +owner's protected data. Similar to the rental distribution terms, a subscription +agreement has the following attributes: + +**Subscription Price** + +The collection owner allows a subscriber to pay a set price to access the data. +The subscription fee is a one-time payment, not a recurring one. Subscriptions +do not auto-renew. + +**Subscription Duration** + +The collection owner allows the subscriber access to the data for a set period +of time. + +## The Subscription Bundle + +Where subscriptions differ from rental distribution terms is that a subscription +may cover more than one protected data. We use the term `subscription bundle` +for this grouping of data. The Data Sharing smart contract supports various +different methods for altering the contents of a subscription bundle. Once a +user purchases a subscription to the bundle, they retain access to all protected +data in the bundle until their subscription term expires. The owner may add new +data to the bundle but may not remove protected data from the bundle as long as +there remains at least one subscriber to it. + +## Subscriber Protection + +The Data Sharing smart contract ensures the subscriber maintains access for the +duration of their subscription term. Once the subscription expires, the consumer +loses access to all protected data. The subscriber has the option to pay the +subscription fee again to retain access. The data owner may update the +subscription fee and duration, but any data included in the subscription remains +available. This assurance is critical to the subscription paradigm to ensure +trust between the data owner and the subscriber. + +## Modifying Subscription Terms + +The collection owner has a few options to manage their subscription bundles: + +- The owner may remove the subscription terms and effectively de-list the + subscription bundle +- The owner may modify the subscription price or duration +- The owner may add additional protected data to the subscription +- The owner may remove protected data from the subscription only if there are no + active subscribers + +Making any of these changes is effective immediately. For any existing +subscribers, updates to the subscription terms take effect upon renewal of the +subscription. diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/removeProtectedDataFromSubscription.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/removeProtectedDataFromSubscription.md new file mode 100644 index 00000000..a093cdb0 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/removeProtectedDataFromSubscription.md @@ -0,0 +1,77 @@ +--- +title: removeProtectedDataFromSubscription +description: + Remove a protected data from your subscription in iExec. Stop providing the + data to current and future subscribers, removing it from subscription access. +--- + +# removeProtectedDataFromSubscription + +Method to remove a protected data from your subscription. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = + await dataProtectorSharing.removeProtectedDataFromSubscription({ + protectedData: '0x123abc...', + }); +``` + +## Pre-conditions + +- You must be the owner of the collection of which the protected data is + currently part of. +- There should be no active subscriptions to this collection. +- The protected data should be part of your subscription. + +## Parameters + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +import { type RemoveProtectedDataFromSubscriptionParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +Address of the protected data you'd like to remove from subscription. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = + await dataProtectorSharing.removeProtectedDataFromSubscription({ + protectedData: '0x123abc...', // [!code focus] + }); +``` + +## Return Value + +```ts twoslash +import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; +``` + +See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/setProtectedDataToSubscription.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/setProtectedDataToSubscription.md new file mode 100644 index 00000000..34673ef0 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/setProtectedDataToSubscription.md @@ -0,0 +1,66 @@ +--- +title: setProtectedDataToSubscription +description: + Add your protected data to a subscription on the iExec platform. Allow active + subscribers to access your data easily by linking it to your subscribers to + access your data easily. +--- + +# setProtectedDataToSubscription + +Method to set a protected data as part of your subscription. + +Any user who has an active subscription to your collection will be able to +consume this protected data. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setToSubscriptionResult = + await dataProtectorSharing.setProtectedDataToSubscription({ + protectedData: '0x123abc...', + }); +``` + +## Parameters + +```ts twoslash +import { type SetProtectedDataToSubscriptionParams } from '@iexec/dataprotector'; +``` + +### protectedData + +**Type:** `AddressOrENS` + +Address of the protected data you'd like to be part of your subscription. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setToSubscriptionResult = + await dataProtectorSharing.setProtectedDataToSubscription({ + protectedData: '0x123abc...', // [!code focus] + }); +``` + +## Return Value + +```ts twoslash +import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; +``` + +See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/setSubscriptionParams.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/setSubscriptionParams.md new file mode 100644 index 00000000..0d360eda --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/setSubscriptionParams.md @@ -0,0 +1,114 @@ +--- +title: setSubscriptionParams +description: + Set subscription parameters for your data collection on the iExec platform. + Define pricing, duration, and manage access to your protected data efficiently + using the Data Sharing smart contract. +--- + +# setSubscriptionParams + +Method to set subscription parameters for a given collection of yours. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setSubscriptionParamsResult = + await dataProtectorSharing.setSubscriptionParams({ + collectionId: 12, + price: 2, // 2 nRLC + duration: 60 * 60 * 24 * 30, // 30 days + }); +``` + +## Parameters + +```ts twoslash +import { type SetSubscriptionParams } from '@iexec/dataprotector'; +``` + +### collectionId + +**Type:** `number` + +Collection ID to which you'd like to set the subscription params. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setSubscriptionParamsResult = + await dataProtectorSharing.setSubscriptionParams({ + collectionId: 12, // [!code focus] + price: 2, // 2 nRLC + duration: 60 * 60 * 24 * 30, // 30 days + }); +``` + +### price + +**Type:** `number` + +The price in nano RLC (nRLC) it's going to cost a subscriber to access your +collection. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setSubscriptionParamsResult = + await dataProtectorSharing.setSubscriptionParams({ + collectionId: 12, + price: 2, // 2 nRLC // [!code focus] + duration: 60 * 60 * 24 * 30, // 30 days + }); +``` + +### duration + +**Type:** `number` + +The duration (in seconds) for a period of subscription. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const setSubscriptionParamsResult = + await dataProtectorSharing.setSubscriptionParams({ + collectionId: 12, + price: 2, // 2 nRLC + duration: 60 * 60 * 24 * 30, // 30 days // [!code focus] + }); +``` + +## Return Value + +```ts twoslash +import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; +``` + +See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/subscribeToCollection.md b/src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/subscribeToCollection.md new file mode 100644 index 00000000..2ea2eac2 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/subscribeToCollection.md @@ -0,0 +1,135 @@ +--- +title: subscribeToCollection +description: + Subscribe to a collection on iExec and gain access to both current and future + protected data. Manage your subscription with a fixed price and duration, with + no automatic renewal, using the Data Sharing smart contract. +--- + +# subscribeToCollection + +Method to subscribe to a collection. + +You subscribe for a certain price and duration. **The subscription will not +automatically renew.** + +With an active subscription, you'll have access to current **and future** +protected data. + +## Usage + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.subscribeToCollection({ + collectionId: 12, + price: 1, // 1 nRLC + duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days +}); +``` + +::: tip + +Technically, `price` and `duration` parameters could be avoided. It is mainly a +protection against front-running "attacks", ie. if the collection owner changes +the price **at the same time** you subscribe to the collection, you would end up +paying more than expected. Passing the `price` here allows the SDK to ensure +you're paying the right price. If prices don't match, the SDK will throw an +error. + +::: + +## Pre-conditions + +- The collection must be available for subscription, ie. the collection owner + must have set a price and a duration. + +## Parameters + +```ts twoslash +import { type SubscribeToCollectionParams } from '@iexec/dataprotector'; +``` + +### collectionId + +**Type:** `number` + +Collection ID to which you'd like to subscribe. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.subscribeToCollection({ + collectionId: 12, // [!code focus] + price: 1, // 1 nRLC + duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days +}); +``` + +### price + +**Type:** `number` + +Price of the rental for the protected data that you expect to rent. This +parameter ensures that you will not be front-run by the owner of the protected +data. The unit is in nano RLC (nRLC). + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.subscribeToCollection({ + collectionId: 12, + price: 1, // 1 nRLC // [!code focus] + duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days +}); +``` + +### duration + +**Type:** `number` + +Duration of the rental for the protected data that you expect to rent. This +parameter ensures that you will not be front-run by the owner of the protected +data. The unit is in seconds. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +const { txHash } = await dataProtectorSharing.subscribeToCollection({ + collectionId: 12, + price: 1, // 1 nRLC + duration: 60 * 60 * 24 * 2, // 172,800 sec = 2 days // [!code focus] +}); +``` + +## Return Value + +```ts twoslash +import { type SuccessWithTransactionHash } from '@iexec/dataprotector'; +``` + +See [`SuccessWithTransactionHash`](../../types.md#successwithtransactionhash) diff --git a/src/documentation/manage-data/dataProtector/getting-started.md b/src/documentation/manage-data/dataProtector/getting-started.md new file mode 100644 index 00000000..68e07cb2 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/getting-started.md @@ -0,0 +1,249 @@ +--- +title: Getting Started +description: DataProtector getting started +--- + +# Getting Started + +[![GitHub package.json version (branch)](https://img.shields.io/badge/npm-2.0.0--beta-green)](https://www.npmjs.com/package/@iexec/dataprotector) + +## Overview + +### Prerequisites + +Before getting started, ensure that you have the following installed on your +system: + +\- [**Node.js**](https://nodejs.org/en/) version 18 or higher + +\- [**NPM**](https://docs.npmjs.com/) (Node.js package manager) + +### Installation + +::: code-group + +```sh [npm] +npm install @iexec/dataprotector +``` + +```sh [yarn] +yarn add @iexec/dataprotector +``` + +```sh [pnpm] +pnpm add @iexec/dataprotector +``` + +```sh [bun] +bun add @iexec/dataprotector +``` + +::: + +**This package is an ESM package. Your project needs to use ESM too.** + [**Read more**](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) + +If you use it with **Webpack**, some polyfills will be needed. You can find a +minimal working project +[here](https://github.com/iExecBlockchainComputing/dataprotector-sdk/tree/115b797cf62dcff0f41e2ba783405d5083d78922/packages/demo/browser-webpack). + +### Instantiate SDK + +Depending on your project's requirements, you can instantiate the SDK using the +umbrella module for full functionality or opt for one of the submodules to +access specific sets of features. + +#### Instantiate Using the Umbrella Module + +For projects requiring the full functionality of the SDK, including both core +and sharing functions. + +::: code-group + +```ts twoslash [Browser] +declare global { + interface Window { + ethereum: any; + } +} +// ---cut--- +import { IExecDataProtector } from '@iexec/dataprotector'; + +const web3Provider = window.ethereum; +// Instantiate using the umbrella module for full functionality +const dataProtector = new IExecDataProtector(web3Provider); + +const dataProtectorCore = dataProtector.core; +const dataProtectorSharing = dataProtector.sharing; +``` + +```ts twoslash [NodeJS] +import { IExecDataProtector, getWeb3Provider } from '@iexec/dataprotector'; + +// Get Web3 provider from a private key +const web3Provider = getWeb3Provider('YOUR_PRIVATE_KEY'); + +// Instantiate using the umbrella module for full functionality +const dataProtector = new IExecDataProtector(web3Provider); + +const dataProtectorCore = dataProtector.core; // access to core methods +const dataProtectorSharing = dataProtector.sharing; // access to methods +``` + +::: + +#### Instantiate Only the `Core` Module + +For projects focusing solely on core data protection functions. + +::: code-group + +```ts twoslash [Browser] +declare global { + interface Window { + ethereum: any; + } +} +// ---cut--- +import { IExecDataProtectorCore } from '@iexec/dataprotector'; + +const web3Provider = window.ethereum; +// Instantiate only the Core module +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +``` + +```ts twoslash [NodeJS] +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +// Get Web3 provider from a private key +const web3Provider = getWeb3Provider('YOUR_PRIVATE_KEY'); + +// Instantiate only the Core module +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +``` + +::: + +#### Instantiate Only the `Sharing` Module + +For projects that need access management functions specifically. + +::: code-group + +```ts twoslash [Browser] +declare global { + interface Window { + ethereum: any; + } +} +// ---cut--- +import { IExecDataProtectorSharing } from '@iexec/dataprotector'; + +const web3Provider = window.ethereum; +// Instantiate only the Sharing module +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +``` + +```ts twoslash [NodeJS] +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +// Get Web3 provider from a private key +const web3Provider = getWeb3Provider('YOUR_PRIVATE_KEY'); + +// Instantiate only the Sharing module +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +``` + +::: + +#### Instantiate Without a Web3 Provider + +For projects that only require read functions, you can instantiate the SDK +without a Web3 provider. + +::: code-group + +```ts twoslash [Singleton Modules] +import { + IExecDataProtectorSharing, + IExecDataProtectorCore, +} from '@iexec/dataprotector'; + +// Instantiate only the Core module for read-only core methods +const dataProtectorCore = new IExecDataProtectorCore(); +// Instantiate only the Sharing module for read-only sharing methods +const dataProtectorSharing = new IExecDataProtectorSharing(); +``` + +```ts twoslash [Umbrella Module] +import { IExecDataProtector } from '@iexec/dataprotector'; + +// Instantiate using the umbrella module for read-only functions +const dataProtector = new IExecDataProtector(); + +// Access to read-only core methods +const dataProtectorCore = dataProtector.core; +// Access to read-only sharing methods +const dataProtectorSharing = dataProtector.sharing; +``` + +::: + +#### Advanced Configuration + +To add optional parameters, see +[advanced configuration](./advanced/advanced-configuration.md). + +::: info + +🧪 While protected data are processed in **TEE** by **intel SGX** technology by +default, `@iexec/dataprotector` can be configured to create and process +protected data in the experimental **intel TDX** environment. + +For more details see: + +- [configure DataProtector TDX](./advanced/advanced-configuration.md#iexecoptions) +- [create TDX protected data](./dataProtectorCore/protectData.md#usage) +- [process TDX protected data](./dataProtectorCore/processProtectedData.md#workerpool) + +⚠️ Keep in mind: TDX mode is experimental and can be subject to instabilities or +discontinuity. + +::: + +## Sandbox + + + + + + + diff --git a/src/documentation/manage-data/dataProtector/migrate-from-v1.md b/src/documentation/manage-data/dataProtector/migrate-from-v1.md new file mode 100644 index 00000000..721fc966 --- /dev/null +++ b/src/documentation/manage-data/dataProtector/migrate-from-v1.md @@ -0,0 +1,146 @@ +--- +title: Migrate from V1 to V2 +description: + Follow this guide to migrate your DataProtector project from v1 to v2beta with + the latest npm package +--- + +# Migrate from V1 to V2 + +::: tip + +This page concerns projects created with DataProtector prior or equal to version +1.0.0 + +::: + +```sh +npm install @iexec/dataprotector@latest +``` + +## Constructor + +The instantiation process has been updated to accommodate **new modular +architecture** introduced in version 2. This change allows for more flexibility +and enables the use of specific functionalities tailored to the developers' +needs. + +When instantiating the IExecDataProtector object, reference the _dataProtector_ +property to use core methods. Newer versions allow to use extended methods using +the _dataProtectorSharing_ property. + +```js +// 1.0.0 and before +const dataProtector = new IExecDataProtector(web3Provider); // [!code --] + +// AFTER 2.0.0 +// with Umbrella Module +const dataProtector = new IExecDataProtector(web3Provider).dataProtector; // [!code ++] +// Or with Core Module +const dataProtector = new IExecDataProtectorCore(web3Provider); // [!code ++] +``` + +## Methods + +Some methods were renamed in order to standardize the SDK, they still provide +the same functionalities as before. + +### Rename `fetchProtectedData` & Add New Filtering Param + +```js +await dataProtector.fetchProtectedData({ // [!code --] +await dataProtector.getProtectedData({ // [!code ++] + owner: '0xa0c15e...', + creationTimestampGte: 1707237580, // Feb 6th, 2024 16:39:40 GMT // [!code ++] +}); +``` + +### Rename `fetchGrantedAccess` + +```js +await dataProtector.fetchGrantedAccess({ // [!code --] +await dataProtector.getGrantedAccess({ // [!code ++] + protectedData: '0x123abc...', + authorizedApp: '0x456def...', + owner: '0xa0c15e...', +}); +``` + +### Removed `protectDataObservable` + +The `protectDataObservable` function has been removed and its functionality has +been integrated into the `protectData` method with a new optional parameter. +This parameter is a callback function that provides status updates during the +data protection process. This change simplifies the API and enhances flexibility +in handling the protection process status updates. + + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const protectedData = await dataProtectorCore.protectData({ + name: 'myEmail', + data: { + email: 'example@gmail.com', + }, + onStatusUpdate: ({ title, isDone }) => { // [!code ++] + console.log(title, isDone); // [!code ++] + }, // [!code ++] +}); +``` + + +### Removed `revokeAllAccessObservable` + +Similarly, the `revokeAllAccessObservable` method has been replaced for +`revokeAllAccess` which does the same thing as `revokeAllAccessObservable` but +includes an optional callback function parameter. This callback allows +developers to receive feedback about the revocation status of the process, +providing more control and better handling of the process. + + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const allAccessRevoked = await dataProtectorCore.revokeAllAccess({ + protectedData: '0x123abc...', + onStatusUpdate: ({ title, isDone }) => { // [!code ++] + console.log(title, isDone); // [!code ++] + }, // [!code ++] +}); + +``` + + +::: tip + +The introduction of callback functions in `protectData` and `revokeAllAccess` +methods allows for real-time status updates, making the data protection and +access revocation processes more interactive and manageable. + +::: + +## Protected Data Schema + +The serialization of the data protected by `protectData()` has been changed to +support a wider range of numbers, and extend the support for processing +protected data in non-JS-based applications. + +The new serialization mechanism is based on the [borsh](https://borsh.io/) +specification. + +Consequently, the data schemas associated with protected data have changed. + +| data type | v1 data schema | v2 data schema | +| --------- | ----------------------------------------------- | ---------------------------------------------- | +| boolean | `"boolean"` | `"bool"` | +| number | `"number"`
restricted to JS safe integers | `"f64"` | +| bigint | not supported | `"i128"`
restricted to 128 bits integers | +| string | `"string"` | `"string"` | +| binary | detected mime type | detected mime type | diff --git a/src/documentation/manage-data/dataProtector/types.md b/src/documentation/manage-data/dataProtector/types.md new file mode 100644 index 00000000..db89799b --- /dev/null +++ b/src/documentation/manage-data/dataProtector/types.md @@ -0,0 +1,124 @@ +--- +title: Types +description: + Complete reference for DataProtector types including GrantedAccess, + ProtectedData, Collection, and other essential data structures. +--- + +# Types + +Types in DataProtector. + +## 🔑 GrantedAccess + +### dataset: `string` + +- Address of the `protectedData` containing user data + +### datasetprice: `string` + +- Price (iun nRLC) to charge the user specified in `requesterrestrict` for each + use of this `protectedData` + +### volume: `string` + +- Number of authorized uses of this `protectedData`; each use decrements this + counter + +### tag: `string` + +- Defines whether a `protectedData` is usable in a TEE environment; `0x00` is + TEE while `0x03` is non-TEE + +### apprestrict: `string` + +- Address of the authorized application; a value of 0x0 indicates any + application may access this data + +### requesterrestrict: `string` + +- Address of the requester authorized to use this `protectedData` in workloads; + a value of 0x0 indicates any requester may use this data + +### workerpoolrestrict: `string` + +- Address of the decentralized infrastructure (worker pool) authorized to + execute the application; a value of 0x0 indicates any worker pool may access + this data + +### salt: `string` + +- Random value to make an order unique and reusable as nonce in a blockchain + transaction + +### sign: `string` + +- Order signature of all the `grantedAccess` fields + +## 🔐 ProtectedData + +### name: `string` + +- Name specified when the protected data was created. This piece of information + is public and visible on-chain. + +### address: `Address` + +- Ethereum address of the protected data. + +### owner: `Address` + +- Ethereum address of the protected data owner. + +### schema: `DataSchema` + +- Data schema for the protected data as defined when the protected data was + created (see [protectedData](./dataProtectorCore/protectData.md)). `schema` + provides a structured representation of the protected data format and + attributes. This field plays a crucial role in understanding and interpreting + the underlying structure of the sensitive information. + +### creationTimestamp: `number` + +- Timestamp specifying when the protected data was created, expressed in + milliseconds since the epoch. This timestamp provides precise information + about the moment of creation and can be used for chronological ordering or + time-based operations. + +### multiaddr: `string` | `undefined` + +- The multiaddr field is the IPFS path of your encrypted data. + +## ❌ RevokedAccess + +### access: [`GrantedAccess`](#-grantedaccess) + +- The granted access that was revoked. + +### txHash: `string` + +- The ID of the transaction that happened on iExec's side chain. You may view + details on the transaction using the + [iExec explorer](https://explorer.iex.ec). + + +_Hash example:_ `0xc9c2d58fc01fe54149b7daf49a0026d4ab1fdd3d10fb7c76350790fff03fe24d` + + +You can read more about he iExec Explorer +[here](https://protocol.docs.iex.ec/for-developers/toolbox/iexec-explorer). + +## ✅ SuccessWithTransactionHash + +### txHash: `string` + +- The hash of the transaction that happened on iExec's side chain. You may view + details on the transaction using the + [iExec explorer](https://explorer.iex.ec). + + +_Hash example:_ `0xc9c2d58fc01fe54149b7daf49a0026d4ab1fdd3d10fb7c76350790fff03fe24d` + + +You can read more about he iExec Explorer +[here](https://protocol.docs.iex.ec/for-developers/toolbox/iexec-explorer). diff --git a/src/documentation/manage-data/guides/handle-schemas-dataset-types.md b/src/documentation/manage-data/guides/handle-schemas-dataset-types.md new file mode 100644 index 00000000..c8d33475 --- /dev/null +++ b/src/documentation/manage-data/guides/handle-schemas-dataset-types.md @@ -0,0 +1,271 @@ +--- +title: Handle Schemas and Dataset Types +description: + Learn how schemas work in DataProtector and how to use them in your iApps +--- + +# 🏷️ Handle Schemas and Dataset Types + +**Schemas are like content labels that describe what's inside your protected +data.** + +They define the structure and types of your data automatically when you protect +it, making it easy for iApps to know what they're working with. + +Think of schemas as **data fingerprints** - they tell iApps "this protected data +contains an email address and a phone number" without revealing the actual +values. + +## How Schemas Work + +When you protect data with DataProtector, the SDK automatically analyzes your +JSON object and generates a schema. **No manual schema definition needed** - +it's all handled for you. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const protectedData = await dataProtectorCore.protectData({ + name: 'User Contact', + data: { + email: 'alice@example.com', + phoneNumber: '+1234567890', + preferences: { + newsletter: true, + notifications: false, + }, + }, +}); + +console.log('✅ Protected data created!'); +console.log('📍 Address:', protectedData.address); +``` + +**🏷️ Generated Schema:** + +```json +{ + "email": "string", + "phoneNumber": "string", + "preferences": { + "newsletter": "bool", + "notifications": "bool" + } +} +``` + +::: info Schema Structure + +The schema automatically maps your data structure to types that iApps can +understand and validate. + +::: + +## Supported Data Types + +The schema automatically detects these types: + +| Type | Description | Example | +| ------------------------------- | -------------- | --------------------- | +| `string` | Text data | `"alice@example.com"` | +| `bool` | Boolean values | `true`, `false` | +| `f64` | Numbers | `42`, `3.14` | +| `i128` | Big integers | `BigInt(123456789)` | +| `application/octet-stream` | Binary data | File contents | +| `image/jpeg`, `image/png`, etc. | Media files | Images, videos | + +::: tip Auto-Detection + +The SDK automatically detects file types based on content. No need to specify +MIME types manually. + +::: + +## Why Schemas Matter + +- **Clarity**: Makes your data easier to understand and reuse +- **Safety**: Ensures iExec apps don’t process the wrong data +- **Structure**: Facilitates structured communication between **front-end and + iApp logic** + +### 🎯 **For iApp Development** + +Schemas let your iApps validate and process data safely: + +```ts twoslash +import { IExecDataProtectorDeserializer } from '@iexec/dataprotector-deserializer'; + +const deserializer = new IExecDataProtectorDeserializer(); +// ---cut--- +// Inside your iApp +const email = await deserializer.getValue('email', 'string'); +const preferences = await deserializer.getValue( + 'preferences.newsletter', + 'bool' +); +``` + +### 🛡️ **For Type Safety** + +Prevents your iApps from processing incompatible data types. + +### 🔍 **For Data Discovery** + +Users can find relevant protected data without seeing the actual content: + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const listProtectedData = await dataProtectorCore.getProtectedData({ + requiredSchema: { + email: 'string', + }, +}); +``` + +## Real Examples + +### Simple User Profile + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const userData = await dataProtectorCore.protectData({ + data: { + email: 'user@example.com', + age: 25, + isSubscribed: true, + }, +}); +``` + +**🏷️ Generated Schema:** + +```json +{ + "email": "string", + "age": "f64", + "isSubscribed": "bool" +} +``` + +### Nested Contact Information + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const contactData = await dataProtectorCore.protectData({ + data: { + personal: { + firstName: 'Alice', + lastName: 'Smith', + }, + contact: { + email: 'alice@example.com', + phone: '+1234567890', + }, + preferences: { + marketing: false, + notifications: true, + }, + }, +}); +``` + +**🏷️ Generated Schema:** + +```json +{ + "personal": { + "firstName": "string", + "lastName": "string" + }, + "contact": { + "email": "string", + "phone": "string" + }, + "preferences": { + "marketing": "bool", + "notifications": "bool" + } +} +``` + +### File Data + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +// Mock function for the example +function createArrayBufferFromFile(file: File): Promise { + return Promise.resolve(new ArrayBuffer(0)); +} + +// Get file from input element +const file = new File([''], 'example.jpg', { type: 'image/jpeg' }); + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const fileBuffer = await createArrayBufferFromFile(file); + +const fileData = await dataProtectorCore.protectData({ + data: { + fileName: file.name, + fileContent: fileBuffer, + uploadDate: Date.now(), + }, +}); +``` + +**🏷️ Schema for file upload:** + +```json +{ + "fileName": "string", + "fileContent": "image/jpeg", + "uploadDate": "f64" +} +``` + +## Using Schemas in iApps + +Once you have protected data with a schema, you'll want to process it inside an +iApp. + +::: warning Type Matching + +**Your iApp and frontend must use the same field names and types.** If they +don't match, you'll get runtime errors when processing the data. + +::: + +→ **Ready to build an iApp?** Check out our detailed +[Inputs and Outputs guide](/documentation/build-iapp/guides/inputs-and-outputs) to learn how +to access schema fields inside your iApp using the deserializer. + +## Next Steps + +**You now understand how schemas work with protected data.** Here's what to +explore next: + +- **Build an iApp**: Check out the + [iApp Generator guide](/documentation/build-iapp/iapp-generator) to create your first data + processor +- **Process data**: Learn about + [processProtectedData](/documentation/manage-data/dataProtector/dataProtectorCore/processProtectedData) + for running computations +- **See it in action**: Try our [Hello World tutorial](/documentation/helloWorld) for + a complete example diff --git a/src/documentation/manage-data/guides/manage-access.md b/src/documentation/manage-data/guides/manage-access.md new file mode 100644 index 00000000..843f0083 --- /dev/null +++ b/src/documentation/manage-data/guides/manage-access.md @@ -0,0 +1,230 @@ +--- +title: Manage Access to your ProtectedData +description: + Learn how to protect data and grant secure access for specific apps and users +--- + +# 🛡️ Manage Access + +**Want to keep your data private while still using it in confidential +applications?** + +DataProtector lets you encrypt data and control access through orders - +specifying who can use it, how many times, and at what price. Protected data is +only accessible in secure enclaves (TEEs) by authorized users and iApps. + +## Installation + +First, install DataProtector in your project: + +::: code-group + +```bash [npm] +npm install @iexec/dataprotector +``` + +```bash [yarn] +yarn add @iexec/dataprotector +``` + +```bash [pnpm] +pnpm add @iexec/dataprotector +``` + +```bash [bun] +bun add @iexec/dataprotector +``` + +::: + +## Protect your Data + +**Here's what happens:** Your data gets encrypted client-side and stored as an +NFT. Only you control who can decrypt and use it. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +const protectedData = await dataProtectorCore.protectData({ + name: 'My Email Contact', + data: { + email: 'alice@example.com', + firstName: 'Alice', + lastName: 'Smith', + }, +}); + +console.log('Protected data address:', protectedData.address); +``` + +### What you Can Protect + +**Data**: Any kind of data you want to keep private and make available for +computations by authorized users and iApps. + +**Supported types**: Common data types like text, numbers, true/false values, +and files. See the [full list here](/documentation/manage-data/dataProtector/types). + +**Storage**: Store your data on IPFS or Arweave. For larger files, you can use +your own IPFS node. + +::: tip + +Need Help ? Check our +[Schema and Dataset Types guide](/documentation/manage-data/guides/handle-schemas-dataset-types) +for detailed formatting instructions. + +::: + +### Debug Mode Option + +Debug mode lets you test with debug iApps during development. As "debug" iApps +don't have the same security standards, we recommend using this mode only during +iApp development. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- + +const protectedData = await dataProtectorCore.protectData({ + data: { + email: 'test@example.com', + }, + allowDebug: true, // [!code focus] +}); +``` + +## Grant Access + +By default, your protected data is private. To let others use it, you need to +grant access to both: + +- An authorized user (who can trigger the processing) +- An authorized iApp (the application that will process the data in the private + environment) + +This ensures that only specific users can use specific applications to process +your data. Here's how to set it up: + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- + +const grantedAccess = await dataProtectorCore.grantAccess({ + protectedData: '0x123abc...', // Your protected data address + authorizedApp: '0x456def...', // iApp that can process the data + authorizedUser: '0x789cba...', // User who can trigger the processing + pricePerAccess: 0, // Cost per use (in nRLC) + numberOfAccess: 10, // Maximum number of uses +}); +``` + +### Parameters + +```ts twoslash +import { type GrantAccessParams } from '@iexec/dataprotector'; +``` + +#### protectedData + +**Type:** `AddressOrENS` + +The ethereum address of the protected data supplied by the user (returned when +you created it). **You must own this data** to grant access. + +#### authorizedApp + +**Type:** `AddressOrENS` + +**What it is**: The iApp address that's allowed to process your data inside the +secure enclave. + +**Why needed**: This ensures only specific, audited applications can access your +data. No random code can touch it. + +**Pro tip**: Use app whitelists for production. Instead of a single app address, +you can specify a whitelist contract that contains multiple app versions. Very +useful for when you need to upgrade your iApps, without losing all the granted +access. + +```ts +// Single app +authorizedApp: 'web3mail.apps.iexec.eth'; + +// Or use a whitelist (recommended for production) +authorizedApp: '0x781482C39CcE25546583EaC4957Fb7Bf04C277D2'; // Web3Mail whitelist +``` + +#### authorizedUser + +**Type:** `AddressOrENS` + +**What it is**: The wallet address of the user that is allowed to process this +data. + +**Why needed**: Even with an authorized app, only specific users can trigger the +computation. This gives you granular control over who uses your data. + +**Don't forget**: Even if you are the owner of the data, you need to authorize +yourself! + +**Special case**: Set to `0x0000000000000000000000000000000000000000` to allow +**any user** to trigger processing (useful for public datasets). + +#### pricePerAccess + +**Type:** `number` +**Default:** `0` + +**Quick explanation**: How much you charge per data usage (in nano RLC - nRLC). + +Set to `0` for free access, or specify a price to monetize your data +automatically. + +**Example**: `pricePerAccess: 1_000_000_000` = 1 RLC per access + +→ **Want to learn more monetization capabilities?** See our detailed +[Monetize Protected Data guide](/documentation/manage-data/guides/monetize-protected-data) + +#### numberOfAccess + +**Quick explanation**: Maximum number of times this authorization can be used. + +::: warning + +Important If someone tries to process your data more times than allowed, they'll +get a "no dataset orders" error. Set this high enough for your use case. + +::: + +**Example values**: + +- `1` - Single use (great for one-time data analysis) +- `100` - Limited campaign (email marketing with usage cap) +- `10000` - Effectively unlimited for most use cases + +## What's Next? + +**You now have protected data with controlled access.** Here are your next +steps: + +- **Process the data**: Use + [processProtectedData](/documentation/manage-data/dataProtector/dataProtectorCore/processProtectedData) + to run computations +- **Manage access**: + [Revoke](/documentation/manage-data/dataProtector/dataProtectorCore/revokeOneAccess) or + [modify permissions](/documentation/manage-data/dataProtector/dataProtectorCore/grantAccess) + anytime +- **Learn data types**: Deep dive into + [schemas and dataset types](/documentation/manage-data/guides/handle-schemas-dataset-types) +- **Monetize data**: Explore + [data monetization strategies](/documentation/manage-data/guides/monetize-protected-data) diff --git a/src/documentation/manage-data/guides/monetize-protected-data.md b/src/documentation/manage-data/guides/monetize-protected-data.md new file mode 100644 index 00000000..b2f0e517 --- /dev/null +++ b/src/documentation/manage-data/guides/monetize-protected-data.md @@ -0,0 +1,256 @@ +--- +title: Monetize Protected Data +description: + Explore different ways to monetize your protected data with signed orders + (usage-based) and time-based access (time-period payments) +--- + +# 💰 Monetize Protected Data + +**Your protected data can generate revenue automatically.** + +iExec offers two fundamental approaches for monetizing your data: + +- **DataProtector Core**: **Signed orders** with pay-per-use counting - specify + exact conditions, users pay for each individual data processing +- **DataProtector Sharing**: **Time-based access** with period payments - users + pay for unlimited access during specific time periods + +## 📝 Signed Orders (DataProtector Core) + +**How it works**: You create and publish signed orders that specify the exact +conditions for accessing your protected data. Each order defines: + +- **Authorized App**: Which iApp can process your data +- **Authorized User**: Who can access your data (specific address or any user) +- **Price per Access**: Cost for each individual use +- **Number of Access**: Maximum times the data can be used +- **Usage Counting**: Each data processing decrements the available access count + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +// ---cut--- +// Create a signed order with specific conditions +const grantedAccess = await dataProtectorCore.grantAccess({ + protectedData: '0x123abc...', // Your data address + authorizedApp: 'email-processor.apps.iexec.eth', // Specific iApp only + authorizedUser: '0x456def...', // Specific user only + pricePerAccess: 5000000000, // 5 RLC per individual use + numberOfAccess: 100, // Maximum 100 total uses +}); + +console.log('Signed order created:', grantedAccess); +``` + +**Perfect for**: + +- Direct partnerships with known clients +- Precise control over access conditions +- Simple setup with specific partners +- Exact counting of data usage + +## 📅 Time-Based Access Implementation (DataProtector Sharing) + +**How it works**: Instead of counting individual uses, DataProtector Sharing +provides **time-based access periods**. Users purchase access for specific +durations (hours, days, months) and can use your protected data unlimited times +during that period. Smart contracts handle all distribution automatically. + +**Key Innovation**: Shift from usage counting to time-based access - users buy +access time, not individual transactions. + +**Access Models Available**: + +- **Rental**: Pay once to access to individual protected data (not all the + collection) +- **Subscription**: Recurring payments for ongoing access +- **Sale**: Permanent ownership transfer + +::: tip See It Live + +The [Content Creator demo](/documentation/use-cases) shows +DataProtector Sharing in action with file monetization. While it uses the +content-delivery iApp for file streaming, the same patterns work for any iApp - +AI models, data processing, oracles, etc. + +::: + +### **Step 1: Create a Collection** + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +// Create a collection to group your data and provide +// a set of protectedData available for the subscription. +const collection = await dataProtectorSharing.createCollection(); +console.log('Collection address:', collection.collectionId); + +// Add your protected data to the collection +await dataProtectorSharing.addToCollection({ + protectedData: '0x123abc...', // Your protected data address + collectionId: collection.collectionId, + addOnlyAppWhitelist: '0x256bcd881c33bdf9df952f2a0148f27d439f2e64', // iExec apps whitelist +}); +``` + +### **Step 2: Choose your Distribution Model** + +**DataProtector Sharing offers three distribution models:** + +#### **Time-Based Rental Model** + +Users pay once for **unlimited access during a specific time period** (not per +use). For a specific protectedData of the created collection : + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +// DATA OWNER: Set up time-based rental terms +await dataProtectorSharing.setProtectedDataToRenting({ + protectedData: '0x123abc...', + price: 5000000000, // 5 RLC for the entire period + duration: 604800, // 7 days of unlimited access +}); + +// CLIENT/SUBSCRIBER: Rent access for the full time period +const rental = await dataProtectorSharing.rentProtectedData({ + protectedData: '0x123abc...', + price: 5000000000, // One payment for full period + duration: 604800, // Unlimited use for 7 days +}); +``` + +**Perfect for**: + +- Time-sensitive datasets (event data, seasonal trends) +- Expensive datasets where users need intensive short-term access +- Content that loses value over time +- Users who need to run multiple analyses during a period + +#### **Time-Based Subscription Model** + +Users pay for **recurring time-based access** to a bundle of data. Unlimited +usage during each subscription period. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); + +// Mock collection for the example +const collection = { collectionId: 123 }; +// ---cut--- +// DATA OWNER: Set time-based subscription parameters +await dataProtectorSharing.setSubscriptionParams({ + collectionId: collection.collectionId, + price: 20000000000, // 20 RLC per 30-day period + duration: 2592000, // 30 days unlimited access +}); + +// DATA OWNER: Add protected data to the time-based subscription bundle +await dataProtectorSharing.setProtectedDataToSubscription({ + protectedData: '0x123abc...', +}); + +// DATA OWNER: Add more data to the same subscription +await dataProtectorSharing.setProtectedDataToSubscription({ + protectedData: '0x456def...', // Additional dataset +}); + +// CLIENT/SUBSCRIBER: Get time-based subscription access +const subscription = await dataProtectorSharing.subscribeToCollection({ + collectionId: collection.collectionId, + price: 20000000000, // Pay for full period + duration: 2592000, // 30 days unlimited usage +}); +``` + +**Perfect for**: + +- Growing datasets (daily market data, news feeds) +- Educational content series +- Research datasets that expand over time +- SaaS-style data access + +#### **Sale Model** + +Transfer permanent ownership of your data to the buyer. + +```ts twoslash +import { + IExecDataProtectorSharing, + getWeb3Provider, +} from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorSharing = new IExecDataProtectorSharing(web3Provider); +// ---cut--- +// DATA OWNER: List data for sale +await dataProtectorSharing.setProtectedDataForSale({ + protectedData: '0x123abc...', + price: 100000000000, // 100 RLC purchase price +}); + +// CLIENT/BUYER: Purchase ownership +const purchase = await dataProtectorSharing.buyProtectedData({ + protectedData: '0x123abc...', + price: 100000000000, +}); +``` + +**Perfect for**: + +- Unique datasets or models +- Digital assets and NFT data +- One-time valuable insights +- When you want to exit data ownership + +## Which Approach to Choose? + +| **Signed Orders (Core)** | **Time-Based Access (Sharing)** | +| ---------------------------- | ------------------------------- | +| Usage counting & pay-per-use | Time periods & unlimited usage | +| Direct signed orders | Smart contract automation | +| High control, simple setup | Medium control, flexible models | + +**Choose Signed Orders when**: You need precise control, direct partnerships, +and usage-based billing. + +**Choose Time-Based Access when**: You want automated distribution, unlimited +usage periods, and flexible pricing models. + +## Next Steps + +**Ready to start monetizing your data?** Here are your next steps: + +- **See it in action**: Try the + [Content Creator demo](/documentation/use-cases) to understand + the full flow +- **Start simple**: Begin with + [pay-per-use via grantAccess](/documentation/manage-data/guides/manage-access) +- **Explore sharing**: Try + [DataProtector Sharing](/documentation/manage-data/dataProtector/dataProtectorSharing) for + automated distribution +- **Build collections**: Learn about + [collection management](/documentation/manage-data/dataProtector/dataProtectorSharing/collection) +- **Set up subscriptions**: Implement + [recurring revenue models](/documentation/manage-data/dataProtector/dataProtectorSharing/subscription) diff --git a/src/documentation/manage-data/what-is-protected-data.md b/src/documentation/manage-data/what-is-protected-data.md new file mode 100644 index 00000000..c768cf78 --- /dev/null +++ b/src/documentation/manage-data/what-is-protected-data.md @@ -0,0 +1,209 @@ +--- +title: What is Protected Data? +description: Understanding iExec's data protection mechanisms +--- + +# ❓ What is Protected Data? + +Protected Data refers to any data encrypted using the **iExec Data Protector +tool**. This end-to-end encryption solution enables users to protect, manage and +monetize their data within the Web3 ecosystem. + +

+

Unlike standard datasets, Protected Data exposes its data types on-chain (for example, indicating that it contains an email address or a photo). This allows anyone to identify entries with the corresponding types.

+
+ +## Key Concepts + +### 👑 Data Governance + +
+ +
+
+
+ +
+ You own your data: The original data never leaves your control +
+
+
+ +
+ Granular permissions: Decide who can access and use your data +
+
+
+ +
+ Revocable access: Grant and revoke permissions at any time +
+
+
+
+ +### 🔒 Privacy-preserving Computation + +
+ +
+
+
+ +
+ Encrypted Processing: Maintains a complete chain of trust. +
+
+
+ +
+ TEE (Trusted Execution Environment): Secure enclaves protect data during processing +
+
+
+ +
+ Secret storage: Secrets stored in a TEE database. +
+
+
+
+ +### 💰 Monetization + +
+ +
+
+
+ +
+ Data marketplaces: Sell access to your protected data +
+
+
+ +
+ Usage tracking: Monitor how your data is being used +
+
+
+ +
+ Fair compensation: Get paid for data usage +
+
+
+
+ +## How it Works + +
+
+
+ 1. +
+ Protect: Encrypt and register your data on the iExec network +
+
+
+ 2. +
+ Share: Authorize specific users and applications to access your data +
+
+
+ 3. +
+ Monitor: Track usage and maintain control +
+
+
+ 4. +
+ Monetize: Earn from your data while keeping it private +
+
+
+ 5. +
+ Compute: Authorized users can compute on your data with the authorized iApps +
+
+
+
+ +## Use Cases + +
+
+
+ 👤 +

Personal Data

+
+
    +
  • Health records
  • +
  • Financial data
  • +
  • Personal preferences
  • +
+
+ +
+
+ 🏢 +

Business Intelligence

+
+
    +
  • Market research
  • +
  • Customer analytics
  • +
  • Proprietary datasets
  • +
+
+ +
+
+ 🤖 +

AI Training

+
+
    +
  • Training models without exposing sensitive data
  • +
  • Federated learning
  • +
  • Privacy-preserving ML
  • +
+
+ +
+
+ 🔬 +

Research

+
+
    +
  • Collaborative research with privacy guarantees
  • +
  • Cross-institutional studies
  • +
  • Clinical trial data
  • +
+
+
+ +
+

Ready to protect your data? Start with our DataProtector guides and learn how to secure your sensitive information while unlocking its value.

+
+ +## Next Steps + +
+
+
+ 📚 + +
+
+ 🚀 +
+ Getting Started: DataProtector Quick Start Guide +
+
+
+
diff --git a/src/documentation/protocol/glossary.md b/src/documentation/protocol/glossary.md new file mode 100644 index 00000000..7d1374e3 --- /dev/null +++ b/src/documentation/protocol/glossary.md @@ -0,0 +1,10 @@ +--- +title: Glossary +description: iExec terms glossary +--- + +# 📖 Glossary + +This page is under development. + + diff --git a/src/documentation/protocol/sdk.md b/src/documentation/protocol/sdk.md new file mode 100644 index 00000000..374e09f8 --- /dev/null +++ b/src/documentation/protocol/sdk.md @@ -0,0 +1,10 @@ +--- +title: iExec SDK +description: iExec SDK +--- + +# 🔧 iExec SDK + +This page is under development. + + diff --git a/src/documentation/protocol/workers.md b/src/documentation/protocol/workers.md new file mode 100644 index 00000000..c9ea366c --- /dev/null +++ b/src/documentation/protocol/workers.md @@ -0,0 +1,12 @@ +--- +title: Workers and Workerpools +description: + Understanding the compute infrastructure of iExec and how workers and + workerpools function +--- + +# ⚙️ Workers & Workerpools + +This page is under development. + + diff --git a/src/documentation/quick-start.md b/src/documentation/quick-start.md new file mode 100644 index 00000000..afad97a4 --- /dev/null +++ b/src/documentation/quick-start.md @@ -0,0 +1,116 @@ +--- +title: Quick Start +description: + Get started quickly with iExec's privacy-preserving technologies and explore + our project starters and interactive sandboxes +--- + +# Quick Start + +Get started quickly with iExec's privacy-preserving technologies. Choose from +our pre-built starters or dive into interactive sandboxes to explore specific +features. + +## Project Starters + +Bootstrap your project with our framework-specific templates and start building +privacy-first applications. + + + + + + + + +## Interactive Sandboxes + +Explore and experiment with iExec features directly in your browser. Perfect for +learning and prototyping. + + + + + + + + + + + + +## Next StepsAfter Exploring our Starters and Sandboxes + +1. **Choose Your Framework**: Start with our Next.js template or wait for + React/Vue options +2. **Experiment**: Try the interactive sandboxes to understand core concepts +3. **Build**: Integrate the features you need into your application +4. **Deploy**: Use our deployment guides for production-ready applications + + diff --git a/src/documentation/rlc.md b/src/documentation/rlc.md new file mode 100644 index 00000000..0de20f57 --- /dev/null +++ b/src/documentation/rlc.md @@ -0,0 +1,134 @@ +--- +title: RLC Token +description: + The RLC token is the cryptocurrency that powers the iExec decentralized + computing ecosystem +--- + +
+ RLC Token Animation +

RLC Token

+

The native cryptocurrency that powers the entire iExec decentralized confidential computing ecosystem

+
+ +**RLC** (**R**un on **L**ots of **C**omputers) serves as the primary medium of +exchange for all interactions within the protocol, enabling users to access +confidential computing services, and rewarding providers for their +contributions. + +## 🎯 What RLC Enables + +RLC is essential for interacting with the iExec protocol and accessing its +decentralized confidential computing services. + +## 💰 Transparent Payment Flow + +When you pay for a task execution with RLC, your payment is automatically and +transparently distributed to all iExec protocol participants: + +
+

🔍 How Your RLC Payment is Distributed

+ + **1. App Provider** - Gets paid for providing the confidential application + + **2. Protected Data Provider** - Gets paid for providing access to protected confidential datasets + + **3. Worker** - Gets paid for running the confidential computation on their decentralized machine +
+ +**💡 Transparent:** Payments are distributed based on pricing and conditions +defined by each provider (iApp, Protected Data, Worker) in their marketplace +orders. + +**🔒 RLC Staking:** To run a task on the protocol (executing an application with +protected data on a decentralized workerpool), you need to **lock RLC** in the +protocol during the task period. In exchange, you receive **sRLC (staked RLC)**. +Once the task is completed, you can recover the RLC that wasn't consumed for the +task payment. + +## 💰 Tokenomics & Ecosystem Metrics + +RLC operates on a utility-driven economic model where demand for confidential +computing services drives token value: + +**Fixed Supply**: RLC has a maximum supply of 87 million tokens, ensuring +scarcity and value preservation. + +**Network Effects**: As more users and providers join the iExec ecosystem, the +demand for RLC increases, driving token value through network effects. + + + +## 🔄 Getting RLC + +You can acquire RLC tokens through several methods: + +
+ + + + + + + +
+ +## 🚀 Ready to Get Started? + +Ready to dive into the iExec ecosystem? Here are the next steps: + +- **[Bridge RLC](./tooling-and-explorers/bridge.md)** - Transfer RLC between + networks +- **[Start Using iExec](./quick-start.md)** - Begin your confidential computing + journey +- **[Earn RLC](/documentation/manage-data/guides/manage-access)** - Become a + provider and monetize your resources + + diff --git a/src/documentation/tooling-and-explorers/blockchain-explorer.md b/src/documentation/tooling-and-explorers/blockchain-explorer.md new file mode 100644 index 00000000..51acadb7 --- /dev/null +++ b/src/documentation/tooling-and-explorers/blockchain-explorer.md @@ -0,0 +1,53 @@ +--- +title: Blockchain Explorers +description: + Explore iExec smart contracts on verified blockchain explorers for both + Arbitrum mainnet and Bellecour network. +--- + +# 🔍 Blockchain Explorers + +Monitor iExec protocol smart contracts on both supported networks through +verified blockchain explorers. All protocol contracts have been verified and are +publicly auditable. + +## 🌐 Supported Networks + +
+ + + +
+ +::: tip 💡 Dev Tip + +Use **Bellecour** for development and testing as it's a gasless blockchain, then +deploy to **Arbitrum** for production workloads. + +::: + + diff --git a/src/documentation/tooling-and-explorers/bridge.md b/src/documentation/tooling-and-explorers/bridge.md new file mode 100644 index 00000000..f79c07d8 --- /dev/null +++ b/src/documentation/tooling-and-explorers/bridge.md @@ -0,0 +1,119 @@ +--- +title: iExec RLC Bridge +description: + Bridge RLC tokens between networks to interact with the iExec protocol. + Transfer RLC to Bellecour (xRLC) and Arbitrum networks using dedicated bridges +--- + +# RLC Bridge + +**RLC** (RLC Token) is the essential cryptocurrency for interacting with the +iExec protocol. Whether you are executing tasks, accessing protected data, or +participating in the iExec decentralized confidential computing network, you +will need RLC tokens on the appropriate network. + +This guide helps you bridge RLC tokens to **Bellecour** (becoming xRLC) and +**Arbitrum** networks using dedicated bridge solutions. + +## 🗂️ Available Bridges + +iExec provides officially supported bridges for seamless token transfer across +networks: + + + + + + + +## 🔄 Bellecour Bridge + +The **Bellecour Bridge** enables seamless transfer of RLC tokens between +Ethereum mainnet and the Bellecour sidechain in both directions. When bridged to +Bellecour, RLC becomes xRLC, the native asset of the Bellecour network. + +### Ethereum <> Bellecour (RLC <> xRLC) + +1. **Connect Wallet**: Visit + [Bellecour Bridge UI](https://bridge-bellecour.iex.ec/) and connect your + wallet +2. **Select Source Network**: The bridge automatically detects your current + network and available tokens (RLC on Ethereum or xRLC on Bellecour) +3. **Choose Destination**: The bridge will show the opposite network as + destination automatically +4. **Select Amount**: Choose the amount of tokens you want to bridge +5. **Confirm Transaction**: Approve the bridge transaction and wait for + confirmation +6. **Receive Tokens**: Your tokens will be available on the destination network + + + +
+

🔄 Bidirectional Bridge

+

The bridge interface automatically detects your wallet's network and available tokens. The process is similar in both directions - simply switch to the appropriate network (source chain) in your wallet and refresh the page to update the bridge direction, then the bridge will handle the conversion between RLC and xRLC seamlessly.

+
+ +## ⚡ Stargate Bridge + +The **Stargate Bridge** powered by LayerZero enables cross-chain transfers of +RLC tokens between Ethereum and Arbitrum mainnet in both directions. + +### Ethereum <> Arbitrum (RLC <> RLC) + +1. **Visit Stargate**: Go to [Stargate UI](https://stargate.finance/bridge) +2. **Connect Wallet**: Connect your wallet to the Stargate interface +3. **Select Networks**: Choose your source network (Ethereum or Arbitrum) and + destination network +4. **Select Token**: Choose RLC as the token to bridge +5. **Enter Amount**: Specify the amount of RLC to transfer +6. **Confirm Transaction**: Approve the bridge transaction and wait for + confirmation + + + +
+

🔄 Bidirectional Bridge

+

The Stargate bridge interface automatically detects your wallet's network and available RLC tokens. The process is similar in both directions - simply select the appropriate source and destination networks to transfer RLC between Ethereum and Arbitrum seamlessly.

+
+ + diff --git a/src/documentation/tooling-and-explorers/builder-dashboard.md b/src/documentation/tooling-and-explorers/builder-dashboard.md new file mode 100644 index 00000000..e5f4687e --- /dev/null +++ b/src/documentation/tooling-and-explorers/builder-dashboard.md @@ -0,0 +1,170 @@ +--- +title: Builder Dashboard +description: + Monitor iExec applications with the powerful Builder Dashboard. Manage your + iApps and your manage vouchers +--- + +# 🏗️ Builder Dashboard + +The **Builder Dashboard** is your comprehensive development hub for iExec +protocol. Monitor voucher usage, track your remaining compute capacity for TEE +iApp runs, and view execution history—all in one place. This powerful interface +streamlines your development workflow and provides deep insights on your +confidential iApps deployed on the protocol. + + + +## 🎯 Key Features + + + + + + + +## 📊 Voucher Consumption & Task History + +The first screen of the Builder Dashboard provides comprehensive voucher +monitoring with detailed task execution history and real-time balance tracking. + + + +### Current Voucher Balance & Management + +
+

💰 Voucher Balance Overview

+
    +
  • Current Balance: View your remaining voucher credits and compute capacity
  • +
  • Claim New Vouchers: Request additional vouchers directly from the dashboard
  • +
  • Expiration Alerts: Get notified before vouchers expire
  • +
+
+ +### Task Execution History + + + + + + + + + + + +## 📱 Confidential iApp Management + +The second screen provides comprehensive management and analytics for your +deployed confidential iApps with detailed statistics and user insights. + + + +### My Confidential iApps Overview + +
+

📱 iApp Portfolio Metrics

+
    +
  • Deployed Applications: Complete list of all your confidential iApps currently deployed
  • +
  • Execution Statistics: See exactly how many times each iApp has been executed
  • +
  • Unique Users: Track total unique users who have run each application
  • +
  • Revenue Insights: Track earnings and profitability per application
  • +
+
+ +### Application Statistics Dashboard + + + + + + + + + + + +::: tip 💡 Dev Tip + +Use the **Builder Dashboard** to monitor your iExec applications and optimize +your development workflow. The comprehensive analytics help you make data-driven +decisions for better application performance. + +::: + + diff --git a/src/documentation/tooling-and-explorers/iexec-explorer.md b/src/documentation/tooling-and-explorers/iexec-explorer.md new file mode 100644 index 00000000..93b981b7 --- /dev/null +++ b/src/documentation/tooling-and-explorers/iexec-explorer.md @@ -0,0 +1,211 @@ +--- +title: iExec Explorer +description: + Explore and analyze transactions on the iExec decentralized computing + platform. Monitor deals, tasks, apps, and datasets in real-time. +--- + +# 🔍 iExec Explorer + +The **iExec Explorer** is your real-time window into the iExec confidential +decentralized computing protocol. Track deals, monitor task execution, and +explore apps and protectedData—all in one powerful dashboard. + + + +## 🎯 What you Can Explore + + + + + + + + + + + + +
+

🏗️ Understanding iExec Architecture

+

Deals are the fundamental orchestration unit - each deal coordinates a set of different stakeholders that share resources and execution parameters to execute a confidential computation task.

+

Each Deal brings together:

+
    +
  • Requester: The entity requesting the computation
  • +
  • iApp: The confidential computation logic
  • +
  • Dataset: The data that will be processed by the iApp
  • +
  • Workerpool: The decentralized network of workers providing computation power
  • +
+
+ +## 💼 Deals & Tasks + + + +> **💡 Understanding Deals**: A deal is a coordinated set of stakeholders that +> brings together all the necessary components (app, dataset, workerpool) to +> execute confidential computation requests. + +### Deal Management Dashboard + +- **Deal Architecture**: Visualize how deals orchestrate multiple stakeholders + with shared resources and parameters +- **Stakeholder Composition**: See which stakeholders belong to each deal and + their execution dependencies +- **Asset Coordination**: Monitor how apps, datasets, and workerpools are + allocated across deal tasks +- **Deal Lifecycle**: Track deals from initiation through task execution to + final completion +- **Pricing Analysis**: View payment distributions across the different assets + of a deal (workerpool, dataset, app) + +### Tasks Overview + + + +Browse and analyze all tasks across the iExec network: + +- **Task Listing**: View all tasks independently of their parent deals +- **Task Status**: Monitor task states across the entire network +- **Performance Analytics**: Analyze execution success rates +- **Historical Analysis**: Access task execution history for trend analysis + +### Task Execution Monitoring + + + + + +> **🔗 Task Assets**: Each task involves four key assets working together: the +> requester, the **iApp** (application logic), the **Dataset** (protected data), +> and the **Workerpool** (execution infrastructure). The Explorer shows how +> these components interact. + +- **Asset Relationships**: Visualize the connections between iApp, dataset, and + workerpool for each task +- **Real-time Progress**: Monitor task status from queued → running → completed + with detailed state transitions +- **Execution Environment**: See which workerpool nodes are processing your + tasks and their TEE capabilities (TDX, SGX) +- **Data Flow**: Track which protected datasets are securely accessed by + authorized iApps +- **Result Management**: + - **Download Results**: Access completed task outputs directly from the + Explorer interface + - **Encryption Status**: Understand if results are encrypted based on + requester specifications + - **Result Verification**: Validate computation correctness and integrity + +## 📱 iApps Listing + + + +Explore the iExec application marketplace: + +- **iApp Directory**: Browse available applications +- **Usage Analytics**: View execution counts, and user adoption +- **Performance Metrics**: Analyze execution success rates + +## 🗄️ Protected Data Listing + + + +Navigate the protected data landscape: + +- **Data Catalog**: Discover available datasets with their metadata and asset + type +- **Usage Patterns**: Analyze dataset popularity and user adoption +- **Schema Validation**: Ensure data structure compatibility with your + applications + +## ⚡ Workerpools + + + +Explore the decentralized computing infrastructure: + +- **Infrastructure Overview**: Monitor the distributed network of computing + nodes +- **Worker Metrics**: Analyze execution success rates +- **Resource Availability**: Check computational resources and capacity +- **Usage Statistics**: Analyze workerpool utilization + + diff --git a/src/documentation/tooling-and-explorers/subgraph-explorer.md b/src/documentation/tooling-and-explorers/subgraph-explorer.md new file mode 100644 index 00000000..7b3768b8 --- /dev/null +++ b/src/documentation/tooling-and-explorers/subgraph-explorer.md @@ -0,0 +1,204 @@ +--- +title: The Graph Explorer +description: + Explore and query blockchain data using The Graph's decentralized indexing + protocol. Access iExec subgraphs on Arbitrum and Bellecour networks. +--- + +# 🔍 The Graph Explorer + +The **iExec protocol** uses **The Graph** as a decentralized protocol for +indexing and querying blockchain data across multiple networks. This powerful +tool allows developers and users to efficiently retrieve and analyze on-chain +data through GraphQL queries. + + + +## 🎯 What is the Graph? + +The Graph is a decentralized protocol for indexing and querying blockchain data. +It enables developers to build and publish open APIs called **subgraphs** that +applications can query using GraphQL. This makes it easy to access blockchain +data without having to run your own indexing infrastructure. + + + +### Key Benefits for iExec + +- **Decentralized Indexing**: Data is indexed by a network of decentralized + indexers +- **Real-time Queries**: Get up-to-date information about deals, tasks, apps, + and protected data +- **GraphQL Interface**: Powerful query language for flexible data retrieval +- **Multi-network Support**: Access data across different blockchain networks + +## 🗂️ Available Subgraphs + +iExec has deployed several subgraphs across different networks to provide +comprehensive data access. Each subgraph indexes specific aspects of the iExec +protocol. + + + + + + + + + + + + +## 🔍 GraphQL Explorer Interface + +The Graph provides an interactive GraphQL explorer that allows you to build and +test queries directly in your browser. This powerful interface makes it easy to +explore the available data and construct complex queries. + + + +### How to Use the GraphQL Explorer + +1. **Navigate to a Subgraph**: Click on any of the subgraph links above to + access the GraphQL explorer +2. **Explore the Schema**: Use the "Schema" tab to browse available entities and + their fields +3. **Build Queries**: Use the "Query" tab to construct and test GraphQL queries +4. **View Results**: Execute queries to see real-time data from the blockchain +5. **Copy Queries**: Use the generated queries in your applications + +### Example Queries + +Here are some example queries you can try in the GraphQL explorer: + +#### Query iApps + +```graphql +query { + apps { + id + name + owner { + id + } + appType + appUri + } +} +``` + +#### Query Protected Data + +```graphql +query { + protectedDatas { + id + name + owner { + id + } + dataType + dataUri + } +} +``` + +#### Query Deals and Tasks + +```graphql +query { + deals { + id + requester { + id + } + app { + name + } + dataset { + name + } + workerpool { + id + } + tasks { + id + status + } + } +} +``` + +
+

💡 Pro Tip

+

Use the GraphQL explorer's auto-completion feature to discover available fields and build complex queries. The schema documentation is always up-to-date with the latest protocol changes.

+
+ + diff --git a/src/documentation/use-cases.md b/src/documentation/use-cases.md new file mode 100644 index 00000000..b370ac5f --- /dev/null +++ b/src/documentation/use-cases.md @@ -0,0 +1,56 @@ +--- +title: Use Cases +description: + Explore real-world applications and demo showcases of iExec's + privacy-preserving technologies in action +--- + +# Use Cases Showcase + +Explore our demo applications showcasing iExec's privacy-preserving technologies +in action. Each use case demonstrates real-world applications of confidential +computing and decentralized data protection. + +
+ + + + + + +
+ + diff --git a/src/documentation/use-iapp/getting-started.md b/src/documentation/use-iapp/getting-started.md new file mode 100644 index 00000000..46cdd0f4 --- /dev/null +++ b/src/documentation/use-iapp/getting-started.md @@ -0,0 +1,150 @@ +--- +title: Getting Started with iApps +description: + Learn the basics of finding and executing iApps on the iExec network +--- + +# 🚀 Getting Started with iApps + +Welcome to the world of secure, privacy-preserving computation! This guide will +walk you through the essential steps to start using iApps on the iExec network. + +## Prerequisites + +Before you begin, make sure you have: + +- A Web3 wallet (MetaMask, WalletConnect, etc.) +- Some RLC tokens for paying computation fees (or access to free vouchers + through learning programs) +- Basic understanding of blockchain transactions + +### 🆓 Use Our Stack for Free! + +Good news! You can start using iApps **completely free** through our learning +programs: + +- **Learn Web3 Program**: Get free access to our entire stack, including + vouchers for iApp executions +- **Free Vouchers**: Pre-funded computation credits provided through learning + initiatives +- **No RLC Required**: Start experimenting and building without any upfront + costs + +### 💰 Getting Started Without RLC + +Don't have RLC tokens yet? No problem! Our learning programs provide everything +you need: + +- **Free Vouchers**: Access to pre-funded computation credits +- **Full Stack Access**: Use all iExec tools and infrastructure at no cost +- **Educational Support**: Learn while you build with our community + +Ready to dive in? Let's get started with finding and executing your first iApp! + +## Step 1: Find Available iApps + +The first step is discovering what iApps are available for your use case. You +can find iApps through several methods: + +1. Visit the [iExec Explorer](https://explorer.iex.ec) +2. Navigate to the "Apps" section +3. Browse available applications by category or search by name +4. Check the app's description, requirements, and pricing + +## Step 2: Understand App Requirements + +Before executing an iApp, understand what it needs: + +- **Protected Data**: Some apps require specific types of protected data +- **Input Parameters**: Check if the app needs command-line arguments +- **Input Files**: Some apps require additional files (URLs to public files) +- **Secrets**: Certain apps need requester secrets for API keys, etc. + +## Step 3: Prepare Your Data + +If the iApp requires protected data: + +1. **Protect Your Data**: Use the + [Data Protector](/documentation/manage-data/dataProtector/dataProtectorCore/protectData) to + encrypt your sensitive information +2. **Grant Access**: Ensure the iApp has permission to access your protected + data using + [grantAccess](/documentation/manage-data/dataProtector/dataProtectorCore/grantAccess) + +## Step 4: Execute the iApp + +### Using the DataProtector SDK + +```typescript +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider(window.ethereum); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); + +// Execute the iApp with protected data +const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', // Your protected data address + app: '0x456def...', // The iApp address + args: 'optional arguments', + maxPrice: 10, // Maximum price in nRLC +}); +``` + +### Using the CLI + +```bash +# Execute an iApp with protected data +iexec app run 0x456def... --dataset 0x123abc... --args "your arguments" +``` + +## Step 5: Monitor and Retrieve Results + +After submitting your task: + +1. **Monitor Progress**: Track your task on the + [iExec Explorer](https://explorer.iex.ec) +2. **Retrieve Results**: Get your results once the task completes + +### Using the DataProtector SDK + +```typescript +// Retrieve results from a completed task +const taskResult = await dataProtectorCore.getResultFromCompletedTask({ + taskId: '0x7ac398...', // Your task ID +}); +``` + +### Using the CLI + +```bash +# Get task result +iexec task show 0x7ac398... + +# Download task result +iexec task show 0x7ac398... --download +``` + +## Step 6: Understand Costs + +iApp execution costs include: + +- **Application Fee**: Paid to the app developer +- **Data Fee**: Paid to the data owner (if using protected data) +- **Workerpool Fee**: Paid to the computation provider +- **Gas Fees**: Blockchain transaction costs (free on Bellecour sidechain) + +## Next Steps + +Now that you understand the basics: + +- Explore our [Guides](/documentation/use-iapp/guides/) for detailed tutorials +- Learn about [Different Ways to Execute](/documentation/use-iapp/guides/different-ways-to-execute) + iApps +- Understand [How to Pay for Executions](/documentation/use-iapp/guides/how-to-pay-executions) +- Discover how to + [Use iApps with Protected Data](/documentation/use-iapp/guides/use-iapp-with-protected-data) + +## Need Help? + +- Check the [iExec Explorer](https://explorer.iex.ec) for app details +- Visit our [Discord community](https://discord.gg/iexec) for support diff --git a/src/documentation/use-iapp/guides/add-inputs-to-execution.md b/src/documentation/use-iapp/guides/add-inputs-to-execution.md new file mode 100644 index 00000000..4de391cb --- /dev/null +++ b/src/documentation/use-iapp/guides/add-inputs-to-execution.md @@ -0,0 +1,799 @@ +--- +title: Add Inputs to iApp Execution +description: + Learn how to provide arguments, files, secrets, and other inputs to iApp + executions +--- + +# 📥 Add Inputs to iApp Execution + +iApps can accept various types of inputs to customize their behavior and provide +necessary data for processing. This guide covers all the different ways to add +inputs to your iApp executions using various iExec tools and SDKs. + +::: tip ENS Addresses **ENS (Ethereum Name Service)** is a naming system for +Ethereum addresses that allows you to use human-readable names instead of long +hexadecimal addresses. For example, instead of using `0x1234567890abcdef...`, +you can use `debug-v8-learn.main.pools.iexec.eth`. + +In the examples below, we use `debug-v8-learn.main.pools.iexec.eth` which is +iExec's official debug workerpool ENS address. This workerpool is specifically +designed for testing and development purposes on the Bellecour testnet. ::: + +## Types of Inputs + +iExec supports several types of inputs for iApp executions: + +1. **Arguments**: Command-line arguments passed to the application +2. **Input Files**: URLs to public files that the app can download +3. **Secrets**: Sensitive data like API keys stored securely +4. **Protected Data**: Encrypted data processed within the TEE + +## Method 1: Adding Command-Line Arguments + +Command-line arguments are passed as a string to the iApp and are visible on the +blockchain. + +### Using SDK Library + +```typescript +import { + IExecConfig, + IExecOrderModule, + IExecOrderbookModule, +} from '@iexec/sdk'; + +// create the configuration +const config = new IExecConfig({ ethProvider: window.ethereum }); + +// instantiate modules sharing the same configuration +const orderModule = IExecOrderModule.fromConfig(config); +const orderbookModule = IExecOrderbookModule.fromConfig(config); + +// Basic arguments +const requestOrder = await orderModule.createRequestOrder({ + app: '0x456def...', + appmaxprice: 10, + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool + params: 'arg1 arg2 arg3', // Command-line arguments + // Other parameters have default values +}); + +// Fetch matching orders from orderbook with filters +const appOrders = await orderbookModule.fetchAppOrderbook({ + app: '0x456def...', // Filter by specific app +}); +const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS +}); + +const taskId = await orderModule.matchOrders({ + requestorder: requestOrder, + apporder: appOrders[0], // Select appropriate app order + workerpoolorder: workerpoolOrders[0], // Select appropriate workerpool order +}); + +// Complex arguments with spaces and quotes +const requestOrder = await orderModule.createRequestOrder({ + app: '0x456def...', + appmaxprice: 10, + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool + params: '--input-file data.csv --output-format json --message "Hello World"', + // Other parameters have default values +}); + +const taskId = await orderModule.matchOrders({ + requestorder: requestOrder, + apporder: appOrders[0], + workerpoolorder: workerpoolOrders[0], +}); + +// With protected data +const requestOrder = await orderModule.createRequestOrder({ + app: '0x456def...', + appmaxprice: 10, + dataset: '0x123abc...', // Protected data address + datasetmaxprice: 5, + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool + params: '--input-path data/input.csv --output-format json --verbose', + // Other parameters have default values +}); + +// Fetch matching orders from orderbook with filters +const appOrders = await orderbookModule.fetchAppOrderbook({ + app: '0x456def...', // Filter by specific app +}); +const datasetOrders = await orderbookModule.fetchDatasetOrderbook({ + dataset: '0x123abc...', // Filter by specific dataset +}); +const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS +}); + +const taskId = await orderModule.matchOrders({ + requestorder: requestOrder, + apporder: appOrders[0], + datasetorder: datasetOrders[0], // Select appropriate dataset order + workerpoolorder: workerpoolOrders[0], +}); +``` + +### Using SDK CLI + +```bash +# Basic arguments +iexec app run 0x456def... --protectedData 0x123abc... --args "arg1 arg2" + +# Complex arguments with spaces +iexec app run 0x456def... --protectedData 0x123abc... --args "--input-file data.csv --output-format json" + +# Arguments with quotes (escape properly) +iexec app run 0x456def... --protectedData 0x123abc... --args "--message \"Hello World\" --config '{\"key\": \"value\"}'" +``` + +### Using DataProtector + +```typescript +import { IExecDataProtectorCore } from '@iexec/dataprotector'; + +const dataProtector = new IExecDataProtectorCore(web3Provider); + +// Process protected data with arguments +const result = await dataProtector.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + args: '--input-path data/input.csv --output-format json --verbose', + maxPrice: 10, +}); +``` + +## Method 2: Adding Input Files + +Input files are URLs to public files that the iApp can download during +execution. + +### Using SDK Library + +```typescript +import { + IExecConfig, + IExecOrderModule, + IExecOrderbookModule, +} from '@iexec/sdk'; + +// create the configuration +const config = new IExecConfig({ ethProvider: window.ethereum }); + +// instantiate modules sharing the same configuration +const orderModule = IExecOrderModule.fromConfig(config); +const orderbookModule = IExecOrderbookModule.fromConfig(config); + +// Single input file +const requestOrder = await orderModule.createRequestOrder({ + app: '0x456def...', + appmaxprice: 10, + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool + // Other parameters have default values +}); + +// Fetch matching orders from orderbook with filters +const appOrders = await orderbookModule.fetchAppOrderbook({ + app: '0x456def...', // Filter by specific app +}); +const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS +}); + +const taskId = await orderModule.matchOrders({ + requestorder: requestOrder, + apporder: appOrders[0], + workerpoolorder: workerpoolOrders[0], + inputFiles: ['https://example.com/config.json'], +}); + +// Multiple input files +const requestOrder = await orderModule.createRequestOrder({ + app: '0x456def...', + appmaxprice: 10, + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool + // Other parameters have default values +}); + +const taskId = await orderModule.matchOrders({ + requestorder: requestOrder, + apporder: appOrders[0], + workerpoolorder: workerpoolOrders[0], + inputFiles: [ + 'https://example.com/config.json', + 'https://example.com/template.html', + 'https://example.com/data.csv', + ], +}); + +// With protected data and input files +const requestOrder = await orderModule.createRequestOrder({ + app: '0x456def...', + appmaxprice: 10, + dataset: '0x123abc...', // Protected data address + datasetmaxprice: 5, + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool + // Other parameters have default values +}); + +// Fetch matching orders from orderbook with filters +const appOrders = await orderbookModule.fetchAppOrderbook({ + app: '0x456def...', // Filter by specific app +}); +const datasetOrders = await orderbookModule.fetchDatasetOrderbook({ + dataset: '0x123abc...', // Filter by specific dataset +}); +const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS +}); + +const taskId = await orderModule.matchOrders({ + requestorder: requestOrder, + apporder: appOrders[0], + datasetorder: datasetOrders[0], + workerpoolorder: workerpoolOrders[0], + inputFiles: [ + 'https://raw.githubusercontent.com/user/repo/main/config.json', + 'https://example.com/public-data.csv', + ], +}); +``` + +### Using SDK CLI + +```bash +# Single input file +iexec app run 0x456def... --protectedData 0x123abc... --inputFiles "https://example.com/config.json" + +# Multiple input files (comma-separated) +iexec app run 0x456def... --protectedData 0x123abc... --inputFiles "https://example.com/config.json,https://example.com/template.html" + +# Multiple input files (space-separated) +iexec app run 0x456def... --protectedData 0x123abc... --inputFiles "https://example.com/config.json" --inputFiles "https://example.com/template.html" +``` + +### Using DataProtector + +```typescript +import { IExecDataProtectorCore } from '@iexec/dataprotector'; + +const dataProtector = new IExecDataProtectorCore(web3Provider); + +// Process protected data with input files +const result = await dataProtector.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + inputFiles: [ + 'https://raw.githubusercontent.com/user/repo/main/config.json', + 'https://example.com/public-data.csv', + ], + maxPrice: 10, +}); +``` + +## Method 3: Adding Secrets + +Secrets are sensitive data like API keys, passwords, or tokens that are stored +securely and made available to the iApp as environment variables. + +### Using SDK Library + +```typescript +import { + IExecConfig, + IExecOrderModule, + IExecOrderbookModule, + IExecSecretsModule, +} from '@iexec/sdk'; + +// create the configuration +const config = new IExecConfig({ ethProvider: window.ethereum }); + +// instantiate modules sharing the same configuration +const orderModule = IExecOrderModule.fromConfig(config); +const orderbookModule = IExecOrderbookModule.fromConfig(config); +const secretsModule = IExecSecretsModule.fromConfig(config); + +// Basic secrets +const requestOrder = await orderModule.createRequestOrder({ + app: '0x456def...', + appmaxprice: 10, + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool + // Other parameters have default values +}); + +// Fetch matching orders from orderbook with filters +const appOrders = await orderbookModule.fetchAppOrderbook({ + app: '0x456def...', // Filter by specific app +}); +const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS +}); + +const taskId = await orderModule.matchOrders({ + requestorder: requestOrder, + apporder: appOrders[0], + workerpoolorder: workerpoolOrders[0], + secrets: { + 1: 'api-key-12345', + 2: 'database-password', + }, +}); + +// Multiple secrets +const requestOrder = await orderModule.createRequestOrder({ + app: '0x456def...', + appmaxprice: 10, + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool + // Other parameters have default values +}); + +const taskId = await orderModule.matchOrders({ + requestorder: requestOrder, + apporder: appOrders[0], + workerpoolorder: workerpoolOrders[0], + secrets: { + 1: 'openai-api-key', + 2: 'database-connection-string', + 3: 'jwt-secret', + 4: 'encryption-key', + }, +}); + +// With protected data and secrets +const requestOrder = await orderModule.createRequestOrder({ + app: '0x456def...', + appmaxprice: 10, + dataset: '0x123abc...', // Protected data address + datasetmaxprice: 5, + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool + // Other parameters have default values +}); + +// Fetch matching orders from orderbook with filters +const appOrders = await orderbookModule.fetchAppOrderbook({ + app: '0x456def...', // Filter by specific app +}); +const datasetOrders = await orderbookModule.fetchDatasetOrderbook({ + dataset: '0x123abc...', // Filter by specific dataset +}); +const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS +}); + +const taskId = await orderModule.matchOrders({ + requestorder: requestOrder, + apporder: appOrders[0], + datasetorder: datasetOrders[0], + workerpoolorder: workerpoolOrders[0], + secrets: { + 1: 'openai-api-key', + 2: 'database-password', + }, +}); +``` + +### Using SDK CLI + +```bash +# Note: CLI doesn't support secrets directly for security reasons +# Use the SDK for secret management + +# Alternative: Use environment variables (less secure) +export IEXEC_SECRET_1="api-key-12345" +export IEXEC_SECRET_2="database-password" +iexec app run 0x456def... --protectedData 0x123abc... +``` + +### Using DataProtector + +```typescript +import { IExecDataProtectorCore } from '@iexec/dataprotector'; + +const dataProtector = new IExecDataProtectorCore(web3Provider); + +// Process protected data with secrets +const result = await dataProtector.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + secrets: { + 1: 'openai-api-key', + 2: 'database-password', + }, + maxPrice: 10, +}); +``` + +## Method 4: Specifying File Paths in Protected Data + +When working with protected data that contains multiple files, you can specify +which file to process. + +### Using SDK Library + +```typescript +import { + IExecConfig, + IExecOrderModule, + IExecOrderbookModule, + IExecResultModule, +} from '@iexec/sdk'; + +// create the configuration +const config = new IExecConfig({ ethProvider: window.ethereum }); + +// instantiate modules sharing the same configuration +const orderModule = IExecOrderModule.fromConfig(config); +const orderbookModule = IExecOrderbookModule.fromConfig(config); +const resultModule = IExecResultModule.fromConfig(config); + +// Basic path specification (with protected data) +const requestOrder = await orderModule.createRequestOrder({ + app: '0x456def...', + appmaxprice: 10, + dataset: '0x123abc...', // Protected data address + datasetmaxprice: 5, + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool + // Other parameters have default values +}); + +// Fetch matching orders from orderbook with filters +const appOrders = await orderbookModule.fetchAppOrderbook({ + app: '0x456def...', // Filter by specific app +}); +const datasetOrders = await orderbookModule.fetchDatasetOrderbook({ + dataset: '0x123abc...', // Filter by specific dataset +}); +const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS +}); + +const taskId = await orderModule.matchOrders({ + requestorder: requestOrder, + apporder: appOrders[0], + datasetorder: datasetOrders[0], + workerpoolorder: workerpoolOrders[0], + path: 'my-content', // Extract specific file from protected data +}); + +// Complex path examples (with protected data) +const requestOrder = await orderModule.createRequestOrder({ + app: '0x456def...', + appmaxprice: 10, + dataset: '0x123abc...', // Protected data address + datasetmaxprice: 5, + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool + // Other parameters have default values +}); + +const taskId = await orderModule.matchOrders({ + requestorder: requestOrder, + apporder: appOrders[0], + datasetorder: datasetOrders[0], + workerpoolorder: workerpoolOrders[0], + path: 'data/processed/results.json', +}); + +// Retrieve specific file from task result +const taskResult = await resultModule.getTaskResult({ + taskId: '0x7ac398...', + path: 'computed.json', // Extract specific file from result +}); +``` + +### Using SDK CLI + +```bash +# Basic path specification +iexec app run 0x456def... --protectedData 0x123abc... --path "my-content" + +# Complex path +iexec app run 0x456def... --protectedData 0x123abc... --path "data/processed/results.json" + +# Retrieve result with specific path +iexec task show 0x7ac398... --path "computed.json" +``` + +### Using DataProtector + +```typescript +import { IExecDataProtectorCore } from '@iexec/dataprotector'; + +const dataProtector = new IExecDataProtectorCore(web3Provider); + +// Process protected data with specific path +const result = await dataProtector.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + path: 'data/input.csv', + maxPrice: 10, +}); + +// Get result with specific path +const taskResult = await dataProtector.getResultFromCompletedTask({ + taskId: '0x7ac398...', + path: 'output/analysis.json', +}); +``` + +## Method 5: Combining Multiple Input Types + +You can combine different types of inputs for complex executions. + +### Using SDK Library + +```typescript +import { + IExecConfig, + IExecOrderModule, + IExecOrderbookModule, + IExecSecretsModule, +} from '@iexec/sdk'; + +// create the configuration +const config = new IExecConfig({ ethProvider: window.ethereum }); + +// instantiate modules sharing the same configuration +const orderModule = IExecOrderModule.fromConfig(config); +const orderbookModule = IExecOrderbookModule.fromConfig(config); +const secretsModule = IExecSecretsModule.fromConfig(config); + +// Complete example with all input types +const requestOrder = await orderModule.createRequestOrder({ + app: '0x456def...', + appmaxprice: 10, + dataset: '0x123abc...', // Protected data address + datasetmaxprice: 5, + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool + params: '--mode production --output-format json', + // Other parameters have default values +}); + +// Fetch matching orders from orderbook with filters +const appOrders = await orderbookModule.fetchAppOrderbook({ + app: '0x456def...', // Filter by specific app +}); +const datasetOrders = await orderbookModule.fetchDatasetOrderbook({ + dataset: '0x123abc...', // Filter by specific dataset +}); +const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS +}); + +const taskId = await orderModule.matchOrders({ + requestorder: requestOrder, + apporder: appOrders[0], + datasetorder: datasetOrders[0], + workerpoolorder: workerpoolOrders[0], + inputFiles: [ + 'https://example.com/config.json', + 'https://example.com/template.html', + ], + secrets: { + 1: 'api-key-12345', + 2: 'database-password', + }, + path: 'data/input.csv', +}); +``` + +### Using SDK CLI + +```bash +# Combine multiple input types +iexec app run 0x456def... \ + --protectedData 0x123abc... \ + --args "--mode production --output-format json" \ + --inputFiles "https://example.com/config.json,https://example.com/template.html" \ + --path "data/input.csv" \ + --maxPrice 10 + +# Note: Secrets must be handled via SDK due to CLI limitations +``` + +### Using DataProtector + +```typescript +import { IExecDataProtectorCore } from '@iexec/dataprotector'; + +const dataProtector = new IExecDataProtectorCore(web3Provider); + +// Process protected data with multiple input types +const result = await dataProtector.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + args: '--mode production --output-format json', + inputFiles: [ + 'https://example.com/config.json', + 'https://example.com/template.html', + ], + secrets: { + 1: 'api-key-12345', + 2: 'database-password', + }, + path: 'data/input.csv', + maxPrice: 10, +}); +``` + +## How Secrets Work in iApps + +Inside the iApp, secrets are available as environment variables: + +```python +# Python iApp example +import os + +api_key = os.environ.get('IEXEC_SECRET_1') # 'api-key-12345' +db_password = os.environ.get('IEXEC_SECRET_2') # 'database-password' +``` + +```javascript +// JavaScript iApp example +const apiKey = process.env.IEXEC_SECRET_1; // 'api-key-12345' +const dbPassword = process.env.IEXEC_SECRET_2; // 'database-password' +``` + +## Input Validation and Error Handling + +### Validate Inputs Before Execution + +```typescript +const validateInputs = (params) => { + const errors = []; + + // Validate arguments + if (params.args && params.args.length > 1000) { + errors.push('Arguments too long (max 1000 characters)'); + } + + // Validate input files + if (params.inputFiles) { + params.inputFiles.forEach((url, index) => { + if (!url.startsWith('https://')) { + errors.push(`Input file ${index + 1} must use HTTPS`); + } + }); + } + + // Validate secrets + if (params.secrets) { + Object.keys(params.secrets).forEach((key) => { + if (!/^\d+$/.test(key)) { + errors.push('Secret keys must be numeric'); + } + }); + } + + return errors; +}; + +// Use validation +const params = { + protectedData: '0x123abc...', + app: '0x456def...', + args: '--test', + inputFiles: ['https://example.com/file.json'], + secrets: { 1: 'secret' }, + maxPrice: 10, +}; + +const errors = validateInputs(params); +if (errors.length > 0) { + console.error('Validation errors:', errors); + return; +} + +const result = await dataProtectorCore.processProtectedData(params); +``` + +### Handle Input-Related Errors + +```typescript +try { + const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + args: '--invalid-option', + maxPrice: 10, + }); +} catch (error) { + if (error.message.includes('argument')) { + console.log('Invalid arguments provided'); + } else if (error.message.includes('file')) { + console.log('Input file not accessible'); + } else if (error.message.includes('secret')) { + console.log('Secret configuration error'); + } else { + console.log('Execution failed:', error.message); + } +} +``` + +## Best Practices + +### 1. Use Appropriate Input Types + +```typescript +// ✅ Use secrets for sensitive data +const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + secrets: { 1: 'api-key' }, // Sensitive data + args: '--mode production', // Non-sensitive configuration + maxPrice: 10, +}); +``` + +### 2. Validate URLs and Files + +```typescript +// ✅ Ensure input files are accessible +const inputFiles = [ + 'https://raw.githubusercontent.com/user/repo/main/config.json', + 'https://example.com/public-data.csv', +]; + +// Test file accessibility before execution +const testFileAccess = async (url) => { + try { + const response = await fetch(url, { method: 'HEAD' }); + return response.ok; + } catch { + return false; + } +}; + +const accessibleFiles = await Promise.all( + inputFiles.map(async (url) => ({ + url, + accessible: await testFileAccess(url), + })) +); + +const validFiles = accessibleFiles + .filter((file) => file.accessible) + .map((file) => file.url); +``` + +### 3. Use Descriptive Arguments + +```typescript +// ✅ Clear, descriptive arguments +const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + args: '--input-format csv --output-format json --verbose --log-level info', + maxPrice: 10, +}); +``` + +### 4. Organize Secrets Logically + +```typescript +// ✅ Logical secret numbering +const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + secrets: { + 1: 'primary-api-key', // Main API key + 2: 'backup-api-key', // Backup API key + 3: 'database-password', // Database credentials + 4: 'encryption-key', // Encryption key + }, + maxPrice: 10, +}); +``` + +## Next Steps + +Now that you understand how to add inputs to iApp executions: + +- Learn about + [Using iApps with Protected Data](./use-iapp-with-protected-data.md) +- Explore [Different Ways to Execute](./different-ways-to-execute.md) iApps +- Check out our [How to Pay for Executions](./how-to-pay-executions.md) guide diff --git a/src/documentation/use-iapp/guides/different-ways-to-execute.md b/src/documentation/use-iapp/guides/different-ways-to-execute.md new file mode 100644 index 00000000..c705b1c4 --- /dev/null +++ b/src/documentation/use-iapp/guides/different-ways-to-execute.md @@ -0,0 +1,120 @@ +--- +title: Different Ways to Execute iApps +description: + Learn about various methods for executing iApps on the iExec network +--- + +# ⚡ Different Ways to Execute iApps + +There are multiple ways to execute iApps on the iExec network. This guide covers +the basic execution methods. For advanced features like protected data, +arguments, and input files, see the dedicated guides. + +::: tip ENS Addresses **ENS (Ethereum Name Service)** is a naming system for +Ethereum addresses that allows you to use human-readable names instead of long +hexadecimal addresses. For example, instead of using `0x1234567890abcdef...`, +you can use `debug-v8-learn.main.pools.iexec.eth`. + +In the examples below, we use `debug-v8-learn.main.pools.iexec.eth` which is +iExec's official debug workerpool ENS address. This workerpool is specifically +designed for testing and development purposes on the Bellecour testnet. ::: + +## Method 1: Using the iExec SDK Library + +The iExec SDK provides a modular JavaScript interface for executing iApps. + +```typescript +import { + IExecConfig, + IExecOrderModule, + IExecOrderbookModule, +} from '@iexec/sdk'; + +// create the configuration +const config = new IExecConfig({ ethProvider: window.ethereum }); + +// instantiate modules sharing the same configuration +const orderModule = IExecOrderModule.fromConfig(config); +const orderbookModule = IExecOrderbookModule.fromConfig(config); + +// Create a request order +const requestOrder = await orderModule.createRequestOrder({ + app: '0x456def...', // The iApp address + appmaxprice: 10, // Maximum price in nRLC + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool + // Other parameters have default values +}); + +// Fetch matching orders from orderbook with filters +const appOrders = await orderbookModule.fetchAppOrderbook({ + app: '0x456def...', // Filter by specific app +}); +const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS +}); + +// Execute the task +const taskId = await orderModule.matchOrders({ + requestorder: requestOrder, + apporder: appOrders[0], + workerpoolorder: workerpoolOrders[0], +}); +``` + +## Method 2: Using the iExec CLI + +The iExec CLI is perfect for quick executions and automation scripts. + +### Installation + +```bash +npm install -g @iexec/iexec +``` + +### Basic CLI Execution + +```bash +# Execute an iApp +iexec app run 0x456def... + +# Set maximum price +iexec app run 0x456def... --maxPrice 10 +``` + +## Method 3: Using the iApp Generator CLI + +The iApp Generator CLI provides a streamlined way to execute iApps, especially +for developers who have built their own iApps. + +### Installation + +```bash +npm install -g @iexec/iapp +``` + +### Basic Execution + +```bash +# Execute a deployed iApp +iapp run 0x456def... +``` + +### Testing Before Execution + +```bash +# Test the iApp locally first +iapp test +``` + +## When to Use Each Method + +- **iExec Library**: For JavaScript applications and web3 integration +- **iExec CLI**: For quick testing and automation scripts +- **iApp Generator CLI**: For developers who have built their own iApps + +## Next Steps + +- Learn how to + [use iApps with protected data](./use-iapp-with-protected-data.md) +- Discover how to [add inputs to execution](./add-inputs-to-execution.md) +- Understand [how to pay for executions](./how-to-pay-executions.md) diff --git a/src/documentation/use-iapp/guides/find-iapps.md b/src/documentation/use-iapp/guides/find-iapps.md new file mode 100644 index 00000000..3b1d9f37 --- /dev/null +++ b/src/documentation/use-iapp/guides/find-iapps.md @@ -0,0 +1,128 @@ +--- +title: Find iApps to Use +description: Discover and explore available iApps on the iExec network +--- + +# 🔍 Find iApps to Use + +Discovering the right iApp for your needs is the first step toward secure, +privacy-preserving computation. Here are several ways to find and explore +available iApps on the iExec network. + +## Using the iExec Explorer + +The [iExec Explorer](https://explorer.iex.ec) is the primary tool for +discovering iApps. + +### Step-by-Step Discovery + +1. **Visit the Explorer**: Go to [explorer.iex.ec](https://explorer.iex.ec) +2. **Navigate to Apps**: Click on the "Apps" section in the navigation +3. **Search by Name**: Use the search function to find specific applications +4. **View Details**: Click on any app to see detailed information + +### What to Look For + +When evaluating an iApp, check these key details: + +- **Description**: What does the app do? +- **Requirements**: What type of data or inputs does it need? +- **Pricing**: How much does execution cost? +- **Developer**: Who created the app? +- **Usage Statistics**: How often is it used? +- **Orders Availability**: Are there active orders available for execution? + +## Using the iExec CLI + +For developers who prefer command-line tools, the iExec CLI provides +programmatic access to app discovery. + +### Installation + +```bash +npm install -g @iexec/iexec +``` + +### Basic Commands + +```bash +# Note: Use the iExec Explorer for discovering applications +# The CLI is primarily for executing applications, not listing them + +# Get detailed information about a specific app (if you have the address) +iexec app show +``` + +## Popular iApp Categories + +### Communication Apps + +- **Web3Mail**: Send encrypted emails without seeing recipient addresses +- **Web3Telegram**: Send Telegram messages with protected contact lists + +### Data Processing + +- **Content Creator**: Process and deliver content using protected data +- **Data Analytics**: Analyze sensitive datasets without exposing raw data + +### Oracle Services + +- **Oracle Factory**: Create and manage price oracles +- **Data Feeds**: Update external systems with private data + +### AI and Machine Learning + +- **Model Training**: Train AI models on protected datasets +- **Inference Services**: Run AI inference on sensitive data + +## Evaluating App Quality + +Before using an iApp, consider these factors: + +### Reliability + +- **Usage History**: How many times has the app been executed? +- **Success Rate**: What percentage of executions succeed? +- **Developer Reputation**: Is the developer known and trusted? + +### Cost Efficiency + +- **Execution Cost**: Is the price reasonable for the computation? +- **Competitive Pricing**: How does it compare to similar apps? + +## Community Resources + +### Discord Community + +Join the [iExec Discord](https://discord.gg/iexec) to: + +- Ask questions about specific apps +- Get recommendations from other users +- Learn about new apps and updates + +### GitHub Repositories + +Many iApp developers share their code on GitHub: + +- Review the source code +- Check for recent updates +- Report issues or suggest improvements + +### Documentation + +- **App-Specific Docs**: Check if the developer provides detailed documentation +- **Community Guides**: Look for tutorials and guides from the community + +## Next Steps + +Once you've found an iApp that meets your needs: + +1. **Read the Documentation**: Understand how to use the app properly +2. **Check Requirements**: Ensure you have the necessary data and permissions +3. **Test with Small Data**: Start with a small test before processing large + datasets +4. **Monitor Execution**: Track your first few executions to ensure everything + works as expected + +Ready to execute your first iApp? Check out our +[Getting Started Guide](../getting-started.md) for the next steps! diff --git a/src/documentation/use-iapp/guides/how-to-pay-executions.md b/src/documentation/use-iapp/guides/how-to-pay-executions.md new file mode 100644 index 00000000..8d1b75ca --- /dev/null +++ b/src/documentation/use-iapp/guides/how-to-pay-executions.md @@ -0,0 +1,396 @@ +--- +title: How to Pay for iApp Executions +description: + Learn about payment methods, pricing, and cost management for iApp executions +--- + +# 💰 How to Pay for iApp Executions + +Understanding how to pay for iApp executions is crucial for using the iExec +network effectively. This guide covers all payment methods, pricing structures, +and cost management strategies. + +## Payment Methods Overview + +iExec supports multiple payment methods for executing iApps: + +1. **RLC Tokens**: Direct payment using RLC (Request Compute Language) tokens +2. **Vouchers**: Pre-funded vouchers for simplified payment +3. **Mixed Payment**: Combination of RLC and vouchers + +## Method 1: Paying with RLC Tokens + +RLC tokens are the native currency of the iExec network. You need to have RLC in +your wallet to pay for executions. + +### Setting Up RLC Payment + +#### Step 1: Get RLC Tokens + +You can obtain RLC tokens from various exchanges: + +- **Centralized Exchanges**: Binance, Coinbase, Kraken +- **Decentralized Exchanges**: Uniswap, SushiSwap +- **Direct Purchase**: Through iExec's official channels + +#### Step 2: Transfer to iExec Sidechain + +RLC tokens need to be on the iExec sidechain (Bellecour) for payments: + +```typescript +import { IExecConfig, IExecAccountModule } from '@iexec/sdk'; + +// create the configuration +const config = new IExecConfig({ ethProvider: window.ethereum }); + +// instantiate account module +const accountModule = IExecAccountModule.fromConfig(config); + +// Check your balance +const balance = await accountModule.show(); +console.log('Current balance:', balance); + +// Deposit RLC to the sidechain +await accountModule.deposit(100); // Deposit 100 RLC +``` + +#### Step 3: Execute with RLC Payment + +```typescript +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider(window.ethereum); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); + +// Execute with RLC payment (default) +const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + maxPrice: 10, // Maximum price in nRLC + useVoucher: false, // Explicitly use RLC payment +}); +``` + +### Using CLI with RLC + +```bash +# Execute with RLC payment +iexec app run 0x456def... --protectedData 0x123abc... --maxPrice 10 + +# Check your balance +iexec account show + +# Deposit RLC +iexec account deposit 100 +``` + +## Method 2: Paying with Vouchers + +Vouchers are pre-funded payment instruments that simplify the payment process +and can be shared with others. + +### Understanding Vouchers + +Vouchers are ERC-20 tokens that represent pre-funded computation credits on the +iExec network. They offer several advantages: + +- **Simplified Payment**: No need to manage RLC transfers +- **Sharing**: Can be shared with team members or users +- **Budget Control**: Set spending limits +- **Automated Top-up**: Can be configured to automatically refill + +### Using Vouchers for Payment + +#### Basic Voucher Usage + +```typescript +const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + maxPrice: 10, + useVoucher: true, // Use voucher for payment +}); +``` + +::: tip + +If your voucher doesn't have enough xRLC to cover the deal, the SDK will +automatically get the required amount to your iExec account. Ensure that your +voucher is authorized to access your iExec account and that your account has +sufficient funds for this transfer to proceed. + +::: + +#### Using Someone Else's Voucher + +```typescript +const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + maxPrice: 10, + useVoucher: true, + voucherOwner: '0x5714eB...', // Voucher owner's address +}); +``` + +::: warning + +Make sure the voucher's owner has authorized you to use it. This parameter must +be used in combination with `useVoucher: true`. + +::: + +#### CLI with Vouchers + +```bash +# Use your own voucher +iexec app run 0x456def... --protectedData 0x123abc... --useVoucher + +# Use someone else's voucher +iexec app run 0x456def... --protectedData 0x123abc... --useVoucher --voucherOwner 0x5714eB... +``` + +## Understanding Pricing + +### Cost Components + +iApp execution costs consist of several components: + +1. **Application Fee**: Paid to the app developer +2. **Data Fee**: Paid to the data owner (if using protected data) +3. **Workerpool Fee**: Paid to the computation provider +4. **Gas Fees**: Blockchain transaction costs + +### Setting Maximum Prices + +You can control costs by setting maximum prices for each component: + +```typescript +const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + dataMaxPrice: 5, // Maximum amount (in nRLC) to pay the protected data owner + appMaxPrice: 3, // Maximum amount (in nRLC) to pay the iApp provider + workerpoolMaxPrice: 2, // Maximum amount (in nRLC) to pay the workerpool provider +}); +``` + +::: info + +All price parameters are in **nRLC** (nano RLC). The default value for all price +parameters is `0`, which means no maximum price limit is set. + +::: + +## Cost Management Strategies + +### 1. Monitor Your Balance + +Regularly check your RLC balance and voucher status: + +```typescript +// Check RLC balance +const balance = await iexec.account.show(); +console.log('RLC Balance:', balance); + +// Check voucher balance (if applicable) +const voucherBalance = await iexec.voucher.show(); +console.log('Voucher Balance:', voucherBalance); +``` + +### 2. Set Reasonable Price Limits + +Always set maximum prices to avoid unexpected costs: + +```typescript +// Good practice: Set explicit price limits +const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + dataMaxPrice: 5, // Maximum for data access + appMaxPrice: 3, // Maximum for app usage + workerpoolMaxPrice: 2, // Maximum for computation +}); +``` + +### 3. Use Vouchers for Regular Usage + +For frequent executions, consider using vouchers: + +```typescript +// Use vouchers for regular operations +const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + maxPrice: 10, + useVoucher: true, // Simplify payment process +}); +``` + +### 4. Batch Operations + +Group multiple executions to optimize costs: + +```typescript +// Execute multiple tasks efficiently +const tasks = [ + { protectedData: '0x123abc...', app: '0x456def...' }, + { protectedData: '0x789def...', app: '0x456def...' }, +]; + +const results = await Promise.all( + tasks.map((task) => + dataProtectorCore.processProtectedData({ + ...task, + dataMaxPrice: 5, + appMaxPrice: 3, + workerpoolMaxPrice: 2, + useVoucher: true, + }) + ) +); +``` + +## Payment Troubleshooting + +### Insufficient Balance + +If you encounter insufficient balance errors: + +```typescript +try { + const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + maxPrice: 10, + }); +} catch (error) { + if (error.message.includes('insufficient balance')) { + console.log('Please add more RLC to your account'); + // Check balance and deposit more if needed + const balance = await iexec.account.show(); + console.log('Current balance:', balance); + } +} +``` + +### Voucher Authorization Issues + +If voucher payment fails: + +```typescript +try { + const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + maxPrice: 10, + useVoucher: true, + voucherOwner: '0x5714eB...', + }); +} catch (error) { + if (error.message.includes('voucher')) { + console.log('Voucher authorization failed. Check voucher permissions.'); + } +} +``` + +### Price Too High Errors + +If execution fails due to price constraints: + +```typescript +try { + const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + dataMaxPrice: 2, // Low price limit + appMaxPrice: 1, + workerpoolMaxPrice: 1, + }); +} catch (error) { + if (error.message.includes('price')) { + console.log('Increase price limits or wait for lower prices'); + // Retry with higher prices + const retryResult = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + dataMaxPrice: 8, // Higher price limit + appMaxPrice: 5, + workerpoolMaxPrice: 3, + }); + } +} +``` + +## Best Practices + +### 1. Always Set Price Limits + +```typescript +// Never execute without price limits +const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + dataMaxPrice: 5, // Always set price limits + appMaxPrice: 3, + workerpoolMaxPrice: 2, +}); +``` + +### 2. Use Vouchers for Production + +```typescript +// Use vouchers for production applications +const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + maxPrice: 10, + useVoucher: true, // More reliable for production +}); +``` + +### 3. Monitor Costs Regularly + +```typescript +// Check costs before and after execution +const balanceBefore = await iexec.account.show(); +const result = await dataProtectorCore.processProtectedData({ + protectedData: '0x123abc...', + app: '0x456def...', + dataMaxPrice: 5, + appMaxPrice: 3, + workerpoolMaxPrice: 2, +}); +const balanceAfter = await iexec.account.show(); +console.log('Cost:', balanceBefore - balanceAfter); +``` + +### 4. Handle Payment Errors Gracefully + +```typescript +const executeWithPaymentRetry = async (params, maxRetries = 3) => { + for (let i = 0; i < maxRetries; i++) { + try { + return await dataProtectorCore.processProtectedData(params); + } catch (error) { + if (error.message.includes('insufficient balance')) { + console.log('Insufficient balance, please add more RLC'); + break; + } + if (i === maxRetries - 1) throw error; + console.log(`Payment retry ${i + 1}/${maxRetries}`); + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + } +}; +``` + +## Next Steps + +Now that you understand payment methods: + +- Learn about [Adding Inputs to Execution](./add-inputs-to-execution.md) +- Explore [Using iApps with Protected Data](./use-iapp-with-protected-data.md) +- Check out our + [Pricing Considerations](../how-to-pay/pricing-considerations.md) for detailed + cost analysis diff --git a/src/documentation/use-iapp/guides/index.md b/src/documentation/use-iapp/guides/index.md new file mode 100644 index 00000000..14d9c83a --- /dev/null +++ b/src/documentation/use-iapp/guides/index.md @@ -0,0 +1,39 @@ +--- +title: Use iApp Guides +description: Complete guide collection for using iApps on the iExec network +--- + +# 🚀 Use iApp Guides + +Welcome to the complete collection of guides for using iApps on the iExec network. These guides will help you find, execute, and interact with privacy-preserving applications. + +## 🔍 Finding & Using iApps + +- **[Find iApps](/documentation/use-iapp/guides/find-iapps)** - Discover available applications +- **[Different Ways to Execute](/documentation/use-iapp/guides/different-ways-to-execute)** - Multiple execution methods +- **[Use iApp with Protected Data](/documentation/use-iapp/guides/use-iapp-with-protected-data)** - Secure data processing + +## 💰 Payment & Execution + +- **[How to Pay for Executions](/documentation/use-iapp/guides/how-to-pay-executions)** - Payment methods and costs +- **[Add Inputs to Execution](/documentation/use-iapp/guides/add-inputs-to-execution)** - Provide data and parameters + +## 📧 Communication Apps + +- **[Web3Mail](/documentation/use-iapp/web3mail)** - Send encrypted emails +- **[Web3Telegram](/documentation/use-iapp/web3telegram)** - Send encrypted Telegram messages + +## 🆓 Getting Started + +- **[Getting Started](/documentation/use-iapp/getting-started)** - Essential steps for beginners +- **[How to Pay for Web3Mail](/documentation/use-iapp/how-to-pay/how-to-pay-for-web3mail)** - Free email service +- **[How to Pay for Web3Telegram](/documentation/use-iapp/how-to-pay/how-to-pay-for-web3telegram)** - Free messaging service + +## 📚 What's Next? + +After mastering these guides, explore: + +- **[Data Protector](/documentation/manage-data/dataProtector)** - Protect your own data +- **[Build iApps](/documentation/build-iapp/what-is-iapp)** - Create your own applications + +Ready to start using iApps? Begin with the [Getting Started](/documentation/use-iapp/getting-started) guide! diff --git a/src/documentation/use-iapp/guides/use-iapp-with-protected-data.md b/src/documentation/use-iapp/guides/use-iapp-with-protected-data.md new file mode 100644 index 00000000..7351d9a0 --- /dev/null +++ b/src/documentation/use-iapp/guides/use-iapp-with-protected-data.md @@ -0,0 +1,473 @@ +--- +title: Use iApps with Protected Data +description: + Learn how to securely process protected data using iApps on the iExec network +--- + +# 🔒 Use iApps with Protected Data + +Protected data is the cornerstone of privacy-preserving computation on iExec. +This guide shows you how to use iApps with protected data, from granting access +to processing and retrieving results. + +## Understanding Protected Data and iApps + +Protected data is encrypted information that can only be processed by authorized +iApps within Trusted Execution Environments (TEEs). The data remains +confidential throughout the entire computation process. + +### The Workflow + +1. **Protect Your Data**: Encrypt sensitive information using the Data Protector +2. **Grant Access**: Authorize specific iApps to process your data +3. **Execute iApp**: Run the iApp with your protected data +4. **Retrieve Results**: Get the computation results while data remains private + +## Step 1: Protect Your Data + +Before using an iApp, you need to protect your sensitive data. + +### Basic Data Protection + +```typescript +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; + +const web3Provider = getWeb3Provider(window.ethereum); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); + +// Protect your data +const { address: protectedDataAddress } = await dataProtectorCore.protectData({ + name: 'My Sensitive Data', + data: { + email: 'user@example.com', + apiKey: 'secret-api-key-12345', + preferences: { + theme: 'dark', + notifications: true, + }, + }, +}); +``` + +### Protecting Different Data Types + +```typescript +// Protect contact list for email applications +const { address: contactListAddress } = await dataProtectorCore.protectData({ + name: 'Email Contact List', + data: { + contacts: { + '0x123abc...': 'john@example.com', + '0x456def...': 'jane@example.com', + '0x789ghi...': 'bob@example.com', + }, + }, +}); + +// Protect trading data for oracle applications +const { address: tradingDataAddress } = await dataProtectorCore.protectData({ + name: 'Trading History', + data: { + trades: { + '2024-01-01': { price: 50000, volume: 100 }, + '2024-01-02': { price: 51000, volume: 150 }, + '2024-01-03': { price: 49000, volume: 200 }, + }, + }, +}); + +// Protect financial data for payment applications +const { address: paymentDataAddress } = await dataProtectorCore.protectData({ + name: 'Payment Information', + data: { + bankAccount: '1234567890', + routingNumber: '987654321', + accountHolder: 'John Doe', + }, +}); +``` + +## Step 2: Grant Access to iApps + +iApps need explicit authorization to access your protected data. + +### Grant Access to a Specific iApp + +```typescript +// Grant access to an iApp +const grantedAccess = await dataProtectorCore.grantAccess({ + protectedData: protectedDataAddress, + authorizedApp: '0x456def...', // The iApp address + authorizedUser: '0x789abc...', // Your wallet address + pricePerAccess: 5, // Price per access in nRLC + numberOfAccess: 10, // Number of allowed accesses +}); +``` + +### Check Granted Access + +```typescript +// Check what access you've granted +const grantedAccessList = await dataProtectorCore.getGrantedAccess({ + protectedData: protectedDataAddress, + authorizedApp: '0x456def...', + authorizedUser: '0x789abc...', +}); + +console.log('Granted access:', grantedAccessList); +``` + +## Step 3: Execute iApp with Protected Data + +Once access is granted, you can execute the iApp with your protected data. + +### Using DataProtector + +```typescript +// Execute iApp with protected data +const result = await dataProtectorCore.processProtectedData({ + protectedData: protectedDataAddress, + app: '0x456def...', // The iApp address + maxPrice: 10, // Maximum price in nRLC +}); +``` + +### Using SDK Library + +```typescript +import { + IExecConfig, + IExecOrderModule, + IExecOrderbookModule, +} from '@iexec/sdk'; + +// create the configuration +const config = new IExecConfig({ ethProvider: window.ethereum }); + +// instantiate modules sharing the same configuration +const orderModule = IExecOrderModule.fromConfig(config); +const orderbookModule = IExecOrderbookModule.fromConfig(config); + +// Create a request order with protected data +const requestOrder = await orderModule.createRequestOrder({ + app: '0x456def...', // The iApp address + appmaxprice: 10, // Maximum price in nRLC + dataset: protectedDataAddress, // Protected data address + datasetmaxprice: 5, // Maximum price for dataset access + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool + // Other parameters have default values +}); + +// Fetch matching orders from orderbook with filters +const appOrders = await orderbookModule.fetchAppOrderbook({ + app: '0x456def...', // Filter by specific app +}); +const datasetOrders = await orderbookModule.fetchDatasetOrderbook({ + dataset: protectedDataAddress, // Filter by specific dataset +}); +const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ + workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS +}); + +// Execute the task +const taskId = await orderModule.matchOrders({ + requestorder: requestOrder, + apporder: appOrders[0], + datasetorder: datasetOrders[0], + workerpoolorder: workerpoolOrders[0], +}); +``` + +### Using CLI with Protected Data + +```bash +# Execute with protected data +iexec app run 0x456def... --dataset 0x123abc... --maxPrice 10 +``` + +## Step 4: Retrieve Results + +After execution completes, retrieve the results from the task. + +### Using DataProtector + +```typescript +// Get the task ID from the execution result +const taskId = result.taskId; + +// Retrieve the result +const taskResult = await dataProtectorCore.getResultFromCompletedTask({ + taskId: taskId, +}); + +// Retrieve a specific file from the result +const taskResult = await dataProtectorCore.getResultFromCompletedTask({ + taskId: taskId, + path: 'computed.json', // Extract specific file +}); +``` + +### Using SDK Library + +```typescript +import { IExecConfig, IExecResultModule } from '@iexec/sdk'; + +// create the configuration +const config = new IExecConfig({ ethProvider: window.ethereum }); + +// instantiate result module +const resultModule = IExecResultModule.fromConfig(config); + +// Get the task ID from the execution result +const taskId = result.taskId; // or taskId from SDK library execution + +// Retrieve the result +const taskResult = await resultModule.getTaskResult({ + taskId: taskId, +}); + +// Retrieve a specific file from the result +const specificFile = await resultModule.getTaskResult({ + taskId: taskId, + path: 'computed.json', // Extract specific file +}); +``` + +### Using CLI + +```bash +# Get the task ID from the execution result +TASK_ID="0x7ac398..." + +# Retrieve the result +iexec task show $TASK_ID + +# Retrieve a specific file from the result +iexec task show $TASK_ID --path "computed.json" +``` + +## Real-World Examples + +### Example 1: Data Analysis System + +```typescript +// 1. Protect sensitive dataset +const { address: datasetAddress } = await dataProtectorCore.protectData({ + name: 'Customer Analytics Data', + data: { + customers: [ + { id: 1, purchases: 1500, category: 'premium' }, + { id: 2, purchases: 800, category: 'standard' }, + { id: 3, purchases: 2200, category: 'premium' }, + ], + }, +}); + +// 2. Grant access to analytics iApp +await dataProtectorCore.grantAccess({ + protectedData: datasetAddress, + authorizedApp: '0xanalytics...', // Analytics iApp address + authorizedUser: '0x789abc...', + pricePerAccess: 3, + numberOfAccess: 50, +}); + +// 3. Execute data analysis +const analysisResult = await dataProtectorCore.processProtectedData({ + protectedData: datasetAddress, + app: '0xanalytics...', + args: '--analyze-customer-segments --output-format json', + maxPrice: 10, +}); +``` + +### Example 2: Oracle Price Update + +```typescript +// 1. Protect trading data +const { address: tradingDataAddress } = await dataProtectorCore.protectData({ + name: 'Trading Data', + data: { + trades: { + '2024-01-01': { price: 50000, volume: 100 }, + '2024-01-02': { price: 51000, volume: 150 }, + }, + }, +}); + +// 2. Grant access to oracle iApp +await dataProtectorCore.grantAccess({ + protectedData: tradingDataAddress, + authorizedApp: '0xoracle...', // Oracle iApp address + authorizedUser: '0x789abc...', + pricePerAccess: 5, + numberOfAccess: 100, +}); + +// 3. Execute oracle update +const oracleResult = await dataProtectorCore.processProtectedData({ + protectedData: tradingDataAddress, + app: '0xoracle...', + args: '--update-price-feed --asset ETH', + maxPrice: 10, +}); +``` + +### Example 3: Automated Payment Processing + +```typescript +// 1. Protect payment data +const { address: paymentDataAddress } = await dataProtectorCore.protectData({ + name: 'Payment Data', + data: { + bankAccount: '1234567890', + routingNumber: '987654321', + accountHolder: 'John Doe', + monthlyAmount: 1000, + }, +}); + +// 2. Grant access to payment iApp +await dataProtectorCore.grantAccess({ + protectedData: paymentDataAddress, + authorizedApp: '0xpayment...', // Payment iApp address + authorizedUser: '0x789abc...', + pricePerAccess: 2, + numberOfAccess: 12, // Monthly payments +}); + +// 3. Execute payment processing +const paymentResult = await dataProtectorCore.processProtectedData({ + protectedData: paymentDataAddress, + app: '0xpayment...', + args: '--process-monthly-payment', + secrets: { + 1: 'bank-api-key', + }, + maxPrice: 8, +}); +``` + +## Advanced Patterns + +### Pattern 1: Batch Processing + +```typescript +// Process multiple protected datasets +const datasets = [ + { address: '0x123abc...', name: 'Dataset 1' }, + { address: '0x456def...', name: 'Dataset 2' }, + { address: '0x789ghi...', name: 'Dataset 3' }, +]; + +const batchResults = await Promise.all( + datasets.map((dataset) => + dataProtectorCore.processProtectedData({ + protectedData: dataset.address, + app: '0x456def...', + args: `--dataset-name ${dataset.name}`, + maxPrice: 10, + }) + ) +); +``` + +### Pattern 2: Result Processing Pipeline + +```typescript +// Process results and use them for further computation +const initialResult = await dataProtectorCore.processProtectedData({ + protectedData: protectedDataAddress, + app: '0x456def...', + maxPrice: 10, +}); + +// Get the result +const taskResult = await dataProtectorCore.getResultFromCompletedTask({ + taskId: initialResult.taskId, +}); + +// Use the result for further processing +const processedData = await processResult(taskResult); + +// Protect the processed data +const { address: newProtectedDataAddress } = + await dataProtectorCore.protectData({ + name: 'Processed Data', + data: processedData, + }); +``` + +## Best Practices + +### 1. Always Grant Access Before Execution + +```typescript +// ✅ Good: Grant access first +await dataProtectorCore.grantAccess({ + protectedData: protectedDataAddress, + authorizedApp: '0x456def...', + authorizedUser: '0x789abc...', + pricePerAccess: 5, + numberOfAccess: 10, +}); + +const result = await dataProtectorCore.processProtectedData({ + protectedData: protectedDataAddress, + app: '0x456def...', + maxPrice: 10, +}); +``` + +### 2. Monitor Access Usage + +```typescript +// Check access usage regularly +const grantedAccess = await dataProtectorCore.getGrantedAccess({ + protectedData: protectedDataAddress, + authorizedApp: '0x456def...', + authorizedUser: '0x789abc...', +}); + +console.log('Remaining access:', grantedAccess.remainingAccess); +console.log('Used access:', grantedAccess.usedAccess); +``` + +### 3. Use Appropriate Price Limits + +```typescript +// Set reasonable price limits +const result = await dataProtectorCore.processProtectedData({ + protectedData: protectedDataAddress, + app: '0x456def...', + maxPrice: 10, // Set appropriate limit +}); +``` + +### 4. Handle Results Properly + +```typescript +// Store task ID and retrieve results later +const result = await dataProtectorCore.processProtectedData({ + protectedData: protectedDataAddress, + app: '0x456def...', + maxPrice: 10, +}); + +// Store task ID for later retrieval +const taskId = result.taskId; +localStorage.setItem('lastTaskId', taskId); + +// Later, retrieve the result +const taskResult = await dataProtectorCore.getResultFromCompletedTask({ + taskId: taskId, +}); +``` + +## Next Steps + +Now that you understand how to use iApps with protected data: + +- Learn about [Different Ways to Execute](./different-ways-to-execute.md) iApps +- Explore [How to Pay for Executions](./how-to-pay-executions.md) +- Check out our [Add Inputs to Execution](./add-inputs-to-execution.md) guide diff --git a/src/documentation/use-iapp/how-to-pay/how-to-pay-for-web3mail.md b/src/documentation/use-iapp/how-to-pay/how-to-pay-for-web3mail.md new file mode 100644 index 00000000..087dcb2f --- /dev/null +++ b/src/documentation/use-iapp/how-to-pay/how-to-pay-for-web3mail.md @@ -0,0 +1,165 @@ +--- +title: How to Pay for Web3Mail +description: + Learn how to pay for Web3Mail's confidential computing power using vouchers + and xLC for secure, blockchain-based email communication. +--- + +# How to Pay for Web3Mail + +[Web3Mail](../web3mail) dev tool offers secure, blockchain-based communication +by encrypting emails and protecting user privacy. + +The `sendEmail` function uses confidential computing power to encrypt and send +messages, ensuring secure and decentralized email exchanges. + +This guide explains how to pay for Web3Mail's computing power using **vouchers** +and **xRLC**, detailing the steps for each method. + +## Using Vouchers for Web3Mail + +### Step 1: Obtain a Voucher + +- **Acquire Vouchers**: Obtain vouchers through the + [iExec Builder Dashboard](https://builder.iex.ec/). Note that the number of + Web3Mail executions and the expiration time of each voucher is restricted + based on its validity period. Refer to + [pricing documentation](https://www.iex.ec/voucher) for more information. +- **Support**: For specific limitations related to your voucher, please contact + iExec Support. + +### Step 2: Use the Builder Dashboard + + + Builder dashboard screenshot + + +The iExec Builder Dashboard is a comprehensive tool for managing vouchers and +resources, providing an intuitive interface for: + +- **Claiming Vouchers**: Builders can claim vouchers during the BUILD and EARN + stages. +- **Top-Up Vouchers**: Future updates will allow direct top-ups via the + dashboard. Currently, builders are redirected to Discord. +- **Checking Voucher Balance**: Track your voucher balance and usage history. + +🧙🏼 [Go here](https://builder.iex.ec/) + +### Step 3: Grant Allowance (If Necessary) + +Use `iexec.account.approve(voucherAddress)` to authorize the voucher smart +contract to debit your account if the voucher balance is insufficient. This +ensures that if the voucher alone doesn't cover the execution cost, the +remaining balance is automatically deducted from your account. + +For additional information on using xRLC for fallback payment in Web3Mail, refer +to the **Using xRLC with Web3Mail** section. + +### Step 4: Execute Web3Mail's SendEmail Function + +When using a voucher for payment, set the `useVoucher` parameter to `true`: + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const sendEmail = await web3mail.sendEmail({ + protectedData: '0x123abc...', + emailSubject: 'My email subject', + emailContent: 'My email content', + useVoucher: true, // [!code focus] +}); +``` + +## Using xRLC for Web3Mail + +If you choose to use xRLC to cover the computational cost of Web3Mail (or if you +need to cover data access costs such as retrieving the recipient's email +address), follow these steps: + +### Install the iExec SDK + +To manage RLC tokens, developers must use the iExec SDK, which offers all the +necessary tools for interacting with the iExec platform. This includes +depositing, withdrawing, and checking balances of RLC and xRLC + +- In your JS/TS project npm install iexec +- Instantiate the iExec SDK (see the + [doc](https://github.com/iExecBlockchainComputing/iexec-sdk/blob/master/docs/README.md#quick-start)) + +```javascript +import { IExec } from 'iexec'; +// connect injected provider +const iexec = new IExec({ ethProvider: window.ethereum }); +``` + +### Purchase RLC + +Obtain RLC tokens from a supported cryptocurrency exchange. + +### Convert to xRLC + +Use the iExec Bridge to convert your RLC into xRLC for use on iExec's sidechain. +The bridging operation follows the lock & mint / burn & unlock principle. When +sending tokens from Mainnet to Bellecour, the bridge locks the initial amount on +the source chain and mints the equivalent on the destination chain. When going +in the other direction, it burns tokens on the Sidechain and unlocks the same +amount on Mainnet. The bridged asset is called xRLC on Bellecour. + +Users can send tokens from the Ethereum Mainnet to the iExec Sidechain or +vice-versa using the Account Manager +([iExec Explorer](https://explorer.iex.ec/bellecour) or +[POA Bridge UI](https://bridge-bellecour.iex.ec/)) available across all iExec +products. + +### Deposit xRLC + +Deposit the xRLC into your iExec account using the command: + +```javascript +iexec.account.deposit(xRLC_amount); +``` + +This converts xRLC into sRLC, used as proof of funds for task execution. + +### Check sRLC Balance + +Use the command below to check your balance: + +```javascript +iexec.account.show(); +``` + +### Execute sendEmail + +Set the `useVoucher` parameter to `false` when using Web3Mail's sendEmail +function to pay with xRLC: + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const sendEmail = await web3mail.sendEmail({ + protectedData: '0x123abc...', + emailSubject: 'My email subject', + emailContent: 'My email content', + useVoucher: false, // [!code focus] +}); +``` + +### Withdraw sRLC (If Desired) + +Convert sRLC back to xRLC and withdraw to your wallet using: + +```javascript +iexec.account.withdraw(RLC_amount); +``` + + diff --git a/src/documentation/use-iapp/how-to-pay/how-to-pay-for-web3telegram.md b/src/documentation/use-iapp/how-to-pay/how-to-pay-for-web3telegram.md new file mode 100644 index 00000000..2cabea8e --- /dev/null +++ b/src/documentation/use-iapp/how-to-pay/how-to-pay-for-web3telegram.md @@ -0,0 +1,178 @@ +--- +title: How to Pay for Web3Telegram +description: + Learn how to pay for Web3Telegram using vouchers or xRLC. This guide walks you + through obtaining vouchers, managing RLC to xRLC conversion, and using both + methods for secure Telegram communication. +--- + +# How to Pay for Web3Telegram + +[Web3Telegram](../web3telegram) dev tool offers secure, blockchain-based +communication by encrypting emails and protecting user privacy. + +The `sendTelegram` function uses confidential computing power to encrypt and +send messages, ensuring secure and decentralized email exchanges. + +This guide explains how to pay for Web3Telegram's computing power using +**vouchers** and **xRLC**, detailing the steps for each method. + +## Using Vouchers for Web3Telegram + +### Step 1: Obtain a Voucher + +- **Acquire Vouchers**: Obtain vouchers through the + [iExec Builder Dashboard](https://builder.iex.ec/). Note that the number of + Web3Telegram executions and the expiration time of each voucher is restricted + based on its validity period. Refer to + [pricing documentation](https://www.iex.ec/voucher) for more information. +- **Support**: For specific limitations related to your voucher, please contact + iExec Support. + +### Step 2: Use the Builder Dashboard + + + Builder dashboard screenshot + + +The iExec Builder Dashboard is a comprehensive tool for managing vouchers and +resources, providing an intuitive interface for: + +- **Claiming Vouchers**: Builders can claim vouchers during the BUILD and EARN + stages. +- **Top-Up Vouchers**: Future updates will allow direct top-ups via the + dashboard. Currently, builders are redirected to Discord. +- **Checking Voucher Balance**: Track your voucher balance and usage history. + +🧙🏼 [Go here](https://builder.iex.ec/) + +### Step 3: Grant Allowance (If Necessary) + +Use `iexec.account.approve(voucherAddress)` to authorize the voucher smart +contract to debit your account if the voucher balance is insufficient. This +ensures that if the voucher alone doesn't cover the execution cost, the +remaining balance is automatically deducted from your account. + +For additional information on using xRLC for fallback payment in Web3Telegram, +refer to the **Using xRLC with Web3Telegram** section. + +### Step 4: Execute Web3Telegram's `sendTelegram` Function + +When using a voucher for payment, set the `useVoucher` parameter to `true`: + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- + +const sendTelegram = await web3telegram.sendTelegram({ + protectedData: '0x123abc...', + telegramContent: 'My telegram message content', + senderName: 'Awesome project team', + label: 'some-cutom-id', + workerpoolAddressOrEns: 'prod-v8-bellecour.main.pools.iexec.eth', + dataMaxPrice: 42, + appMaxPrice: 42, + workerpoolMaxPrice: 42, + useVoucher: true, // [!code focus] +}); +``` + +## Using xRLC for Web3Telegram + +If you choose to use xRLC to cover the computational cost of Web3Telegram (or if +you need to cover data access costs such as retrieving the recipient's Chat Id), +follow these steps: + +### Install the iExec SDK + +To manage RLC tokens, developers must use the iExec SDK, which offers all the +necessary tools for interacting with the iExec platform. This includes +depositing, withdrawing, and checking balances of RLC and xRLC + +- In your JS/TS project npm install iexec +- Instantiate the iExec SDK (see the + [doc](https://github.com/iExecBlockchainComputing/iexec-sdk/blob/master/docs/README.md#quick-start)) + +```javascript +import { IExec } from 'iexec'; +// connect injected provider +const iexec = new IExec({ ethProvider: window.ethereum }); +``` + +### Purchase RLC + +Obtain RLC tokens from a supported cryptocurrency exchange. + +### Convert to xRLC + +Use the iExec Bridge to convert your RLC into xRLC for use on iExec's sidechain. +The bridging operation follows the lock & mint / burn & unlock principle. When +sending tokens from Mainnet to Bellecour, the bridge locks the initial amount on +the source chain and mints the equivalent on the destination chain. When going +in the other direction, it burns tokens on the Sidechain and unlocks the same +amount on Mainnet. The bridged asset is called xRLC on Bellecour. + +Users can send tokens from the Ethereum Mainnet to the iExec Sidechain or +vice-versa using the Account Manager +([iExec Explorer](https://explorer.iex.ec/bellecour) or +[POA Bridge UI](https://bridge-bellecour.iex.ec/)) available across all iExec +products. + +### Deposit xRLC + +Deposit the xRLC into your iExec account using the command: + +```javascript +iexec.account.deposit(xRLC_amount); +``` + +This converts xRLC into sRLC, used as proof of funds for task execution. + +### Check sRLC Balance + +Use the command below to check your balance: + +```javascript +iexec.account.show(); +``` + +### Execute `sendTelegram` + +Set the `useVoucher` parameter to `false` when using Web3Telegram's sendTelegram +function to pay with xRLC: + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- + +const sendTelegram = await web3telegram.sendTelegram({ + protectedData: '0x123abc...', + telegramContent: 'My telegram message content', + senderName: 'Awesome project team', + label: 'some-cutom-id', + workerpoolAddressOrEns: 'prod-v8-bellecour.main.pools.iexec.eth', + dataMaxPrice: 42, + appMaxPrice: 42, + workerpoolMaxPrice: 42, + useVoucher: false, // [!code focus] +}); +``` + +### Withdraw sRLC (If Desired) + +Convert sRLC back to xRLC and withdraw to your wallet using: + +```javascript +iexec.account.withdraw(RLC_amount); +``` + + diff --git a/src/documentation/use-iapp/how-to-pay/pricing-considerations.md b/src/documentation/use-iapp/how-to-pay/pricing-considerations.md new file mode 100644 index 00000000..09587a20 --- /dev/null +++ b/src/documentation/use-iapp/how-to-pay/pricing-considerations.md @@ -0,0 +1,10 @@ +--- +title: Pricing Considerations +description: Pricing considerations +--- + +# Pricing Considerations + +This page is under development. + + diff --git a/src/documentation/use-iapp/how-to-pay/voucher.md b/src/documentation/use-iapp/how-to-pay/voucher.md new file mode 100644 index 00000000..cf1c7822 --- /dev/null +++ b/src/documentation/use-iapp/how-to-pay/voucher.md @@ -0,0 +1,10 @@ +--- +title: Voucher Guide +description: Voucher Guide +--- + +# Voucher Guide + +This page is under development. + + diff --git a/src/documentation/use-iapp/introduction.md b/src/documentation/use-iapp/introduction.md new file mode 100644 index 00000000..2d331c5d --- /dev/null +++ b/src/documentation/use-iapp/introduction.md @@ -0,0 +1,53 @@ +--- +title: Introduction to Using iApps +description: + Learn how to use iExec Applications (iApps) to securely process protected data + in a privacy-safe environment +--- + +# 📝 Introduction to Using iApps + +iExec Applications (iApps) are your gateway to secure, privacy-preserving +computation on the iExec network. These applications run inside Trusted +Execution Environments (TEEs) like Intel SGX or Intel TDX, ensuring your data +remains confidential even during processing. + +## What are iApps? + +iApps are regular applications (Python scripts, AI models, data processors, +etc.) that have been adapted to run securely on the iExec network. They can +process protected data without ever seeing the raw information, making them +perfect for scenarios where data privacy is crucial. + +## Key Benefits + +- **🔒 Privacy-First**: Your data never leaves the secure TEE environment +- **⚡ Trusted Execution**: Applications run in hardware-protected environments +- **🌐 Decentralized**: No single point of failure or control +- **💰 Cost-Effective**: Pay only for the computation you need +- **🔧 Developer-Friendly**: Use familiar programming languages and tools + +## How iApps Work + +1. **Data Protection**: Users protect their sensitive data using the + [Data Protector](/documentation/manage-data/dataProtector/dataProtectorCore/protectData) +2. **Application Deployment**: Developers deploy their applications to the iExec + network +3. **Secure Processing**: iApps process protected data inside TEEs without + accessing raw data +4. **Result Delivery**: Only the computation results are returned, keeping + original data private + +## Use Cases + +- **Email Notifications**: Send emails without seeing recipient addresses +- **Oracle Updates**: Update price feeds using private trading data +- **Automated Transactions**: Process payments with protected financial data +- **AI Model Training**: Train models on sensitive datasets +- **Data Analytics**: Analyze private data without exposing it + +## Getting Started + +Ready to start using iApps? Check out our +[Getting Started Guide](./getting-started.md) to learn how to find, execute, and +interact with iApps on the iExec network. diff --git a/src/documentation/use-iapp/web3mail.md b/src/documentation/use-iapp/web3mail.md new file mode 100644 index 00000000..0362f336 --- /dev/null +++ b/src/documentation/use-iapp/web3mail.md @@ -0,0 +1,49 @@ +--- +title: Web3Mail +description: + Web3Mail enables secure, private email communication on the blockchain using + Ethereum addresses. Manage contact permissions and send emails without + revealing personal information. +--- + + + +# ✉ Web3Mail + +The Web3Mail tool offers a secure method to manage email-based communications +via the blockchain. This mechanism helps protect the personal information of the +email recipients through use of Ethereum addresses. + +The email address is stored as a `protectedData` entity using the +[iExec Data Protector tool](/documentation/manage-data/dataProtector). Through this mechanism, users +have complete control over which applications may use their email address for +sending communications. Sending a user a message, therefore, requires knowledge +of the Ethereum address of their `protectedData` as well positive authorization +for your account to contact them. Your account may be bound to either an +application or an individual. At any time a user may revoke permissions and this +revocation is immediate, giving users complete control over the privacy and +security of their information. + +Apps using the Web3Mail tool can: + +- enable an entity (such as an application provider or an end-user) to email an + Ethereum account holder without knowing their email address +- grant users complete control over which entities are authorized to use their + email address to send them communications + +The Web3Mail tool currently supports the following methods: + +- **fetchMyContacts** — retrieve a list of Ethereum addresses whose owners have + authorized you to email them +- **fetchUserContacts** — retrieve a list of Ethereum addresses whose owners + have authorized a given entity to email them +- **sendEmail** — send an email message to a user knowing only the Ethereum + address for the `protectedData` containing their email address + +**Try the demo:** + + + Web3Messaging Demo + diff --git a/src/documentation/use-iapp/web3mail/advanced-configuration.md b/src/documentation/use-iapp/web3mail/advanced-configuration.md new file mode 100644 index 00000000..1eccb10f --- /dev/null +++ b/src/documentation/use-iapp/web3mail/advanced-configuration.md @@ -0,0 +1,121 @@ +--- +title: Advanced Configuration +description: + Customize iExec Web3Mail with advanced options like custom iApp address, iApp + whitelist, IPFS node, and subgraph URL for tailored blockchain email + communication. +--- + +# Advanced Configuration + +The `IExecWeb3mail` constructor accepts configuration options object. As these +options are very specific, you won't need to use them for a standard usage of +`@iexec/web3mail`. + +## Parameters + +```ts twoslash +import { type Web3MailConfigOptions } from '@iexec/web3mail'; +``` + +### dappAddressOrENS + +The Ethereum contract address or ENS (Ethereum Name Service) for the web3mail +dApp. + +If not provided, the default ENS `web3mail.apps.iexec.eth` pointing to the +latest version of the web3mail dApp provided by iExec will be used. + +You can find the corresponding dApp address with Bellecour explorer: +[https://explorer.iex.ec/bellecour/search/web3mail.apps.iexec.eth](https://explorer.iex.ec/bellecour/search/web3mail.apps.iexec.eth). + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const web3mail = new IExecWeb3mail(web3Provider, { + dappAddressOrENS: 'web3mail.apps.iexec.eth', // [!code focus] +}); +``` + +### dappWhitelistAddress + +The Ethereum contract address for the web3mail dApps whitelist. By granting +access to a whitelist, email address owners ensure their email is still +available to consumers even after a new version of web3mail dApp gets released. + +If not provided, the default whitelist smart contract address provided by iExec +will be used. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const web3mail = new IExecWeb3mail(web3Provider, { + dappWhitelistAddress: '0x781482C39CcE25546583EaC4957Fb7Bf04C277D2', // [!code focus] +}); +``` + +See it in +[https://blockscout-bellecour.iex.ec/](https://blockscout-bellecour.iex.ec/address/0x781482C39CcE25546583EaC4957Fb7Bf04C277D2) + +### dataProtectorSubgraph + +The subgraph URL for querying data. + +If not provided, the default data protector subgraph provided by iExec will be +used. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const web3mail = new IExecWeb3mail(web3Provider, { + dataProtectorSubgraph: + 'https://thegraph-product.iex.ec/subgraphs/name/bellecour/dataprotector', // [!code focus] +}); +``` + +### ipfsNode + +The IPFS node URL for content uploads. Use this option if you want to use your +own IPFS node to upload content. + +If not provided, the default IPFS node provided by iExec will be used. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const web3mail = new IExecWeb3mail(web3Provider, { + ipfsNode: 'https://ipfs-upload.v8-bellecour.iex.ec', // [!code focus] +}); +``` + +### ipfsGateway + +The IPFS gateway URL used for content downloads. Mainly used for checking +content uploaded on the IPFS network. Use this option if you want to use your +own IPFS node for content downloads. + +If not provided, the default IPFS gateway provided by iExec will be used. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const web3mail = new IExecWeb3mail(web3Provider, { + ipfsGateway: 'https://ipfs-gateway.v8-bellecour.iex.ec', // [!code focus] +}); +``` + +### iexecOptions + +Low level configuration options for `iexec` SDK, see +[iexec SDK documentation IExecConfigOptions](https://github.com/iExecBlockchainComputing/iexec-sdk/blob/master/docs/interfaces/IExecConfigOptions.md) +for more details. diff --git a/src/documentation/use-iapp/web3mail/getting-started.md b/src/documentation/use-iapp/web3mail/getting-started.md new file mode 100644 index 00000000..70a2390f --- /dev/null +++ b/src/documentation/use-iapp/web3mail/getting-started.md @@ -0,0 +1,115 @@ +--- +title: Getting Started +description: + Get started with the iExec Web3Mail SDK. Learn how to install, configure, and + instantiate it with or without a Web3 provider to enable blockchain-based + email communication. +--- + +# Getting Started + +[![GitHub package.json version (branch)](https://img.shields.io/github/package-json/v/iExecBlockchainComputing/web3mail-sdk?color=green)](https://github.com/iExecBlockchainComputing/web3mail-sdk) + +## Overview + +### Prerequisites + +Before getting started, ensure that you have the following installed on your +system: + +\- [**Node.js**](https://nodejs.org/en/) version 18 or higher + +\- [**NPM**](https://docs.npmjs.com/) (Node.js package manager) + +### Installation + +::: code-group + +```sh [npm] +npm install @iexec/web3mail +``` + +```sh [yarn] +yarn add @iexec/web3mail +``` + +```sh [pnpm] +pnpm add @iexec/web3mail +``` + +```sh [bun] +bun add @iexec/web3mail +``` + +::: + +**This package is an ESM package. Your project needs to use ESM too.** + [**Read more**](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) + +If you use it with **Webpack**, some polyfills will be needed. You can find a +working project +[here](https://github.com/iExecBlockchainComputing/web3mail-sdk/tree/main/demo/browser-webpack). + +### Instantiate with a Web3 Provider + +::: code-group + +```ts twoslash [Browser] +declare global { + interface Window { + ethereum: any; + } +} +// ---cut--- +import { IExecWeb3mail } from '@iexec/web3mail'; + +const web3Provider = window.ethereum; +// instantiate +const web3mail = new IExecWeb3mail(web3Provider); +``` + +```ts twoslash [NodeJS] +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +// get web3 provider from a private key +const web3Provider = getWeb3Provider('YOUR_PRIVATE_KEY'); +// instantiate +const web3mail = new IExecWeb3mail(web3Provider); +``` + +::: + +### Instantiate Without a Web3 Provider + +For projects that only require read functions, you can instantiate the SDK +without a Web3 provider. + +::: code-group + +```ts twoslash [Browser] +import { IExecWeb3mail } from '@iexec/web3mail'; + +// instantiate +const web3mail = new IExecWeb3mail(); +``` + +```ts twoslash [NodeJS] +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +// instantiate +const web3mail = new IExecWeb3mail(); +``` + +::: + +## Sandbox + + + ⚡  Code Sandbox + + +Corresponding GitHub repository: + + + 🔎  GitHub repository sandbox + diff --git a/src/documentation/use-iapp/web3mail/methods/fetchMyContacts.md b/src/documentation/use-iapp/web3mail/methods/fetchMyContacts.md new file mode 100644 index 00000000..f4117677 --- /dev/null +++ b/src/documentation/use-iapp/web3mail/methods/fetchMyContacts.md @@ -0,0 +1,67 @@ +--- +title: fetchMyContacts +description: + Use the fetchMyContacts method from iExec Web3Mail to retrieve contact infos + of users who authorized you to email them. +--- + +# fetchMyContacts + +This method provides a list of `contact` objects identifying all users who +previously granted authorization to send them email messages. Each contact +contains the contact's ETH address as well as the ETH address for the +`protectedData` containing their email address. + +## Usage + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const contactsList = await web3mail.fetchMyContacts(); +``` + +## Parameters + +```ts twoslash +import { type FetchMyContactsParams } from '@iexec/web3mail'; +``` + +### isUserStrict + +**Type:** `boolean` + +This parameter enables fetching contacts who granted access exclusively to the +user and no one else. + +:::tip + +When you grant access to someone, you can choose to grant access to a specific +user (a wallet) or to any user (`0x0000000000000000000000000000000000000000`). + +::: + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const contactsList = await web3mail.fetchMyContacts({ + isUserStrict: true, // [!code focus] +}); +``` + +## Return Value + +The result object contains a list of `contact` objects. Each `contact` +represents one user who previously granted you authorization to send them +messages. + +```ts twoslash +import { type Contact } from '@iexec/web3mail'; +``` + +[`Contact[]`](/documentation/manage-data/dataProtector/types#contact) diff --git a/src/documentation/use-iapp/web3mail/methods/fetchUserContacts.md b/src/documentation/use-iapp/web3mail/methods/fetchUserContacts.md new file mode 100644 index 00000000..9395f6b3 --- /dev/null +++ b/src/documentation/use-iapp/web3mail/methods/fetchUserContacts.md @@ -0,0 +1,87 @@ +--- +title: fetchUserContacts +description: + Use fetchUserContacts from iExec Web3Mail to get users who authorized a + specific address to email them. +--- + +# fetchUserContacts + +This method provides a list of `contact` objects identifying all entities who +previously granted authorization to a specified entity to send them email +messages. Each contact contains the contact's ETH address as well as the ETH +address for the `protectedData` containing their email address. + +## Usage + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const contactsList = await web3mail.fetchUserContacts({ + userAddress: '0x789cba...', +}); +``` + +## Parameters + +```ts twoslash +import { type FetchUserContactsParams } from '@iexec/web3mail'; +``` + +### userAddress + +**Type:** `Address` + +The user for which you wish to obtain the list of contacts. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const contactsList = await web3mail.fetchUserContacts({ + userAddress: '0x789cba...', // [!code focus] +}); +``` + +### isUserStrict + +**Type:** `boolean` + +This parameter enables fetching contacts who granted access exclusively to the +user and no one else. + +:::tip + +When someone grants access, you can choose to grant access to a specific user (a +wallet) or to any user (`0x0000000000000000000000000000000000000000`). + +::: + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const contactsList = await web3mail.fetchUserContacts({ + userAddress: '0x789cba...', + isUserStrict: true, // [!code focus] +}); +``` + +## Return Value + +The result object contains a list of `contact` objects. Each `contact` +represents one user who previously granted authorization for the user identified +with `userAddress` to send them messages. + +```ts twoslash +import { type Contact } from '@iexec/web3mail'; +``` + +[`Contract[]`](/documentation/manage-data/dataProtector/types#contact) diff --git a/src/documentation/use-iapp/web3mail/methods/sendEmail.md b/src/documentation/use-iapp/web3mail/methods/sendEmail.md new file mode 100644 index 00000000..432d8a9f --- /dev/null +++ b/src/documentation/use-iapp/web3mail/methods/sendEmail.md @@ -0,0 +1,392 @@ +--- +title: sendEmail +description: + Send secure, permissioned emails using iExec Web3Mail's sendEmail method—no + need to know the user's email, just their authorized protectedData address. +--- + +# sendEmail + +This method allows an authorized entity to send an email message to a user +without needing to know their email address. + +The recipient email address is stored in a `protectedData` entity. The user +receiving the email must explicitly authorize you to send them email +communications and permission must be granted for the `Web3Mail` tool to use the +`protectedData` entity containing their email address. This is best done by +granting authorization to the Web3Mail app whitelist +`0x781482C39CcE25546583EaC4957Fb7Bf04C277D2` as `authorizedApp`. Refer to the +[Data Protector `grantAccess`](/documentation/manage-data/dataProtector/dataProtectorCore/grantAccess.md) +documentation for more details. + +::: tip + +For executing the `sendEmail` method with a voucher or xRLC, refer to the +dedicated section in the documentation under +"[How to Pay for web3mail](/documentation/use-iapp/how-to-pay/how-to-pay-for-web3mail)". + +::: + +## Usage + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const sendEmail = await web3mail.sendEmail({ + protectedData: '0x123abc...', + emailSubject: 'My email subject', + emailContent: 'My email content', +}); +``` + +## Parameters + +```ts twoslash +import { type SendEmailParams } from '@iexec/web3mail'; +``` + +### protectedData + +**Type:** `Address` + +The address of the `protectedData` holding the contact's email address. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const sendEmail = await web3mail.sendEmail({ + protectedData: '0x123abc...', // [!code focus] + emailSubject: 'My email subject', + emailContent: 'My email content', +}); +``` + +### emailSubject + +**Type:** `string` +**Max**: 78 characters + +The subject line for the email you are sending. This field is limited to 78 +characters. Any characters beyond that limited are truncated. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const sendEmail = await web3mail.sendEmail({ + protectedData: '0x123abc...', + emailSubject: 'My email subject', // [!code focus] + emailContent: 'My email content', +}); +``` + +### emailContent + +**Type:** `string` + +optionally HTML encoded + +_maximum size_: 512 kb + +The email content that needs to be sent. The content is limited to 512 kb in +size. Email content will be encrypted and stored in IPFS. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const sendEmail = await web3mail.sendEmail({ + protectedData: '0x123abc...', + emailSubject: 'My email subject', + emailContent: 'My email content', // [!code focus] +}); +``` + +### useVoucher + +**Type:** `boolean` +**Default:** `false` + +This optional param allows you to pay for the deal using your voucher. Make sure +that your voucher is held by your connected wallet. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const sendEmail = await web3mail.sendEmail({ + protectedData: '0x123abc...', + emailSubject: 'My email subject', + emailContent: 'My email content', + useVoucher: true, // [!code focus] +}); +``` + +::: tip + +If your voucher doesn't have enough xRLC to cover the deal, the SDK will +automatically get the required amount to your iExec account. Ensure that your +voucher is authorized to access your iExec account and that your account has +sufficient funds for this transfer to proceed. + +::: + +### contentType + +**Type:** `text/plain` or `text/html` +**Default:** `text/plain` + +This is used by the mail client to properly render the delivered text. Set this +to `text/html` to enable rich HTML content in your email. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const sendEmail = await web3mail.sendEmail({ + protectedData: '0x123abc...', + emailSubject: 'My email subject', + emailContent: '

Hello world!

', + contentType: 'text/html', // [!code focus] +}); +``` + +### senderName + +**Type:** `string` +**Default:** `Web3Mail` +**Min:** 3 characters +**Max:** 20 characters + +Allows specifying a sender name for the email. This is used by the mail client +in rendering the email to the user. The Web3Mail tool appends `via Web3Mail` to +the supplied name. Setting this to `Tom`, for example, will result in a sender +name of, `Tom via Web3Mail`, in the delivered email. If no name is specified, +the Web3Mail tool sets this to a value of `Web3Mail`. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const sendEmail = await web3mail.sendEmail({ + protectedData: '0x123abc...', + emailSubject: 'My email subject', + emailContent: 'My email content', + senderName: 'Awesome project team', // [!code focus] +}); +``` + +### label + +**Type:** `string` + +Allows adding a custom public label. The Web3Mail tool writes this onchain as +`iexec_args` in the deal params. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const sendEmail = await web3mail.sendEmail({ + protectedData: '0x123abc...', + emailSubject: 'My email subject', + emailContent: 'My email content', + label: 'some-cutom-id', // [!code focus] +}); +``` + +### workerpoolAddressOrEns + +**Type:** `workerpoolAddressOrEns` +**Default:** iExec's production workerpool + +Allows specifying the workerpool that will run the Web3Mail application. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const sendEmail = await web3mail.sendEmail({ + protectedData: '0x123abc...', + emailSubject: 'My email subject', + emailContent: 'My email content', + workerpoolAddressOrEns: 'prod-v8-bellecour.main.pools.iexec.eth', // [!code focus] +}); +``` + +::: tip + +iExec currently offers a production workerpool located at the Ethereum Name +Service (ENS) address `prod-v8-bellecour.main.pools.iexec.eth`. This is the +default workerpool for running confidential computations on the iExec platform. + +::: + +### dataMaxPrice + +**Type:** `number` +**Default:** `0` + +Allows specifying the maximum amount (in nRLC) you are willing to pay the email +address owner for using their data. The owner of the protected email address +receives this as a payment for sharing their data. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const sendEmail = await web3mail.sendEmail({ + protectedData: '0x123abc...', + emailSubject: 'My email subject', + emailContent: 'My email content', + dataMaxPrice: 42, // [!code focus] +}); +``` + +### appMaxPrice + +**Type:** `number` +**Default:** `0` + +Allows specifying the maximum amount (in nRLC) you are willing to pay the +Web3Mail app provider (iExec) for using the Web3Mail application. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const sendEmail = await web3mail.sendEmail({ + protectedData: '0x123abc...', + emailSubject: 'My email subject', + emailContent: 'My email content', + appMaxPrice: 42, // [!code focus] +}); +``` + +### workerpoolMaxPrice + +**Type:** `number` +**Default:** `0` + +Allows specifying the maximum amount you want to pay the workerpool provider for +using their infrastructure to run the web3mail app in nRLC. + +```ts twoslash +import { IExecWeb3mail, getWeb3Provider } from '@iexec/web3mail'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3mail = new IExecWeb3mail(web3Provider); +// ---cut--- +const sendEmail = await web3mail.sendEmail({ + protectedData: '0x123abc...', + emailSubject: 'My email subject', + emailContent: 'My email content', + workerpoolMaxPrice: 42, // [!code focus] +}); +``` + +## Return Value + +```ts twoslash +import { type SendEmailResponse } from '@iexec/web3mail'; +``` + +### taskId + +**Type:** `Addess` + +This uniquely identifies the email task on the iExec side chain. You can view +the status of the `sendEmail` method by monitoring the task on the +[iExec Explorer](https://explorer.iex.ec/bellecour). + +## Error Handling + +### Validation Errors + +We use [yup](https://github.com/jquense/yup) to validate input parameters. + +In case one is not valid, you'll get **a yup ValidationError**. + +Example to check received Validation errors: + +```ts +import { ValidationError } from '@iexec/web3mail'; + +try { + await web3mail.sendEmail({ + protectedData, + senderName: 'ab', // Bad input + emailSubject, + emailContent, + }); +} catch (err) { + console.error(err.message); // "senderName must be at least 3 characters" + + // Or list all validation errors: + if (err instanceof ValidationError) { + console.error('Validation errors:', (err as ValidationError).errors); + } +} +``` + +### Email Schema Error + +To be able to send an email to a protected data, it needs to contain, well, an +email address. + +If not, you'll get a `WorkflowError` in the form of: + +```json5 +{ + message: 'Failed to sendEmail', + errorCause: Error('This protected data does not contain "email:string" in its schema.') +} +``` + +### iExec Protocol Errors + +In case the iExec stack is to blame, we'll make it clear and you'll get a +specific `WorkflowError`: + +```json5 +{ + message: "A service in the iExec protocol appears to be unavailable. You can retry later or contact iExec's technical support for help.", + errorCause: , + isProtocolError: true +} +``` + +### Workflow Errors + +For any other errors, you'll get a `WorkflowError` error in the form of: + +```json5 +{ + message: 'Failed to sendEmail', + errorCause: +} +``` diff --git a/src/documentation/use-iapp/web3telegram.md b/src/documentation/use-iapp/web3telegram.md new file mode 100644 index 00000000..6bc0c708 --- /dev/null +++ b/src/documentation/use-iapp/web3telegram.md @@ -0,0 +1,46 @@ +--- +title: Web3Telegram +description: + Web3Telegram enables private, blockchain-based Telegram messaging using + Ethereum addresses. Users retain full control over who can contact + them—without sharing their chat ID. +--- + + + +# :speech_balloon: Web3Telegram + +Web3Telegram offers a secure method to manage telegram communications via the +blockchain. This mechanism helps protect the personal information of the +telegram chat ID recipients through use of Ethereum addresses. + +The telegram chat ID address is stored as a `protectedData` entity using +[iExec Data Protector](/documentation/manage-data/dataProtector). Through this mechanism, users have +complete control over which applications may use their +[chat ID](./web3telegram/integration-guide.md#_1-get-your-users-to-retrieve-their-chat-id) +for sending communications. + +Sending a user a message, therefore, requires knowledge of the Ethereum address +of their `protectedData` as well as an explicit authorization for your account +to contact them. But also requires the receiver to send a telegram message to +the bot first. + +Your account may be bound to either an application or an individual. At any time +a user may revoke permissions and this revocation is immediate, giving users +complete control over the privacy and security of their information. + +Apps using Web3Telegram can: + +- enable an entity (such as an application provider or an end-user) to message + an Ethereum account holder with telegram without knowing their chat ID or + username +- grant users complete control over which entities are authorized to use their + chat ID to send them communications + +**Try the demo:** + + + Web3Messaging Demo + diff --git a/src/documentation/use-iapp/web3telegram/advanced-configuration.md b/src/documentation/use-iapp/web3telegram/advanced-configuration.md new file mode 100644 index 00000000..c18a4a38 --- /dev/null +++ b/src/documentation/use-iapp/web3telegram/advanced-configuration.md @@ -0,0 +1,121 @@ +--- +title: Advanced Configuration +description: + Explore the advanced configuration options for integrating Web3Telegram. Learn + how to customize the dApp address, whitelist, subgraph, IPFS node, and more + for secure Telegram communication on the iExec blockchain. +--- + +# Advanced Configuration + +The `IExecWeb3Telegram` constructor accepts configuration options object. As +these options are very specific, you won't need to use them for a standard usage +of `@iexec/web3telegram`. + +## Parameters + +```ts twoslash +import { type Web3TelegramConfigOptions } from '@iexec/web3telegram'; +``` + +### dappAddressOrENS + +The Ethereum contract address or ENS (Ethereum Name Service) for the +web3telegram dApp. + +If not provided, the default ENS `web3telegram.apps.iexec.eth` pointing to the +latest version of the web3telegram dApp provided by iExec will be used. + +You can find the corresponding dApp address with Bellecour explorer: +[https://explorer.iex.ec/bellecour/search/web3telegram.apps.iexec.eth](https://explorer.iex.ec/bellecour/search/web3telegram.apps.iexec.eth). + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const web3telegram = new IExecWeb3telegram(web3Provider, { + dappAddressOrENS: 'web3telegram.apps.iexec.eth', // [!code focus] +}); +``` + +### dappWhitelistAddress + +The Ethereum contract address for the web3telegram dApps whitelist. By granting +access to a whitelist, Chat Id owners ensure their Chat Id is still available to +consumers even after a new version of web3telegram dApp gets released. + +If not provided, the default whitelist smart contract address provided by iExec +will be used. + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const web3telegram = new IExecWeb3telegram(web3Provider, { + dappWhitelistAddress: '0x192C6f5AccE52c81Fcc2670f10611a3665AAA98F', // [!code focus] +}); +``` + +See it in +[https://blockscout-bellecour.iex.ec/](https://blockscout-bellecour.iex.ec/address/0x192C6f5AccE52c81Fcc2670f10611a3665AAA98F) + +### dataProtectorSubgraph + +The subgraph URL for querying data. + +If not provided, the default data protector subgraph provided by iExec will be +used. + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const web3telegram = new IExecWeb3telegram(web3Provider, { + dataProtectorSubgraph: + 'https://thegraph-product.iex.ec/subgraphs/name/bellecour/dataprotector', // [!code focus] +}); +``` + +### ipfsNode + +The IPFS node URL for content uploads. Use this option if you want to use your +own IPFS node to upload content. + +If not provided, the default IPFS node provided by iExec will be used. + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const web3telegram = new IExecWeb3telegram(web3Provider, { + ipfsNode: 'https://ipfs-upload.v8-bellecour.iex.ec', // [!code focus] +}); +``` + +### ipfsGateway + +The IPFS gateway URL used for content downloads. Mainly used for checking +content uploaded on the IPFS network. Use this option if you want to use your +own IPFS node for content downloads. + +If not provided, the default IPFS gateway provided by iExec will be used. + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +// ---cut--- +const web3telegram = new IExecWeb3telegram(web3Provider, { + ipfsGateway: 'https://ipfs-gateway.v8-bellecour.iex.ec', // [!code focus] +}); +``` + +### iexecOptions + +Low level configuration options for `iexec` SDK, see +[iexec SDK documentation IExecConfigOptions](https://github.com/iExecBlockchainComputing/iexec-sdk/blob/master/docs/interfaces/IExecConfigOptions.md) +for more details. diff --git a/src/documentation/use-iapp/web3telegram/getting-started.md b/src/documentation/use-iapp/web3telegram/getting-started.md new file mode 100644 index 00000000..a6825d72 --- /dev/null +++ b/src/documentation/use-iapp/web3telegram/getting-started.md @@ -0,0 +1,90 @@ +--- +title: Getting Started +description: + Get started with Web3Telegram, a secure blockchain-based tool for sending + Telegram messages. Install the SDK and integrate it with your Web3 project. +--- + +# Getting Started + +[![GitHub package.json version (branch)](https://img.shields.io/github/package-json/v/iExecBlockchainComputing/web3telegram-sdk?color=green)](https://github.com/iExecBlockchainComputing/web3telegram-sdk) + +## Overview + +### Prerequisites + +Before getting started, ensure that you have the following installed on your +system: + +\- [**Node.js**](https://nodejs.org/en/) version 18 or higher + +\- [**NPM**](https://docs.npmjs.com/) (Node.js package manager) + +### Installation + +::: code-group + +```sh [npm] +npm install @iexec/web3telegram +``` + +```sh [yarn] +yarn add @iexec/web3telegram +``` + +```sh [pnpm] +pnpm add @iexec/web3telegram +``` + +```sh [bun] +bun add @iexec/web3telegram +``` + +::: + +**This package is an ESM package. Your project needs to use ESM too.** + [**Read more**](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c) + +If you use it with **Webpack**, some polyfills will be needed. You will find +later a working project + +### Instantiate SDK + +::: code-group + +```ts twoslash [Browser] +declare global { + interface Window { + ethereum: any; + } +} +// ---cut--- +import { IExecWeb3telegram } from '@iexec/web3telegram'; + +const web3Provider = window.ethereum; +// instantiate +const web3telegram = new IExecWeb3telegram(web3Provider); +``` + +```ts twoslash [NodeJS] +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +// get web3 provider from a private key +const web3Provider = getWeb3Provider('YOUR_PRIVATE_KEY'); +// instantiate +const web3telegram = new IExecWeb3telegram(web3Provider); +``` + +::: + +## Sandbox + + + ⚡  Code Sandbox + + +Corresponding GitHub repository: + + + 🔎  GitHub repository sandbox + diff --git a/src/documentation/use-iapp/web3telegram/integration-guide.md b/src/documentation/use-iapp/web3telegram/integration-guide.md new file mode 100644 index 00000000..898cc0f1 --- /dev/null +++ b/src/documentation/use-iapp/web3telegram/integration-guide.md @@ -0,0 +1,112 @@ +--- +title: iExec Web3Telegram Integration Guide +description: + Integrate iExec Web3Telegram to enable secure and private Telegram messaging + via blockchain-based access control, ensuring user privacy and decentralized + messaging control +--- + +# iExec Web3Telegram Integration Guide + +## Overview + +Integrating **iExec Web3Telegram** enables secure and private messaging on +Telegram using blockchain-based access control. This allows users to send and +receive messages while maintaining full control over their data. + +The integration process consists of the following steps: + +1. **Get your user to retrieve their Chat ID from the iExec Web3Telegram bot.** +2. **Create the protected data via the iExec Data Protector SDK.** +3. **Grant access via the Data Protector SDK to authorize users to receive + messages.** +4. **Send messages securely using the Web3Telegram SDK.** + +## 1. Get your Users to Retrieve their Chat ID + +To enable messaging via Web3Telegram, you need to retrieve the recipient's Chat +ID. + +A **Chat ID** is a unique identifier assigned to your Telegram account. It +allows applications to send messages to you **without revealing your actual +Telegram username or phone number**. By **protecting your Chat ID with iExec**, +you ensure that it remains **encrypted and private**, so only **authorized +senders** can contact you. + +### Steps: + +- Ask the recipient to open Telegram and start a conversation with + [**@IExecWeb3Telegrambot**](https://t.me/IExecWeb3TelegramBot). +- The bot will reply with their unique Chat ID. +- Save this Chat ID as you will need it for the next steps. + +::: tip + +- Once the Chat ID is protected, all messages will arrive within this bot + conversation. +- The recipient can leave the conversation at any time to stop receiving + messages. + +::: + +## 2. Create the Protected Data with Data Protector SDK + +After obtaining your user's Chat ID, you need to protect it using iExec’s Data +Protector to ensure privacy and security. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +const protectedData = await dataProtectorCore.protectData({ + data: { + telegram_chatId: '12345678', // Recipient's Chat ID + }, +}); +``` + +## 3. Grant Access via Data Protector SDK + +To allow users to send messages, you must explicitly grant access to specific +users. + +```ts twoslash +import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const dataProtectorCore = new IExecDataProtectorCore(web3Provider); +const grantedAccess = await dataProtectorCore.grantAccess({ + protectedData: '0x123abc...', // Protected Chat ID data address + authorizedApp: '0x456def...', // Web3Telegram app address + authorizedUser: '0x789cba...', // Ethereum address of the authorized sender + pricePerAccess: 3, // Cost per message (in iExec tokens) + numberOfAccess: 10, // Allowed message count + onStatusUpdate: ({ title, isDone }) => { + console.log(title, isDone); + }, +}); +``` + +## 4. Send Messages via Web3Telegram SDK + +Once authorized, a user can send messages via Web3Telegram SDK. + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +const sendTelegram = await web3telegram.sendTelegram({ + protectedData: '0x123abc...', // Protected Chat ID data address + senderName: 'Arthur', + telegramContent: 'My telegram message content', +}); +``` + +## Conclusion + +By integrating **iExec Web3Telegram**, you ensure privacy, security, and +decentralized control over your Telegram messaging. Your users decide who can +send them messages and set a cost for access while keeping their Telegram handle +hidden. + +For further support, join the iExec community on +[Discord](https://discord.com/invite/pbt9m98wnU). diff --git a/src/documentation/use-iapp/web3telegram/methods/fetchMyContacts.md b/src/documentation/use-iapp/web3telegram/methods/fetchMyContacts.md new file mode 100644 index 00000000..11f98faf --- /dev/null +++ b/src/documentation/use-iapp/web3telegram/methods/fetchMyContacts.md @@ -0,0 +1,67 @@ +--- +title: fetchMyContacts +description: + Use the fetchMyContacts method from iExec web3telegram to retrieve contact + infos of users who authorized you to message them +--- + +# fetchMyContacts + +This method provides a list of `contact` objects identifying all users who +previously granted authorization to send them telegram messages. Each contact +contains the contact's ETH address as well as the ETH address for the +`protectedData` containing their telegram chat ID. + +## Usage + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- +const contactsList = await web3telegram.fetchMyContacts(); +``` + +## Parameters + +```ts twoslash +import { type FetchMyContactsParams } from '@iexec/web3telegram'; +``` + +### isUserStrict + +**Type:** `boolean` + +This parameter enables fetching contacts who granted access exclusively to the +user and no one else. + +:::tip + +When you grant access to someone, you can choose to grant access to a specific +user (a wallet) or to any user (`0x0000000000000000000000000000000000000000`). + +::: + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- +const contactsList = await web3telegram.fetchMyContacts({ + isUserStrict: true, // [!code focus] +}); +``` + +## Return Value + +The result object contains a list of `contact` objects. Each `contact` +represents one user who previously granted you authorization to send them +messages. + +```ts twoslash +import { type Contact } from '@iexec/web3telegram'; +``` + +[`Contact[]`](/documentation/manage-data/dataProtector/types#contact) diff --git a/src/documentation/use-iapp/web3telegram/methods/fetchUserContacts.md b/src/documentation/use-iapp/web3telegram/methods/fetchUserContacts.md new file mode 100644 index 00000000..e6c92338 --- /dev/null +++ b/src/documentation/use-iapp/web3telegram/methods/fetchUserContacts.md @@ -0,0 +1,87 @@ +--- +title: fetchUserContacts +description: + Use fetchUserContacts method from iExec web3telegram to get users who + authorized a specific Ethereum address to message them +--- + +# fetchUserContacts + +This method provides a list of `contact` objects identifying all entities who +previously granted authorization to a specified entity to send them telegram +messages. Each contact contains the contact's ETH address as well as the ETH +address for the `protectedData` containing their telegram chat ID. + +## Usage + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- +const contactsList = await web3telegram.fetchUserContacts({ + userAddress: '0xF048eF3d7E3B33A465E0599E641BB29421f7Df92', +}); +``` + +## Parameters + +```ts twoslash +import { type FetchUserContactsParams } from '@iexec/web3telegram'; +``` + +### userAddress + +`Address` + +The entity for which you wish to obtain the list of contacts. + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- +const contactsList = await web3telegram.fetchUserContacts({ + userAddress: '0xF048eF3d7E3B33A465E0599E641BB29421f7Df92', // [!code focus] +}); +``` + +### isUserStrict + +**Type:** `boolean` + +This parameter enables fetching contacts who granted access exclusively to the +user and no one else. + +:::tip + +When someone grants access, you can choose to grant access to a specific user (a +wallet) or to any user (`0x0000000000000000000000000000000000000000`). + +::: + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- +const contactsList = await web3telegram.fetchUserContacts({ + userAddress: '0x789cba...', + isUserStrict: true, // [!code focus] +}); +``` + +## Return Value + +The result object contains a list of `contact` objects. Each `contact` +represents one user who previously granted authorization for the user identified +with `userAddress` to send them messages. + +```ts twoslash +import { type Contact } from '@iexec/web3telegram'; +``` + +[`Contract[]`](/documentation/manage-data/dataProtector/types#contact) diff --git a/src/documentation/use-iapp/web3telegram/methods/sendTelegram.md b/src/documentation/use-iapp/web3telegram/methods/sendTelegram.md new file mode 100644 index 00000000..a159e26f --- /dev/null +++ b/src/documentation/use-iapp/web3telegram/methods/sendTelegram.md @@ -0,0 +1,295 @@ +--- +title: sendTelegram +description: + Use the sendTelegram method from Web3Telegram to send secure Telegram messages + without knowing the recipient's username or chat ID. +--- + +# sendTelegram + +This method allows an authorized entity to send a telegram message to a User +without needing to know their username or Chat ID. + +The recipient Chat ID is stored in a `protectedData` entity. The user receiving +message must explicitly authorize you to send them telegram communications and +permission must be granted for the `Web3Telegram` tool to use the +`protectedData` entity containing their chat ID. This is best done by granting +authorization to the Web3Telegram app whitelist +`0x192C6f5AccE52c81Fcc2670f10611a3665AAA98F` as `authorizedApp`. Refer to the +[Data Protector `grantAccess`](/documentation/manage-data/dataProtector/dataProtectorCore/grantAccess) +documentation for more details. + +::: tip + +For executing the `sendTelegram` method with a voucher or xRLC, refer to the +dedicated section in the documentation under +"[How to Pay for web3telegram](/documentation/use-iapp/how-to-pay/how-to-pay-for-web3telegram)". + +::: + +## Usage + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- + +const sendTelegram = await web3telegram.sendTelegram({ + protectedData: '0x123abc...', + telegramContent: 'My telegram message content', + senderName: 'Awesome project team', + label: 'some-cutom-id', + workerpoolAddressOrEns: 'prod-v8-bellecour.main.pools.iexec.eth', + dataMaxPrice: 42, + appMaxPrice: 42, + workerpoolMaxPrice: 42, +}); +``` + +## Parameters + +```ts twoslash +import { type SendTelegramParams } from '@iexec/web3telegram'; +``` + +### protectedData + +`Address` + +The address of the `protectedData` holding the contact's telegram chat ID. + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- + +const sendTelegram = await web3telegram.sendTelegram({ + protectedData: '0x123abc...', // [!code focus] + senderName: 'Arthur', + telegramContent: 'My telegram message content', +}); +``` + +### senderName + +`string` + +The name of the telegram message sender. + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- + +const sendTelegram = await web3telegram.sendTelegram({ + protectedData: '0x123abc...', + senderName: 'Arthur', // [!code focus] + telegramContent: 'My telegram message content', +}); +``` + +### telegramContent + +`string` + +_maximum size_: 512 kb + +The telegram message content that needs to be sent. The content is limited to +512 kb in size. Telegram content is encrypted and stored in IPFS. + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- + +const sendTelegram = await web3telegram.sendTelegram({ + protectedData: '0x123abc...', + senderName: 'Arthur', + telegramContent: 'My telegram message content', // [!code focus] +}); +``` + +### useVoucher + +**Type:** `boolean` +**Default:** `false` + +This optional param allows you to pay for the deal using your voucher. Make sure +that your voucher is held by your connected wallet. + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- + +const sendTelegram = await web3telegram.sendTelegram({ + protectedData: '0x123abc...', + telegramContent: 'My telegram message content', + senderName: 'Awesome project team', + label: 'some-cutom-id', + workerpoolAddressOrEns: 'prod-v8-bellecour.main.pools.iexec.eth', + dataMaxPrice: 42, + appMaxPrice: 42, + workerpoolMaxPrice: 42, + useVoucher: true, // [!code focus] +}); +``` + +::: tip + +If your voucher doesn't have enough xRLC to cover the deal, the SDK will +automatically get the required amount to your iExec account. Ensure that your +voucher is authorized to access your iExec account and that your account has +sufficient funds for this transfer to proceed. + +::: + +### label + +`string | undefined` + +Allows adding a custom public label. The Web3telegram tool writes this onchain +as `iexec_args` in the deal params. + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- + +const sendTelegram = await web3telegram.sendTelegram({ + protectedData: '0x123abc...', + senderName: 'Arthur', + telegramContent: 'My telegram message content', + label: 'some-cutom-id', // [!code focus] +}); +``` + +### workerpoolAddressOrEns + +`workerpoolAddressOrEns | undefined` + +_default_: iExec's production workerpool + +Allows specifying the workerpool that will run the Web3Telegram application. + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- + +const sendTelegram = await web3telegram.sendTelegram({ + protectedData: '0x123abc...', + senderName: 'Arthur', + telegramContent: 'My telegram message content', + workerpoolAddressOrEns: 'prod-v8-bellecour.main.pools.iexec.eth', // [!code focus] +}); +``` + +::: tip + +iExec currently offers a production workerpool located at the Ethereum Name +Service (ENS) address `prod-v8-bellecour.main.pools.iexec.eth`. This is the +default workerpool for running confidential computations on the iExec platform. + +::: + +### dataMaxPrice + +`number | undefined` + +_default_: `0` + +Allows specifying the maximum amount (in nRLC) you are willing to pay the +telegram chat ID owner for using their data. The owner of the protected chat ID +receives this as a payment for sharing their data. + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- + +const sendTelegram = await web3telegram.sendTelegram({ + protectedData: '0x123abc...', + senderName: 'Arthur', + telegramContent: 'My telegram message content', + dataMaxPrice: 42, // [!code focus] +}); +``` + +### appMaxPrice + +`number | undefined` + +_default_: `0` + +Allows specifying the maximum amount (in nRLC) you are willing to pay the +Web3telegram app provider (iExec) for using the Web3telegram application. + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- + +const sendTelegram = await web3telegram.sendTelegram({ + protectedData: '0x123abc...', + senderName: 'Arthur', + telegramContent: 'My telegram message content', + appMaxPrice: 42, // [!code focus] +}); +``` + +### workerpoolMaxPrice + +`number | undefined` + +_default_: `0` + +Allows specifying the maximum amount you want to pay the workerpool provider for +using their infrastructure to run the web3telegram app in nRLC. + +```ts twoslash +import { IExecWeb3telegram, getWeb3Provider } from '@iexec/web3telegram'; + +const web3Provider = getWeb3Provider('PRIVATE_KEY'); +const web3telegram = new IExecWeb3telegram(web3Provider); +// ---cut--- + +const sendTelegram = await web3telegram.sendTelegram({ + protectedData: '0x123abc...', + senderName: 'Arthur', + telegramContent: 'My telegram message content', + workerpoolMaxPrice: 42, // [!code focus] +}); +``` + +## Return Value + +```ts twoslash +import { type SendTelegramResponse } from '@iexec/web3telegram'; +``` + +### taskId + +`Addess` + +This uniquely identifies the telegram task on the iExec side chain. You can view +the status of the `sendTelegram` method by monitoring the task on the +[iExec Explorer](https://explorer.iex.ec/bellecour). diff --git a/src/documentation/welcome.md b/src/documentation/welcome.md new file mode 100644 index 00000000..8b31febc --- /dev/null +++ b/src/documentation/welcome.md @@ -0,0 +1,188 @@ +--- +title: About iExec +description: + Discover how iExec empowers developers to build privacy-first applications +--- + +# 💡 Privacy Made Easy + +
+

Welcome to iExec

+

Your complete toolkit for building privacy-first Web3 applications that protect and use sensitive data

+
+ +## 🤔 Why iExec? + +In a world where data privacy is more critical than ever, developers face a +tough challenge: how to build innovative applications without compromising +sensitive user data. + +
+

iExec solves this by providing a privacy-first toolkit that makes it simple to protect, manage, and compute data securely, even in untrusted environments. We believe privacy should be a built-in feature, not an afterthought.

+
+ +## 🛠️ How we Make it Happen + +iExec provides a complete toolkit that makes privacy simple and actionable: + +
+
+
+ 🔌 +
+ Privacy: Integrate confidentiality features without managing complex infrastructure +
+
+
+ 🎮 +
+ Governance and Ownership: Control your assets through orders, choose who can access your data, with which privacy applications, and at what price +
+
+
+ ⛓️ +
+ Multi-Chain Interoperability: Build privacy-first applications on supported networks +
+
+
+ 🔧 +
+ Plug & Play tools: Fluide experience with comprehensive toolkit, SDKs, and ready-to-use components for seamless development +
+
+
+
+ +## 🔧 What we Provide + +Ready-to-use components to protect sensitive data and computation: + +
+
+
+ 🔒 +
+ DataProtector: Secure and control access to sensitive information +
+
+
+ +
+ iApp Generator: Bootstrap your Privacy Apps on TEEs. +Confidential Computing made easy +
+
+
+ 🛡️ +
+ Decentralized Confidential Computing: Process data securely in secure and confidential environments while controlling access and monetization through blockchain +
+
+
+
+ +## 💡 Real-world Use Cases + +
+
+
+ 💰 +

Finance

+
+
    +
  • Analyze sensitive financial data
  • +
  • Process credit scores without exposing personal information
  • +
+
+ +
+
+ 🤖 +

AI/ML

+
+
    +
  • Train models on private datasets
  • +
  • Perform confidential predictions
  • +
+
+ +
+
+ 🔬 +

Research

+
+
    +
  • Share and analyze research data securely
  • +
  • Collaborate while protecting intellectual property
  • +
+
+ +
+
+ 📊 +

Business Analytics

+
+
    +
  • Process competitive market data
  • +
  • Analyze business metrics confidentially
  • +
+
+ +
+
+ 🎮 +

Gaming

+
+
    +
  • Protect player data and game assets
  • +
  • Process in-game transactions securely
  • +
+
+
+
+ 🏥 +

Healthcare

+
+
    +
  • Process patient records privately
  • +
  • Run medical analyses while preserving patient confidentiality
  • +
+
+
+ +
+

In the next chapters, we'll guide you through our Hello World journey, a 30 minutes start that will teach you everything about iExec, from protecting sensitive data to building and deploying confidential apps.

+
+ +## 🚀 Start Building + +Ready to build privacy-first applications? Choose your path: + +
+
+
+ +
+ Quick Start: Follow our Hello World guide to build your first confidential app in minutes +
+
+
+ 🔍 +
+ Explore Use Cases: Browse real-world examples to see what you can build with iExec +
+
+
+ 💬 +
+ Join the Community: Connect with other builders and get support on our Discord +
+
+
+
+ +--- + +_Ready to transform how you handle sensitive data? Let's build something amazing +together._ From 1be94dbfdd637f7b38689ae02b26737aae43b0d4 Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Mon, 11 Aug 2025 21:32:54 +0200 Subject: [PATCH 3/8] Refactor documentation for iApps: update titles, descriptions, and content structure across multiple guides. Mark several pages as under development, including guides on executing iApps, finding iApps, payment methods, and using iApps with protected data. Adjust links and examples for clarity and consistency. Update Web3Mail method documentation to reflect changes in access granting. --- src/documentation/use-iapp/getting-started.md | 147 +--- .../guides/add-inputs-to-execution.md | 799 +----------------- .../guides/different-ways-to-execute.md | 120 +-- .../use-iapp/guides/find-iapps.md | 126 +-- .../use-iapp/guides/how-to-pay-executions.md | 396 +-------- .../guides/use-iapp-with-protected-data.md | 473 +---------- src/documentation/use-iapp/introduction.md | 51 +- .../use-iapp/web3mail/methods/sendEmail.md | 2 +- 8 files changed, 34 insertions(+), 2080 deletions(-) diff --git a/src/documentation/use-iapp/getting-started.md b/src/documentation/use-iapp/getting-started.md index 46cdd0f4..f0604249 100644 --- a/src/documentation/use-iapp/getting-started.md +++ b/src/documentation/use-iapp/getting-started.md @@ -1,150 +1,11 @@ --- title: Getting Started with iApps description: - Learn the basics of finding and executing iApps on the iExec network + Your first steps to discover and execute existing iApps on the iExec platform --- -# 🚀 Getting Started with iApps +# 🚀 Getting Started -Welcome to the world of secure, privacy-preserving computation! This guide will -walk you through the essential steps to start using iApps on the iExec network. +This page is under development. -## Prerequisites - -Before you begin, make sure you have: - -- A Web3 wallet (MetaMask, WalletConnect, etc.) -- Some RLC tokens for paying computation fees (or access to free vouchers - through learning programs) -- Basic understanding of blockchain transactions - -### 🆓 Use Our Stack for Free! - -Good news! You can start using iApps **completely free** through our learning -programs: - -- **Learn Web3 Program**: Get free access to our entire stack, including - vouchers for iApp executions -- **Free Vouchers**: Pre-funded computation credits provided through learning - initiatives -- **No RLC Required**: Start experimenting and building without any upfront - costs - -### 💰 Getting Started Without RLC - -Don't have RLC tokens yet? No problem! Our learning programs provide everything -you need: - -- **Free Vouchers**: Access to pre-funded computation credits -- **Full Stack Access**: Use all iExec tools and infrastructure at no cost -- **Educational Support**: Learn while you build with our community - -Ready to dive in? Let's get started with finding and executing your first iApp! - -## Step 1: Find Available iApps - -The first step is discovering what iApps are available for your use case. You -can find iApps through several methods: - -1. Visit the [iExec Explorer](https://explorer.iex.ec) -2. Navigate to the "Apps" section -3. Browse available applications by category or search by name -4. Check the app's description, requirements, and pricing - -## Step 2: Understand App Requirements - -Before executing an iApp, understand what it needs: - -- **Protected Data**: Some apps require specific types of protected data -- **Input Parameters**: Check if the app needs command-line arguments -- **Input Files**: Some apps require additional files (URLs to public files) -- **Secrets**: Certain apps need requester secrets for API keys, etc. - -## Step 3: Prepare Your Data - -If the iApp requires protected data: - -1. **Protect Your Data**: Use the - [Data Protector](/documentation/manage-data/dataProtector/dataProtectorCore/protectData) to - encrypt your sensitive information -2. **Grant Access**: Ensure the iApp has permission to access your protected - data using - [grantAccess](/documentation/manage-data/dataProtector/dataProtectorCore/grantAccess) - -## Step 4: Execute the iApp - -### Using the DataProtector SDK - -```typescript -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider(window.ethereum); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); - -// Execute the iApp with protected data -const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', // Your protected data address - app: '0x456def...', // The iApp address - args: 'optional arguments', - maxPrice: 10, // Maximum price in nRLC -}); -``` - -### Using the CLI - -```bash -# Execute an iApp with protected data -iexec app run 0x456def... --dataset 0x123abc... --args "your arguments" -``` - -## Step 5: Monitor and Retrieve Results - -After submitting your task: - -1. **Monitor Progress**: Track your task on the - [iExec Explorer](https://explorer.iex.ec) -2. **Retrieve Results**: Get your results once the task completes - -### Using the DataProtector SDK - -```typescript -// Retrieve results from a completed task -const taskResult = await dataProtectorCore.getResultFromCompletedTask({ - taskId: '0x7ac398...', // Your task ID -}); -``` - -### Using the CLI - -```bash -# Get task result -iexec task show 0x7ac398... - -# Download task result -iexec task show 0x7ac398... --download -``` - -## Step 6: Understand Costs - -iApp execution costs include: - -- **Application Fee**: Paid to the app developer -- **Data Fee**: Paid to the data owner (if using protected data) -- **Workerpool Fee**: Paid to the computation provider -- **Gas Fees**: Blockchain transaction costs (free on Bellecour sidechain) - -## Next Steps - -Now that you understand the basics: - -- Explore our [Guides](/documentation/use-iapp/guides/) for detailed tutorials -- Learn about [Different Ways to Execute](/documentation/use-iapp/guides/different-ways-to-execute) - iApps -- Understand [How to Pay for Executions](/documentation/use-iapp/guides/how-to-pay-executions) -- Discover how to - [Use iApps with Protected Data](/documentation/use-iapp/guides/use-iapp-with-protected-data) - -## Need Help? - -- Check the [iExec Explorer](https://explorer.iex.ec) for app details -- Visit our [Discord community](https://discord.gg/iexec) for support + diff --git a/src/documentation/use-iapp/guides/add-inputs-to-execution.md b/src/documentation/use-iapp/guides/add-inputs-to-execution.md index 4de391cb..0965928e 100644 --- a/src/documentation/use-iapp/guides/add-inputs-to-execution.md +++ b/src/documentation/use-iapp/guides/add-inputs-to-execution.md @@ -1,799 +1,10 @@ --- -title: Add Inputs to iApp Execution -description: - Learn how to provide arguments, files, secrets, and other inputs to iApp - executions +title: Add Inputs to the Execution +description: Add inputs to the execution --- -# 📥 Add Inputs to iApp Execution +# Add Inputs to the Execution -iApps can accept various types of inputs to customize their behavior and provide -necessary data for processing. This guide covers all the different ways to add -inputs to your iApp executions using various iExec tools and SDKs. +This page is under development. -::: tip ENS Addresses **ENS (Ethereum Name Service)** is a naming system for -Ethereum addresses that allows you to use human-readable names instead of long -hexadecimal addresses. For example, instead of using `0x1234567890abcdef...`, -you can use `debug-v8-learn.main.pools.iexec.eth`. - -In the examples below, we use `debug-v8-learn.main.pools.iexec.eth` which is -iExec's official debug workerpool ENS address. This workerpool is specifically -designed for testing and development purposes on the Bellecour testnet. ::: - -## Types of Inputs - -iExec supports several types of inputs for iApp executions: - -1. **Arguments**: Command-line arguments passed to the application -2. **Input Files**: URLs to public files that the app can download -3. **Secrets**: Sensitive data like API keys stored securely -4. **Protected Data**: Encrypted data processed within the TEE - -## Method 1: Adding Command-Line Arguments - -Command-line arguments are passed as a string to the iApp and are visible on the -blockchain. - -### Using SDK Library - -```typescript -import { - IExecConfig, - IExecOrderModule, - IExecOrderbookModule, -} from '@iexec/sdk'; - -// create the configuration -const config = new IExecConfig({ ethProvider: window.ethereum }); - -// instantiate modules sharing the same configuration -const orderModule = IExecOrderModule.fromConfig(config); -const orderbookModule = IExecOrderbookModule.fromConfig(config); - -// Basic arguments -const requestOrder = await orderModule.createRequestOrder({ - app: '0x456def...', - appmaxprice: 10, - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool - params: 'arg1 arg2 arg3', // Command-line arguments - // Other parameters have default values -}); - -// Fetch matching orders from orderbook with filters -const appOrders = await orderbookModule.fetchAppOrderbook({ - app: '0x456def...', // Filter by specific app -}); -const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS -}); - -const taskId = await orderModule.matchOrders({ - requestorder: requestOrder, - apporder: appOrders[0], // Select appropriate app order - workerpoolorder: workerpoolOrders[0], // Select appropriate workerpool order -}); - -// Complex arguments with spaces and quotes -const requestOrder = await orderModule.createRequestOrder({ - app: '0x456def...', - appmaxprice: 10, - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool - params: '--input-file data.csv --output-format json --message "Hello World"', - // Other parameters have default values -}); - -const taskId = await orderModule.matchOrders({ - requestorder: requestOrder, - apporder: appOrders[0], - workerpoolorder: workerpoolOrders[0], -}); - -// With protected data -const requestOrder = await orderModule.createRequestOrder({ - app: '0x456def...', - appmaxprice: 10, - dataset: '0x123abc...', // Protected data address - datasetmaxprice: 5, - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool - params: '--input-path data/input.csv --output-format json --verbose', - // Other parameters have default values -}); - -// Fetch matching orders from orderbook with filters -const appOrders = await orderbookModule.fetchAppOrderbook({ - app: '0x456def...', // Filter by specific app -}); -const datasetOrders = await orderbookModule.fetchDatasetOrderbook({ - dataset: '0x123abc...', // Filter by specific dataset -}); -const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS -}); - -const taskId = await orderModule.matchOrders({ - requestorder: requestOrder, - apporder: appOrders[0], - datasetorder: datasetOrders[0], // Select appropriate dataset order - workerpoolorder: workerpoolOrders[0], -}); -``` - -### Using SDK CLI - -```bash -# Basic arguments -iexec app run 0x456def... --protectedData 0x123abc... --args "arg1 arg2" - -# Complex arguments with spaces -iexec app run 0x456def... --protectedData 0x123abc... --args "--input-file data.csv --output-format json" - -# Arguments with quotes (escape properly) -iexec app run 0x456def... --protectedData 0x123abc... --args "--message \"Hello World\" --config '{\"key\": \"value\"}'" -``` - -### Using DataProtector - -```typescript -import { IExecDataProtectorCore } from '@iexec/dataprotector'; - -const dataProtector = new IExecDataProtectorCore(web3Provider); - -// Process protected data with arguments -const result = await dataProtector.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - args: '--input-path data/input.csv --output-format json --verbose', - maxPrice: 10, -}); -``` - -## Method 2: Adding Input Files - -Input files are URLs to public files that the iApp can download during -execution. - -### Using SDK Library - -```typescript -import { - IExecConfig, - IExecOrderModule, - IExecOrderbookModule, -} from '@iexec/sdk'; - -// create the configuration -const config = new IExecConfig({ ethProvider: window.ethereum }); - -// instantiate modules sharing the same configuration -const orderModule = IExecOrderModule.fromConfig(config); -const orderbookModule = IExecOrderbookModule.fromConfig(config); - -// Single input file -const requestOrder = await orderModule.createRequestOrder({ - app: '0x456def...', - appmaxprice: 10, - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool - // Other parameters have default values -}); - -// Fetch matching orders from orderbook with filters -const appOrders = await orderbookModule.fetchAppOrderbook({ - app: '0x456def...', // Filter by specific app -}); -const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS -}); - -const taskId = await orderModule.matchOrders({ - requestorder: requestOrder, - apporder: appOrders[0], - workerpoolorder: workerpoolOrders[0], - inputFiles: ['https://example.com/config.json'], -}); - -// Multiple input files -const requestOrder = await orderModule.createRequestOrder({ - app: '0x456def...', - appmaxprice: 10, - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool - // Other parameters have default values -}); - -const taskId = await orderModule.matchOrders({ - requestorder: requestOrder, - apporder: appOrders[0], - workerpoolorder: workerpoolOrders[0], - inputFiles: [ - 'https://example.com/config.json', - 'https://example.com/template.html', - 'https://example.com/data.csv', - ], -}); - -// With protected data and input files -const requestOrder = await orderModule.createRequestOrder({ - app: '0x456def...', - appmaxprice: 10, - dataset: '0x123abc...', // Protected data address - datasetmaxprice: 5, - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool - // Other parameters have default values -}); - -// Fetch matching orders from orderbook with filters -const appOrders = await orderbookModule.fetchAppOrderbook({ - app: '0x456def...', // Filter by specific app -}); -const datasetOrders = await orderbookModule.fetchDatasetOrderbook({ - dataset: '0x123abc...', // Filter by specific dataset -}); -const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS -}); - -const taskId = await orderModule.matchOrders({ - requestorder: requestOrder, - apporder: appOrders[0], - datasetorder: datasetOrders[0], - workerpoolorder: workerpoolOrders[0], - inputFiles: [ - 'https://raw.githubusercontent.com/user/repo/main/config.json', - 'https://example.com/public-data.csv', - ], -}); -``` - -### Using SDK CLI - -```bash -# Single input file -iexec app run 0x456def... --protectedData 0x123abc... --inputFiles "https://example.com/config.json" - -# Multiple input files (comma-separated) -iexec app run 0x456def... --protectedData 0x123abc... --inputFiles "https://example.com/config.json,https://example.com/template.html" - -# Multiple input files (space-separated) -iexec app run 0x456def... --protectedData 0x123abc... --inputFiles "https://example.com/config.json" --inputFiles "https://example.com/template.html" -``` - -### Using DataProtector - -```typescript -import { IExecDataProtectorCore } from '@iexec/dataprotector'; - -const dataProtector = new IExecDataProtectorCore(web3Provider); - -// Process protected data with input files -const result = await dataProtector.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - inputFiles: [ - 'https://raw.githubusercontent.com/user/repo/main/config.json', - 'https://example.com/public-data.csv', - ], - maxPrice: 10, -}); -``` - -## Method 3: Adding Secrets - -Secrets are sensitive data like API keys, passwords, or tokens that are stored -securely and made available to the iApp as environment variables. - -### Using SDK Library - -```typescript -import { - IExecConfig, - IExecOrderModule, - IExecOrderbookModule, - IExecSecretsModule, -} from '@iexec/sdk'; - -// create the configuration -const config = new IExecConfig({ ethProvider: window.ethereum }); - -// instantiate modules sharing the same configuration -const orderModule = IExecOrderModule.fromConfig(config); -const orderbookModule = IExecOrderbookModule.fromConfig(config); -const secretsModule = IExecSecretsModule.fromConfig(config); - -// Basic secrets -const requestOrder = await orderModule.createRequestOrder({ - app: '0x456def...', - appmaxprice: 10, - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool - // Other parameters have default values -}); - -// Fetch matching orders from orderbook with filters -const appOrders = await orderbookModule.fetchAppOrderbook({ - app: '0x456def...', // Filter by specific app -}); -const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS -}); - -const taskId = await orderModule.matchOrders({ - requestorder: requestOrder, - apporder: appOrders[0], - workerpoolorder: workerpoolOrders[0], - secrets: { - 1: 'api-key-12345', - 2: 'database-password', - }, -}); - -// Multiple secrets -const requestOrder = await orderModule.createRequestOrder({ - app: '0x456def...', - appmaxprice: 10, - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool - // Other parameters have default values -}); - -const taskId = await orderModule.matchOrders({ - requestorder: requestOrder, - apporder: appOrders[0], - workerpoolorder: workerpoolOrders[0], - secrets: { - 1: 'openai-api-key', - 2: 'database-connection-string', - 3: 'jwt-secret', - 4: 'encryption-key', - }, -}); - -// With protected data and secrets -const requestOrder = await orderModule.createRequestOrder({ - app: '0x456def...', - appmaxprice: 10, - dataset: '0x123abc...', // Protected data address - datasetmaxprice: 5, - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool - // Other parameters have default values -}); - -// Fetch matching orders from orderbook with filters -const appOrders = await orderbookModule.fetchAppOrderbook({ - app: '0x456def...', // Filter by specific app -}); -const datasetOrders = await orderbookModule.fetchDatasetOrderbook({ - dataset: '0x123abc...', // Filter by specific dataset -}); -const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS -}); - -const taskId = await orderModule.matchOrders({ - requestorder: requestOrder, - apporder: appOrders[0], - datasetorder: datasetOrders[0], - workerpoolorder: workerpoolOrders[0], - secrets: { - 1: 'openai-api-key', - 2: 'database-password', - }, -}); -``` - -### Using SDK CLI - -```bash -# Note: CLI doesn't support secrets directly for security reasons -# Use the SDK for secret management - -# Alternative: Use environment variables (less secure) -export IEXEC_SECRET_1="api-key-12345" -export IEXEC_SECRET_2="database-password" -iexec app run 0x456def... --protectedData 0x123abc... -``` - -### Using DataProtector - -```typescript -import { IExecDataProtectorCore } from '@iexec/dataprotector'; - -const dataProtector = new IExecDataProtectorCore(web3Provider); - -// Process protected data with secrets -const result = await dataProtector.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - secrets: { - 1: 'openai-api-key', - 2: 'database-password', - }, - maxPrice: 10, -}); -``` - -## Method 4: Specifying File Paths in Protected Data - -When working with protected data that contains multiple files, you can specify -which file to process. - -### Using SDK Library - -```typescript -import { - IExecConfig, - IExecOrderModule, - IExecOrderbookModule, - IExecResultModule, -} from '@iexec/sdk'; - -// create the configuration -const config = new IExecConfig({ ethProvider: window.ethereum }); - -// instantiate modules sharing the same configuration -const orderModule = IExecOrderModule.fromConfig(config); -const orderbookModule = IExecOrderbookModule.fromConfig(config); -const resultModule = IExecResultModule.fromConfig(config); - -// Basic path specification (with protected data) -const requestOrder = await orderModule.createRequestOrder({ - app: '0x456def...', - appmaxprice: 10, - dataset: '0x123abc...', // Protected data address - datasetmaxprice: 5, - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool - // Other parameters have default values -}); - -// Fetch matching orders from orderbook with filters -const appOrders = await orderbookModule.fetchAppOrderbook({ - app: '0x456def...', // Filter by specific app -}); -const datasetOrders = await orderbookModule.fetchDatasetOrderbook({ - dataset: '0x123abc...', // Filter by specific dataset -}); -const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS -}); - -const taskId = await orderModule.matchOrders({ - requestorder: requestOrder, - apporder: appOrders[0], - datasetorder: datasetOrders[0], - workerpoolorder: workerpoolOrders[0], - path: 'my-content', // Extract specific file from protected data -}); - -// Complex path examples (with protected data) -const requestOrder = await orderModule.createRequestOrder({ - app: '0x456def...', - appmaxprice: 10, - dataset: '0x123abc...', // Protected data address - datasetmaxprice: 5, - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool - // Other parameters have default values -}); - -const taskId = await orderModule.matchOrders({ - requestorder: requestOrder, - apporder: appOrders[0], - datasetorder: datasetOrders[0], - workerpoolorder: workerpoolOrders[0], - path: 'data/processed/results.json', -}); - -// Retrieve specific file from task result -const taskResult = await resultModule.getTaskResult({ - taskId: '0x7ac398...', - path: 'computed.json', // Extract specific file from result -}); -``` - -### Using SDK CLI - -```bash -# Basic path specification -iexec app run 0x456def... --protectedData 0x123abc... --path "my-content" - -# Complex path -iexec app run 0x456def... --protectedData 0x123abc... --path "data/processed/results.json" - -# Retrieve result with specific path -iexec task show 0x7ac398... --path "computed.json" -``` - -### Using DataProtector - -```typescript -import { IExecDataProtectorCore } from '@iexec/dataprotector'; - -const dataProtector = new IExecDataProtectorCore(web3Provider); - -// Process protected data with specific path -const result = await dataProtector.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - path: 'data/input.csv', - maxPrice: 10, -}); - -// Get result with specific path -const taskResult = await dataProtector.getResultFromCompletedTask({ - taskId: '0x7ac398...', - path: 'output/analysis.json', -}); -``` - -## Method 5: Combining Multiple Input Types - -You can combine different types of inputs for complex executions. - -### Using SDK Library - -```typescript -import { - IExecConfig, - IExecOrderModule, - IExecOrderbookModule, - IExecSecretsModule, -} from '@iexec/sdk'; - -// create the configuration -const config = new IExecConfig({ ethProvider: window.ethereum }); - -// instantiate modules sharing the same configuration -const orderModule = IExecOrderModule.fromConfig(config); -const orderbookModule = IExecOrderbookModule.fromConfig(config); -const secretsModule = IExecSecretsModule.fromConfig(config); - -// Complete example with all input types -const requestOrder = await orderModule.createRequestOrder({ - app: '0x456def...', - appmaxprice: 10, - dataset: '0x123abc...', // Protected data address - datasetmaxprice: 5, - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool - params: '--mode production --output-format json', - // Other parameters have default values -}); - -// Fetch matching orders from orderbook with filters -const appOrders = await orderbookModule.fetchAppOrderbook({ - app: '0x456def...', // Filter by specific app -}); -const datasetOrders = await orderbookModule.fetchDatasetOrderbook({ - dataset: '0x123abc...', // Filter by specific dataset -}); -const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS -}); - -const taskId = await orderModule.matchOrders({ - requestorder: requestOrder, - apporder: appOrders[0], - datasetorder: datasetOrders[0], - workerpoolorder: workerpoolOrders[0], - inputFiles: [ - 'https://example.com/config.json', - 'https://example.com/template.html', - ], - secrets: { - 1: 'api-key-12345', - 2: 'database-password', - }, - path: 'data/input.csv', -}); -``` - -### Using SDK CLI - -```bash -# Combine multiple input types -iexec app run 0x456def... \ - --protectedData 0x123abc... \ - --args "--mode production --output-format json" \ - --inputFiles "https://example.com/config.json,https://example.com/template.html" \ - --path "data/input.csv" \ - --maxPrice 10 - -# Note: Secrets must be handled via SDK due to CLI limitations -``` - -### Using DataProtector - -```typescript -import { IExecDataProtectorCore } from '@iexec/dataprotector'; - -const dataProtector = new IExecDataProtectorCore(web3Provider); - -// Process protected data with multiple input types -const result = await dataProtector.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - args: '--mode production --output-format json', - inputFiles: [ - 'https://example.com/config.json', - 'https://example.com/template.html', - ], - secrets: { - 1: 'api-key-12345', - 2: 'database-password', - }, - path: 'data/input.csv', - maxPrice: 10, -}); -``` - -## How Secrets Work in iApps - -Inside the iApp, secrets are available as environment variables: - -```python -# Python iApp example -import os - -api_key = os.environ.get('IEXEC_SECRET_1') # 'api-key-12345' -db_password = os.environ.get('IEXEC_SECRET_2') # 'database-password' -``` - -```javascript -// JavaScript iApp example -const apiKey = process.env.IEXEC_SECRET_1; // 'api-key-12345' -const dbPassword = process.env.IEXEC_SECRET_2; // 'database-password' -``` - -## Input Validation and Error Handling - -### Validate Inputs Before Execution - -```typescript -const validateInputs = (params) => { - const errors = []; - - // Validate arguments - if (params.args && params.args.length > 1000) { - errors.push('Arguments too long (max 1000 characters)'); - } - - // Validate input files - if (params.inputFiles) { - params.inputFiles.forEach((url, index) => { - if (!url.startsWith('https://')) { - errors.push(`Input file ${index + 1} must use HTTPS`); - } - }); - } - - // Validate secrets - if (params.secrets) { - Object.keys(params.secrets).forEach((key) => { - if (!/^\d+$/.test(key)) { - errors.push('Secret keys must be numeric'); - } - }); - } - - return errors; -}; - -// Use validation -const params = { - protectedData: '0x123abc...', - app: '0x456def...', - args: '--test', - inputFiles: ['https://example.com/file.json'], - secrets: { 1: 'secret' }, - maxPrice: 10, -}; - -const errors = validateInputs(params); -if (errors.length > 0) { - console.error('Validation errors:', errors); - return; -} - -const result = await dataProtectorCore.processProtectedData(params); -``` - -### Handle Input-Related Errors - -```typescript -try { - const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - args: '--invalid-option', - maxPrice: 10, - }); -} catch (error) { - if (error.message.includes('argument')) { - console.log('Invalid arguments provided'); - } else if (error.message.includes('file')) { - console.log('Input file not accessible'); - } else if (error.message.includes('secret')) { - console.log('Secret configuration error'); - } else { - console.log('Execution failed:', error.message); - } -} -``` - -## Best Practices - -### 1. Use Appropriate Input Types - -```typescript -// ✅ Use secrets for sensitive data -const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - secrets: { 1: 'api-key' }, // Sensitive data - args: '--mode production', // Non-sensitive configuration - maxPrice: 10, -}); -``` - -### 2. Validate URLs and Files - -```typescript -// ✅ Ensure input files are accessible -const inputFiles = [ - 'https://raw.githubusercontent.com/user/repo/main/config.json', - 'https://example.com/public-data.csv', -]; - -// Test file accessibility before execution -const testFileAccess = async (url) => { - try { - const response = await fetch(url, { method: 'HEAD' }); - return response.ok; - } catch { - return false; - } -}; - -const accessibleFiles = await Promise.all( - inputFiles.map(async (url) => ({ - url, - accessible: await testFileAccess(url), - })) -); - -const validFiles = accessibleFiles - .filter((file) => file.accessible) - .map((file) => file.url); -``` - -### 3. Use Descriptive Arguments - -```typescript -// ✅ Clear, descriptive arguments -const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - args: '--input-format csv --output-format json --verbose --log-level info', - maxPrice: 10, -}); -``` - -### 4. Organize Secrets Logically - -```typescript -// ✅ Logical secret numbering -const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - secrets: { - 1: 'primary-api-key', // Main API key - 2: 'backup-api-key', // Backup API key - 3: 'database-password', // Database credentials - 4: 'encryption-key', // Encryption key - }, - maxPrice: 10, -}); -``` - -## Next Steps - -Now that you understand how to add inputs to iApp executions: - -- Learn about - [Using iApps with Protected Data](./use-iapp-with-protected-data.md) -- Explore [Different Ways to Execute](./different-ways-to-execute.md) iApps -- Check out our [How to Pay for Executions](./how-to-pay-executions.md) guide + diff --git a/src/documentation/use-iapp/guides/different-ways-to-execute.md b/src/documentation/use-iapp/guides/different-ways-to-execute.md index c705b1c4..a2efbf89 100644 --- a/src/documentation/use-iapp/guides/different-ways-to-execute.md +++ b/src/documentation/use-iapp/guides/different-ways-to-execute.md @@ -1,120 +1,10 @@ --- -title: Different Ways to Execute iApps -description: - Learn about various methods for executing iApps on the iExec network +title: Different Ways to Execute an iApp +description: Different ways to execute an iApp --- -# ⚡ Different Ways to Execute iApps +# Different Ways to Execute an iApp -There are multiple ways to execute iApps on the iExec network. This guide covers -the basic execution methods. For advanced features like protected data, -arguments, and input files, see the dedicated guides. +This page is under development. -::: tip ENS Addresses **ENS (Ethereum Name Service)** is a naming system for -Ethereum addresses that allows you to use human-readable names instead of long -hexadecimal addresses. For example, instead of using `0x1234567890abcdef...`, -you can use `debug-v8-learn.main.pools.iexec.eth`. - -In the examples below, we use `debug-v8-learn.main.pools.iexec.eth` which is -iExec's official debug workerpool ENS address. This workerpool is specifically -designed for testing and development purposes on the Bellecour testnet. ::: - -## Method 1: Using the iExec SDK Library - -The iExec SDK provides a modular JavaScript interface for executing iApps. - -```typescript -import { - IExecConfig, - IExecOrderModule, - IExecOrderbookModule, -} from '@iexec/sdk'; - -// create the configuration -const config = new IExecConfig({ ethProvider: window.ethereum }); - -// instantiate modules sharing the same configuration -const orderModule = IExecOrderModule.fromConfig(config); -const orderbookModule = IExecOrderbookModule.fromConfig(config); - -// Create a request order -const requestOrder = await orderModule.createRequestOrder({ - app: '0x456def...', // The iApp address - appmaxprice: 10, // Maximum price in nRLC - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool - // Other parameters have default values -}); - -// Fetch matching orders from orderbook with filters -const appOrders = await orderbookModule.fetchAppOrderbook({ - app: '0x456def...', // Filter by specific app -}); -const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS -}); - -// Execute the task -const taskId = await orderModule.matchOrders({ - requestorder: requestOrder, - apporder: appOrders[0], - workerpoolorder: workerpoolOrders[0], -}); -``` - -## Method 2: Using the iExec CLI - -The iExec CLI is perfect for quick executions and automation scripts. - -### Installation - -```bash -npm install -g @iexec/iexec -``` - -### Basic CLI Execution - -```bash -# Execute an iApp -iexec app run 0x456def... - -# Set maximum price -iexec app run 0x456def... --maxPrice 10 -``` - -## Method 3: Using the iApp Generator CLI - -The iApp Generator CLI provides a streamlined way to execute iApps, especially -for developers who have built their own iApps. - -### Installation - -```bash -npm install -g @iexec/iapp -``` - -### Basic Execution - -```bash -# Execute a deployed iApp -iapp run 0x456def... -``` - -### Testing Before Execution - -```bash -# Test the iApp locally first -iapp test -``` - -## When to Use Each Method - -- **iExec Library**: For JavaScript applications and web3 integration -- **iExec CLI**: For quick testing and automation scripts -- **iApp Generator CLI**: For developers who have built their own iApps - -## Next Steps - -- Learn how to - [use iApps with protected data](./use-iapp-with-protected-data.md) -- Discover how to [add inputs to execution](./add-inputs-to-execution.md) -- Understand [how to pay for executions](./how-to-pay-executions.md) + diff --git a/src/documentation/use-iapp/guides/find-iapps.md b/src/documentation/use-iapp/guides/find-iapps.md index 3b1d9f37..2353eed1 100644 --- a/src/documentation/use-iapp/guides/find-iapps.md +++ b/src/documentation/use-iapp/guides/find-iapps.md @@ -1,128 +1,10 @@ --- title: Find iApps to Use -description: Discover and explore available iApps on the iExec network +description: Find iApps to Use --- -# 🔍 Find iApps to Use +# Find iApps to Use -Discovering the right iApp for your needs is the first step toward secure, -privacy-preserving computation. Here are several ways to find and explore -available iApps on the iExec network. +This page is under development. -## Using the iExec Explorer - -The [iExec Explorer](https://explorer.iex.ec) is the primary tool for -discovering iApps. - -### Step-by-Step Discovery - -1. **Visit the Explorer**: Go to [explorer.iex.ec](https://explorer.iex.ec) -2. **Navigate to Apps**: Click on the "Apps" section in the navigation -3. **Search by Name**: Use the search function to find specific applications -4. **View Details**: Click on any app to see detailed information - -### What to Look For - -When evaluating an iApp, check these key details: - -- **Description**: What does the app do? -- **Requirements**: What type of data or inputs does it need? -- **Pricing**: How much does execution cost? -- **Developer**: Who created the app? -- **Usage Statistics**: How often is it used? -- **Orders Availability**: Are there active orders available for execution? - -## Using the iExec CLI - -For developers who prefer command-line tools, the iExec CLI provides -programmatic access to app discovery. - -### Installation - -```bash -npm install -g @iexec/iexec -``` - -### Basic Commands - -```bash -# Note: Use the iExec Explorer for discovering applications -# The CLI is primarily for executing applications, not listing them - -# Get detailed information about a specific app (if you have the address) -iexec app show -``` - -## Popular iApp Categories - -### Communication Apps - -- **Web3Mail**: Send encrypted emails without seeing recipient addresses -- **Web3Telegram**: Send Telegram messages with protected contact lists - -### Data Processing - -- **Content Creator**: Process and deliver content using protected data -- **Data Analytics**: Analyze sensitive datasets without exposing raw data - -### Oracle Services - -- **Oracle Factory**: Create and manage price oracles -- **Data Feeds**: Update external systems with private data - -### AI and Machine Learning - -- **Model Training**: Train AI models on protected datasets -- **Inference Services**: Run AI inference on sensitive data - -## Evaluating App Quality - -Before using an iApp, consider these factors: - -### Reliability - -- **Usage History**: How many times has the app been executed? -- **Success Rate**: What percentage of executions succeed? -- **Developer Reputation**: Is the developer known and trusted? - -### Cost Efficiency - -- **Execution Cost**: Is the price reasonable for the computation? -- **Competitive Pricing**: How does it compare to similar apps? - -## Community Resources - -### Discord Community - -Join the [iExec Discord](https://discord.gg/iexec) to: - -- Ask questions about specific apps -- Get recommendations from other users -- Learn about new apps and updates - -### GitHub Repositories - -Many iApp developers share their code on GitHub: - -- Review the source code -- Check for recent updates -- Report issues or suggest improvements - -### Documentation - -- **App-Specific Docs**: Check if the developer provides detailed documentation -- **Community Guides**: Look for tutorials and guides from the community - -## Next Steps - -Once you've found an iApp that meets your needs: - -1. **Read the Documentation**: Understand how to use the app properly -2. **Check Requirements**: Ensure you have the necessary data and permissions -3. **Test with Small Data**: Start with a small test before processing large - datasets -4. **Monitor Execution**: Track your first few executions to ensure everything - works as expected - -Ready to execute your first iApp? Check out our -[Getting Started Guide](../getting-started.md) for the next steps! + diff --git a/src/documentation/use-iapp/guides/how-to-pay-executions.md b/src/documentation/use-iapp/guides/how-to-pay-executions.md index 8d1b75ca..68a42d82 100644 --- a/src/documentation/use-iapp/guides/how-to-pay-executions.md +++ b/src/documentation/use-iapp/guides/how-to-pay-executions.md @@ -1,396 +1,10 @@ --- -title: How to Pay for iApp Executions -description: - Learn about payment methods, pricing, and cost management for iApp executions +title: How to Pay the Executions +description: How to pay for executions --- -# 💰 How to Pay for iApp Executions +# How to Pay the Executions -Understanding how to pay for iApp executions is crucial for using the iExec -network effectively. This guide covers all payment methods, pricing structures, -and cost management strategies. +This page is under development. -## Payment Methods Overview - -iExec supports multiple payment methods for executing iApps: - -1. **RLC Tokens**: Direct payment using RLC (Request Compute Language) tokens -2. **Vouchers**: Pre-funded vouchers for simplified payment -3. **Mixed Payment**: Combination of RLC and vouchers - -## Method 1: Paying with RLC Tokens - -RLC tokens are the native currency of the iExec network. You need to have RLC in -your wallet to pay for executions. - -### Setting Up RLC Payment - -#### Step 1: Get RLC Tokens - -You can obtain RLC tokens from various exchanges: - -- **Centralized Exchanges**: Binance, Coinbase, Kraken -- **Decentralized Exchanges**: Uniswap, SushiSwap -- **Direct Purchase**: Through iExec's official channels - -#### Step 2: Transfer to iExec Sidechain - -RLC tokens need to be on the iExec sidechain (Bellecour) for payments: - -```typescript -import { IExecConfig, IExecAccountModule } from '@iexec/sdk'; - -// create the configuration -const config = new IExecConfig({ ethProvider: window.ethereum }); - -// instantiate account module -const accountModule = IExecAccountModule.fromConfig(config); - -// Check your balance -const balance = await accountModule.show(); -console.log('Current balance:', balance); - -// Deposit RLC to the sidechain -await accountModule.deposit(100); // Deposit 100 RLC -``` - -#### Step 3: Execute with RLC Payment - -```typescript -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider(window.ethereum); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); - -// Execute with RLC payment (default) -const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - maxPrice: 10, // Maximum price in nRLC - useVoucher: false, // Explicitly use RLC payment -}); -``` - -### Using CLI with RLC - -```bash -# Execute with RLC payment -iexec app run 0x456def... --protectedData 0x123abc... --maxPrice 10 - -# Check your balance -iexec account show - -# Deposit RLC -iexec account deposit 100 -``` - -## Method 2: Paying with Vouchers - -Vouchers are pre-funded payment instruments that simplify the payment process -and can be shared with others. - -### Understanding Vouchers - -Vouchers are ERC-20 tokens that represent pre-funded computation credits on the -iExec network. They offer several advantages: - -- **Simplified Payment**: No need to manage RLC transfers -- **Sharing**: Can be shared with team members or users -- **Budget Control**: Set spending limits -- **Automated Top-up**: Can be configured to automatically refill - -### Using Vouchers for Payment - -#### Basic Voucher Usage - -```typescript -const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - maxPrice: 10, - useVoucher: true, // Use voucher for payment -}); -``` - -::: tip - -If your voucher doesn't have enough xRLC to cover the deal, the SDK will -automatically get the required amount to your iExec account. Ensure that your -voucher is authorized to access your iExec account and that your account has -sufficient funds for this transfer to proceed. - -::: - -#### Using Someone Else's Voucher - -```typescript -const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - maxPrice: 10, - useVoucher: true, - voucherOwner: '0x5714eB...', // Voucher owner's address -}); -``` - -::: warning - -Make sure the voucher's owner has authorized you to use it. This parameter must -be used in combination with `useVoucher: true`. - -::: - -#### CLI with Vouchers - -```bash -# Use your own voucher -iexec app run 0x456def... --protectedData 0x123abc... --useVoucher - -# Use someone else's voucher -iexec app run 0x456def... --protectedData 0x123abc... --useVoucher --voucherOwner 0x5714eB... -``` - -## Understanding Pricing - -### Cost Components - -iApp execution costs consist of several components: - -1. **Application Fee**: Paid to the app developer -2. **Data Fee**: Paid to the data owner (if using protected data) -3. **Workerpool Fee**: Paid to the computation provider -4. **Gas Fees**: Blockchain transaction costs - -### Setting Maximum Prices - -You can control costs by setting maximum prices for each component: - -```typescript -const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - dataMaxPrice: 5, // Maximum amount (in nRLC) to pay the protected data owner - appMaxPrice: 3, // Maximum amount (in nRLC) to pay the iApp provider - workerpoolMaxPrice: 2, // Maximum amount (in nRLC) to pay the workerpool provider -}); -``` - -::: info - -All price parameters are in **nRLC** (nano RLC). The default value for all price -parameters is `0`, which means no maximum price limit is set. - -::: - -## Cost Management Strategies - -### 1. Monitor Your Balance - -Regularly check your RLC balance and voucher status: - -```typescript -// Check RLC balance -const balance = await iexec.account.show(); -console.log('RLC Balance:', balance); - -// Check voucher balance (if applicable) -const voucherBalance = await iexec.voucher.show(); -console.log('Voucher Balance:', voucherBalance); -``` - -### 2. Set Reasonable Price Limits - -Always set maximum prices to avoid unexpected costs: - -```typescript -// Good practice: Set explicit price limits -const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - dataMaxPrice: 5, // Maximum for data access - appMaxPrice: 3, // Maximum for app usage - workerpoolMaxPrice: 2, // Maximum for computation -}); -``` - -### 3. Use Vouchers for Regular Usage - -For frequent executions, consider using vouchers: - -```typescript -// Use vouchers for regular operations -const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - maxPrice: 10, - useVoucher: true, // Simplify payment process -}); -``` - -### 4. Batch Operations - -Group multiple executions to optimize costs: - -```typescript -// Execute multiple tasks efficiently -const tasks = [ - { protectedData: '0x123abc...', app: '0x456def...' }, - { protectedData: '0x789def...', app: '0x456def...' }, -]; - -const results = await Promise.all( - tasks.map((task) => - dataProtectorCore.processProtectedData({ - ...task, - dataMaxPrice: 5, - appMaxPrice: 3, - workerpoolMaxPrice: 2, - useVoucher: true, - }) - ) -); -``` - -## Payment Troubleshooting - -### Insufficient Balance - -If you encounter insufficient balance errors: - -```typescript -try { - const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - maxPrice: 10, - }); -} catch (error) { - if (error.message.includes('insufficient balance')) { - console.log('Please add more RLC to your account'); - // Check balance and deposit more if needed - const balance = await iexec.account.show(); - console.log('Current balance:', balance); - } -} -``` - -### Voucher Authorization Issues - -If voucher payment fails: - -```typescript -try { - const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - maxPrice: 10, - useVoucher: true, - voucherOwner: '0x5714eB...', - }); -} catch (error) { - if (error.message.includes('voucher')) { - console.log('Voucher authorization failed. Check voucher permissions.'); - } -} -``` - -### Price Too High Errors - -If execution fails due to price constraints: - -```typescript -try { - const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - dataMaxPrice: 2, // Low price limit - appMaxPrice: 1, - workerpoolMaxPrice: 1, - }); -} catch (error) { - if (error.message.includes('price')) { - console.log('Increase price limits or wait for lower prices'); - // Retry with higher prices - const retryResult = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - dataMaxPrice: 8, // Higher price limit - appMaxPrice: 5, - workerpoolMaxPrice: 3, - }); - } -} -``` - -## Best Practices - -### 1. Always Set Price Limits - -```typescript -// Never execute without price limits -const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - dataMaxPrice: 5, // Always set price limits - appMaxPrice: 3, - workerpoolMaxPrice: 2, -}); -``` - -### 2. Use Vouchers for Production - -```typescript -// Use vouchers for production applications -const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - maxPrice: 10, - useVoucher: true, // More reliable for production -}); -``` - -### 3. Monitor Costs Regularly - -```typescript -// Check costs before and after execution -const balanceBefore = await iexec.account.show(); -const result = await dataProtectorCore.processProtectedData({ - protectedData: '0x123abc...', - app: '0x456def...', - dataMaxPrice: 5, - appMaxPrice: 3, - workerpoolMaxPrice: 2, -}); -const balanceAfter = await iexec.account.show(); -console.log('Cost:', balanceBefore - balanceAfter); -``` - -### 4. Handle Payment Errors Gracefully - -```typescript -const executeWithPaymentRetry = async (params, maxRetries = 3) => { - for (let i = 0; i < maxRetries; i++) { - try { - return await dataProtectorCore.processProtectedData(params); - } catch (error) { - if (error.message.includes('insufficient balance')) { - console.log('Insufficient balance, please add more RLC'); - break; - } - if (i === maxRetries - 1) throw error; - console.log(`Payment retry ${i + 1}/${maxRetries}`); - await new Promise((resolve) => setTimeout(resolve, 1000)); - } - } -}; -``` - -## Next Steps - -Now that you understand payment methods: - -- Learn about [Adding Inputs to Execution](./add-inputs-to-execution.md) -- Explore [Using iApps with Protected Data](./use-iapp-with-protected-data.md) -- Check out our - [Pricing Considerations](../how-to-pay/pricing-considerations.md) for detailed - cost analysis + diff --git a/src/documentation/use-iapp/guides/use-iapp-with-protected-data.md b/src/documentation/use-iapp/guides/use-iapp-with-protected-data.md index 7351d9a0..74467330 100644 --- a/src/documentation/use-iapp/guides/use-iapp-with-protected-data.md +++ b/src/documentation/use-iapp/guides/use-iapp-with-protected-data.md @@ -1,473 +1,10 @@ --- -title: Use iApps with Protected Data -description: - Learn how to securely process protected data using iApps on the iExec network +title: Use iApp with Protected Data +description: Use iApp with Protected Data --- -# 🔒 Use iApps with Protected Data +# Use iApp with Protected Data -Protected data is the cornerstone of privacy-preserving computation on iExec. -This guide shows you how to use iApps with protected data, from granting access -to processing and retrieving results. +This page is under development. -## Understanding Protected Data and iApps - -Protected data is encrypted information that can only be processed by authorized -iApps within Trusted Execution Environments (TEEs). The data remains -confidential throughout the entire computation process. - -### The Workflow - -1. **Protect Your Data**: Encrypt sensitive information using the Data Protector -2. **Grant Access**: Authorize specific iApps to process your data -3. **Execute iApp**: Run the iApp with your protected data -4. **Retrieve Results**: Get the computation results while data remains private - -## Step 1: Protect Your Data - -Before using an iApp, you need to protect your sensitive data. - -### Basic Data Protection - -```typescript -import { IExecDataProtectorCore, getWeb3Provider } from '@iexec/dataprotector'; - -const web3Provider = getWeb3Provider(window.ethereum); -const dataProtectorCore = new IExecDataProtectorCore(web3Provider); - -// Protect your data -const { address: protectedDataAddress } = await dataProtectorCore.protectData({ - name: 'My Sensitive Data', - data: { - email: 'user@example.com', - apiKey: 'secret-api-key-12345', - preferences: { - theme: 'dark', - notifications: true, - }, - }, -}); -``` - -### Protecting Different Data Types - -```typescript -// Protect contact list for email applications -const { address: contactListAddress } = await dataProtectorCore.protectData({ - name: 'Email Contact List', - data: { - contacts: { - '0x123abc...': 'john@example.com', - '0x456def...': 'jane@example.com', - '0x789ghi...': 'bob@example.com', - }, - }, -}); - -// Protect trading data for oracle applications -const { address: tradingDataAddress } = await dataProtectorCore.protectData({ - name: 'Trading History', - data: { - trades: { - '2024-01-01': { price: 50000, volume: 100 }, - '2024-01-02': { price: 51000, volume: 150 }, - '2024-01-03': { price: 49000, volume: 200 }, - }, - }, -}); - -// Protect financial data for payment applications -const { address: paymentDataAddress } = await dataProtectorCore.protectData({ - name: 'Payment Information', - data: { - bankAccount: '1234567890', - routingNumber: '987654321', - accountHolder: 'John Doe', - }, -}); -``` - -## Step 2: Grant Access to iApps - -iApps need explicit authorization to access your protected data. - -### Grant Access to a Specific iApp - -```typescript -// Grant access to an iApp -const grantedAccess = await dataProtectorCore.grantAccess({ - protectedData: protectedDataAddress, - authorizedApp: '0x456def...', // The iApp address - authorizedUser: '0x789abc...', // Your wallet address - pricePerAccess: 5, // Price per access in nRLC - numberOfAccess: 10, // Number of allowed accesses -}); -``` - -### Check Granted Access - -```typescript -// Check what access you've granted -const grantedAccessList = await dataProtectorCore.getGrantedAccess({ - protectedData: protectedDataAddress, - authorizedApp: '0x456def...', - authorizedUser: '0x789abc...', -}); - -console.log('Granted access:', grantedAccessList); -``` - -## Step 3: Execute iApp with Protected Data - -Once access is granted, you can execute the iApp with your protected data. - -### Using DataProtector - -```typescript -// Execute iApp with protected data -const result = await dataProtectorCore.processProtectedData({ - protectedData: protectedDataAddress, - app: '0x456def...', // The iApp address - maxPrice: 10, // Maximum price in nRLC -}); -``` - -### Using SDK Library - -```typescript -import { - IExecConfig, - IExecOrderModule, - IExecOrderbookModule, -} from '@iexec/sdk'; - -// create the configuration -const config = new IExecConfig({ ethProvider: window.ethereum }); - -// instantiate modules sharing the same configuration -const orderModule = IExecOrderModule.fromConfig(config); -const orderbookModule = IExecOrderbookModule.fromConfig(config); - -// Create a request order with protected data -const requestOrder = await orderModule.createRequestOrder({ - app: '0x456def...', // The iApp address - appmaxprice: 10, // Maximum price in nRLC - dataset: protectedDataAddress, // Protected data address - datasetmaxprice: 5, // Maximum price for dataset access - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // ENS address for iExec's debug workerpool - // Other parameters have default values -}); - -// Fetch matching orders from orderbook with filters -const appOrders = await orderbookModule.fetchAppOrderbook({ - app: '0x456def...', // Filter by specific app -}); -const datasetOrders = await orderbookModule.fetchDatasetOrderbook({ - dataset: protectedDataAddress, // Filter by specific dataset -}); -const workerpoolOrders = await orderbookModule.fetchWorkerpoolOrderbook({ - workerpool: 'debug-v8-learn.main.pools.iexec.eth', // Filter by specific workerpool ENS -}); - -// Execute the task -const taskId = await orderModule.matchOrders({ - requestorder: requestOrder, - apporder: appOrders[0], - datasetorder: datasetOrders[0], - workerpoolorder: workerpoolOrders[0], -}); -``` - -### Using CLI with Protected Data - -```bash -# Execute with protected data -iexec app run 0x456def... --dataset 0x123abc... --maxPrice 10 -``` - -## Step 4: Retrieve Results - -After execution completes, retrieve the results from the task. - -### Using DataProtector - -```typescript -// Get the task ID from the execution result -const taskId = result.taskId; - -// Retrieve the result -const taskResult = await dataProtectorCore.getResultFromCompletedTask({ - taskId: taskId, -}); - -// Retrieve a specific file from the result -const taskResult = await dataProtectorCore.getResultFromCompletedTask({ - taskId: taskId, - path: 'computed.json', // Extract specific file -}); -``` - -### Using SDK Library - -```typescript -import { IExecConfig, IExecResultModule } from '@iexec/sdk'; - -// create the configuration -const config = new IExecConfig({ ethProvider: window.ethereum }); - -// instantiate result module -const resultModule = IExecResultModule.fromConfig(config); - -// Get the task ID from the execution result -const taskId = result.taskId; // or taskId from SDK library execution - -// Retrieve the result -const taskResult = await resultModule.getTaskResult({ - taskId: taskId, -}); - -// Retrieve a specific file from the result -const specificFile = await resultModule.getTaskResult({ - taskId: taskId, - path: 'computed.json', // Extract specific file -}); -``` - -### Using CLI - -```bash -# Get the task ID from the execution result -TASK_ID="0x7ac398..." - -# Retrieve the result -iexec task show $TASK_ID - -# Retrieve a specific file from the result -iexec task show $TASK_ID --path "computed.json" -``` - -## Real-World Examples - -### Example 1: Data Analysis System - -```typescript -// 1. Protect sensitive dataset -const { address: datasetAddress } = await dataProtectorCore.protectData({ - name: 'Customer Analytics Data', - data: { - customers: [ - { id: 1, purchases: 1500, category: 'premium' }, - { id: 2, purchases: 800, category: 'standard' }, - { id: 3, purchases: 2200, category: 'premium' }, - ], - }, -}); - -// 2. Grant access to analytics iApp -await dataProtectorCore.grantAccess({ - protectedData: datasetAddress, - authorizedApp: '0xanalytics...', // Analytics iApp address - authorizedUser: '0x789abc...', - pricePerAccess: 3, - numberOfAccess: 50, -}); - -// 3. Execute data analysis -const analysisResult = await dataProtectorCore.processProtectedData({ - protectedData: datasetAddress, - app: '0xanalytics...', - args: '--analyze-customer-segments --output-format json', - maxPrice: 10, -}); -``` - -### Example 2: Oracle Price Update - -```typescript -// 1. Protect trading data -const { address: tradingDataAddress } = await dataProtectorCore.protectData({ - name: 'Trading Data', - data: { - trades: { - '2024-01-01': { price: 50000, volume: 100 }, - '2024-01-02': { price: 51000, volume: 150 }, - }, - }, -}); - -// 2. Grant access to oracle iApp -await dataProtectorCore.grantAccess({ - protectedData: tradingDataAddress, - authorizedApp: '0xoracle...', // Oracle iApp address - authorizedUser: '0x789abc...', - pricePerAccess: 5, - numberOfAccess: 100, -}); - -// 3. Execute oracle update -const oracleResult = await dataProtectorCore.processProtectedData({ - protectedData: tradingDataAddress, - app: '0xoracle...', - args: '--update-price-feed --asset ETH', - maxPrice: 10, -}); -``` - -### Example 3: Automated Payment Processing - -```typescript -// 1. Protect payment data -const { address: paymentDataAddress } = await dataProtectorCore.protectData({ - name: 'Payment Data', - data: { - bankAccount: '1234567890', - routingNumber: '987654321', - accountHolder: 'John Doe', - monthlyAmount: 1000, - }, -}); - -// 2. Grant access to payment iApp -await dataProtectorCore.grantAccess({ - protectedData: paymentDataAddress, - authorizedApp: '0xpayment...', // Payment iApp address - authorizedUser: '0x789abc...', - pricePerAccess: 2, - numberOfAccess: 12, // Monthly payments -}); - -// 3. Execute payment processing -const paymentResult = await dataProtectorCore.processProtectedData({ - protectedData: paymentDataAddress, - app: '0xpayment...', - args: '--process-monthly-payment', - secrets: { - 1: 'bank-api-key', - }, - maxPrice: 8, -}); -``` - -## Advanced Patterns - -### Pattern 1: Batch Processing - -```typescript -// Process multiple protected datasets -const datasets = [ - { address: '0x123abc...', name: 'Dataset 1' }, - { address: '0x456def...', name: 'Dataset 2' }, - { address: '0x789ghi...', name: 'Dataset 3' }, -]; - -const batchResults = await Promise.all( - datasets.map((dataset) => - dataProtectorCore.processProtectedData({ - protectedData: dataset.address, - app: '0x456def...', - args: `--dataset-name ${dataset.name}`, - maxPrice: 10, - }) - ) -); -``` - -### Pattern 2: Result Processing Pipeline - -```typescript -// Process results and use them for further computation -const initialResult = await dataProtectorCore.processProtectedData({ - protectedData: protectedDataAddress, - app: '0x456def...', - maxPrice: 10, -}); - -// Get the result -const taskResult = await dataProtectorCore.getResultFromCompletedTask({ - taskId: initialResult.taskId, -}); - -// Use the result for further processing -const processedData = await processResult(taskResult); - -// Protect the processed data -const { address: newProtectedDataAddress } = - await dataProtectorCore.protectData({ - name: 'Processed Data', - data: processedData, - }); -``` - -## Best Practices - -### 1. Always Grant Access Before Execution - -```typescript -// ✅ Good: Grant access first -await dataProtectorCore.grantAccess({ - protectedData: protectedDataAddress, - authorizedApp: '0x456def...', - authorizedUser: '0x789abc...', - pricePerAccess: 5, - numberOfAccess: 10, -}); - -const result = await dataProtectorCore.processProtectedData({ - protectedData: protectedDataAddress, - app: '0x456def...', - maxPrice: 10, -}); -``` - -### 2. Monitor Access Usage - -```typescript -// Check access usage regularly -const grantedAccess = await dataProtectorCore.getGrantedAccess({ - protectedData: protectedDataAddress, - authorizedApp: '0x456def...', - authorizedUser: '0x789abc...', -}); - -console.log('Remaining access:', grantedAccess.remainingAccess); -console.log('Used access:', grantedAccess.usedAccess); -``` - -### 3. Use Appropriate Price Limits - -```typescript -// Set reasonable price limits -const result = await dataProtectorCore.processProtectedData({ - protectedData: protectedDataAddress, - app: '0x456def...', - maxPrice: 10, // Set appropriate limit -}); -``` - -### 4. Handle Results Properly - -```typescript -// Store task ID and retrieve results later -const result = await dataProtectorCore.processProtectedData({ - protectedData: protectedDataAddress, - app: '0x456def...', - maxPrice: 10, -}); - -// Store task ID for later retrieval -const taskId = result.taskId; -localStorage.setItem('lastTaskId', taskId); - -// Later, retrieve the result -const taskResult = await dataProtectorCore.getResultFromCompletedTask({ - taskId: taskId, -}); -``` - -## Next Steps - -Now that you understand how to use iApps with protected data: - -- Learn about [Different Ways to Execute](./different-ways-to-execute.md) iApps -- Explore [How to Pay for Executions](./how-to-pay-executions.md) -- Check out our [Add Inputs to Execution](./add-inputs-to-execution.md) guide + diff --git a/src/documentation/use-iapp/introduction.md b/src/documentation/use-iapp/introduction.md index 2d331c5d..3434e4f6 100644 --- a/src/documentation/use-iapp/introduction.md +++ b/src/documentation/use-iapp/introduction.md @@ -1,53 +1,12 @@ --- title: Introduction to Using iApps description: - Learn how to use iExec Applications (iApps) to securely process protected data - in a privacy-safe environment + Learn how to discover, execute, and interact with iExec applications for + privacy-preserving computation --- -# 📝 Introduction to Using iApps +# 📝 Introduction -iExec Applications (iApps) are your gateway to secure, privacy-preserving -computation on the iExec network. These applications run inside Trusted -Execution Environments (TEEs) like Intel SGX or Intel TDX, ensuring your data -remains confidential even during processing. +This page is under development. -## What are iApps? - -iApps are regular applications (Python scripts, AI models, data processors, -etc.) that have been adapted to run securely on the iExec network. They can -process protected data without ever seeing the raw information, making them -perfect for scenarios where data privacy is crucial. - -## Key Benefits - -- **🔒 Privacy-First**: Your data never leaves the secure TEE environment -- **⚡ Trusted Execution**: Applications run in hardware-protected environments -- **🌐 Decentralized**: No single point of failure or control -- **💰 Cost-Effective**: Pay only for the computation you need -- **🔧 Developer-Friendly**: Use familiar programming languages and tools - -## How iApps Work - -1. **Data Protection**: Users protect their sensitive data using the - [Data Protector](/documentation/manage-data/dataProtector/dataProtectorCore/protectData) -2. **Application Deployment**: Developers deploy their applications to the iExec - network -3. **Secure Processing**: iApps process protected data inside TEEs without - accessing raw data -4. **Result Delivery**: Only the computation results are returned, keeping - original data private - -## Use Cases - -- **Email Notifications**: Send emails without seeing recipient addresses -- **Oracle Updates**: Update price feeds using private trading data -- **Automated Transactions**: Process payments with protected financial data -- **AI Model Training**: Train models on sensitive datasets -- **Data Analytics**: Analyze private data without exposing it - -## Getting Started - -Ready to start using iApps? Check out our -[Getting Started Guide](./getting-started.md) to learn how to find, execute, and -interact with iApps on the iExec network. + diff --git a/src/documentation/use-iapp/web3mail/methods/sendEmail.md b/src/documentation/use-iapp/web3mail/methods/sendEmail.md index 432d8a9f..faba89d4 100644 --- a/src/documentation/use-iapp/web3mail/methods/sendEmail.md +++ b/src/documentation/use-iapp/web3mail/methods/sendEmail.md @@ -16,7 +16,7 @@ communications and permission must be granted for the `Web3Mail` tool to use the `protectedData` entity containing their email address. This is best done by granting authorization to the Web3Mail app whitelist `0x781482C39CcE25546583EaC4957Fb7Bf04C277D2` as `authorizedApp`. Refer to the -[Data Protector `grantAccess`](/documentation/manage-data/dataProtector/dataProtectorCore/grantAccess.md) +[Data Protector `grantAccess`](/documentation/manage-data/dataProtector/dataProtectorCore/grantAccess) documentation for more details. ::: tip From 3828cb99012c25c63cd73ff18af5ae698ba8f4c8 Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Tue, 12 Aug 2025 00:12:36 +0200 Subject: [PATCH 4/8] Enhance sidebar structure: add collapsible sections for better navigation and restore TOOLING & EXPLORERS section with relevant links. --- .vitepress/sidebar.ts | 79 +++++++++++++++++++++++++++---------------- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/.vitepress/sidebar.ts b/.vitepress/sidebar.ts index f68ba174..fa817dab 100644 --- a/.vitepress/sidebar.ts +++ b/.vitepress/sidebar.ts @@ -10,6 +10,7 @@ export function getSidebar() { { text: '👋 Hello World', link: '/documentation/helloWorld', + collapsed: true, items: [ { text: 'iExec Overview', @@ -19,7 +20,10 @@ export function getSidebar() { text: 'Protect Data', link: '/documentation/helloWorld/2-protectData', }, - { text: 'Build iApp', link: '/documentation/helloWorld/3-buildIApp' }, + { + text: 'Build iApp', + link: '/documentation/helloWorld/3-buildIApp', + }, { text: 'Manage Data Access', link: '/documentation/helloWorld/4-manageDataAccess', @@ -48,40 +52,16 @@ export function getSidebar() { }, ], }, - { - text: 'TOOLING & EXPLORERS', - items: [ - { - text: 'iExec Explorer', - link: '/documentation/tooling-and-explorers/iexec-explorer', - }, - { - text: 'Builder Dashboard', - link: '/documentation/tooling-and-explorers/builder-dashboard', - }, - { - text: 'RLC Bridge', - link: '/documentation/tooling-and-explorers/bridge', - }, - { - text: 'Subgraph Explorer', - link: '/documentation/tooling-and-explorers/subgraph-explorer', - }, - { - text: 'Blockchain Explorer', - link: '/documentation/tooling-and-explorers/blockchain-explorer', - }, - ], - }, { text: 'PROTECT AND MANAGE DATA', items: [ { - text: '❓  What is Protected Data?', + text: '❓  What is Protected Data ?', link: '/documentation/manage-data/what-is-protected-data', }, { text: '📖 Guides', + collapsed: true, items: [ { text: 'Manage Access', @@ -100,6 +80,7 @@ export function getSidebar() { { text: '🔐 DataProtector', link: '/documentation/manage-data/dataProtector', + collapsed: true, items: [ { text: 'Getting Started', @@ -331,9 +312,13 @@ export function getSidebar() { { text: 'BUILD YOUR iAPP', items: [ - { text: '❓ What is an iApp?', link: '/documentation/build-iapp/what-is-iapp' }, + { + text: '❓ What is an iApp ?', + link: '/documentation/build-iapp/what-is-iapp', + }, { text: '📖 Guides', + collapsed: true, items: [ { text: 'Build and Deploy', @@ -364,6 +349,7 @@ export function getSidebar() { { text: '🤖 iApp Generator', link: '/documentation/build-iapp/iapp-generator', + collapsed: true, items: [ { text: 'Getting Started', @@ -391,10 +377,17 @@ export function getSidebar() { { text: 'USE AN iAPP', items: [ - { text: '📝 Introduction', link: '/documentation/use-iapp/introduction' }, - { text: '🚀 Getting Started', link: '/documentation/use-iapp/getting-started' }, + { + text: '📝 Introduction', + link: '/documentation/use-iapp/introduction', + }, + { + text: '🚀 Getting Started', + link: '/documentation/use-iapp/getting-started', + }, { text: '📖 Guides', + collapsed: true, items: [ { text: 'Different Ways to Execute an iApp', @@ -420,6 +413,7 @@ export function getSidebar() { }, { text: '💰 How to Pay', + collapsed: true, items: [ { text: 'How to Pay for Web3Mail', @@ -511,6 +505,31 @@ export function getSidebar() { }, ], }, + { + text: 'TOOLING & EXPLORERS', + items: [ + { + text: 'iExec Explorer', + link: '/documentation/tooling-and-explorers/iexec-explorer', + }, + { + text: 'Builder Dashboard', + link: '/documentation/tooling-and-explorers/builder-dashboard', + }, + { + text: 'RLC Bridge', + link: '/documentation/tooling-and-explorers/bridge', + }, + { + text: 'Subgraph Explorer', + link: '/documentation/tooling-and-explorers/subgraph-explorer', + }, + { + text: 'Blockchain Explorer', + link: '/documentation/tooling-and-explorers/blockchain-explorer', + }, + ], + }, { text: 'PROTOCOL', items: [ From e4c846022f229ce0938a1cd335fa722ae86ed58d Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Tue, 12 Aug 2025 00:37:12 +0200 Subject: [PATCH 5/8] Add Proof of Contribution documentation and worker management guides - Introduced a comprehensive documentation for the Proof of Contribution (PoCo) protocol, detailing its objectives, workflow, and consensus mechanism. - Added a section on managing workerpool access, including how to deploy a workerpool, publish and remove orders, and manage monetization rules. - Created a quick start guide for workers, outlining the steps to connect to a workerpool, start a worker, and wallet restrictions. --- .vitepress/sidebar.ts | 32 +- .../build-iapp/guides/debugging.md | 4 +- .../guides/how-to-get-and-decrypt-results.md | 15 +- src/documentation/build-iapp/guides/index.md | 31 +- .../build-iapp/guides/inputs-and-outputs.md | 4 +- .../build-iapp/guides/using-tdx.md | 8 +- .../build-iapp/iapp-generator.md | 37 +- src/documentation/build-iapp/what-is-iapp.md | 7 +- .../dataProtector/advanced/apps-whitelist.md | 7 +- .../advanced/dps-smart-contract.md | 3 +- .../guides/handle-schemas-dataset-types.md | 12 +- .../manage-data/guides/manage-access.md | 6 +- .../guides/monetize-protected-data.md | 17 +- src/documentation/protocol/glossary.md | 368 ++++++- .../protocol/poco/pay-per-task.md | 34 + .../protocol/poco/proof-of-contribution.md | 896 ++++++++++++++++++ .../protocol/worker/manage-access.md | 108 +++ .../protocol/worker/quick-start.md | 46 + src/documentation/protocol/workers.md | 12 - src/documentation/use-iapp/guides/index.md | 40 +- src/documentation/use-iapp/web3mail.md | 16 +- src/documentation/use-iapp/web3telegram.md | 4 +- 22 files changed, 1599 insertions(+), 108 deletions(-) create mode 100644 src/documentation/protocol/poco/pay-per-task.md create mode 100644 src/documentation/protocol/poco/proof-of-contribution.md create mode 100644 src/documentation/protocol/worker/manage-access.md create mode 100644 src/documentation/protocol/worker/quick-start.md delete mode 100644 src/documentation/protocol/workers.md diff --git a/.vitepress/sidebar.ts b/.vitepress/sidebar.ts index fa817dab..d9426cb2 100644 --- a/.vitepress/sidebar.ts +++ b/.vitepress/sidebar.ts @@ -534,15 +534,39 @@ export function getSidebar() { text: 'PROTOCOL', items: [ { - text: '🔧  iExec SDK', + text: '🔧 iExec SDK', link: '/documentation/protocol/sdk', }, { - text: '⚙️  Workers & Workerpools', - link: '/documentation/protocol/workers', + text: '⚙️ Workers & Workerpools', + collapsed: true, + items: [ + { + text: '🚀 Worker Quick Start', + link: '/documentation/protocol/worker/quick-start', + }, + { + text: '🔒 Manage Workerpool Access', + link: '/documentation/protocol/worker/manage-access', + }, + ], + }, + { + text: '⚙️ PoCo', + collapsed: true, + items: [ + { + text: '� Pay Per Task Model', + link: '/documentation/protocol/poco/pay-per-task', + }, + { + text: '🛡️ Proof of Contribution', + link: '/documentation/protocol/poco/proof-of-contribution', + }, + ], }, { - text: '📖  Glossary', + text: '�📖 Glossary', link: '/documentation/protocol/glossary', }, ], diff --git a/src/documentation/build-iapp/guides/debugging.md b/src/documentation/build-iapp/guides/debugging.md index 769df047..6bc6ea84 100644 --- a/src/documentation/build-iapp/guides/debugging.md +++ b/src/documentation/build-iapp/guides/debugging.md @@ -169,7 +169,7 @@ except Exception as e: Continue improving your iApps: -- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - Handle data - in TEE +- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - + Handle data in TEE - **[How to Get and Decrypt Results](/documentation/build-iapp/guides/how-to-get-and-decrypt-results)** - Retrieve results diff --git a/src/documentation/build-iapp/guides/how-to-get-and-decrypt-results.md b/src/documentation/build-iapp/guides/how-to-get-and-decrypt-results.md index 1b99c031..b4f72fbf 100644 --- a/src/documentation/build-iapp/guides/how-to-get-and-decrypt-results.md +++ b/src/documentation/build-iapp/guides/how-to-get-and-decrypt-results.md @@ -300,15 +300,16 @@ iexec task show Integrate result handling into your applications: -- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - Understand - what your iApp can output +- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - + Understand what your iApp can output - **[Debugging Your iApp](/documentation/build-iapp/guides/debugging)** - Troubleshoot execution issues -- **[App Access Control and Pricing](/documentation/build-iapp/guides/manage-access)** - Control who - can run your iApp +- **[App Access Control and Pricing](/documentation/build-iapp/guides/manage-access)** - + Control who can run your iApp ### Advanced Topics -- **[DataProtector SDK](/documentation/manage-data/dataProtector)** - Complete SDK - documentation -- **[SDK Deep Dive](/documentation/protocol/sdk)** - Advanced result handling techniques +- **[DataProtector SDK](/documentation/manage-data/dataProtector)** - Complete + SDK documentation +- **[SDK Deep Dive](/documentation/protocol/sdk)** - Advanced result handling + techniques diff --git a/src/documentation/build-iapp/guides/index.md b/src/documentation/build-iapp/guides/index.md index 3769d00e..8fca2266 100644 --- a/src/documentation/build-iapp/guides/index.md +++ b/src/documentation/build-iapp/guides/index.md @@ -5,28 +5,39 @@ description: Complete guide collection for building privacy-first iApps on iExec # 🛠️ Build iApp Guides -Welcome to the complete collection of guides for building privacy-first applications on iExec. These guides will walk you through every aspect of creating, testing, and deploying confidential iApps. +Welcome to the complete collection of guides for building privacy-first +applications on iExec. These guides will walk you through every aspect of +creating, testing, and deploying confidential iApps. ## 🚀 Getting Started -- **[Build & Deploy](/documentation/build-iapp/guides/build-&-deploy)** - Your first iApp in 15 minutes -- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - Handle data flow in TEE environment -- **[Debugging](/documentation/build-iapp/guides/debugging)** - Troubleshoot execution issues +- **[Build & Deploy](/documentation/build-iapp/guides/build-&-deploy)** - Your + first iApp in 15 minutes +- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - + Handle data flow in TEE environment +- **[Debugging](/documentation/build-iapp/guides/debugging)** - Troubleshoot + execution issues ## 🔐 Access Control & Management -- **[Manage Access](/documentation/build-iapp/guides/manage-access)** - Control who can use your iApp -- **[How to Get and Decrypt Results](/documentation/build-iapp/guides/how-to-get-and-decrypt-results)** - Retrieve and use outputs +- **[Manage Access](/documentation/build-iapp/guides/manage-access)** - Control + who can use your iApp +- **[How to Get and Decrypt Results](/documentation/build-iapp/guides/how-to-get-and-decrypt-results)** - + Retrieve and use outputs ## 🧪 Advanced Features -- **[Using TDX (Experimental)](/documentation/build-iapp/guides/using-tdx)** - Next-gen TEE technology +- **[Using TDX (Experimental)](/documentation/build-iapp/guides/using-tdx)** - + Next-gen TEE technology ## 📚 What's Next? After mastering these guides, explore: -- **[iApp Generator](/documentation/build-iapp/iapp-generator)** - Complete development toolkit -- **[What is an iApp?](/documentation/build-iapp/what-is-iapp)** - Core concepts and TEE overview +- **[iApp Generator](/documentation/build-iapp/iapp-generator)** - Complete + development toolkit +- **[What is an iApp?](/documentation/build-iapp/what-is-iapp)** - Core concepts + and TEE overview -Ready to build your first privacy-preserving application? Start with the [Build & Deploy](/documentation/build-iapp/guides/build-&-deploy) guide! +Ready to build your first privacy-preserving application? Start with the +[Build & Deploy](/documentation/build-iapp/guides/build-&-deploy) guide! diff --git a/src/documentation/build-iapp/guides/inputs-and-outputs.md b/src/documentation/build-iapp/guides/inputs-and-outputs.md index 781ac9d2..1fb65e15 100644 --- a/src/documentation/build-iapp/guides/inputs-and-outputs.md +++ b/src/documentation/build-iapp/guides/inputs-and-outputs.md @@ -627,8 +627,8 @@ guide for the complete user workflow. Continue building with these guides: -- **[App Access Control and Pricing](/documentation/build-iapp/guides/manage-access)** - Control who - can use your iApp +- **[App Access Control and Pricing](/documentation/build-iapp/guides/manage-access)** - + Control who can use your iApp - **[Debugging Your iApp](/documentation/build-iapp/guides/debugging)** - Troubleshoot execution issues - **[How to Get and Decrypt Results](/documentation/build-iapp/guides/how-to-get-and-decrypt-results)** - diff --git a/src/documentation/build-iapp/guides/using-tdx.md b/src/documentation/build-iapp/guides/using-tdx.md index bb1bef73..d5cb9169 100644 --- a/src/documentation/build-iapp/guides/using-tdx.md +++ b/src/documentation/build-iapp/guides/using-tdx.md @@ -186,7 +186,7 @@ EXPERIMENTAL_TDX_APP=true iapp run - **[Debugging Your iApp](/documentation/build-iapp/guides/debugging)** - Troubleshoot execution issues -- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - Handle data - in TEE environment -- **[App Access Control and Pricing](/documentation/build-iapp/guides/manage-access)** - Deploy - production-ready iApps +- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - + Handle data in TEE environment +- **[App Access Control and Pricing](/documentation/build-iapp/guides/manage-access)** - + Deploy production-ready iApps diff --git a/src/documentation/build-iapp/iapp-generator.md b/src/documentation/build-iapp/iapp-generator.md index e51a918e..fc2f54e6 100644 --- a/src/documentation/build-iapp/iapp-generator.md +++ b/src/documentation/build-iapp/iapp-generator.md @@ -41,10 +41,10 @@ minutes, not months. Start here to understand what iApps are and how they work: -- **[What Is an iApp?](/documentation/build-iapp/what-is-iapp)** - Core - concepts and TEE overview -- **[Getting Started](/documentation/build-iapp/iapp-generator/getting-started)** - Your first - iApp in 15 minutes +- **[What Is an iApp?](/documentation/build-iapp/what-is-iapp)** - Core concepts + and TEE overview +- **[Getting Started](/documentation/build-iapp/iapp-generator/getting-started)** - + Your first iApp in 15 minutes - **[Building Your iApp](/documentation/build-iapp/iapp-generator/building-your-iexec-app)** - Complete development guide @@ -52,12 +52,12 @@ Start here to understand what iApps are and how they work: Once you've built your first iApp, level up with these practical guides: -- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - Handle data - flow in TEE environment +- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - + Handle data flow in TEE environment - **[Debugging Your iApp](/documentation/build-iapp/guides/debugging)** - Troubleshoot execution issues -- **[App Access Control and Pricing](/documentation/build-iapp/guides/manage-access)** - Control who - can use your iApp +- **[App Access Control and Pricing](/documentation/build-iapp/guides/manage-access)** - + Control who can use your iApp - **[How to Get and Decrypt Results](/documentation/build-iapp/guides/how-to-get-and-decrypt-results)** - Retrieve and use outputs @@ -67,8 +67,8 @@ Ready for production? Dive into specialized topics: - **[Using TDX (Experimental)](/documentation/build-iapp/guides/using-tdx)** - Next-gen TEE technology -- **[Complete Guides Overview](/documentation/build-iapp/guides/)** - All development guides in - one place +- **[Complete Guides Overview](/documentation/build-iapp/guides/)** - All + development guides in one place ## Why Choose iApp Generator? @@ -99,16 +99,19 @@ applications: ::: tip Quick Path -1. **[Getting Started](/documentation/build-iapp/iapp-generator/getting-started)** - Build - your first iApp (15 minutes) -2. **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - Handle data - properly -3. **[Debugging](/documentation/build-iapp/guides/debugging)** - Fix issues quickly -4. **[App Access Control](/documentation/build-iapp/guides/manage-access)** - Go to production ::: +1. **[Getting Started](/documentation/build-iapp/iapp-generator/getting-started)** - + Build your first iApp (15 minutes) +2. **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - + Handle data properly +3. **[Debugging](/documentation/build-iapp/guides/debugging)** - Fix issues + quickly +4. **[App Access Control](/documentation/build-iapp/guides/manage-access)** - Go + to production ::: ### Need Help? -- **[Complete Guides](/documentation/build-iapp/guides/)** - All development guides +- **[Complete Guides](/documentation/build-iapp/guides/)** - All development + guides - **[iExec Discord](https://discord.com/invite/pbt9m98wnU)** - Community support - **[Protocol Documentation](https://protocol.docs.iex.ec)** - Technical deep dive diff --git a/src/documentation/build-iapp/what-is-iapp.md b/src/documentation/build-iapp/what-is-iapp.md index cb7181e7..25260129 100644 --- a/src/documentation/build-iapp/what-is-iapp.md +++ b/src/documentation/build-iapp/what-is-iapp.md @@ -7,8 +7,8 @@ description: Privacy-first applications that run on decentralized infrastructure An iExec Application (iApp) is your regular application code (Python script, AI model, data processor, ...) that can securely process protected data (created by -[DataProtector](/documentation/manage-data/dataProtector)) inside a confidential computing -environment called TEE (a Trusted Execution Environment). +[DataProtector](/documentation/manage-data/dataProtector)) inside a confidential +computing environment called TEE (a Trusted Execution Environment). ## Why iApps Matter ? @@ -161,7 +161,8 @@ iApp can't see what's happening inside the enclave. ::: details 🚀 How do I deploy my first iApp? Try our [Hello World](/documentation/helloWorld) for a quick start, or check the -[iApp Generator](/documentation/build-iapp/iapp-generator) section for detailed instructions. +[iApp Generator](/documentation/build-iapp/iapp-generator) section for detailed +instructions. ::: diff --git a/src/documentation/manage-data/dataProtector/advanced/apps-whitelist.md b/src/documentation/manage-data/dataProtector/advanced/apps-whitelist.md index 026a8e08..b2a14726 100644 --- a/src/documentation/manage-data/dataProtector/advanced/apps-whitelist.md +++ b/src/documentation/manage-data/dataProtector/advanced/apps-whitelist.md @@ -12,7 +12,8 @@ In order to consume a protected data, an iExec TEE dApp needs to be provided. ::: tip -**TEE** stands for Trusted Execution Environment. Find more details [here](https://protocol.docs.iex.ec/help/glossary#trusted-execution-environment-tee) +**TEE** stands for Trusted Execution Environment. Find more details +[here](https://protocol.docs.iex.ec/help/glossary#trusted-execution-environment-tee) ::: @@ -33,8 +34,8 @@ The story goes as follow: ## Protected Data Delivery iApp Built for the needs of -[Content Creator use case](https://demo.iex.ec/content-creator/), -this iExec TEE dApp is simple: +[Content Creator use case](https://demo.iex.ec/content-creator/), this iExec TEE +dApp is simple: 1. Download the protected data from IPFS. It expects to find a property named `file` in the protected data. diff --git a/src/documentation/manage-data/dataProtector/advanced/dps-smart-contract.md b/src/documentation/manage-data/dataProtector/advanced/dps-smart-contract.md index 762f3304..08f2e408 100644 --- a/src/documentation/manage-data/dataProtector/advanced/dps-smart-contract.md +++ b/src/documentation/manage-data/dataProtector/advanced/dps-smart-contract.md @@ -16,7 +16,8 @@ data, and their owners. ## Code -You can find the Solidity code [here](https://github.com/iExecBlockchainComputing/dataprotector-sdk/tree/main/packages/sharing-smart-contract) +You can find the Solidity code +[here](https://github.com/iExecBlockchainComputing/dataprotector-sdk/tree/main/packages/sharing-smart-contract) ## DataProtectorSharing diff --git a/src/documentation/manage-data/guides/handle-schemas-dataset-types.md b/src/documentation/manage-data/guides/handle-schemas-dataset-types.md index c8d33475..e67cb050 100644 --- a/src/documentation/manage-data/guides/handle-schemas-dataset-types.md +++ b/src/documentation/manage-data/guides/handle-schemas-dataset-types.md @@ -253,8 +253,8 @@ don't match, you'll get runtime errors when processing the data. ::: → **Ready to build an iApp?** Check out our detailed -[Inputs and Outputs guide](/documentation/build-iapp/guides/inputs-and-outputs) to learn how -to access schema fields inside your iApp using the deserializer. +[Inputs and Outputs guide](/documentation/build-iapp/guides/inputs-and-outputs) +to learn how to access schema fields inside your iApp using the deserializer. ## Next Steps @@ -262,10 +262,10 @@ to access schema fields inside your iApp using the deserializer. explore next: - **Build an iApp**: Check out the - [iApp Generator guide](/documentation/build-iapp/iapp-generator) to create your first data - processor + [iApp Generator guide](/documentation/build-iapp/iapp-generator) to create + your first data processor - **Process data**: Learn about [processProtectedData](/documentation/manage-data/dataProtector/dataProtectorCore/processProtectedData) for running computations -- **See it in action**: Try our [Hello World tutorial](/documentation/helloWorld) for - a complete example +- **See it in action**: Try our + [Hello World tutorial](/documentation/helloWorld) for a complete example diff --git a/src/documentation/manage-data/guides/manage-access.md b/src/documentation/manage-data/guides/manage-access.md index 843f0083..ab7438ba 100644 --- a/src/documentation/manage-data/guides/manage-access.md +++ b/src/documentation/manage-data/guides/manage-access.md @@ -66,7 +66,8 @@ console.log('Protected data address:', protectedData.address); computations by authorized users and iApps. **Supported types**: Common data types like text, numbers, true/false values, -and files. See the [full list here](/documentation/manage-data/dataProtector/types). +and files. See the +[full list here](/documentation/manage-data/dataProtector/types). **Storage**: Store your data on IPFS or Arweave. For larger files, you can use your own IPFS node. @@ -221,7 +222,8 @@ steps: [processProtectedData](/documentation/manage-data/dataProtector/dataProtectorCore/processProtectedData) to run computations - **Manage access**: - [Revoke](/documentation/manage-data/dataProtector/dataProtectorCore/revokeOneAccess) or + [Revoke](/documentation/manage-data/dataProtector/dataProtectorCore/revokeOneAccess) + or [modify permissions](/documentation/manage-data/dataProtector/dataProtectorCore/grantAccess) anytime - **Learn data types**: Deep dive into diff --git a/src/documentation/manage-data/guides/monetize-protected-data.md b/src/documentation/manage-data/guides/monetize-protected-data.md index b2f0e517..50f58314 100644 --- a/src/documentation/manage-data/guides/monetize-protected-data.md +++ b/src/documentation/manage-data/guides/monetize-protected-data.md @@ -71,10 +71,10 @@ access time, not individual transactions. ::: tip See It Live -The [Content Creator demo](/documentation/use-cases) shows -DataProtector Sharing in action with file monetization. While it uses the -content-delivery iApp for file streaming, the same patterns work for any iApp - -AI models, data processing, oracles, etc. +The [Content Creator demo](/documentation/use-cases) shows DataProtector Sharing +in action with file monetization. While it uses the content-delivery iApp for +file streaming, the same patterns work for any iApp - AI models, data +processing, oracles, etc. ::: @@ -242,14 +242,13 @@ usage periods, and flexible pricing models. **Ready to start monetizing your data?** Here are your next steps: -- **See it in action**: Try the - [Content Creator demo](/documentation/use-cases) to understand - the full flow +- **See it in action**: Try the [Content Creator demo](/documentation/use-cases) + to understand the full flow - **Start simple**: Begin with [pay-per-use via grantAccess](/documentation/manage-data/guides/manage-access) - **Explore sharing**: Try - [DataProtector Sharing](/documentation/manage-data/dataProtector/dataProtectorSharing) for - automated distribution + [DataProtector Sharing](/documentation/manage-data/dataProtector/dataProtectorSharing) + for automated distribution - **Build collections**: Learn about [collection management](/documentation/manage-data/dataProtector/dataProtectorSharing/collection) - **Set up subscriptions**: Implement diff --git a/src/documentation/protocol/glossary.md b/src/documentation/protocol/glossary.md index 7d1374e3..55f92760 100644 --- a/src/documentation/protocol/glossary.md +++ b/src/documentation/protocol/glossary.md @@ -3,8 +3,370 @@ title: Glossary description: iExec terms glossary --- -# 📖 Glossary +# Glossary -This page is under development. +## A - +### Application + +A computer program designed to automate processes. An application is generally +under the control of different communities such as product managers, project +owners, or data center administrators. An application can also be decentralized +thanks to the blockchain: see “DApp”. + +## B + +### Bellecour Sidechain + +iExec’s product sidechain. It is linked to Ethereum Mainnet with a bridge +allowing for the transfer of assets between networks. It allows iExec to be used +without paying Ethereum gas fees. + +See “Sidechain and xRLC” for more information. + +### Beneficiary + +An entity indicated by the requester that benefits from the result of a +computational execution. It can be the requester, a third party, a smart +contract etc. + +## C + +### Confidential Computing / Trusted Computing + +Ensures computation confidentiality through mechanisms of memory encryption at +the hardware level (in a Trusted Execution Environment or TEE). It can be used +so that only authorized code can run inside a protected area and manipulate its +data. In some cases, ensuring that code runs correctly without any third party +altering the execution, is even more important than hiding the computation's +data. + +This concept is called Trusted Computing. These guarantees are critical for a +decentralized cloud where code is being executed on a remote machine, that is +not controlled by the requester. They are also required to prevent leakage while +monetizing data sets. + +See ”Trusted Execution Environments ('TEE')” for more information + +## D + +### Decentralized Application (DApp) + +An application built on a decentralized network that combines a smart contract +and a frontend user interface. A DApp has its backend code running on a +decentralized peer-to-peer network. Contrast this with a traditional application +where the backend code is running in fully controlled environments. + +See “Application” for more information. + +### DApp Provider + +Writes, configures, and deploys applications on the iExec platform. Those +applications can be DApps or legacy (traditional) applications. Providers can +make their applications available for free or ask for a fixed fee for each use +of their application. + +### Dataset + +A dataset is a collection of related sets of information that is composed of +separate elements, such as numbers, semantic-data or variables, that can be +manipulated by a computer for practical application. For example, data within +the medical industry can be use by healthcare professionals, care providers, +insurers, and government agencies. + +### Dataset Provider + +Owns datasets that can be monetized in the iExec marketplace. + +### Deal + +An agreement between all parties (requester and providers) in the iExec network. +A deal is created when requester and providers’ orders are matched in the +marketplace and recorded in the PoCo smart contract. + +## E + +### Ethereum + +A decentralized, open-source blockchain with smart contract functionality. +Ethereum proposes using blockchain technology to maintain a decentralized +payment network and to store computer code that can be used to power +decentralized applications. + +### Enclave + +In confidential computing jargon, an "enclave" is the special memory zone +protected by the CPU. For simplicity's sake, we can refer to private regions of +memory defined by Intel® SGX application as "enclaves". + +### EVM (Ethereum Virtual Machine) + +The Ethereum's execution environment. This is the virtual machine which executes +the smart contracts on the blockchain. + +### Explorer (iExec Explorer) + +Tracks and displays all transactions occuring on iExec’s platform. It provides +detailed information on the latest deals, tasks, apps, and datasets deployed. + +### ERC-20 + +A standard for fungible tokens. This property makes each token exactly the same +in type and value as another token. + +### ERC-721 + +A standard for non-fungible tokens. This property makes each token unique and +capable of having a different value from another token from the same Smart +Contract. This uniqueness may come from a variety of sources, including age, +rarity, or appearance. + +### iExec Academy + +iExec’s content aggregator where people can find content related to the project, +including articles, demos, documentation, and tutorials. + +## I + +### ICO (Initial Coin Offering) + +A fundraising mechanism where a blockchain-based startup mints its own native +crypto asset in exchange for other cryptocurrencies. The goal is to raise funds +and, in turn, create a community of incentivized users who want the project to +succeed so that the presale tokens gain in value. + +## M + +### Mainnet + +An independent blockchain running its own peer-to-peer network with its own +technology and protocol (e.g. Bitcoin and Ethereum). It is a live blockchain +where its own cryptocurrencies or tokens have value (when compared against a +testnet network). + +### Marketplace (iExec) + +The iExec Marketplace connects resource providers with resource users, allowing +anyone to monetize or rent computing power, datasets, and applications. The +marketplace is a smart contract that acts as escrow anytime you need the network +to exchange computing assets. + +### Minting + +A decentralized method that enables to generate a new token without the +involvement of a central authority. It can either be a non-fungible token or a +crypto coin. + +Please note that minting an NFT (non-fungible token) is a different procedure. +To mint an NFT, users usually sign up with a cryptocurrecny wallet on an NFT +marketplace (or other platform). Then that create an NFT by uploading a file and +paying for the creation. Once the transaction is verified, a new NFT is minted. +This process to add NFTs to a blockchain allows creators to sell their photos, +videos, and digital 3D objects. + +See “ERC-721” for more information. + +### MREnclave + +The MREnclave is a hash value that identifies every enclave. It is obtained from +the content of memory pages and access rights. The MREnclave is available after +the TEE application is built. + +## N + +### nRLC + +N in nRLC stands for nano RLC. + +nRLC is the smallest subdivision of the RLC token, 1 RLC equals to 10^9 nRLC. + +nRLC is the base unit for the PoCo, orders prices in a deal are calculated in +nRLC. + +See “Deal and PoCo” for more information. + +## O + +### Oilers + +A term of affection used to designate the iExec community. Oilers are said to be +holding “digital oil”. This term could also be in reference to the iExec +whitepaper that states: “ iExec introduces a new paradigm in cloud computing: it +will allow the trading of computing resources as commodities; in the same way we +may observe with resources such as oil, gold or rice.” + +### Oracle + +Data feeds that connect the off-chain world to blockchain products. Oracles act +as a link between data that exists in traditional web2 and blockchain smart +contracts, and are used to query data in smart contracts. + +### Oracle Factory + +The iExec developer interface which allows users to create custom oracles. This +interface permits users to create oracles for any type of data, without +requiring coding experience, in a few minutes directly from their browser. + +## P + +### PoCo (Proof of Contribution) + +The protocol used by iExec for consensus over off-chain computing. + +The iExec platform provides a network where application providers, workers, and +users can gather and work together. The fully decentralized nature of iExec +implies that no single agent is trusted by default, and that those agents +require incentives to contribute correctly. PoCo is a protocol designed to +provides trust in an open and decentralized environment of untrusted machines. +It also orchestrates the different contributions to the iExec network, ensuring +payments are always fair and timely. + +## R + +### Requester + +A person who buys the execution of a task in the iExec marketplace. + +### RLC (Run on lots of computers) + +An Ethereum (ERC20) token launched during the iExec ICO in 2017. This utility +token is used on the iExec marketplace to buy and sell computing assets. + +### Remote attestation + +As explained by +[Intel](https://software.intel.com/en-us/sgx/attestation-services), the remote +attestation is the process that happens before any exchange between a remote +provider and an enclave. It allows the provider to verify that the expected +software is running in an Intel® SGX-protected way, as well as getting other +details about the application being attested. If the attestation is successful, +a secure communication channel is established between the provider and the +enclave, and secrets can safely land in the latter. + +### Roadmap + +Describes business and technical developments towards the adoption of the iExec +protocol. The timeline for this roadmap is organized by quarter for an overview +on ongoing projects and the future work. It is available here + + +## S + +### Scheduler + +Organizes the work distribution for workers in a worker pool. + +### SDK (Software Development Kit) + +A set of tools for interaction with smart contracts and the iExec’s marketplace. +It is available as a CLI and JS library. Access SDK here: + + +### SGX (Secure Guard Extension by Intel) + +Is a confidential computing technology developed by Intel. + +See “Confidential Computing” for more information. + +### Sidechain + +A controlled blockchain deployed over a data center and linked to Ethereum +Mainnet with a bridge permitting to transfer assets between the two. iExec’s +sidechain “Bellecour” is iExec’s mainnet bridged to ethereum mainnet. + +### Smart Contracts + +The fundamental building blocks of Ethereum applications. They are computer +programs stored on the blockchain that allows us to convert traditional +contracts into digital parallels. Smart contracts are very logical - following +an if this then that structure. This means they behave exactly as programmed and +cannot be changed. + +Nick Szabo used the term "smart contract" in 1994, when he wrote “an +introduction to the concept” and, in 1996, “an exploration of what smart +contracts could do”. + +### SMS (Secret Management Service) + +An encrypted database where users' secrets are stored. + +### sRLC + +S in sRLC stands for Staked RLC. + +The sRLC balance of an account reflects the amount of RLC deposited by the +account on the iExec PoCo smart contract. The sRLC balance of an account is +managed by the PoCo smart contracts to ensure payments across the platform +users. At any time, sRLC can be converted to RLC in the wallet by a withdrawal +operation. + +### Staking (of RLC) + +A mechanism in PoCo that involves a certain amount of Workers’ RLC being +‘locked-up’ during the execution of a task. To prevent malicious workers, the +locked RLC is staked as a security deposit. Workers who computed a false result +will lose their stake. + +See “PoCo” for more information. + +## T + +### Task + +A task within iExec is an instance where computing power is required. + +### Testnet + +Used by programmers and developers to test and troubleshoot the aspects and +features of a blockchain network to ensure it is ready for mainnet launch. + +### Trusted Execution Environment (TEE) + +A hardware secure area used to guarantee the confidentiality, integrity, and +ownership of code and data. + +See” Confidential Computing ” for more information + +## W + +### Whitepaper + +Explain the purpose and technology behind a project. Producing a whitepaper is a +key step for a crypto startup to help investors understand technical information +about its concept; whitepapers usually include a roadmap for how the project +plans to grow and succeed. iExec’s whitepaper is available +[here](https://www.iex.ec/whitepaper) + +### Workers + +Individuals or companies who own computing resources and are willing to make +them available for the computation of tasks against payments in RLC. Similarly +to blockchain miners, workers want a simple solution that will make their +computer part of a large infrastructure that will take care of the details for +them. + +### Worker Pools + +Organize the contributions of Workers. A worker pool is a group of machines, +often with similar characteristics, that is led by a Pool Manager. + +### Worker Pool Manager + +In charge of proposing available computing resources (workers) to task +requesters. + +## X + +### xRLC + +X in xRLC stands for eXternal. + +A xRLC is the native token of iExec sidechains (it means it is used as ETH on +ethereum mainnet). A xRLC is a RLC bridged on an external sidechain and only the +bridge can mint\* xRLC. + +The bridge helps maintain the parity between the main chain and the external +chain. For example, one xRLC on the sidechain has the same value as 1 RLC on the +mainchain. + +See “Sidechain, Bellecour Sidechain or Minting” for more information. diff --git a/src/documentation/protocol/poco/pay-per-task.md b/src/documentation/protocol/poco/pay-per-task.md new file mode 100644 index 00000000..1834fa34 --- /dev/null +++ b/src/documentation/protocol/poco/pay-per-task.md @@ -0,0 +1,34 @@ +--- +title: Pay Per Task Model +description: + Pricing model and task categories for iExec protocol, including computing and + deal time limits. +--- + +# Pay per task model + +We are introducing a new method for pricing and we have defined several task +categories that describe the execution boundaries. We'll setup a test +infrastructure so that application developers can evaluate the category of their +submissions. Conversely, worker pools will be able to benchmark their +infrastructures against the reference machine. + +In the future, we'll redefine the categories, and provide more advanced tools +for helping developers to maximize the usage of the infrastructure + +**Categories Description:** + +| **Category** | **Maximum Computing Time (C)** | **Maximum Deal Time (D)** | +| ------------ | ------------------------------ | ------------------------- | +| 0 – XS | 5 min | 50 min | +| 1 – S | 20 min | 200 min (3h20m) | +| 2 – M | 1 hour | 10h | +| 3 – L | 3 hour | 30h (1d6h) | +| 4 – XL | 10 hour | 100h (4d4h) | + +- Each worker will allocate **C** minutes to compute the application. If the + computation is not completed within this **Maximum Computing Time**, the + running application will be stopped. +- From a buyer perspective, a requester will be able to claim a task of a deal + after **D** minutes if the task is not completed within the **Maximum Deal + Time**. diff --git a/src/documentation/protocol/poco/proof-of-contribution.md b/src/documentation/protocol/poco/proof-of-contribution.md new file mode 100644 index 00000000..d1311dd4 --- /dev/null +++ b/src/documentation/protocol/poco/proof-of-contribution.md @@ -0,0 +1,896 @@ +--- +title: Proof of Contribution +description: + PoCo protocol for trust, consensus, and secure payment in decentralized iExec + computing. +--- + +# Proof of Contribution + +> PoCo is a protocol designed to provide trust in an open and decentralized +> environment of untrusted machines. + +The iExec platform provides a network where application providers, workers, and +users can gather and work together. The fully decentralized nature of iExec +implies that no single agent is trusted by default, and that those agents +require incentives to contribute correctly. + +In this context, Proof-of-Contribution (PoCo) is the protocol used by iExec for +consensus over off-chain computing. + +## Protocol + +### Objectives + +PoCo is a protocol designed to provide trust in an open and decentralized +environment of untrusted machines. + +In addition to providing trust, PoCo also orchestrates the different +contributions to the iExec network, ensuring payments are always fair and +timely. + +A major quality of PoCo lies in the fact that it is a modular protocol. It comes +with features that are context-specific. + +### Result consolidation + +PoCo relies on replication to achieve result consolidation. This is purely a +software solution that enforces a confidence level on the result. +[This confidence level can be customized by the requester.](proof-of-contribution.md#replication-and-trust) + +This layer also supports the onchain consolidation of execution results carried +out in Trusted Execution Environments (TEE) such as Intel SGX. + +### Secure payment + +Once a deal is sealed on the iExec Marketplace, requester funds are locked to +ensure all resource providers are paid for their contributions. Resources can +take the form of data, applications or computing power. + +Workers must achieve consensus on the execution result to get the requester’s +funds. If consensus is not achieved, the requester is reimbursed. + +Worker and scheduler must stake RLC to participate as a computing providers. Bad +behaviour from an actor results in a loss of stake. + +This is essential on the public blockchain, but all values can be set to 0 for +private blockchain solutions. + +### Permissioning + +For an execution to happen, a deal must be signed between the different parties +involved. A permission mechanism can be used to control access to applications, +datasets and worker pools. + +The secure payment layer can be disabled for a private blockchain, or it can +also be used in the context of the public blockchain to increase security. An +example of permissioning is dataset restriction for a specific application. + +### Overview + +PoCo describes the succession of contributions that are required to achieve +consensus on a given result. Its logic is detailed in two blog articles: + +- [PoCo series #1: Initial PoCo description](https://medium.com/iex-ec/about-trust-and-agents-incentives-4651c138974c) +- [PoCo series #3: Updated PoCo description](https://medium.com/iex-ec/poco-series-3-poco-protocole-update-a2c8f8f30126) + +The +[nominal workflow](https://github.com/iExecBlockchainComputing/iexec-doc/raw/master/techreport/nominalworkflow-ODB.png) +is also available in the [technical report section](../help/glossary.md) + +Below are the details of the implementations: + +**1. Deal:** + +[A deal is sealed by the Clerk.](proof-of-contribution.md#brokering) This marks +the beginning of the execution. An event is created to notify the worker pool’s +scheduler. + +The consensus timer starts when the deal is signed. The corresponding task must +be completed before the end of this countdown. Otherwise, the scheduler gets +punished by a loss of stake and reputation, and the user reimbursed. + +**2. Initialization:** + +The scheduler calls the `initialize` method. Given a deal id and a position in +the request order (within the deal window), this function initializes the +corresponding task and returns the +_taskid_.`bytes32 taskid = keccak256(abi.encodePacked(_dealid, idx));` + +**3. Authorization signature:** + +The scheduler designates workers that participate in this task. The scheduler’s +Ethereum wallet signs a message containing the worker’s Ethereum address, the +taskid, and (optionally) the Ethereum address of the worker's enclave. If the +worker doesn't use an enclave, this field must be filled with `address(0)`. + +This Ethereum signature (authorization) is sent to the worker through an +off-chain channel implemented by the middleware. + +**4. Task computation:** + +Once the authorization is received and verified, the worker computes the +requested tasks. Results from this execution are placed in the `/iexec_out` +folder. The following values are then computed: + +- _bytes32 digest_: a digest (sha256) of the result folder. +- _bytes32 hash_: the hash of the _digest_, used to produce a consensus +- _bytes32 seal_: the salted hash of the _digest_, used to prove a worker’s knew + the _digest_ value before it is published. + `resultHash == keccak256(abi.encodePacked( taskid, resultDigest))` + `resultSeal == keccak256(abi.encodePacked(worker, taskid, resultDigest))` + +In computer science, a deterministic algorithm is an algorithm which, given a +particular input, will always produce the same output. + +Both the `digest`, the `hash` and the `seal` are automatically computed based on +the output of the application. If the output is not entirely deterministic, then +the application can specify a deterministic file that should be used for +building consensus. In order to do so, the application just has to provide the +path to the deterministic file using a specific entry +`deterministic-output-path` in `${IEXEC_OUT}/computed.json`. + +Alternatively, if the application is used in a doracle context (the results are +designed to be processed on-chain by receiver smart-contracts), then the value +of this callback must be specified in `${IEXEC_OUT}/computed.json` under the +entry `callback-data`. + +If a TEE was used to produce the result, the post-processing enclave will +automatically produce an `enclave-signature` entry that contains the enclave +signature (of the resultHash and resultSeal). TEE certification of results is +transparent to the application developer. + +**5. Contribution:** + +Once the execution has been performed, the worker pushes its contribution using +the `contribute` method. The contribution contains: + +- _bytes32 taskid_ +- _bytes32 resultHash_ +- _bytes32 resultSeal_ +- _address enclaveChallenge_ + +The address of the enclave (specified in the scheduler’s authorization). If no +enclave is specified, this parameter should be set to `address(0)`. + +- _bytes enclaveSign_ + +The enclave signature. This is required if the `enclaveChallenge` is not +`address(0)`. Otherwise, it should be set to the empty byte string `0x`. + +- _bytes workerpoolSign_ + +The signature computed by the scheduler at step 2. + +**6. Consensus:** + +During the contribution, the consensus is updated and verified. Contributions +are possible until the consensus is reached, at which point the contributions +are closed. We then enter a 2h reveal phase. + +**7. Reveal:** + +During the reveal phase, workers that have contributed to the consensus must +call the `reveal` method with the `resultDigest`. This verifies that the +`resultHash` and `resultSeal` they provided are valid. Failure to reveal is +equivalent to a bad contribution, and results in a loss of stake and reputation. + +**8. Finalize:** + +Once all contributions have been revealed, or at the end of the reveal period if +some (but not all) reveals are missing, the scheduler must call the `finalize` +method. This finalizes the task, rewards good contribution and punishes bad +ones. This must be called before the end of the consensus timer. + +### Staking and Payment + +Among the objectives of PoCo, we want to ensure a worker that contributes +correctly is rewarded and, at the same time, that a requester won’t be charged +unless a consensus is achieved. This is achieved by locking the requester’s +funds for the duration of the consensus, and unlocking them depending on the +outcome. + +Staking is used to prevent bad behavior and encourage good contributions. + +Your account, managed by the `Escrow` part of the `IexecClerk`, separates +between `balance.stake` (available, can be withdrawn) and `balanced.locked` +(unavailable, frozen by a running task). The `Escrow` exposes the following +mechanism: + +`lock`: Moves value from the `balance.stake` to `balance.lock` + +- Locks the requester stake for payment +- Locks the scheduler stake to protect against failed consensus +- Locks the worker stake when making a contribution + +`unlock`: Moves value from the `balance.lock` back to the `balance.stake` + +- Unlock the requester stake when consensus fails +- Unlock the scheduler stake when consensus is achieved +- Unlock the worker stake when they contributed to a successful consensus + +`seize`: Confiscate value from `balance.lock` + +- Seize the requester stake when the consensus is achieved (payment) +- Seize the scheduler stake when consensus fails (send to the reward kitty) +- Seize the worker stake when a contribution fails (redistributed to the other + workers in the task) + +`reward`: Award value to the `balance.stake` + +- Reward the scheduler when consensus is achieved +- Reward the worker when they contributed to a successful consensus +- Reward the app and dataset owner + +The requester payment is composed of 3 parts, one for the worker pool, one for +the application and one for the dataset. When a consensus is finalized, the +payment is seized from the requester and the application and dataset owners are +rewarded accordingly. The worker pool part is put inside the `totalReward`. +Stake from the losing workers is also added to the `totalReward`. The scheduler +takes a fixed portion of the `totalReward` as defined in the worker pool smart +contract (`schedulerRewardRatioPolicy`). + +The remaining reward is then divided between the successful workers +proportionally to the impact their contribution made on the consensus. If there +is anything left (division rounding, a few nRLC at most) the scheduler gets it. +The scheduler also gets part of the reward kitty. + +#### Parameters + +`FINAL_DEADLINE_RATIO = 10`, `CONTRIBUTION_DEADLINE_RATIO = 7`, +`REVEAL_DEADLINE_RATIO = 2` + +Parameters of the consensus timer. They express the number of reference timers +(category duration) that are dedicated to each phase. These settings correspond +to a 70%-20%-10% distribution between the contribution phase, the reveal phase +and the finalize phase. + +- `FINAL_DEADLINE_RATIO` This describes the total duration of the consensus. At + the end of this timer the consensus must be finalized. If it is not, the user + can make a claim to get a refund. +- `CONTRIBUTION_DEADLINE_RATIO` This describes the duration of the contribution + period. The consensus can finalize before that, but no contribution will be + allowed after the timer to ensure enough time is left for the reveal and + finalize steps. +- `REVEAL_DEADLINE_RATIO` This describes the duration of the reveal period. + Whenever a contribution triggers a consensus, a reveal period of this duration + is reserved for the workers to reveal their contribution. Note that this + period will necessarily start before the end of the contribution phase. + +Let's consider a task of category GigaPlus, which reference duration is 1 hour. +If the task was submitted at 9:27AM, the contributions must be sent before +4:27PM (16:27). Whenever a contribution triggers a consensus, a 2 hours long +reveal period will start. Whatever happens, the consensus has to be achieved by +7:27PM (19:27). + +`WORKERPOOL_STAKE_RATIO = 30` + +Percentage of the worker pool price that has to be staked by the scheduler. For +example, for a `20 RLC` task, with an additional `1 RLC` for the application and +`5 RLC` for the dataset, the worker will have to lock `26 RLC` in total and the +scheduler will have to lock (stake) `30% * 20 = 6 RLC`. + +This stake is lost and transferred to the reward kitty if the consensus is not +finalized by the end of the consensus timer. + +`KITTY_RATIO = 10` + +Percentage of the reward kitty for the scheduler per successful execution. If +the reward kitty contains 42 RLC when a finalize is called, then the scheduler +will get 4.2 extra RLC and the reward kitty will be left with 37.8 RLC. + +`KITTY_MIN = 1 RLC` + +Minimum reward on successful execution (up to the reward kitty value). + +- If the reward kitty contains 42.0 RLC, the reward is 4.2 +- If the reward kitty contains 5.0 RLC, the reward should be 0.5 but gets raised + to 1.0 +- If the reward kitty contains 0.7 RLC, the reward should be 0.07 but gets + raised to 0.7 (the whole kitty) + +`reward = kitty.percentage(KITTY_RATIO).max(KITTY_MIN).min(kitty)` + +#### Example + +Let's consider a worker pool with the policies `workerStakeRatioPolicy = 35%` +and `workerStakeRatioPolicy = 5%`. + +- A requester offers `20 RLC` to run a task. The task is free, but it uses a + dataset that costs `1 RLC`. The requester locks `21 RLC` and the scheduler + `30% * 20 = 6 RLC`. The trust objective is `99%` (`trust = 100`) +- 3 workers contribute: + - The first one (`score = 12 → power = 3`) contributes `17`. It has to lock + `7 RLC` (35% of the `20 RLC` awarded to the worker pool). + - The second worker (`score = 100 → power = 32`) contributes `42`. It also + locks `7 RLC`. + - The third worker (`score = 300 → power = 99`) contributes `42`. It also + locks `7 RLC`. +- After the third contribution, the value `42` has reached a `99.87%` + likelihood. Consensus is achieved and the two workers who contributed toward + `42` have to reveal. +- After both workers reveal, the scheduler finalizes the task: + - The requester locked value of `21 RLC` is seized. + - The dataset owner gets `1 RLC` for the use of its dataset. + - Stake from the scheduler is unlocked. + - Stakes from workers 2 and 3 are also unlocked. + - The first worker stake is seized, and it loses a third of its score. The + corresponding `7 RLC` are added to the `totalReward`. + - We now have `totalReward = 27 RLC`: + - We save 5% for the scheduler, `workersReward = 95% * 27 = 25.65 RLC` + - Worker 2 has weight `log2(32) = 5` and worker 3 has a weight + `log2(99) = 6`. Total weight is `5+6=11` + - Worker 2 takes `25.65 * 5/11 = 11.659090909 RLC` + - Worker 3 takes `25.65 * 6/11 = 13.990909090 RLC` + - Scheduler takes the remaining `1.350000001 RLC` + - If the reward kitty is not empty, the scheduler also takes a part of it. + +## Replication & Trust + +### How to achieve trust ? + +The PoCo offers a consensus mechanism that can certify the likelihood of a +result to be valid. This consensus relies on the scoring of workers and the +replication of a task’s execution to combine the score of the workers that come +up with the same result. This consensus is largely based on Sarmenta’s work +[[Sarmenta2002]](proof-of-contribution.md#references) with specific tuning of +the scoring function [[Trust2018]](proof-of-contribution.md#references). + +### Contribution credibility + +Each worker’s contribution has an associated credibility. This credibility +derives from the worker’s history score. As described in +[[Trust2018]](proof-of-contribution.md#references), a worker score is a positive +integer that is incremented for each valid and verified contribution. In case of +bad contribution, a worker loses one third of it’s score. This credibility can +be expressed as a likelihood percentage but also as a weight value that can be +used to detect consensus without resorting to floating point arithmetics, more +details in [[Trust2018]](proof-of-contribution.md#references). + +### Requiring a trust level + +Based on [[Sarmenta2002]](proof-of-contribution.md#references) describing the +way to combine worker’s contribution and to evaluate a result’s likelihood, a +requester can ask for the level of trust as an input for the PoCo processing, to +impose a certain quality of service. The trust level corresponds to a minimum +correctness likelihood that a result must achieve to be valid. For example, a +trust level of 0 means any contribution would be accepted, regardless of the +score of the worker proposing it. On the other hand a trust level of 99.99% +means a result will only be accepted if the contribution towards it result shows +a correctness probability higher than 99.99%. + +The trust level is expressed, on-chain, by an integer `trust` such that +`threshold = 1 - 1 / trust`. + +| **Trust** | **PoCo enforced confidence threshold** | +| --------- | -------------------------------------- | +| 0 | 0% | +| 1 | 0% | +| 2 | 50% | +| 100 | 99% | +| 10000 | 99.99% | +| 1000000 | 99.9999% | + +### Limitation + +This consensus mechanism requires replicable applications. Non-deterministic +applications, long-running jobs such as webservers, do not meet this requirement +out of the box. When it is possible, the application developer must provide a +deterministic result using the `deterministic-output-path` entry of the +`${IEXEC_OUT}/computed.json` file. Otherwise, the user can still run its +application on the iExec platform but would have to disable the PoCo’s consensus +layer. Hardware security as TEE is an option to overpass this limitation. + +#### References + +| | | +| -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [Sarmenta2002] | (1, 2) Luis F.G.Sarmenta. Sabotage-tolerance mechanisms for volunteer computing systems. 2002. Future Generation Computer Systems, 18(4), 561–572 [Sarmenta2002 PDF](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.67.2962&rep=rep1&type=pdf) | +| [Trust2018] | (1, 2, 3) Trust management in the Proof of Contribution protocol. 2018. Technical report. [Trust2018 PDF](https://github.com/iExecBlockchainComputing/iexec-doc/raw/master/techreport/iExec_PoCo_and_trustmanagement_v1.pdf) | + +## Brokering + +### Overview + +We studied many possible evolutions of the brokering process. The first +requirement is to include bid orders to complete the already available ask +orders. It soon became clear that the evolution had to go beyond a simple +interaction. We considered on-chain and off-chain approach and finally went for +a solution where both the pairing and the order book are off-chain. It might +sound counterintuitive, but it has many advantages. + +If the orders and the pairing are handled off-chain, how can we build an +on-chain agreement knowing that all parties have agreed? Is there a threat to +the platform security? + +This option relies on the use of cryptographic signatures for order +authentication. We represent orders using structures containing all the required +details. The hash of this structure uniquely identifies the order. The structure +by itself is worthless as anyone could write and publish it. However, if we add +a valid cryptographic signature (of the identifying hash), then the origin of +the order can be certified with the same level of security as if it was +published on-chain. This role is fulfilled by a smart-contract called the +iExecClerk. + +An overview of the iExecODB (Open Decentralized Brokering) is available in this +blog article: + +- [PoCo series #5: Open decentralized brokering on the iExec platform](https://medium.com/iex-ec/poco-series-5-open-decentralized-brokering-on-the-iexec-platform-67b266e330d8) + +### Orders structure + +As discussed earlier, iExec introduces the offchain signature of orders as a new +core element of the iExec Open Decentralized Brokering, the iExec Clerk should +match these orders. There are 4 types of orders corresponding to the 4 actors +involved: the worker pool, the application, the dataset and the requester. Each +order types has to follow a specific structure and is signed using +[EIP-712](https://eips.ethereum.org/EIPS/eip-712) structure signature mechanism. + +### Orders description + +#### **AppOrder** + +```text +struct AppOrder +{ + address app; + uint256 appprice; + uint256 volume; + uint256 tag; + address datasetrestrict; + address workerpoolrestrict; + address requesterrestrict; + bytes32 salt; + bytes sign; +} +``` + +- `app` Address of the smartcontract describing the application. Must be + registered in the AppRegistry. +- `appprice` Price of a run of the application. +- `volume` Number of run authorized (by this order). +- `tag` Special requirements for the application (see + [tag](proof-of-contribution.md#tag)). +- `datasetrestrict` Matching restrictions. Dataset or group of datasets that can + be matched. Let null value to disable. +- `workerpoolrestrict` Matching restrictions. Workerpool or group of worker + pools that can be matched. Let null value to disable. +- `requesterrestrict` Matching restrictions. Requester or group of requesters + that can be matched. Let null value to disable. +- `salt` A random value to ensure order uniqueness. +- `sign` cryptographic signature of the order, the smart contract is securely + linked to application owner. + +### **DatasetOrder** + +```text +struct DatasetOrder +{ + address dataset; + uint256 datasetprice; + uint256 volume; + uint256 tag; + address apprestrict; + address workerpoolrestrict; + address requesterrestrict; + bytes32 salt; + bytes sign; +} +``` + +- `dataset` Address of the smartcontract describing the dataset. Must be + registered in the DatasetRegistry. +- `datasetprice` Price of a use of the dataset. +- `volume` Number of authorized uses (by this order). +- `tag` Special requirements of the dataset (see + [tag](proof-of-contribution.md#tag)). +- `apprestrict` Matching restrictions. App or group of apps that can be matched. + Let null value to disable. +- `workerpoolrestrict` Matching restrictions. Workerpool or group of workerpools + that can be matched. Let null value to disable. +- `requesterrestrict` Matching restrictions. Requester or group of requesters + that can be matched. Let null value to disable. +- `salt` A random value to ensure order uniqueness. +- `sign` cryptographic signature of the order, the smart contract is securely + linked to dataset owner. + +### **WorkerpoolOrder** + +```text +struct WorkerpoolOrder +{ + address workerpool; + uint256 workerpoolprice; + uint256 volume; + uint256 tag; + uint256 category; + uint256 trust; + address apprestrict; + address datasetrestrict; + address requesterrestrict; + bytes32 salt; + bytes sign; +} +``` + +- `workerpool` Address of the smartcontract describing the worker pool. Must be + registered in the WorkerpoolRegistry. +- `workerpoolprice` Price of an execution on the worker pool. +- `volume` Number of executions proposed (by this order). +- `tag` Special features proposed by the workerpool (see + [tag](proof-of-contribution.md#tag)). +- `category` Order category. +- `trust` Trust level used to consolidated results. +- `apprestrict` Matching restrictions. App or group of apps that can be matched. + Let null value to disable. +- `datasetrestrict` Matching restrictions. Dataset or group of datasets that can + be matched. Let null value to disable. +- `requesterrestrict` Matching restrictions. Requester or group of requesters + that can be matched. Let null value to disable. +- `salt` A random value to ensure order uniqueness. +- `sign` cryptographic signature of the order, the smart contract is securely + linked to worker pool manager. + +### **RequesterOrder** + +```text +struct RequestOrder +{ + address app; + uint256 appmaxprice; + address dataset; + uint256 datasetmaxprice; + address workerpool; + uint256 workerpoolmaxprice; + address requester; + uint256 volume; + uint256 tag; + uint256 category; + uint256 trust; + address beneficiary; + address callback; + string params; + bytes32 salt; + bytes sign; +} +``` + +- `app` Address of the smartcontract describing the application. Must be + registered in the AppRegistry. +- `appmaxprice` Maximum price allowed by the requester for the payment of the + application. +- `dataset` Address of the smartcontract describing the dataset. Must be + registered in the DatasetRegistry. Null if no dataset is required. +- `datasetmaxprice` Maximum price allowed by the requester for the payment of + the dataset (if any). +- `workerpool` Matching restrictions. Worker pool or group of worker pools that + are allowed to run tasks from this order. Leave null value to disable check. +- `workerpoolmaxprice` Maximum price allowed by the requester for the payment of + the execution (scheduler + workers). +- `requester` Address of the requester (paying for the executions). +- `volume` Number of tasks that are part of this order (size of the Bag Of + Task). +- `tag` Special features required by the requester (see + [tag](proof-of-contribution.md#tag)). +- `category` tasks category required. +- `trust` Minimum trust level required by the requester. +- `beneficiary` Address of the beneficiary of the computation. Used to require + output data encryption. +- `callback` Address to callback with the results (following EIP1154 interface). + Let empty (null) if no callback is required. Learn more about the callback + mechanism. +- `params` Parameters of the application (application specific). +- `salt` A random value to ensure order uniqueness. +- `sign` Cryptographic signature of the order, the smart contract is securely + linked to requester. + +## Tag + +The tag specifies the need or the availability of features that go beyond the +specifications of the category. The tag is a requirement when it is part of an +app order, a dataset order or a requester order. On the other hand, the tag in +the workerpool order expresses the availability of the corresponding features. + +In V3, tags are 32 bytes (256 bits) long array where each bit corresponds to a +feature. + +| **Value** | **Description** | +| -------------------------------------------------------------------- | ---------------------- | +| `0x0000000000000000000000000000000000000000000000000000000000000001` | TEE (physical enclave) | +| `0x0000000000000000000000000000000000000000000000000000000000000002` | — | +| `0x0000000000000000000000000000000000000000000000000000000000000004` | — | +| `0x0000000000000000000000000000000000000000000000000000000000000008` | — | +| `0x0000000000000000000000000000000000000000000000000000000000000010` | — | +| … | … | +| `0x8000000000000000000000000000000000000000000000000000000000000000` | — | + +For orders matching, the worker pool order must enable all bits that are enabled +in any of the app order, dataset order and requester order. Meaning that if the +app order tag is `0x12 = 0x10 | 0x02`, the dataset order is `0x81 = 0x80 | 0x01` +and the requester order is `0x03 = 0x02 | 0x01`, then the worker pool order +must, at least, have a tag `0x93 = 0x80 | 0x10 | 0x02 | 0x01`. + +### Matching Conditions + +In order to trigger an execution, a deal must be registered by the iExec Clerk. +Deals are produced when orders are successfully matched by the clerk. A match +requires 3 or 4 orders depending on the requester requirements, the dataset +order is optional. + +Orders compatibility required: + +**1. The worker pool’s category and the requester’s category must be equal.** + +```sol +require(_requestorder.category == _workerpoolorder.category); +``` + +**2. The worker pool’s trust must be greater or equal to the requester’s +trust.** + +```sol +require(_requestorder.trust == _workerpoolorder.trust); +``` + +**3. The app’s, dataset’s and worker pool’s prices must be less or equal to the +requester’s appmaxprice, datasetmaxprice and workerpoolmaxprice.** + +```sol +require(_requestorder.appmaxprice >= _apporder.appprice); +require(_requestorder.datasetmaxprice >= _datasetorder.datasetprice); +require(_requestorder.workerpoolmaxprice >= _workerpoolorder.workerpoolprice); +``` + +**4. The worker pool’s tag must enable all the features required by the app’s +tag, the dataset’s tag and the worker pool’s tag.** + +```sol +require(tag & ~_workerpoolorder.tag == 0x0); +require(tag & ~_workerpoolorder.tag == 0x0); +``` + +**5. If TEE tag is required, then application must be TEE compatible.** + +```sol +require((tag ^ _apporder.tag)[31] & 0x01 == 0x0); +``` + +**6. The app provided by the apporder must match the one required by the +requester.** + +```sol +require(_requestorder.app == _apporder.app); +``` + +**7. The dataset provided by the datasetorder must match the one required by the +requester.** + +```sol +require(_requestorder.dataset == _datasetorder.dataset); +``` + +**8. If the requester specified a worker pool restriction, the worker pool must +match this value or be part of the corresponding group.** + +```sol +require(_checkIdentity(_requestorder.workerpool, _workerpoolorder.workerpool, GROUPMEMBER_PURPOSE)); +``` + +**9. The application must fit the dataset’s and the worker pool’s application +restrictions (if any).** + +```sol +require(_checkIdentity(_datasetorder.apprestrict, _apporder.app, GROUPMEMBER_PURPOSE)); +require(_checkIdentity(_workerpoolorder.apprestrict, _apporder.app, GROUPMEMBER_PURPOSE)); +``` + +**10. The dataset must fit the application’s and the worker pool’s restrictions +(if any).** + +```sol +require(_checkIdentity(_apporder.datasetrestrict, _datasetorder.dataset, GROUPMEMBER_PURPOSE)); +require(_checkIdentity(_workerpoolorder.datasetrestrict, _datasetorder.dataset, GROUPMEMBER_PURPOSE)); +``` + +**11. The worker pool must fit the application’s and the dataset’s restrictions +(if any).** + +```sol +require(_checkIdentity(_apporder.workerpoolrestrict, _workerpoolorder.workerpool, GROUPMEMBER_PURPOSE)); +require(_checkIdentity(_datasetorder.workerpoolrestrict, _workerpoolorder.workerpool, GROUPMEMBER_PURPOSE)); +``` + +**12. The requester must fit the application’s, the dataset’s and the worker +pool’s restrictions (if any).** + +```sol +require(_checkIdentity(_apporder.requesterrestrict, _requestorder.requester, GROUPMEMBER_PURPOSE)); +require(_checkIdentity(_datasetorder.requesterrestrict, _requestorder.requester, GROUPMEMBER_PURPOSE)); +require(_checkIdentity(_workerpoolorder.requesterrestrict, _requestorder.requester, GROUPMEMBER_PURPOSE)); +``` + +**13. All resources must be registered in the corresponding registries.** + +```sol +require(m_appregistry.isRegistered(_apporder.app)); +require(m_datasetregistry.isRegistered(_datasetorder.dataset)); +require(m_workerpoolregistry.isRegistered(_workerpoolorder.workerpool)); +``` + +**14. All orders must be signed or presigned.** + +```sol +require(_checkPresignatureOrSignature(App(_apporder.app).m_owner(), _apporder.hash(), _apporder.sign)); +require(_checkPresignatureOrSignature(Dataset(_datasetorder.dataset).m_owner(), _datasetorder.hash(), _datasetorder.sign)); +require(_checkPresignatureOrSignature(Workerpool(_workerpoolorder.workerpool).m_owner(), _workerpoolorder.hash(), _workerpoolorder.sign)); +require(_checkPresignatureOrSignature(_requestorder.requester, _requestorder.hash(), _requestorder.sign)); +``` + +**15. The deal produced must contain at least one task.** + +**16. Requester and worker pool must be able to stake.** + +### FAQ : How to write an order ? + +**[Requester] How do I enable PoCo’s consolidation of results?** + +A requester can enable the trust layer of the PoCo by setting the trust value in +the requestorder. As described here, the trust is defined with the required +confidence level. If the requester wants à 99.99% confidence level on the +results, it must set the `trust` field to `10000`. + +**[Requester] How do I run a non deterministic application despite requiring +determinism?** + +The PoCo requires an application to be deterministic for the replication layer +to provide trust in the result. If an application is not deterministic, +consensus cannot be achieved and replication is not necessary. + +In order to obtain a result, the requester must prevent replication by asking a +`trust` value of `0`. To protect your result, the requester can ask to run in an +enclave by setting the `tag` to `0x1`. + +**[Requester] How do I protect my result using encryption?** + +The result of an execution can be valuable to the end user, and the requester +might want to protect this result from leaking with encryption. + +Anyone can set up an encryption key in an SMS (Secret Management Service) of its +choice and set up the SMS address in the directory. + +Once a user set an encryption key (see TODO), any computation result can be +encrypted with, you have to set up the beneficiary address in the +RequesterOrder. + +An application can only perform result encryption inside an enclave. No +encryption key will be provided by the SMS to an application that doesn't run +outside an enclave. + +(TODO: potential issue, key leaking to malicious application with the requester +attacking a beneficiary) + +**[Dataset owner] How do I limit the usage of my dataset to a specific +application?** + +iExec’s Data wallet and Data store is a complete solution to monetize valuable +datasets preserving privacy. Before uploading a dataset you should encrypt it +using the iExec SDK. Through this process, the encryption key becomes the +valuable data that has to be protected. + +The encryption key must be stored in an SMS and the address of the corresponding +SMS recorded in the directory. The SMS stores this encryption key and will only +communicate it to an application running in an enclave. + +Before a worker runs this application, the worker must first prove that its +access is legitimate by providing the scheduler authorization. The SMS will +verify that this authorization’s signature is valid and that the corresponding +task is registered onchain. This means that any deal signed in the iExec Clerk +will grant access to the dataset’s encryption key. + +Therefore, in order to restrict the dataset’s usage, the dataset owner should +set up restriction before signing a brokering order. This is done through the +`apprestrict` field of the datasetorder. The dataset owner can deploy a +`SimpleGroup` smart contract, have the `apprestrict` field point to it, and then +whitelisting the applications that will have access to the dataset’s encryption +key. + +**[Scheduler] How do I protect myself from non-deterministic applications?** + +When a scheduler publishes an order, it makes a commitment to achieve consensus +on any task that is part of a deal made. While everything is done to ensure an +application cannot hurt a worker pool’s machines, not reaching the consensus +would cause a loss of stake for the scheduler. The scheduler must therefore take +action to prevent this. + +Whenever the scheduler proposes to certify a result using the PoCo’s trust +layer, it should make sure the application’s developer took the actions required +to make it compatible with the PoCo. On the other hand, any application could be +executed with the PoCo’s trust layer disabled. + +A scheduler could therefore emit two kinds of workerpoolorder: + +- A workerpoolorder offering execution with the PoCo’s trust layer disabled + (`trust = 0`) and accepting all applications (`apprestrict = 0`) +- A workerpoolorder offering secure execution of whitelisted tasks. The + application whitelist would use the `GroupInterface` to be verified by the + iExec Clerk. This group could either be managed by the scheduler or by a + certification authority that would check application's determinism. + +## Other technical choices + +### Callback + +Some requester might want an onchain callback with the result of the execution. +The callback mechanism is based on +[[EIP-1154]](proof-of-contribution.md#references-1). The result is a `bytes` +value that is set during the `finalize`. The `IexecProxy` implements both side +of the [[EIP-1154]](proof-of-contribution.md#references-1). + +**Pull:** + +Results are identified by their `taskid` and can be pulled through the +`resultFor` method. + +**Push:** + +In order to use the push approach, the requester can use the `callback` field to +specify the address of a smart contract that implements the `receiveResult` +method specified in [[EIP-1154]](proof-of-contribution.md#references-1). This +method will be called during the finalization with a minimum of 200000 nanoRLC +gas to proceed [[*]](proof-of-contribution.md#references-1). + +In order to protect the scheduler and the workers, any error raised during this +callback will be disregarded and will not prevent the finalization from +happening. The same mechanism goes for the callback running out of gas. + +### Consensus & Reveal duration + +When orders match, IexecClerk records the deal which details the parameters of +the task. If a consensus on a result is achieved before the countdown, the task +is successful. After the countdown, as no consensus is reached, the execution is +failed. The duration of the consensus timer is a balance between the quality of +service offered to the requester (short timer) and the margin available for the +scheduler and the worker to achieve consensus (and go through the reveal +process). + +The maximum duration of a task is governed by the category the task fits in. +While the consensus duration can obviously not be shorter than the task runtime, +a significant margin is required for the scheduler to do its job correctly. +Multiple workers are likely to contribute and extra time must be planed for the +revealing and finalization steps. + +The consensus timer starts when the deal is recorded by the IexecClerk. + +In case of failure, the requester can claim the refund. + +In the v3-alpha version, this timer lasts for 10 category runtime, for all +category. The reveal timer starts whenever a consensus is reached and determines +the time frame the workers have to reveal their contributions. This should be +long enough for worker to have time to reveal while not being too long so that +the requester waits too long for its result or the consensus fails because the +scheduler cannot finalize in time. In the v3-alpha version, this timer lasts for +2 runtime whatever the category runtime. This leaves a gap of at least 1 time +the category runtime for the scheduler to finalize the task. + +### Reward kitty + +When a consensus fails, the requester gets a refund and the scheduler loses its +stake. In order to remove an attack vector, the requester does not get any of +the seized stake. If this was a feature, anyone could build a flawed application +that would not reach consensus to drain money from the scheduler. This would +force the scheduler to whitelist all applications thus reducing the usability of +the platform. Seized stake from the scheduler goes into a specific account +called the _reward kitty_. No one controls this account, nor can withdraw from +it. However, the tokens are not burned. Whenever a task is finalized, the +scheduler that organized the execution of this task is rewarded by the requester +and also gets a small part of the reward kitty. + +As described in the protocol parameters section, this reward is +`reward = kitty.percentage(KITTY_RATIO).max(KITTY_MIN).min(kitty)`. + +### References + +| | | +| -------------------------------------------------------------- | --------------------------------------------------------------------- | +| [[EIP-1154]](proof-of-contribution.md#other-technical-choices) | [EIP-1154: Oracle Interface](https://eips.ethereum.org/EIPS/eip-1154) | +| [[*]](proof-of-contribution.md#other-technical-choices) | value susceptible to change. | diff --git a/src/documentation/protocol/worker/manage-access.md b/src/documentation/protocol/worker/manage-access.md new file mode 100644 index 00000000..9201f092 --- /dev/null +++ b/src/documentation/protocol/worker/manage-access.md @@ -0,0 +1,108 @@ +--- +title: Manage Workerpool Access +description: + A workerpool manager is an essential actor in the iExec network. It will be in + charge of proposing available computing resources (workers) to task + requesters. +--- + +# Manage a workerpool + +> A workerpool manager is an essential actor in the iExec network. It will be in +> charge of proposing available computing resources (workers) to task +> requesters. + +An iExec workerpool is orchestrated by an _iExec Core Scheduler_. The _iExec +Core Scheduler_ watches on-chain deals and schedules off-chain computation among +available workers. + +## Deploy a workerpool + +You can deploy your own workerpool on iExec with +[this repository](https://github.com/iExecBlockchainComputing/deploy-workerpool). +Be sure to check a particular +[release](https://github.com/iExecBlockchainComputing/deploy-workerpool/releases) +before starting your deployment. + +## Manage your workerpoolorders + +Orders enable setting custom governance for resources. + +Here you will learn how to manage your workerpool's monetization rules. + +### Publish a workerpool order + +Initialize a `workerpoolorder`: + +```bash +iexec order init --workerpool +``` + +Edit the `workerpoolorder` part in `iexec.json` to set the conditions to use +your workerpool: + +| key | description | +| ------------------- | ------------------------------------------------------------------------------------ | +| `workerpool` | workerpool address | +| `workerpoolprice` | price to charge the requester for each execution of the app (in nRLC) | +| `volume` | number of authorized uses, each use decreases this number | +| `tag` | restrict usage to a specific runtime such as **Scone** or **Gramine** TEE frameworks | +| `category` | Order category, will define the deal `workClockTimeRef` and its deadlines | +| `trust` | Trust level of the execution, impacts the number of replicates | +| `apprestrict` | restrict the workerpool usage to a specifig app (1) | +| `datasetrestrict` | restrict the workerpool usage to a specific dataset (1) | +| `requesterrestrict` | restrict the workerpool usage to a specific requester (1) | + +1. the restriction is disabled by default with + 0x0000000000000000000000000000000000000000. + +The supported tags for workerpool orders are: + +| Tag value | Description | +| -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | +| `0x0000000000000000000000000000000000000000000000000000000000000000` | Order for the execution of a standard task | +| `0x0000000000000000000000000000000000000000000000000000000000000003` | Order for the execution of a TEE task with Scone framework | +| `0x0000000000000000000000000000000000000000000000000000000000000005` | Order for the execution of a TEE task with Gramine framework (reserved value, do not use) | + +::: warning + +1. Do not publish workerpool orders with a tag value out of the specified list. + Such an order could produce undesirable and unpredictable behaviors. The + [iExec SDK](../for-developers/toolbox/iexec-sdk.md) implements all required + preflight checks to avoid erroneous orders publishing. +2. Currently, TEE workflow do not support tasks replication on several workers. + TEE workerpool orders must be published with `trust` value equal to `1`. +3. TEE tasks with Gramine TEE framework are not supported yet. Do not publish + orders with both `tee` and `gramine` tag bits enabled. + +::: + +As soon as your `ẁorkerpoolorder` complies to your requirements, you must sign +it and you may publish it. + +```bash +iexec order sign --workerpool && iexec order publish --workerpool +``` + +## Remove an order from iExec Marketplace + +List the published orders for your workerpool. + +```bash +iexec orderbook workerpool +``` + +Copy the `orderHash` of the order you want to remove + +Unpublish the `workerpoolorder` from the iExec Marketplace + +```bash +iexec order unpublish --workerpool +``` + +An unpublished order is still valid on the blockchain, to invalidate it use the +cancel command. + +```bash +iexec order cancel --workerpool +``` diff --git a/src/documentation/protocol/worker/quick-start.md b/src/documentation/protocol/worker/quick-start.md new file mode 100644 index 00000000..47f10e4e --- /dev/null +++ b/src/documentation/protocol/worker/quick-start.md @@ -0,0 +1,46 @@ +--- +title: Worker Quick Start +description: + A worker is an essential actor of the iExec Network. It will be in charge of + computing tasks sent by requesters on the iExec Marketplace +--- + +# Quick start + +> A worker is an essential actor of the iExec Network. It will be in charge of +> computing tasks sent by requesters on the iExec Marketplace. + +The iExec Worker participates in a workerpool by computing tasks purchased by +requesters on the iExec marketplace. The iExec Worker must connect to the iExec +Core Scheduler of the workerpool to actively participate in the computation. + +A worker will be rewarded with RLC for every properly computed tasks. + +Please note that: + +- your wallet must be loaded with RLC. Some RLC must be deposited to your iExec + account in order to stake for incoming tasks. +- your computer needs at least 2 CPUs. + +## Start a worker + +Find a workerpool which allows your worker to connect. + +As an example of how you could deploy you own worker, you can check the +configuration documentation of the +[Worker Pass](https://github.com/iExecBlockchainComputing/wpwp-worker-setup). + +For security reason, it is **highly recommended** to launch your worker in a +virtual machine. + +After having loaded some RLC and deposited them to your iExec account, you can +start your worker. + +When the worker initialization process is complete, the worker will be started +and you will get something like: **You worker is all set**. Your worker will now +be able to compute some tasks coming from the iExec network to earn some RLCs. + +## Wallet restriction + +An exclusive wallet must be associated to your worker. You need N wallets if you +want N workers. diff --git a/src/documentation/protocol/workers.md b/src/documentation/protocol/workers.md deleted file mode 100644 index c9ea366c..00000000 --- a/src/documentation/protocol/workers.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -title: Workers and Workerpools -description: - Understanding the compute infrastructure of iExec and how workers and - workerpools function ---- - -# ⚙️ Workers & Workerpools - -This page is under development. - - diff --git a/src/documentation/use-iapp/guides/index.md b/src/documentation/use-iapp/guides/index.md index 14d9c83a..8541a822 100644 --- a/src/documentation/use-iapp/guides/index.md +++ b/src/documentation/use-iapp/guides/index.md @@ -5,35 +5,49 @@ description: Complete guide collection for using iApps on the iExec network # 🚀 Use iApp Guides -Welcome to the complete collection of guides for using iApps on the iExec network. These guides will help you find, execute, and interact with privacy-preserving applications. +Welcome to the complete collection of guides for using iApps on the iExec +network. These guides will help you find, execute, and interact with +privacy-preserving applications. ## 🔍 Finding & Using iApps -- **[Find iApps](/documentation/use-iapp/guides/find-iapps)** - Discover available applications -- **[Different Ways to Execute](/documentation/use-iapp/guides/different-ways-to-execute)** - Multiple execution methods -- **[Use iApp with Protected Data](/documentation/use-iapp/guides/use-iapp-with-protected-data)** - Secure data processing +- **[Find iApps](/documentation/use-iapp/guides/find-iapps)** - Discover + available applications +- **[Different Ways to Execute](/documentation/use-iapp/guides/different-ways-to-execute)** - + Multiple execution methods +- **[Use iApp with Protected Data](/documentation/use-iapp/guides/use-iapp-with-protected-data)** - + Secure data processing ## 💰 Payment & Execution -- **[How to Pay for Executions](/documentation/use-iapp/guides/how-to-pay-executions)** - Payment methods and costs -- **[Add Inputs to Execution](/documentation/use-iapp/guides/add-inputs-to-execution)** - Provide data and parameters +- **[How to Pay for Executions](/documentation/use-iapp/guides/how-to-pay-executions)** - + Payment methods and costs +- **[Add Inputs to Execution](/documentation/use-iapp/guides/add-inputs-to-execution)** - + Provide data and parameters ## 📧 Communication Apps - **[Web3Mail](/documentation/use-iapp/web3mail)** - Send encrypted emails -- **[Web3Telegram](/documentation/use-iapp/web3telegram)** - Send encrypted Telegram messages +- **[Web3Telegram](/documentation/use-iapp/web3telegram)** - Send encrypted + Telegram messages ## 🆓 Getting Started -- **[Getting Started](/documentation/use-iapp/getting-started)** - Essential steps for beginners -- **[How to Pay for Web3Mail](/documentation/use-iapp/how-to-pay/how-to-pay-for-web3mail)** - Free email service -- **[How to Pay for Web3Telegram](/documentation/use-iapp/how-to-pay/how-to-pay-for-web3telegram)** - Free messaging service +- **[Getting Started](/documentation/use-iapp/getting-started)** - Essential + steps for beginners +- **[How to Pay for Web3Mail](/documentation/use-iapp/how-to-pay/how-to-pay-for-web3mail)** - + Free email service +- **[How to Pay for Web3Telegram](/documentation/use-iapp/how-to-pay/how-to-pay-for-web3telegram)** - + Free messaging service ## 📚 What's Next? After mastering these guides, explore: -- **[Data Protector](/documentation/manage-data/dataProtector)** - Protect your own data -- **[Build iApps](/documentation/build-iapp/what-is-iapp)** - Create your own applications +- **[Data Protector](/documentation/manage-data/dataProtector)** - Protect your + own data +- **[Build iApps](/documentation/build-iapp/what-is-iapp)** - Create your own + applications -Ready to start using iApps? Begin with the [Getting Started](/documentation/use-iapp/getting-started) guide! +Ready to start using iApps? Begin with the +[Getting Started](/documentation/use-iapp/getting-started) guide! diff --git a/src/documentation/use-iapp/web3mail.md b/src/documentation/use-iapp/web3mail.md index 0362f336..77f3d46e 100644 --- a/src/documentation/use-iapp/web3mail.md +++ b/src/documentation/use-iapp/web3mail.md @@ -17,14 +17,14 @@ via the blockchain. This mechanism helps protect the personal information of the email recipients through use of Ethereum addresses. The email address is stored as a `protectedData` entity using the -[iExec Data Protector tool](/documentation/manage-data/dataProtector). Through this mechanism, users -have complete control over which applications may use their email address for -sending communications. Sending a user a message, therefore, requires knowledge -of the Ethereum address of their `protectedData` as well positive authorization -for your account to contact them. Your account may be bound to either an -application or an individual. At any time a user may revoke permissions and this -revocation is immediate, giving users complete control over the privacy and -security of their information. +[iExec Data Protector tool](/documentation/manage-data/dataProtector). Through +this mechanism, users have complete control over which applications may use +their email address for sending communications. Sending a user a message, +therefore, requires knowledge of the Ethereum address of their `protectedData` +as well positive authorization for your account to contact them. Your account +may be bound to either an application or an individual. At any time a user may +revoke permissions and this revocation is immediate, giving users complete +control over the privacy and security of their information. Apps using the Web3Mail tool can: diff --git a/src/documentation/use-iapp/web3telegram.md b/src/documentation/use-iapp/web3telegram.md index 6bc0c708..0454d3c5 100644 --- a/src/documentation/use-iapp/web3telegram.md +++ b/src/documentation/use-iapp/web3telegram.md @@ -17,8 +17,8 @@ blockchain. This mechanism helps protect the personal information of the telegram chat ID recipients through use of Ethereum addresses. The telegram chat ID address is stored as a `protectedData` entity using -[iExec Data Protector](/documentation/manage-data/dataProtector). Through this mechanism, users have -complete control over which applications may use their +[iExec Data Protector](/documentation/manage-data/dataProtector). Through this +mechanism, users have complete control over which applications may use their [chat ID](./web3telegram/integration-guide.md#_1-get-your-users-to-retrieve-their-chat-id) for sending communications. From 48eb0c4a9fc77a6220f03503cf8602f157ea98d2 Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Tue, 12 Aug 2025 00:46:03 +0200 Subject: [PATCH 6/8] Update sidebar icons and enhance SDK documentation with new sections and links --- .vitepress/sidebar.ts | 4 +-- .../protocol/poco/proof-of-contribution.md | 2 +- src/documentation/protocol/sdk.md | 31 +++++++++++++++++-- .../protocol/worker/manage-access.md | 4 +-- 4 files changed, 33 insertions(+), 8 deletions(-) diff --git a/.vitepress/sidebar.ts b/.vitepress/sidebar.ts index d9426cb2..4c011d9c 100644 --- a/.vitepress/sidebar.ts +++ b/.vitepress/sidebar.ts @@ -552,11 +552,11 @@ export function getSidebar() { ], }, { - text: '⚙️ PoCo', + text: '⛓️‍💥 PoCo', collapsed: true, items: [ { - text: '� Pay Per Task Model', + text: '💸 Pay Per Task Model', link: '/documentation/protocol/poco/pay-per-task', }, { diff --git a/src/documentation/protocol/poco/proof-of-contribution.md b/src/documentation/protocol/poco/proof-of-contribution.md index d1311dd4..821073b3 100644 --- a/src/documentation/protocol/poco/proof-of-contribution.md +++ b/src/documentation/protocol/poco/proof-of-contribution.md @@ -76,7 +76,7 @@ consensus on a given result. Its logic is detailed in two blog articles: The [nominal workflow](https://github.com/iExecBlockchainComputing/iexec-doc/raw/master/techreport/nominalworkflow-ODB.png) -is also available in the [technical report section](../help/glossary.md) +is also available in the [technical report section](../glossary.md) Below are the details of the implementations: diff --git a/src/documentation/protocol/sdk.md b/src/documentation/protocol/sdk.md index 374e09f8..54ee6c83 100644 --- a/src/documentation/protocol/sdk.md +++ b/src/documentation/protocol/sdk.md @@ -3,8 +3,33 @@ title: iExec SDK description: iExec SDK --- -# 🔧 iExec SDK +# iExec SDK -This page is under development. +The iExec SDK is a [CLI](#command-line-interface) and a +[JS library](#javascripttypescript-library) that allows easy interactions with +iExec decentralized marketplace in order to run off-chain computations. - +iExec SDK is available on [npm](https://www.npmjs.com/package/iexec) and +[GitHub](https://github.com/iExecBlockchainComputing/iexec-sdk) + +## Command Line Interface + +The CLI documentation is available +[here](https://github.com/iExecBlockchainComputing/iexec-sdk/blob/master/docs/README.md) + +## JavaScript/TypeScript Library + +The library documentation is available +[here](https://github.com/iExecBlockchainComputing/iexec-sdk/blob/master/docs/README.md) + +### Browser integration + + + ⚡  Code Sandbox + + +### NodeJS integration + + + ⚡  Code Sandbox + diff --git a/src/documentation/protocol/worker/manage-access.md b/src/documentation/protocol/worker/manage-access.md index 9201f092..28b5815c 100644 --- a/src/documentation/protocol/worker/manage-access.md +++ b/src/documentation/protocol/worker/manage-access.md @@ -68,8 +68,8 @@ The supported tags for workerpool orders are: 1. Do not publish workerpool orders with a tag value out of the specified list. Such an order could produce undesirable and unpredictable behaviors. The - [iExec SDK](../for-developers/toolbox/iexec-sdk.md) implements all required - preflight checks to avoid erroneous orders publishing. + [iExec SDK](../sdk.md) implements all required preflight checks to avoid + erroneous orders publishing. 2. Currently, TEE workflow do not support tasks replication on several workers. TEE workerpool orders must be published with `trust` value equal to `1`. 3. TEE tasks with Gramine TEE framework are not supported yet. Do not publish From 18eb35d38e24872e8e05762dbad3a842d3c3635f Mon Sep 17 00:00:00 2001 From: Le-Caignec Date: Tue, 12 Aug 2025 10:47:42 +0200 Subject: [PATCH 7/8] Add framework AI support comparison between TDX and SGX --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ea55c9b0..751fcb44 100644 --- a/README.md +++ b/README.md @@ -168,3 +168,4 @@ Fork this repository and ensure you're working on the `main` branch: the UI + SRLC/RLC on account section feature of the protocol - Update or add design illustrations based on the new design system - Check how to pay guide to update with the launch on Arbitrum (RLC vs xRLC) +- framework AI supporté TDX vs SGX From ad9dd75ca18ae9ef3e2a604d2dd7284be4ba35e3 Mon Sep 17 00:00:00 2001 From: Robin Le Caignec <72495599+Le-Caignec@users.noreply.github.com> Date: Wed, 13 Aug 2025 12:56:16 +0200 Subject: [PATCH 8/8] Feature/integrate arbitrum (#35) --- .vitepress/config.mts | 6 +- .vitepress/sidebar.ts | 774 +++++++++--------- README.md | 4 + package-lock.json | 30 +- package.json | 6 +- src/components/ChainSelector.vue | 19 + src/documentation/build-iapp/guides/index.md | 43 - src/documentation/use-iapp/guides/index.md | 53 -- .../develop-with-ai.md | 0 .../helloWorld.md | 12 + .../helloWorld/1-overview.md | 2 +- .../helloWorld/2-protectData.md | 4 - .../helloWorld/3-buildIApp.md | 392 +++++++-- .../helloWorld/4-manageDataAccess.md | 2 +- .../helloWorld/5-bonusChapter.md | 19 +- .../overview}/what-is-iapp.md | 130 +-- src/get-started/overview/what-is-iexec.md | 57 ++ .../overview}/what-is-protected-data.md | 4 +- .../overview/what-is-workerpool.md | 11 + .../protocol}/pay-per-task.md | 0 .../protocol}/proof-of-contribution.md | 30 +- .../protocol/worker/manage-access.md | 4 +- .../protocol/worker/quick-start.md | 0 .../quick-start.md | 0 src/{documentation => get-started}/rlc.md | 4 +- .../blockchain-explorer.md | 0 .../tooling-and-explorers/bridge.md | 0 .../builder-dashboard.md | 0 .../tooling-and-explorers/iexec-explorer.md | 0 .../subgraph-explorer.md | 0 src/get-started/toolkit.md | 10 + .../use-cases.md | 0 src/{documentation => get-started}/welcome.md | 4 +- .../build-iapp}/build-&-deploy.md | 0 .../guides => guides/build-iapp}/debugging.md | 6 +- .../how-to-get-and-decrypt-results.md | 17 +- src/guides/build-iapp/index.md | 42 + .../build-iapp}/inputs-and-outputs.md | 12 +- .../build-iapp}/manage-access.md | 0 .../guides => guides/build-iapp}/using-tdx.md | 10 +- .../handle-schemas-dataset-types.md | 14 +- .../manage-data}/manage-access.md | 18 +- .../manage-data}/monetize-protected-data.md | 14 +- .../use-iapp}/add-inputs-to-execution.md | 0 .../use-iapp}/different-ways-to-execute.md | 0 .../guides => guides/use-iapp}/find-iapps.md | 0 .../use-iapp/getting-started.md | 0 .../use-iapp}/how-to-pay-executions.md | 0 .../how-to-pay/how-to-pay-for-web3mail.md | 4 +- .../how-to-pay/how-to-pay-for-web3telegram.md | 4 +- .../how-to-pay/pricing-considerations.md | 0 .../use-iapp/how-to-pay/voucher.md | 0 .../use-iapp/introduction.md | 0 .../use-iapp}/use-iapp-with-protected-data.md | 0 src/index.md | 16 +- src/modules/helloWorld/GrantAccess.vue | 31 +- src/modules/helloWorld/ProtectData.vue | 55 +- .../dataProtector.md | 2 +- .../advanced/advanced-configuration.md | 0 .../dataProtector/advanced/apps-whitelist.md | 0 .../addAppToAddOnlyAppWhitelist.md | 0 .../createAddOnlyAppWhitelist.md | 0 .../getUserAddOnlyAppWhitelist.md | 0 .../advanced/dps-smart-contract.md | 0 .../dataProtector/dataProtectorCore.md | 0 .../dataProtectorCore/getGrantedAccess.md | 0 .../dataProtectorCore/getProtectedData.md | 0 .../getResultFromCompletedTask.md | 0 .../dataProtectorCore/grantAccess.md | 0 .../dataProtectorCore/processProtectedData.md | 0 .../dataProtectorCore/protectData.md | 2 +- .../dataProtectorCore/revokeAllAccess.md | 0 .../dataProtectorCore/revokeOneAccess.md | 0 .../dataProtectorCore/transferOwnership.md | 0 .../dataProtector/dataProtectorSharing.md | 0 .../dataProtectorSharing/collection.md | 0 .../collection/addToCollection.md | 0 .../collection/createCollection.md | 0 .../collection/removeCollection.md | 0 .../removeProtectedDataFromCollection.md | 0 .../consume/consumeProtectedData.md | 0 .../dataProtectorSharing/data-sharing-sc.png | Bin .../inside-a-collection.png | Bin .../read/getCollectionOwners.md | 0 .../read/getCollectionSubscriptions.md | 0 .../read/getCollectionsByOwner.md | 0 .../read/getProtectedDataInCollections.md | 0 .../read/getProtectedDataPricingParams.md | 0 .../dataProtectorSharing/read/getRentals.md | 0 .../dataProtectorSharing/renting.md | 0 .../renting/removeProtectedDataFromRenting.md | 0 .../renting/rentProtectedData.md | 0 .../renting/setProtectedDataRentingParams.md | 0 .../renting/setProtectedDataToRenting.md | 0 .../dataProtectorSharing/selling.md | 0 .../selling/buyProtectedData.md | 0 .../selling/removeProtectedDataForSale.md | 0 .../selling/setProtectedDataForSale.md | 0 .../dataProtectorSharing/subscription.md | 0 .../removeProtectedDataFromSubscription.md | 0 .../setProtectedDataToSubscription.md | 0 .../subscription/setSubscriptionParams.md | 0 .../subscription/subscribeToCollection.md | 0 .../dataProtector/getting-started.md | 0 .../dataProtector/migrate-from-v1.md | 2 +- .../dataProtector/types.md | 0 .../protocol => references}/glossary.md | 0 .../iapp-generator.md | 48 +- .../iapp-generator/building-your-iexec-app.md | 0 .../iapp-generator/deserializer.md | 0 .../iapp-generator/deserializer/getValue.md | 0 .../iapp-generator/getting-started.md | 0 .../protocol => references}/sdk.md | 0 .../use-iapp => references}/web3mail.md | 16 +- .../web3mail/advanced-configuration.md | 0 .../web3mail/getting-started.md | 0 .../web3mail/methods/fetchMyContacts.md | 2 +- .../web3mail/methods/fetchUserContacts.md | 2 +- .../web3mail/methods/sendEmail.md | 4 +- .../use-iapp => references}/web3telegram.md | 4 +- .../web3telegram/advanced-configuration.md | 0 .../web3telegram/getting-started.md | 0 .../web3telegram/integration-guide.md | 0 .../web3telegram/methods/fetchMyContacts.md | 2 +- .../web3telegram/methods/fetchUserContacts.md | 2 +- .../web3telegram/methods/sendTelegram.md | 4 +- 126 files changed, 1147 insertions(+), 806 deletions(-) delete mode 100644 src/documentation/build-iapp/guides/index.md delete mode 100644 src/documentation/use-iapp/guides/index.md rename src/{documentation => get-started}/develop-with-ai.md (100%) rename src/{documentation => get-started}/helloWorld.md (94%) rename src/{documentation => get-started}/helloWorld/1-overview.md (98%) rename src/{documentation => get-started}/helloWorld/2-protectData.md (94%) rename src/{documentation => get-started}/helloWorld/3-buildIApp.md (53%) rename src/{documentation => get-started}/helloWorld/4-manageDataAccess.md (98%) rename src/{documentation => get-started}/helloWorld/5-bonusChapter.md (91%) rename src/{documentation/build-iapp => get-started/overview}/what-is-iapp.md (57%) create mode 100644 src/get-started/overview/what-is-iexec.md rename src/{documentation/manage-data => get-started/overview}/what-is-protected-data.md (94%) create mode 100644 src/get-started/overview/what-is-workerpool.md rename src/{documentation/protocol/poco => get-started/protocol}/pay-per-task.md (100%) rename src/{documentation/protocol/poco => get-started/protocol}/proof-of-contribution.md (99%) rename src/{documentation => get-started}/protocol/worker/manage-access.md (97%) rename src/{documentation => get-started}/protocol/worker/quick-start.md (100%) rename src/{documentation => get-started}/quick-start.md (100%) rename src/{documentation => get-started}/rlc.md (97%) rename src/{documentation => get-started}/tooling-and-explorers/blockchain-explorer.md (100%) rename src/{documentation => get-started}/tooling-and-explorers/bridge.md (100%) rename src/{documentation => get-started}/tooling-and-explorers/builder-dashboard.md (100%) rename src/{documentation => get-started}/tooling-and-explorers/iexec-explorer.md (100%) rename src/{documentation => get-started}/tooling-and-explorers/subgraph-explorer.md (100%) create mode 100644 src/get-started/toolkit.md rename src/{documentation => get-started}/use-cases.md (100%) rename src/{documentation => get-started}/welcome.md (94%) rename src/{documentation/build-iapp/guides => guides/build-iapp}/build-&-deploy.md (100%) rename src/{documentation/build-iapp/guides => guides/build-iapp}/debugging.md (95%) rename src/{documentation/build-iapp/guides => guides/build-iapp}/how-to-get-and-decrypt-results.md (93%) create mode 100644 src/guides/build-iapp/index.md rename src/{documentation/build-iapp/guides => guides/build-iapp}/inputs-and-outputs.md (97%) rename src/{documentation/build-iapp/guides => guides/build-iapp}/manage-access.md (100%) rename src/{documentation/build-iapp/guides => guides/build-iapp}/using-tdx.md (94%) rename src/{documentation/manage-data/guides => guides/manage-data}/handle-schemas-dataset-types.md (93%) rename src/{documentation/manage-data/guides => guides/manage-data}/manage-access.md (88%) rename src/{documentation/manage-data/guides => guides/manage-data}/monetize-protected-data.md (93%) rename src/{documentation/use-iapp/guides => guides/use-iapp}/add-inputs-to-execution.md (100%) rename src/{documentation/use-iapp/guides => guides/use-iapp}/different-ways-to-execute.md (100%) rename src/{documentation/use-iapp/guides => guides/use-iapp}/find-iapps.md (100%) rename src/{documentation => guides}/use-iapp/getting-started.md (100%) rename src/{documentation/use-iapp/guides => guides/use-iapp}/how-to-pay-executions.md (100%) rename src/{documentation => guides}/use-iapp/how-to-pay/how-to-pay-for-web3mail.md (97%) rename src/{documentation => guides}/use-iapp/how-to-pay/how-to-pay-for-web3telegram.md (97%) rename src/{documentation => guides}/use-iapp/how-to-pay/pricing-considerations.md (100%) rename src/{documentation => guides}/use-iapp/how-to-pay/voucher.md (100%) rename src/{documentation => guides}/use-iapp/introduction.md (100%) rename src/{documentation/use-iapp/guides => guides/use-iapp}/use-iapp-with-protected-data.md (100%) rename src/{documentation/manage-data => references}/dataProtector.md (95%) rename src/{documentation/manage-data => references}/dataProtector/advanced/advanced-configuration.md (100%) rename src/{documentation/manage-data => references}/dataProtector/advanced/apps-whitelist.md (100%) rename src/{documentation/manage-data => references}/dataProtector/advanced/apps-whitelist/addAppToAddOnlyAppWhitelist.md (100%) rename src/{documentation/manage-data => references}/dataProtector/advanced/apps-whitelist/createAddOnlyAppWhitelist.md (100%) rename src/{documentation/manage-data => references}/dataProtector/advanced/apps-whitelist/getUserAddOnlyAppWhitelist.md (100%) rename src/{documentation/manage-data => references}/dataProtector/advanced/dps-smart-contract.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorCore.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorCore/getGrantedAccess.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorCore/getProtectedData.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorCore/getResultFromCompletedTask.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorCore/grantAccess.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorCore/processProtectedData.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorCore/protectData.md (99%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorCore/revokeAllAccess.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorCore/revokeOneAccess.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorCore/transferOwnership.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/collection.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/collection/addToCollection.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/collection/createCollection.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/collection/removeCollection.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/collection/removeProtectedDataFromCollection.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/consume/consumeProtectedData.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/data-sharing-sc.png (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/inside-a-collection.png (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/read/getCollectionOwners.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/read/getCollectionSubscriptions.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/read/getCollectionsByOwner.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/read/getProtectedDataInCollections.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/read/getProtectedDataPricingParams.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/read/getRentals.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/renting.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/renting/removeProtectedDataFromRenting.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/renting/rentProtectedData.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/renting/setProtectedDataRentingParams.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/renting/setProtectedDataToRenting.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/selling.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/selling/buyProtectedData.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/selling/removeProtectedDataForSale.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/selling/setProtectedDataForSale.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/subscription.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/subscription/removeProtectedDataFromSubscription.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/subscription/setProtectedDataToSubscription.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/subscription/setSubscriptionParams.md (100%) rename src/{documentation/manage-data => references}/dataProtector/dataProtectorSharing/subscription/subscribeToCollection.md (100%) rename src/{documentation/manage-data => references}/dataProtector/getting-started.md (100%) rename src/{documentation/manage-data => references}/dataProtector/migrate-from-v1.md (98%) rename src/{documentation/manage-data => references}/dataProtector/types.md (100%) rename src/{documentation/protocol => references}/glossary.md (100%) rename src/{documentation/build-iapp => references}/iapp-generator.md (66%) rename src/{documentation/build-iapp => references}/iapp-generator/building-your-iexec-app.md (100%) rename src/{documentation/build-iapp => references}/iapp-generator/deserializer.md (100%) rename src/{documentation/build-iapp => references}/iapp-generator/deserializer/getValue.md (100%) rename src/{documentation/build-iapp => references}/iapp-generator/getting-started.md (100%) rename src/{documentation/protocol => references}/sdk.md (100%) rename src/{documentation/use-iapp => references}/web3mail.md (71%) rename src/{documentation/use-iapp => references}/web3mail/advanced-configuration.md (100%) rename src/{documentation/use-iapp => references}/web3mail/getting-started.md (100%) rename src/{documentation/use-iapp => references}/web3mail/methods/fetchMyContacts.md (96%) rename src/{documentation/use-iapp => references}/web3mail/methods/fetchUserContacts.md (96%) rename src/{documentation/use-iapp => references}/web3mail/methods/sendEmail.md (98%) rename src/{documentation/use-iapp => references}/web3telegram.md (91%) rename src/{documentation/use-iapp => references}/web3telegram/advanced-configuration.md (100%) rename src/{documentation/use-iapp => references}/web3telegram/getting-started.md (100%) rename src/{documentation/use-iapp => references}/web3telegram/integration-guide.md (100%) rename src/{documentation/use-iapp => references}/web3telegram/methods/fetchMyContacts.md (96%) rename src/{documentation/use-iapp => references}/web3telegram/methods/fetchUserContacts.md (97%) rename src/{documentation/use-iapp => references}/web3telegram/methods/sendTelegram.md (97%) diff --git a/.vitepress/config.mts b/.vitepress/config.mts index 616014d7..3aa45736 100644 --- a/.vitepress/config.mts +++ b/.vitepress/config.mts @@ -98,9 +98,9 @@ export default defineConfig({ themeConfig: { // https://vitepress.dev/reference/default-theme-config nav: [ - { text: 'Documentation', link: '/documentation/welcome' }, - { text: 'Guides', link: '/' }, - { text: 'References', link: '/' }, + { text: 'Get Started', link: '/get-started/welcome' }, + { text: 'Guides', link: '/guides/build-iapp/' }, + { text: 'References', link: '/references/dataProtector' }, { component: 'ChainSelector', props: { diff --git a/.vitepress/sidebar.ts b/.vitepress/sidebar.ts index 4c011d9c..e27784e4 100644 --- a/.vitepress/sidebar.ts +++ b/.vitepress/sidebar.ts @@ -2,577 +2,569 @@ import type { DefaultTheme } from 'vitepress'; export function getSidebar() { return { - '/documentation/': [ + '/get-started/': [ { text: 'GET STARTED', items: [ - { text: '💡 Welcome', link: '/documentation/welcome' }, + { text: '💡 Welcome', link: '/get-started/welcome' }, + { + text: '🛠️ Toolkit', + link: '/get-started/toolkit', + }, { text: '👋 Hello World', - link: '/documentation/helloWorld', + link: '/get-started/helloWorld', collapsed: true, items: [ { text: 'iExec Overview', - link: '/documentation/helloWorld/1-overview', + link: '/get-started/helloWorld/1-overview', }, { text: 'Protect Data', - link: '/documentation/helloWorld/2-protectData', + link: '/get-started/helloWorld/2-protectData', }, { text: 'Build iApp', - link: '/documentation/helloWorld/3-buildIApp', + link: '/get-started/helloWorld/3-buildIApp', }, { text: 'Manage Data Access', - link: '/documentation/helloWorld/4-manageDataAccess', + link: '/get-started/helloWorld/4-manageDataAccess', }, { text: 'Bonus Chapter !', - link: '/documentation/helloWorld/5-bonusChapter', + link: '/get-started/helloWorld/5-bonusChapter', }, ], }, { text: '🚀 Quick Start', - link: '/documentation/quick-start', + link: '/get-started/quick-start', }, { text: '📋 Use Cases', - link: '/documentation/use-cases', + link: '/get-started/use-cases', + }, + { + text: '🤖 Develop with AI', + link: '/get-started/develop-with-ai', + }, + ], + }, + { + text: 'iExec OVERVIEW', + items: [ + { + text: '❓ What is iExec ?', + link: '/get-started/overview/what-is-iexec', + }, + { + text: '❓  What is Protected Data ?', + link: '/get-started/overview/what-is-protected-data', + }, + { + text: '❓ What is an iApp ?', + link: '/get-started/overview/what-is-iapp', + }, + { + text: '❓ What is a Workerpool ?', + link: '/get-started/overview/what-is-workerpool', }, { text: '🪙 RLC Token', - link: '/documentation/rlc', + link: '/get-started/rlc', }, + ], + }, + { + text: 'TOOLING & EXPLORERS', + items: [ { - text: '🤖 Develop with AI', - link: '/documentation/develop-with-ai', + text: 'iExec Explorer', + link: '/get-started/tooling-and-explorers/iexec-explorer', + }, + { + text: 'Builder Dashboard', + link: '/get-started/tooling-and-explorers/builder-dashboard', + }, + { + text: 'RLC Bridge', + link: '/get-started/tooling-and-explorers/bridge', + }, + { + text: 'Subgraph Explorer', + link: '/get-started/tooling-and-explorers/subgraph-explorer', + }, + { + text: 'Blockchain Explorer', + link: '/get-started/tooling-and-explorers/blockchain-explorer', }, ], }, + { + text: 'PROTOCOL', + items: [ + { + text: '🛡️ Proof of Contribution', + link: '/get-started/protocol/proof-of-contribution', + }, + { + text: '💸 Pay Per Task Model', + link: '/get-started/protocol/pay-per-task', + }, + { + text: '⚙️ Workers & Workerpools', + collapsed: true, + items: [ + { + text: '🚀 Worker Quick Start', + link: '/get-started/protocol/worker/quick-start', + }, + { + text: '🔒 Manage Workerpool Access', + link: '/get-started/protocol/worker/manage-access', + }, + ], + }, + ], + }, + ], + '/guides/': [ { text: 'PROTECT AND MANAGE DATA', items: [ { - text: '❓  What is Protected Data ?', - link: '/documentation/manage-data/what-is-protected-data', + text: 'Manage Access', + link: '/guides/manage-data/manage-access', + }, + { + text: 'Handle Schemas and Dataset Types', + link: '/guides/manage-data/handle-schemas-dataset-types', + }, + { + text: 'Monetize Protected Data', + link: '/guides/manage-data/monetize-protected-data', + }, + ], + }, + { + text: 'BUILD YOUR iAPP', + items: [ + { + text: 'Build and Deploy', + link: '/guides/build-iapp/build-&-deploy', + }, + { + text: 'Manage Access', + link: '/guides/build-iapp/manage-access', }, { - text: '📖 Guides', + text: 'Inputs and Outputs', + link: '/guides/build-iapp/inputs-and-outputs', + }, + { + text: 'Using TDX', + link: '/guides/build-iapp/using-tdx', + }, + { + text: 'How to Get and Decrypt Results', + link: '/guides/build-iapp/how-to-get-and-decrypt-results', + }, + { + text: 'Debugging', + link: '/guides/build-iapp/debugging', + }, + ], + }, + { + text: 'USE AN iAPP', + items: [ + { + text: '📝 Introduction', + link: '/guides/use-iapp/introduction', + }, + { + text: '🚀 Getting Started', + link: '/guides/use-iapp/getting-started', + }, + { + text: 'Different Ways to Execute an iApp', + link: '/guides/use-iapp/different-ways-to-execute', + }, + { + text: 'Add Inputs to the Execution', + link: '/guides/use-iapp/add-inputs-to-execution', + }, + { + text: 'Use iApp with Protected Data', + link: '/guides/use-iapp/use-iapp-with-protected-data', + }, + { + text: 'Find iApps to Use', + link: '/guides/use-iapp/find-iapps', + }, + { + text: 'How to Pay the Executions', + link: '/guides/use-iapp/how-to-pay-executions', + }, + { + text: '💰 How to Pay', collapsed: true, items: [ { - text: 'Manage Access', - link: '/documentation/manage-data/guides/manage-access', + text: 'How to Pay for Web3Mail', + link: '/guides/use-iapp/how-to-pay/how-to-pay-for-web3mail', }, { - text: 'Handle Schemas and Dataset Types', - link: '/documentation/manage-data/guides/handle-schemas-dataset-types', + text: 'How to Pay for Web3Telegram', + link: '/guides/use-iapp/how-to-pay/how-to-pay-for-web3telegram', }, { - text: 'Monetize Protected Data', - link: '/documentation/manage-data/guides/monetize-protected-data', + text: 'Pricing Considerations', + link: '/guides/use-iapp/how-to-pay/pricing-considerations', + }, + { + text: 'Voucher', + link: '/guides/use-iapp/how-to-pay/voucher', }, ], }, + ], + }, + ], + '/references/': [ + { + text: '🔐 DataProtector', + link: '/references/dataProtector', + collapsed: true, + items: [ { - text: '🔐 DataProtector', - link: '/documentation/manage-data/dataProtector', + text: 'Getting Started', + link: '/references/dataProtector/getting-started', + }, + { + text: 'DataProtector Core', + link: '/references/dataProtector/dataProtectorCore', collapsed: true, items: [ { - text: 'Getting Started', - link: '/documentation/manage-data/dataProtector/getting-started', + text: 'protectData', + link: '/references/dataProtector/dataProtectorCore/protectData', + }, + { + text: 'getProtectedData', + link: '/references/dataProtector/dataProtectorCore/getProtectedData', + }, + { + text: 'transferOwnership', + link: '/references/dataProtector/dataProtectorCore/transferOwnership', + }, + { + text: 'grantAccess', + link: '/references/dataProtector/dataProtectorCore/grantAccess', + }, + { + text: 'getGrantedAccess', + link: '/references/dataProtector/dataProtectorCore/getGrantedAccess', + }, + { + text: 'revokeOneAccess', + link: '/references/dataProtector/dataProtectorCore/revokeOneAccess', + }, + { + text: 'revokeAllAccess', + link: '/references/dataProtector/dataProtectorCore/revokeAllAccess', }, { - text: 'DataProtector Core', - link: '/documentation/manage-data/dataProtector/dataProtectorCore', + text: 'processProtectedData', + link: '/references/dataProtector/dataProtectorCore/processProtectedData', + }, + { + text: 'getResultFromCompletedTask', + link: '/references/dataProtector/dataProtectorCore/getResultFromCompletedTask', + }, + ], + }, + { + text: 'DataProtector Sharing', + link: '/references/dataProtector/dataProtectorSharing', + collapsed: true, + items: [ + { + text: 'Collection', + link: '/references/dataProtector/dataProtectorSharing/collection', collapsed: true, items: [ { - text: 'protectData', - link: '/documentation/manage-data/dataProtector/dataProtectorCore/protectData', + text: 'createCollection', + link: '/references/dataProtector/dataProtectorSharing/collection/createCollection', }, { - text: 'getProtectedData', - link: '/documentation/manage-data/dataProtector/dataProtectorCore/getProtectedData', + text: 'removeCollection', + link: '/references/dataProtector/dataProtectorSharing/collection/removeCollection', }, { - text: 'transferOwnership', - link: '/documentation/manage-data/dataProtector/dataProtectorCore/transferOwnership', + text: 'addToCollection', + link: '/references/dataProtector/dataProtectorSharing/collection/addToCollection', }, { - text: 'grantAccess', - link: '/documentation/manage-data/dataProtector/dataProtectorCore/grantAccess', - }, - { - text: 'getGrantedAccess', - link: '/documentation/manage-data/dataProtector/dataProtectorCore/getGrantedAccess', + text: 'removeProtectedDataFromCollection', + link: '/references/dataProtector/dataProtectorSharing/collection/removeProtectedDataFromCollection', }, + ], + }, + { + text: 'Renting', + link: '/references/dataProtector/dataProtectorSharing/renting', + collapsed: true, + items: [ { - text: 'revokeOneAccess', - link: '/documentation/manage-data/dataProtector/dataProtectorCore/revokeOneAccess', + text: 'setProtectedDataToRenting', + link: '/references/dataProtector/dataProtectorSharing/renting/setProtectedDataToRenting', }, { - text: 'revokeAllAccess', - link: '/documentation/manage-data/dataProtector/dataProtectorCore/revokeAllAccess', + text: 'setProtectedDataRentingParams', + link: '/references/dataProtector/dataProtectorSharing/renting/setProtectedDataRentingParams', }, { - text: 'processProtectedData', - link: '/documentation/manage-data/dataProtector/dataProtectorCore/processProtectedData', + text: 'rentProtectedData', + link: '/references/dataProtector/dataProtectorSharing/renting/rentProtectedData', }, { - text: 'getResultFromCompletedTask', - link: '/documentation/manage-data/dataProtector/dataProtectorCore/getResultFromCompletedTask', + text: 'removeProtectedDataFromRenting', + link: '/references/dataProtector/dataProtectorSharing/renting/removeProtectedDataFromRenting', }, ], }, { - text: 'DataProtector Sharing', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing', + text: 'Selling', + link: '/references/dataProtector/dataProtectorSharing/selling', collapsed: true, items: [ { - text: 'Collection', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/collection', - collapsed: true, - items: [ - { - text: 'createCollection', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/collection/createCollection', - }, - { - text: 'removeCollection', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/collection/removeCollection', - }, - { - text: 'addToCollection', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/collection/addToCollection', - }, - { - text: 'removeProtectedDataFromCollection', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/collection/removeProtectedDataFromCollection', - }, - ], + text: 'setProtectedDataForSale', + link: '/references/dataProtector/dataProtectorSharing/selling/setProtectedDataForSale', + }, + { + text: 'buyProtectedData', + link: '/references/dataProtector/dataProtectorSharing/selling/buyProtectedData', }, { - text: 'Renting', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/renting', - collapsed: true, - items: [ - { - text: 'setProtectedDataToRenting', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataToRenting', - }, - { - text: 'setProtectedDataRentingParams', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/renting/setProtectedDataRentingParams', - }, - { - text: 'rentProtectedData', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/renting/rentProtectedData', - }, - { - text: 'removeProtectedDataFromRenting', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/renting/removeProtectedDataFromRenting', - }, - ], + text: 'removeProtectedDataForSale', + link: '/references/dataProtector/dataProtectorSharing/selling/removeProtectedDataForSale', }, + ], + }, + { + text: 'Subscription', + link: '/references/dataProtector/dataProtectorSharing/subscription', + collapsed: true, + items: [ { - text: 'Selling', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/selling', - collapsed: true, - items: [ - { - text: 'setProtectedDataForSale', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/selling/setProtectedDataForSale', - }, - { - text: 'buyProtectedData', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/selling/buyProtectedData', - }, - { - text: 'removeProtectedDataForSale', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/selling/removeProtectedDataForSale', - }, - ], + text: 'setProtectedDataToSubscription', + link: '/references/dataProtector/dataProtectorSharing/subscription/setProtectedDataToSubscription', }, { - text: 'Subscription', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/subscription', - collapsed: true, - items: [ - { - text: 'setProtectedDataToSubscription', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/setProtectedDataToSubscription', - }, - { - text: 'setSubscriptionParams', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/setSubscriptionParams', - }, - { - text: 'subscribeToCollection', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/subscribeToCollection', - }, - { - text: 'removeProtectedDataFromSubscription', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/subscription/removeProtectedDataFromSubscription', - }, - ], + text: 'setSubscriptionParams', + link: '/references/dataProtector/dataProtectorSharing/subscription/setSubscriptionParams', }, { - text: 'Consume', - collapsed: true, - items: [ - { - text: 'consumeProtectedData', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/consume/consumeProtectedData', - }, - ], + text: 'subscribeToCollection', + link: '/references/dataProtector/dataProtectorSharing/subscription/subscribeToCollection', }, { - text: 'Read Data', - collapsed: true, - items: [ - { - text: 'getProtectedDataInCollections', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataInCollections', - }, - { - text: 'getProtectedDataPricingParams', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/read/getProtectedDataPricingParams', - }, - { - text: 'getCollectionOwners', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionOwners', - }, - { - text: 'getCollectionsByOwner', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionsByOwner', - }, - { - text: 'getCollectionSubscriptions', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/read/getCollectionSubscriptions', - }, - { - text: 'getRentals', - link: '/documentation/manage-data/dataProtector/dataProtectorSharing/read/getRentals', - }, - ], + text: 'removeProtectedDataFromSubscription', + link: '/references/dataProtector/dataProtectorSharing/subscription/removeProtectedDataFromSubscription', }, ], }, { - text: 'Types', - link: '/documentation/manage-data/dataProtector/types', + text: 'Consume', + collapsed: true, + items: [ + { + text: 'consumeProtectedData', + link: '/references/dataProtector/dataProtectorSharing/consume/consumeProtectedData', + }, + ], }, { - text: 'Advanced', + text: 'Read Data', collapsed: true, items: [ { - text: 'Advanced Configuration', - link: '/documentation/manage-data/dataProtector/advanced/advanced-configuration', + text: 'getProtectedDataInCollections', + link: '/references/dataProtector/dataProtectorSharing/read/getProtectedDataInCollections', }, { - text: 'Sharing smart contract', - link: '/documentation/manage-data/dataProtector/advanced/dps-smart-contract', + text: 'getProtectedDataPricingParams', + link: '/references/dataProtector/dataProtectorSharing/read/getProtectedDataPricingParams', }, { - text: 'Apps whitelist', - link: '/documentation/manage-data/dataProtector/advanced/apps-whitelist', - collapsed: true, - items: [ - { - text: 'createAddOnlyAppWhitelist', - link: '/documentation/manage-data/dataProtector/advanced/apps-whitelist/createAddOnlyAppWhitelist', - }, - { - text: 'addAppToAddOnlyAppWhitelist', - link: '/documentation/manage-data/dataProtector/advanced/apps-whitelist/addAppToAddOnlyAppWhitelist', - }, - { - text: 'getUserAddOnlyAppWhitelist', - link: '/documentation/manage-data/dataProtector/advanced/apps-whitelist/getUserAddOnlyAppWhitelist', - }, - ], + text: 'getCollectionOwners', + link: '/references/dataProtector/dataProtectorSharing/read/getCollectionOwners', + }, + { + text: 'getCollectionsByOwner', + link: '/references/dataProtector/dataProtectorSharing/read/getCollectionsByOwner', + }, + { + text: 'getCollectionSubscriptions', + link: '/references/dataProtector/dataProtectorSharing/read/getCollectionSubscriptions', + }, + { + text: 'getRentals', + link: '/references/dataProtector/dataProtectorSharing/read/getRentals', }, ], }, - { - text: 'Migrate from v1 to v2', - link: '/documentation/manage-data/dataProtector/migrate-from-v1', - }, ], }, - ], - }, - { - text: 'BUILD YOUR iAPP', - items: [ { - text: '❓ What is an iApp ?', - link: '/documentation/build-iapp/what-is-iapp', - }, - { - text: '📖 Guides', - collapsed: true, - items: [ - { - text: 'Build and Deploy', - link: '/documentation/build-iapp/guides/build-&-deploy', - }, - { - text: 'Manage Access', - link: '/documentation/build-iapp/guides/manage-access', - }, - { - text: 'Inputs and Outputs', - link: '/documentation/build-iapp/guides/inputs-and-outputs', - }, - { - text: 'Using TDX', - link: '/documentation/build-iapp/guides/using-tdx', - }, - { - text: 'How to Get and Decrypt Results', - link: '/documentation/build-iapp/guides/how-to-get-and-decrypt-results', - }, - { - text: 'Debugging', - link: '/documentation/build-iapp/guides/debugging', - }, - ], + text: 'Types', + link: '/references/dataProtector/types', }, { - text: '🤖 iApp Generator', - link: '/documentation/build-iapp/iapp-generator', + text: 'Advanced', collapsed: true, items: [ { - text: 'Getting Started', - link: '/documentation/build-iapp/iapp-generator/getting-started', + text: 'Advanced Configuration', + link: '/references/dataProtector/advanced/advanced-configuration', }, { - text: 'Building your iApp', - link: '/documentation/build-iapp/iapp-generator/building-your-iexec-app', + text: 'Sharing smart contract', + link: '/references/dataProtector/advanced/dps-smart-contract', }, { - text: 'Deserialize ProtectedData', - link: '/documentation/build-iapp/iapp-generator/deserializer', + text: 'Apps whitelist', + link: '/references/dataProtector/advanced/apps-whitelist', collapsed: true, items: [ { - text: 'getValue', - link: '/documentation/build-iapp/iapp-generator/deserializer/getValue', + text: 'createAddOnlyAppWhitelist', + link: '/references/dataProtector/advanced/apps-whitelist/createAddOnlyAppWhitelist', + }, + { + text: 'addAppToAddOnlyAppWhitelist', + link: '/references/dataProtector/advanced/apps-whitelist/addAppToAddOnlyAppWhitelist', + }, + { + text: 'getUserAddOnlyAppWhitelist', + link: '/references/dataProtector/advanced/apps-whitelist/getUserAddOnlyAppWhitelist', }, ], }, ], }, + { + text: 'Migrate from v1 to v2', + link: '/references/dataProtector/migrate-from-v1', + }, ], }, { - text: 'USE AN iAPP', + text: '🤖 iApp Generator', + link: '/references/iapp-generator', items: [ { - text: '📝 Introduction', - link: '/documentation/use-iapp/introduction', + text: 'Getting Started', + link: '/references/iapp-generator/getting-started', }, { - text: '🚀 Getting Started', - link: '/documentation/use-iapp/getting-started', + text: 'Building your iApp', + link: '/references/iapp-generator/building-your-iexec-app', }, { - text: '📖 Guides', + text: 'Deserialize ProtectedData', + link: '/references/iapp-generator/deserializer', collapsed: true, items: [ { - text: 'Different Ways to Execute an iApp', - link: '/documentation/use-iapp/guides/different-ways-to-execute', - }, - { - text: 'Add Inputs to the Execution', - link: '/documentation/use-iapp/guides/add-inputs-to-execution', - }, - { - text: 'Use iApp with Protected Data', - link: '/documentation/use-iapp/guides/use-iapp-with-protected-data', - }, - { - text: 'Find iApps to Use', - link: '/documentation/use-iapp/guides/find-iapps', - }, - { - text: 'How to Pay the Executions', - link: '/documentation/use-iapp/guides/how-to-pay-executions', - }, - ], - }, - { - text: '💰 How to Pay', - collapsed: true, - items: [ - { - text: 'How to Pay for Web3Mail', - link: '/documentation/use-iapp/how-to-pay/how-to-pay-for-web3mail', - }, - { - text: 'How to Pay for Web3Telegram', - link: '/documentation/use-iapp/how-to-pay/how-to-pay-for-web3telegram', - }, - { - text: 'Pricing Considerations', - link: '/documentation/use-iapp/how-to-pay/pricing-considerations', - }, - { - text: 'Voucher', - link: '/documentation/use-iapp/how-to-pay/voucher', + text: 'getValue', + link: '/references/iapp-generator/deserializer/getValue', }, ], }, + ], + }, + { + text: '✉ Web3Mail', + link: '/references/web3mail', + items: [ { - text: '✉ Web3Mail', - link: '/use-iapp/web3mail', - collapsed: true, - items: [ - { - text: 'Getting Started', - link: '/documentation/use-iapp/web3mail/getting-started', - }, - { - text: 'Methods', - collapsed: true, - items: [ - { - text: 'fetchMyContacts', - link: '/documentation/use-iapp/web3mail/methods/fetchMyContacts', - }, - { - text: 'fetchUserContacts', - link: '/documentation/use-iapp/web3mail/methods/fetchUserContacts', - }, - { - text: 'sendEmail', - link: '/documentation/use-iapp/web3mail/methods/sendEmail', - }, - ], - }, - { - text: 'Advanced Configuration', - link: '/documentation/use-iapp/web3mail/advanced-configuration', - }, - ], + text: 'Getting Started', + link: '/references/web3mail/getting-started', }, { - text: '💬 Web3Telegram', - link: '/documentation/use-iapp/web3telegram', + text: 'Methods', collapsed: true, items: [ { - text: 'Getting Started', - link: '/documentation/use-iapp/web3telegram/getting-started', + text: 'fetchMyContacts', + link: '/references/web3mail/methods/fetchMyContacts', }, { - text: 'Methods', - collapsed: true, - items: [ - { - text: 'fetchMyContacts', - link: '/documentation/use-iapp/web3telegram/methods/fetchMyContacts', - }, - { - text: 'fetchUserContacts', - link: '/documentation/use-iapp/web3telegram/methods/fetchUserContacts', - }, - { - text: 'sendTelegram', - link: '/documentation/use-iapp/web3telegram/methods/sendTelegram', - }, - ], + text: 'fetchUserContacts', + link: '/references/web3mail/methods/fetchUserContacts', }, { - text: 'Integration Guide', - link: '/documentation/use-iapp/web3telegram/integration-guide', - }, - { - text: 'Advanced Configuration', - link: '/documentation/use-iapp/web3telegram/advanced-configuration', + text: 'sendEmail', + link: '/references/web3mail/methods/sendEmail', }, ], }, - ], - }, - { - text: 'TOOLING & EXPLORERS', - items: [ - { - text: 'iExec Explorer', - link: '/documentation/tooling-and-explorers/iexec-explorer', - }, - { - text: 'Builder Dashboard', - link: '/documentation/tooling-and-explorers/builder-dashboard', - }, - { - text: 'RLC Bridge', - link: '/documentation/tooling-and-explorers/bridge', - }, - { - text: 'Subgraph Explorer', - link: '/documentation/tooling-and-explorers/subgraph-explorer', - }, { - text: 'Blockchain Explorer', - link: '/documentation/tooling-and-explorers/blockchain-explorer', + text: 'Advanced Configuration', + link: '/references/web3mail/advanced-configuration', }, ], }, { - text: 'PROTOCOL', + text: '💬 Web3Telegram', + link: '/references/web3telegram', items: [ { - text: '🔧 iExec SDK', - link: '/documentation/protocol/sdk', + text: 'Getting Started', + link: '/references/web3telegram/getting-started', }, { - text: '⚙️ Workers & Workerpools', + text: 'Methods', collapsed: true, items: [ { - text: '🚀 Worker Quick Start', - link: '/documentation/protocol/worker/quick-start', + text: 'fetchMyContacts', + link: '/references/web3telegram/methods/fetchMyContacts', }, { - text: '🔒 Manage Workerpool Access', - link: '/documentation/protocol/worker/manage-access', - }, - ], - }, - { - text: '⛓️‍💥 PoCo', - collapsed: true, - items: [ - { - text: '💸 Pay Per Task Model', - link: '/documentation/protocol/poco/pay-per-task', + text: 'fetchUserContacts', + link: '/references/web3telegram/methods/fetchUserContacts', }, { - text: '🛡️ Proof of Contribution', - link: '/documentation/protocol/poco/proof-of-contribution', + text: 'sendTelegram', + link: '/references/web3telegram/methods/sendTelegram', }, ], }, { - text: '�📖 Glossary', - link: '/documentation/protocol/glossary', + text: 'Integration Guide', + link: '/references/web3telegram/integration-guide', + }, + { + text: 'Advanced Configuration', + link: '/references/web3telegram/advanced-configuration', }, ], }, + { + text: '🔧 iExec SDK', + link: '/references/sdk', + }, + { + text: '�📖 Glossary', + link: '/references/glossary', + }, ], - '/guides/': [], - '/references/': [], } satisfies DefaultTheme.Sidebar; } diff --git a/README.md b/README.md index 751fcb44..96b44dc9 100644 --- a/README.md +++ b/README.md @@ -169,3 +169,7 @@ Fork this repository and ensure you're working on the `main` branch: - Update or add design illustrations based on the new design system - Check how to pay guide to update with the launch on Arbitrum (RLC vs xRLC) - framework AI supporté TDX vs SGX +- check glossary +- migrate github SDK doc here +- migrate pay-per-task page into a guide +- check pages (introduction & getting-started) for use-iapp guide diff --git a/package-lock.json b/package-lock.json index fabb7040..e84543e2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,10 +8,10 @@ "name": "iexec-documentation", "version": "0.0.0", "dependencies": { - "@iexec/dataprotector": "^2.0.0-beta.17", + "@iexec/dataprotector": "^2.0.0-beta.19", "@iexec/dataprotector-deserializer": "^0.1.1", - "@iexec/web3mail": "^1.2.2", - "@iexec/web3telegram": "^0.1.0-alpha.1", + "@iexec/web3mail": "^1.5.0", + "@iexec/web3telegram": "^0.1.0-alpha.4", "@reown/appkit": "^1.7.17", "@reown/appkit-adapter-wagmi": "^1.7.17", "@tailwindcss/vite": "^4.1.11", @@ -761,7 +761,9 @@ } }, "node_modules/@iexec/dataprotector": { - "version": "2.0.0-beta.17", + "version": "2.0.0-beta.19", + "resolved": "https://registry.npmjs.org/@iexec/dataprotector/-/dataprotector-2.0.0-beta.19.tgz", + "integrity": "sha512-nKfM8H2AGFPmSHt96FhNSOIctqRWyQt34zh9chVeI7PSy6TVFQMnEV4rj1ce+O8yFO9TM/8YXxu+V2izBO00WQ==", "license": "Apache-2.0", "dependencies": { "@ethersproject/bytes": "^5.7.0", @@ -773,7 +775,7 @@ "debug": "^4.3.4", "ethers": "^6.13.2", "graphql-request": "^6.0.0", - "iexec": "^8.16.0", + "iexec": "^8.18.0", "jszip": "^3.7.1", "kubo-rpc-client": "^4.1.1", "magic-bytes.js": "^1.0.15", @@ -783,6 +785,8 @@ }, "node_modules/@iexec/dataprotector-deserializer": { "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@iexec/dataprotector-deserializer/-/dataprotector-deserializer-0.1.1.tgz", + "integrity": "sha512-CSz1JWnslm2X3gjL1cx/qqovnmvJSFWDyJMw0ZGvqnYnNatgIqHn+Aky2iO4K0HsArfqmgV3ySIpdPfu/N2M0w==", "license": "Apache-2.0", "dependencies": { "borsh": "^2.0.0", @@ -846,7 +850,9 @@ } }, "node_modules/@iexec/web3mail": { - "version": "1.2.2", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@iexec/web3mail/-/web3mail-1.5.0.tgz", + "integrity": "sha512-ohgPocAidWreTOchamwMiuwIt0Nns290vS/KbcLbhOB4ZUDtQGS8V5lES3s9WUZjMvf+bWByN4RQdoaNhgIPAw==", "license": "Apache-2.0", "dependencies": { "@ethersproject/bytes": "^5.7.0", @@ -854,13 +860,15 @@ "buffer": "^6.0.3", "ethers": "^6.13.2", "graphql-request": "^6.1.0", - "iexec": "^8.13.1", + "iexec": "^8.18.0", "kubo-rpc-client": "^4.1.1", "yup": "^1.1.1" } }, "node_modules/@iexec/web3telegram": { - "version": "0.1.0-alpha.1", + "version": "0.1.0-alpha.4", + "resolved": "https://registry.npmjs.org/@iexec/web3telegram/-/web3telegram-0.1.0-alpha.4.tgz", + "integrity": "sha512-JYWjLpV7Ufi8wjlHlwCOYnjHf1oRGh9GycfKuhIq2TZDK4JrEG/tUytMagakn3C9T0UOByrM9DM/hMMoxVcG6A==", "license": "Apache-2.0", "dependencies": { "@ethersproject/bytes": "^5.7.0", @@ -868,7 +876,7 @@ "buffer": "^6.0.3", "ethers": "^6.8.1", "graphql-request": "^6.1.0", - "iexec": "^8.13.1", + "iexec": "^8.18.0", "kubo-rpc-client": "^4.1.3", "yup": "^1.1.1" } @@ -7962,7 +7970,9 @@ "license": "BSD-3-Clause" }, "node_modules/iexec": { - "version": "8.17.0", + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/iexec/-/iexec-8.18.0.tgz", + "integrity": "sha512-lj2La67p0IKyCHhpV4F2SZD7kmE1AFhGmD26jf8si7/sTelrXun3wNTun6WAOEO9uMi49J0AlA6MxUNBxuCflg==", "license": "Apache-2.0", "dependencies": { "@ensdomains/ens-contracts": "^1.2.5", diff --git a/package.json b/package.json index e805e9b9..10d5c266 100644 --- a/package.json +++ b/package.json @@ -9,10 +9,10 @@ "format": "prettier --ignore-path .gitignore --write ." }, "dependencies": { - "@iexec/dataprotector": "^2.0.0-beta.17", + "@iexec/dataprotector": "^2.0.0-beta.19", "@iexec/dataprotector-deserializer": "^0.1.1", - "@iexec/web3mail": "^1.2.2", - "@iexec/web3telegram": "^0.1.0-alpha.1", + "@iexec/web3mail": "^1.5.0", + "@iexec/web3telegram": "^0.1.0-alpha.4", "@reown/appkit": "^1.7.17", "@reown/appkit-adapter-wagmi": "^1.7.17", "@tailwindcss/vite": "^4.1.11", diff --git a/src/components/ChainSelector.vue b/src/components/ChainSelector.vue index fde4a4ea..1a903718 100644 --- a/src/components/ChainSelector.vue +++ b/src/components/ChainSelector.vue @@ -68,8 +68,26 @@ const userStore = useUserStore(); // Data const supportedChains = getSupportedChains(); +// Default initialization: check first if wallet is connected +if (!userStore.chainId) { + if (chainId.value) { + // If wallet is connected, use its chain + const walletChain = getChainById(chainId.value); + if (walletChain) { + userStore.setSelectedChain(walletChain); + } + } else { + // Otherwise, use Bellecour as default + const defaultChain = getChainById(0x86); // Bellecour + if (defaultChain) { + userStore.setSelectedChain(defaultChain); + } + } +} + // Computed const selectedChain = computed(() => { + // Priority: 1. Connected wallet chain, 2. Selected chain in store const currentChainId = chainId.value || userStore.chainId; return currentChainId ? getChainById(currentChainId) : undefined; }); @@ -91,6 +109,7 @@ const selectedChainId = computed({ }, }); +// Watch to synchronize store with wallet chain watch(chainId, (newChainId) => { if (newChainId) { const chain = getChainById(newChainId); diff --git a/src/documentation/build-iapp/guides/index.md b/src/documentation/build-iapp/guides/index.md deleted file mode 100644 index 8fca2266..00000000 --- a/src/documentation/build-iapp/guides/index.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -title: Build iApp Guides -description: Complete guide collection for building privacy-first iApps on iExec ---- - -# 🛠️ Build iApp Guides - -Welcome to the complete collection of guides for building privacy-first -applications on iExec. These guides will walk you through every aspect of -creating, testing, and deploying confidential iApps. - -## 🚀 Getting Started - -- **[Build & Deploy](/documentation/build-iapp/guides/build-&-deploy)** - Your - first iApp in 15 minutes -- **[Inputs and Outputs](/documentation/build-iapp/guides/inputs-and-outputs)** - - Handle data flow in TEE environment -- **[Debugging](/documentation/build-iapp/guides/debugging)** - Troubleshoot - execution issues - -## 🔐 Access Control & Management - -- **[Manage Access](/documentation/build-iapp/guides/manage-access)** - Control - who can use your iApp -- **[How to Get and Decrypt Results](/documentation/build-iapp/guides/how-to-get-and-decrypt-results)** - - Retrieve and use outputs - -## 🧪 Advanced Features - -- **[Using TDX (Experimental)](/documentation/build-iapp/guides/using-tdx)** - - Next-gen TEE technology - -## 📚 What's Next? - -After mastering these guides, explore: - -- **[iApp Generator](/documentation/build-iapp/iapp-generator)** - Complete - development toolkit -- **[What is an iApp?](/documentation/build-iapp/what-is-iapp)** - Core concepts - and TEE overview - -Ready to build your first privacy-preserving application? Start with the -[Build & Deploy](/documentation/build-iapp/guides/build-&-deploy) guide! diff --git a/src/documentation/use-iapp/guides/index.md b/src/documentation/use-iapp/guides/index.md deleted file mode 100644 index 8541a822..00000000 --- a/src/documentation/use-iapp/guides/index.md +++ /dev/null @@ -1,53 +0,0 @@ ---- -title: Use iApp Guides -description: Complete guide collection for using iApps on the iExec network ---- - -# 🚀 Use iApp Guides - -Welcome to the complete collection of guides for using iApps on the iExec -network. These guides will help you find, execute, and interact with -privacy-preserving applications. - -## 🔍 Finding & Using iApps - -- **[Find iApps](/documentation/use-iapp/guides/find-iapps)** - Discover - available applications -- **[Different Ways to Execute](/documentation/use-iapp/guides/different-ways-to-execute)** - - Multiple execution methods -- **[Use iApp with Protected Data](/documentation/use-iapp/guides/use-iapp-with-protected-data)** - - Secure data processing - -## 💰 Payment & Execution - -- **[How to Pay for Executions](/documentation/use-iapp/guides/how-to-pay-executions)** - - Payment methods and costs -- **[Add Inputs to Execution](/documentation/use-iapp/guides/add-inputs-to-execution)** - - Provide data and parameters - -## 📧 Communication Apps - -- **[Web3Mail](/documentation/use-iapp/web3mail)** - Send encrypted emails -- **[Web3Telegram](/documentation/use-iapp/web3telegram)** - Send encrypted - Telegram messages - -## 🆓 Getting Started - -- **[Getting Started](/documentation/use-iapp/getting-started)** - Essential - steps for beginners -- **[How to Pay for Web3Mail](/documentation/use-iapp/how-to-pay/how-to-pay-for-web3mail)** - - Free email service -- **[How to Pay for Web3Telegram](/documentation/use-iapp/how-to-pay/how-to-pay-for-web3telegram)** - - Free messaging service - -## 📚 What's Next? - -After mastering these guides, explore: - -- **[Data Protector](/documentation/manage-data/dataProtector)** - Protect your - own data -- **[Build iApps](/documentation/build-iapp/what-is-iapp)** - Create your own - applications - -Ready to start using iApps? Begin with the -[Getting Started](/documentation/use-iapp/getting-started) guide! diff --git a/src/documentation/develop-with-ai.md b/src/get-started/develop-with-ai.md similarity index 100% rename from src/documentation/develop-with-ai.md rename to src/get-started/develop-with-ai.md diff --git a/src/documentation/helloWorld.md b/src/get-started/helloWorld.md similarity index 94% rename from src/documentation/helloWorld.md rename to src/get-started/helloWorld.md index eb12705c..4b06eb3b 100644 --- a/src/documentation/helloWorld.md +++ b/src/get-started/helloWorld.md @@ -7,12 +7,24 @@ description: # 👋 Welcome to iExec > Reading time 🕒 2 mins +
+
+ 🌐 +

Select Your Network

+
+
+ Network: + +
+
+

Start Your Web3 Privacy Journey

And learn how to build Privacy-preserving decentralized applications (dApps) with iExec in this interactive guide.

diff --git a/src/documentation/helloWorld/1-overview.md b/src/get-started/helloWorld/1-overview.md similarity index 98% rename from src/documentation/helloWorld/1-overview.md rename to src/get-started/helloWorld/1-overview.md index c603ca4d..d416ccc7 100644 --- a/src/documentation/helloWorld/1-overview.md +++ b/src/get-started/helloWorld/1-overview.md @@ -85,7 +85,7 @@ Computing technologies. iExec combines three fundamental elements that work together seamlessly: -#### 1. Protect Data with our Devtool [DataProtector](/documentation/manage-data/dataProtector/getting-started) +#### 1. Protect Data with our Devtool [DataProtector](/references/dataProtector/getting-started) - Encrypt your sensitive data and store it securely on Arweave or IPFS - Only you control who can access it and when diff --git a/src/documentation/helloWorld/2-protectData.md b/src/get-started/helloWorld/2-protectData.md similarity index 94% rename from src/documentation/helloWorld/2-protectData.md rename to src/get-started/helloWorld/2-protectData.md index af4b0ae8..b7e7903d 100644 --- a/src/documentation/helloWorld/2-protectData.md +++ b/src/get-started/helloWorld/2-protectData.md @@ -45,10 +45,6 @@ their dApps with these key features: Features an SDK for easy integration into your DApp, enhancing functionality and user experience. -
-

DataProtector interacts with iExec's Bellecour sidechain, which is gasless, meaning you can use it completely free without needing any tokens!

-
- ## 🧩 Let's Create Protected Data diff --git a/src/documentation/helloWorld/3-buildIApp.md b/src/get-started/helloWorld/3-buildIApp.md similarity index 53% rename from src/documentation/helloWorld/3-buildIApp.md rename to src/get-started/helloWorld/3-buildIApp.md index 31538fe8..849e9c17 100644 --- a/src/documentation/helloWorld/3-buildIApp.md +++ b/src/get-started/helloWorld/3-buildIApp.md @@ -9,13 +9,9 @@ description: > Reading time 🕒 10 mins - -

Time to build!

-

Let's build an iApp that can process protected data in a secure environment using the iExec iApp generator tool. This tool helps you create, test and deploy iApps with just a few commands.

+

Let's build an iApp that can process protected data in a secure environment using the iExec iApp generator tool. This tool helps you create, test and deploy iApps with just a few commands.

If you wanna explore and deep dive in the CLI. You can check the @@ -63,23 +59,23 @@ Here are some popular use cases: Send privacy-preserving emails to registered Ethereum account holders without knowing or storing their email addresses. [Github](https://github.com/iExecBlockchainComputing/web3mail-sdk/tree/main/dapp) -| [Documentation](../use-iapp/web3mail) +| [Documentation](/references/web3mail) ### 💬 Web3 Telegram Send privacy-preserving Telegram messages without knowing or storing their Telegram handles. [Github](https://github.com/iExecBlockchainComputing/web3telegram-sdk/tree/main/dapp) -| [Documentation](../use-iapp/web3telegram) +| [Documentation](/references/web3telegram) ### 🌐 Content Delivery Transfer, sell or rent protected content to authorized users. [Github](https://github.com/iExecBlockchainComputing/dataprotector-sdk/tree/main/packages/protected-data-delivery-dapp) -| [Documentation](/documentation/manage-data/dataProtector/dataProtectorSharing) +| [Documentation](/references/dataProtector/dataProtectorSharing)
-

These are just a few examples, the possibilities are endless. Want to explore iApp Generator? Check out our documentation and see what you can build!

+

These are just a few examples, the possibilities are endless. Want to explore iApp Generator? Check out our documentation and see what you can build!

## 💾 Installation (Win / Mac / Linux) @@ -130,19 +126,53 @@ iapp init You will be prompted with the following message: -```txt - ___ _ ____ ____ - |_ _| / \ | _ \| _ \ - | | / _ \ | |_) | |_) | - | | / ___ \| __/| __/ - |___/_/ \_\_| |_| - -✔ What's your project name? (A folder with this name will be created) … hello-world -✔ Which language do you want to use? › JavaScript -? What kind of project do you want to init? › - Use arrow-keys. Return to submit. -❯ Hello World - iapp quick start - advanced -``` +
@@ -151,10 +181,6 @@ You will be prompted with the following message:
-```txt -? What's your project name? (A folder with this name will be created) ... -``` -
2 @@ -162,12 +188,6 @@ You will be prompted with the following message:
-```txt -? Which language do you want to use? › - Use arrow-keys. Return to submit. -❯ JavaScript - Python -``` -
3 @@ -175,22 +195,10 @@ You will be prompted with the following message:
-```txt -? What kind of project do you want to init? › - Use arrow-keys. Return to submit. -❯ Hello World - iapp quick start - advanced -``` -

We recommend selecting "Hello World" to quickly discover how iApp works! use advanced only if you are familiar with iExec.

-```txt -✔ [Chosen language] app setup complete. -✔ Generated ethereum wallet (0xD4A28d.........................) - -``` - - An iApp project is setup with the selected language - An ethereum wallet has been created (we use it to sign the iApp creation onchain) @@ -199,26 +207,87 @@ You will be prompted with the following message: ## 🧪 Test your iApp -To test your iApp, run `iapp test` command - -```sh -iapp test -``` - -It uses your local docker to build and execute the app. +To test your iApp, run the `iapp test` command. This will build a Docker image +and run your application locally to simulate the TEE environment. You'll see the +following steps: + + + +The `iapp test` command uses your local Docker to build and execute the app, +simulating how it will run in the iExec network's TEE environment.
-

- If you have Error: Docker daemon is not accessible Make sure Docker is installed and running.

-
-

- If you have Error: Failed to locate iApp project root error: Ensure you are in your project folder before proceeding.

+

Common Issues:

+

- If you get Error: Docker daemon is not accessible: Make sure Docker is installed and running.

+

- If you get Error: Failed to locate iApp project root: Ensure you are in your project folder before proceeding.

-You can see the output of the computation by saying yes to the question: - -```txt -? Would you like to see the result? (View ./output/) (Y/n) -``` - ### 🧩 Using Arguments You can pass arguments to your iApp using the `--args` option. This allows you @@ -272,12 +341,33 @@ Deploy your iApp on the iExec protocol.
-Once you have your token, you can deploy your iApp using the following command: - -```sh -# You need your username and the access token (it can take a few minutes to deploy) -iapp deploy -``` +Once you have your token, you can deploy your iApp. + + + +

📝 Make sure to save your iApp address after deployment - you'll need it later!

@@ -322,3 +412,173 @@ protocol documentation [here](https://protocol.docs.iex.ec/).

Next up: Alice will learn how to authorize the iApp and Bob to access and use her protected data! 🚀

+ + diff --git a/src/documentation/helloWorld/4-manageDataAccess.md b/src/get-started/helloWorld/4-manageDataAccess.md similarity index 98% rename from src/documentation/helloWorld/4-manageDataAccess.md rename to src/get-started/helloWorld/4-manageDataAccess.md index 6ec51501..7addca54 100644 --- a/src/documentation/helloWorld/4-manageDataAccess.md +++ b/src/get-started/helloWorld/4-manageDataAccess.md @@ -115,7 +115,7 @@ Want to see it in action? Check out our - Track your earnings For more technical details, see the -[DataProtector Sharing](/documentation/manage-data/dataProtector/dataProtectorSharing) +[DataProtector Sharing](/references/dataProtector/dataProtectorSharing) documentation.
diff --git a/src/documentation/helloWorld/5-bonusChapter.md b/src/get-started/helloWorld/5-bonusChapter.md similarity index 91% rename from src/documentation/helloWorld/5-bonusChapter.md rename to src/get-started/helloWorld/5-bonusChapter.md index fba85313..1c04bd22 100644 --- a/src/documentation/helloWorld/5-bonusChapter.md +++ b/src/get-started/helloWorld/5-bonusChapter.md @@ -5,11 +5,6 @@ description: Hello World tutorial series. --- - - # 🎉 Bonus Chapter > Reading time 🕒 4 mins @@ -37,7 +32,8 @@ import Button from '@/components/ui/Button.vue';

Need help setting up or got some questions? Join our Discord Community for support!

-## 🎁 Claim your Voucher + + + diff --git a/src/documentation/build-iapp/what-is-iapp.md b/src/get-started/overview/what-is-iapp.md similarity index 57% rename from src/documentation/build-iapp/what-is-iapp.md rename to src/get-started/overview/what-is-iapp.md index 25260129..06f051fe 100644 --- a/src/documentation/build-iapp/what-is-iapp.md +++ b/src/get-started/overview/what-is-iapp.md @@ -7,8 +7,8 @@ description: Privacy-first applications that run on decentralized infrastructure An iExec Application (iApp) is your regular application code (Python script, AI model, data processor, ...) that can securely process protected data (created by -[DataProtector](/documentation/manage-data/dataProtector)) inside a confidential -computing environment called TEE (a Trusted Execution Environment). +[DataProtector](/references/dataProtector)) inside a confidential computing +environment called TEE (a Trusted Execution Environment). ## Why iApps Matter ? @@ -99,105 +99,63 @@ guarantees** that privacy is preserved within the TEE execution environment. ## Use Cases -
-
-
- 📧 -

Private Communication

-
-

Users send emails, notifications, or messages using their protected contact lists without exposing recipient information.

+
+
+

🏥 Healthcare

+

Process medical data for AI diagnosis without exposing patient information

- -
-
- 🔮 -

Trustworthy Oracles

-
-

Users contribute real data to oracles while keeping their private information confidential.

+
+

💰 Finance

+

Analyze financial data for credit scoring while maintaining privacy

- -
-
- 🤖 -

Personal AI Assistants

-
-

Users let AI models perform actions based on their private data - trading, scheduling, recommendations...

+
+

🎬 Media

+

Content recommendation engines that don't track user behavior

- -
-
- -

Automated Actions

-
-

Users let AI models perform actions based on their private data - trading, scheduling, recommendations...

+
+

🔬 Research

+

Collaborative research on sensitive datasets across institutions

-## ❓ Frequently Asked Questions - -::: details 📦 What can I build with iApps? - -Anything that runs in Docker! AI models, data processing scripts, web scrapers, -image processing, financial calculations, etc. If it runs in a container, it can -be an iApp. - -::: - -::: details ⚡How fast are iApps? - -Initial task scheduling takes a few seconds (depending on the resources the -worker download, congestion etc), then your code runs at normal speed depending -on complexity. - -::: +## Getting Started -::: details 🛡️ Are iApps really secure? - -Yes! Code runs in Intel SGX or TDX secure enclaves. Even the worker running your -iApp can't see what's happening inside the enclave. +
+

Time to build!

+

Let's build an iApp that can process protected data in a secure environment using the iExec iApp generator tool. This tool helps you create, test and deploy iApps with just a few commands.

+
-::: +### Quick Start Path -::: details 🚀 How do I deploy my first iApp? +1. **Protect your data** with [DataProtector](/references/dataProtector) +2. **Build your iApp** using the [iApp Generator](/references/iapp-generator) +3. **Deploy and test** your application +4. **Process protected data** securely -Try our [Hello World](/documentation/helloWorld) for a quick start, or check the -[iApp Generator](/documentation/build-iapp/iapp-generator) section for detailed -instructions. +### What You'll Learn -::: +- How to create a Docker container for your application +- How to handle inputs and outputs securely +- How to deploy to the iExec network +- How to process protected data in TEE environments -::: details 🔧 What programming languages are supported? +
+

These are just a few examples, the possibilities are endless. Want to explore iApp Generator? Check out our documentation and see what you can build!

+
-iApps can be built in any language that runs in Docker (Python, JavaScript, R, -Java, Go, etc.). However, **iApp Generator** currently supports only Python and -Node.js for simplified development. +## Technical Requirements -::: +- **Docker**: Your application must be containerized +- **Input/Output**: Define clear input and output schemas +- **TEE Compatibility**: Ensure your code runs in secure enclaves +- **Network Access**: Configure any external API calls or dependencies ## Next Steps -
- -
-
-
📚
-
- Learn More - iApp Generator: - Complete DataProtector Documentation -
-
-
-
🚀
-
- Getting Started - deploy your first iApp: - DataProtector Quick Start Guide -
-
-
- -
- ---- +Ready to build your first privacy-preserving application? Start with our +[Hello World tutorial](/get-started/helloWorld) or dive into the +[iApp Generator documentation](/references/iapp-generator). -**TL;DR**: iApps = Your code + Secure execution + User privacy + Verifiable -results. Cloud computing, but nobody can spy on your stuff. 🔒 +For more technical details, see the +[DataProtector Sharing](/references/dataProtector/dataProtectorSharing) +documentation. diff --git a/src/get-started/overview/what-is-iexec.md b/src/get-started/overview/what-is-iexec.md new file mode 100644 index 00000000..11f43eb5 --- /dev/null +++ b/src/get-started/overview/what-is-iexec.md @@ -0,0 +1,57 @@ +--- +title: What is iExec? +description: + Learn about iExec, the decentralized computing platform that enables + privacy-preserving applications +--- + +# What is iExec? + +iExec is a **decentralized computing platform** that enables developers to build +and deploy privacy-preserving applications using confidential computing +technology. + +## 🎯 **Mission** + +iExec's mission is to democratize access to **confidential computing** by +providing a decentralized infrastructure that combines: + +- **🔐 Privacy Protection** - Data remains encrypted during computation +- **⚡ Scalable Computing** - Access to distributed computing resources +- **💰 Monetization** - Fair compensation for computing providers +- **🌐 Decentralization** - No single point of failure or control + +## 🏗️ **Core Architecture** + +### **Three Main Components:** + +1. **🤖 iApps (iExec Applications)** + - Confidential computing applications + - Run in secure enclaves (TEEs) + - Process encrypted data without exposing it + +2. **🔐 DataProtector** + - End-to-end encryption solution + - Manage data access and sharing + - Monetize protected datasets + +3. **⚙️ Worker Network** + - Distributed computing providers + - Execute iApps securely + - Earn RLC tokens for contributions + +## 🚀 **Key Features** + +- **TEE (Trusted Execution Environment)** support +- **Zero-knowledge computation** +- **Decentralized marketplace** for computing resources +- **RLC token** for payments and governance +- **Cross-chain compatibility** + +## 🔗 **Get Started** + +Ready to explore iExec? Check out: + +- [What is Protected Data?](/get-started/overview/what-is-protected-data) +- [What is an iApp?](/get-started/overview/what-is-iapp) +- [Hello World Tutorial](/get-started/helloWorld) diff --git a/src/documentation/manage-data/what-is-protected-data.md b/src/get-started/overview/what-is-protected-data.md similarity index 94% rename from src/documentation/manage-data/what-is-protected-data.md rename to src/get-started/overview/what-is-protected-data.md index c768cf78..b77571da 100644 --- a/src/documentation/manage-data/what-is-protected-data.md +++ b/src/get-started/overview/what-is-protected-data.md @@ -196,13 +196,13 @@ monetize their data within the Web3 ecosystem.
🚀
diff --git a/src/get-started/overview/what-is-workerpool.md b/src/get-started/overview/what-is-workerpool.md new file mode 100644 index 00000000..3ef69aa9 --- /dev/null +++ b/src/get-started/overview/what-is-workerpool.md @@ -0,0 +1,11 @@ +--- +title: What is a Workerpool? +description: + Learn about workerpools in iExec - the computing resources that execute iApps +--- + +# What is a Workerpool? + +This page is under development. + + diff --git a/src/documentation/protocol/poco/pay-per-task.md b/src/get-started/protocol/pay-per-task.md similarity index 100% rename from src/documentation/protocol/poco/pay-per-task.md rename to src/get-started/protocol/pay-per-task.md diff --git a/src/documentation/protocol/poco/proof-of-contribution.md b/src/get-started/protocol/proof-of-contribution.md similarity index 99% rename from src/documentation/protocol/poco/proof-of-contribution.md rename to src/get-started/protocol/proof-of-contribution.md index 821073b3..f8a9ca83 100644 --- a/src/documentation/protocol/poco/proof-of-contribution.md +++ b/src/get-started/protocol/proof-of-contribution.md @@ -76,7 +76,7 @@ consensus on a given result. Its logic is detailed in two blog articles: The [nominal workflow](https://github.com/iExecBlockchainComputing/iexec-doc/raw/master/techreport/nominalworkflow-ODB.png) -is also available in the [technical report section](../glossary.md) +is also available in the [technical report section](/references/glossary) Below are the details of the implementations: @@ -621,21 +621,21 @@ Orders compatibility required: **1. The worker pool’s category and the requester’s category must be equal.** -```sol +```text require(_requestorder.category == _workerpoolorder.category); ``` **2. The worker pool’s trust must be greater or equal to the requester’s trust.** -```sol +```text require(_requestorder.trust == _workerpoolorder.trust); ``` **3. The app’s, dataset’s and worker pool’s prices must be less or equal to the requester’s appmaxprice, datasetmaxprice and workerpoolmaxprice.** -```sol +```text require(_requestorder.appmaxprice >= _apporder.appprice); require(_requestorder.datasetmaxprice >= _datasetorder.datasetprice); require(_requestorder.workerpoolmaxprice >= _workerpoolorder.workerpoolprice); @@ -644,42 +644,42 @@ require(_requestorder.workerpoolmaxprice >= _workerpoolorder.workerpoolprice); **4. The worker pool’s tag must enable all the features required by the app’s tag, the dataset’s tag and the worker pool’s tag.** -```sol +```text require(tag & ~_workerpoolorder.tag == 0x0); require(tag & ~_workerpoolorder.tag == 0x0); ``` **5. If TEE tag is required, then application must be TEE compatible.** -```sol +```text require((tag ^ _apporder.tag)[31] & 0x01 == 0x0); ``` **6. The app provided by the apporder must match the one required by the requester.** -```sol +```text require(_requestorder.app == _apporder.app); ``` **7. The dataset provided by the datasetorder must match the one required by the requester.** -```sol +```text require(_requestorder.dataset == _datasetorder.dataset); ``` **8. If the requester specified a worker pool restriction, the worker pool must match this value or be part of the corresponding group.** -```sol +```text require(_checkIdentity(_requestorder.workerpool, _workerpoolorder.workerpool, GROUPMEMBER_PURPOSE)); ``` **9. The application must fit the dataset’s and the worker pool’s application restrictions (if any).** -```sol +```text require(_checkIdentity(_datasetorder.apprestrict, _apporder.app, GROUPMEMBER_PURPOSE)); require(_checkIdentity(_workerpoolorder.apprestrict, _apporder.app, GROUPMEMBER_PURPOSE)); ``` @@ -687,7 +687,7 @@ require(_checkIdentity(_workerpoolorder.apprestrict, _apporder.app, GROUPMEMBER_ **10. The dataset must fit the application’s and the worker pool’s restrictions (if any).** -```sol +```text require(_checkIdentity(_apporder.datasetrestrict, _datasetorder.dataset, GROUPMEMBER_PURPOSE)); require(_checkIdentity(_workerpoolorder.datasetrestrict, _datasetorder.dataset, GROUPMEMBER_PURPOSE)); ``` @@ -695,7 +695,7 @@ require(_checkIdentity(_workerpoolorder.datasetrestrict, _datasetorder.dataset, **11. The worker pool must fit the application’s and the dataset’s restrictions (if any).** -```sol +```text require(_checkIdentity(_apporder.workerpoolrestrict, _workerpoolorder.workerpool, GROUPMEMBER_PURPOSE)); require(_checkIdentity(_datasetorder.workerpoolrestrict, _workerpoolorder.workerpool, GROUPMEMBER_PURPOSE)); ``` @@ -703,7 +703,7 @@ require(_checkIdentity(_datasetorder.workerpoolrestrict, _workerpoolorder.worker **12. The requester must fit the application’s, the dataset’s and the worker pool’s restrictions (if any).** -```sol +```text require(_checkIdentity(_apporder.requesterrestrict, _requestorder.requester, GROUPMEMBER_PURPOSE)); require(_checkIdentity(_datasetorder.requesterrestrict, _requestorder.requester, GROUPMEMBER_PURPOSE)); require(_checkIdentity(_workerpoolorder.requesterrestrict, _requestorder.requester, GROUPMEMBER_PURPOSE)); @@ -711,7 +711,7 @@ require(_checkIdentity(_workerpoolorder.requesterrestrict, _requestorder.request **13. All resources must be registered in the corresponding registries.** -```sol +```text require(m_appregistry.isRegistered(_apporder.app)); require(m_datasetregistry.isRegistered(_datasetorder.dataset)); require(m_workerpoolregistry.isRegistered(_workerpoolorder.workerpool)); @@ -719,7 +719,7 @@ require(m_workerpoolregistry.isRegistered(_workerpoolorder.workerpool)); **14. All orders must be signed or presigned.** -```sol +```text require(_checkPresignatureOrSignature(App(_apporder.app).m_owner(), _apporder.hash(), _apporder.sign)); require(_checkPresignatureOrSignature(Dataset(_datasetorder.dataset).m_owner(), _datasetorder.hash(), _datasetorder.sign)); require(_checkPresignatureOrSignature(Workerpool(_workerpoolorder.workerpool).m_owner(), _workerpoolorder.hash(), _workerpoolorder.sign)); diff --git a/src/documentation/protocol/worker/manage-access.md b/src/get-started/protocol/worker/manage-access.md similarity index 97% rename from src/documentation/protocol/worker/manage-access.md rename to src/get-started/protocol/worker/manage-access.md index 28b5815c..b3631024 100644 --- a/src/documentation/protocol/worker/manage-access.md +++ b/src/get-started/protocol/worker/manage-access.md @@ -68,8 +68,8 @@ The supported tags for workerpool orders are: 1. Do not publish workerpool orders with a tag value out of the specified list. Such an order could produce undesirable and unpredictable behaviors. The - [iExec SDK](../sdk.md) implements all required preflight checks to avoid - erroneous orders publishing. + [iExec SDK](/references/sdk) implements all required preflight checks to + avoid erroneous orders publishing. 2. Currently, TEE workflow do not support tasks replication on several workers. TEE workerpool orders must be published with `trust` value equal to `1`. 3. TEE tasks with Gramine TEE framework are not supported yet. Do not publish diff --git a/src/documentation/protocol/worker/quick-start.md b/src/get-started/protocol/worker/quick-start.md similarity index 100% rename from src/documentation/protocol/worker/quick-start.md rename to src/get-started/protocol/worker/quick-start.md diff --git a/src/documentation/quick-start.md b/src/get-started/quick-start.md similarity index 100% rename from src/documentation/quick-start.md rename to src/get-started/quick-start.md diff --git a/src/documentation/rlc.md b/src/get-started/rlc.md similarity index 97% rename from src/documentation/rlc.md rename to src/get-started/rlc.md index 0de20f57..166063ec 100644 --- a/src/documentation/rlc.md +++ b/src/get-started/rlc.md @@ -121,8 +121,8 @@ Ready to dive into the iExec ecosystem? Here are the next steps: networks - **[Start Using iExec](./quick-start.md)** - Begin your confidential computing journey -- **[Earn RLC](/documentation/manage-data/guides/manage-access)** - Become a - provider and monetize your resources +- **[Earn RLC](/guides/manage-data/manage-access)** - Become a provider and + monetize your resources