Prerequisites
- Ensure Node.js v18 or later is installed.
-
Install iExec's SDKs:
yarn add @iexec/dataprotector @iexec/web3mail
-
We'll use Wagmi to interact with wallets, though you may use any other dApp library:
yarn add wagmi
An Ethereum wallet that will be used to "send" emails.
Step 1: Save an Email Address and Grant Access
In this step, you will:
- Securely store email addresses using iExec's DataProtector Core.
- Grant controlled access to a dApp without revealing the email addresses.
- Allow users to revoke access at any time.
If you have already completed this step, you can proceed to Step 2.
Add Bellecour Network to Your App
Here's an example for Wagmi:
const bellecourNetwork = defineChain({
id: 134,
name: 'iExec Sidechain',
nativeCurrency: { name: 'xRLC', symbol: 'xRLC', decimals: 18 },
rpcUrls: { default: { http: ['https://bellecour.iex.ec'] } },
blockExplorers: { default: { name: 'IXEC Explorer', url: 'https://blockscout.bellecour.iex.ec' } },
})
export const config = createConfig(
getDefaultConfig({
chains: [
// Other chains...
bellecourNetwork,
],
transports: { 134: http('https://bellecour.iex.ec') },
// Other settings...
})
)
Switching Networks
Use the useSwitchChain
hook to switch to the Bellecour network:
const { switchChain } = useSwitchChain({ config })
Saving Email as Protected Data
const saveEmailAsProtected = async () => {
if (!isValidEmail(email)) {
setError('Please enter a valid email address')
return
}
// Switch to Bellecour
await switchChain(134)
if (chainId !== 134) return
setIsInProgress(true)
try {
await dataProtectorCore.protectData({
// Unique identifier for the dataset
name: 'uniqueDatasetName',
data: { email },
})
setHasProtectedEmail(true)
} catch (error) {
// Handle errors
console.error(error)
setError('Failed to save email address')
} finally {
setIsInProgress(false)
}
}
💡 Note: All implementations should be adapted to fit your specific requirements.
Retrieving Protected Data
Retrieve the dataset for the following purposes:
- Check if the user has already saved their email.
- Fetch the dataset address to grant access to a dApp.
const getProtectedData = async () => {
if (chainId !== 134) return
setIsInProgress(true)
try {
const result = await dataProtectorCore.getProtectedData({
owner: address,
requiredSchema: { email: 'string' },
})
// Filter the dataset by name as there may be multiple datasets
// with the same schema.
return result.filter(({ name }) => name === 'uniqueDatasetName')
} catch (error) {
console.error(error)
setError('Failed to get email address')
} finally {
setIsInProgress(false)
}
}
Granting Access
Allow dApps to access datasets:
const grantAccess = async () => {
const protectedData = await getProtectedData()
if (!protectedData?.length || chainId !== 134) {
setError('Please switch to iExec\'s sidechain or check your data.')
return
}
setIsInProgress(true)
try {
await dataProtectorCore.grantAccess({
// Get the address of the first dataset
protectedData: protectedData[0].address,
// This is iExec's Web3Mail app whitelist address
// https://tools.docs.iex.ec/tools/web3mail/methods/sendEmail#sendemail
authorizedApp: '0x781482C39CcE25546583EaC4957Fb7Bf04C277D2',
// The Ethereum address that will be used to send emails on the backend
authorizedUser: '0x...',
numberOfAccess: 100000,
})
} catch (error) {
console.error(error)
setError('Failed to grant email address access')
} finally {
setIsInProgress(false)
}
}
Revoking Access
Users may want to revoke access to their data. It can be done using revokeAllAccess
:
const revokeAccess = async () => {
const protectedData = await getProtectedData()
if (!protectedData?.length || chainId !== 134) {
setError('Please switch to iExec\'s sidechain or check your data.')
return
}
setIsInProgress(true)
try {
await dataProtectorCore.revokeAllAccess({
protectedData: protectedData[0].address,
authorizedApp: '0x781482C39CcE25546583EaC4957Fb7Bf04C277D2',
authorizedUser: '0x...',
})
} catch (error) {
console.error(error)
setError('Failed to revoke access')
} finally {
setIsInProgress(false)
}
}
Step 2: Using Web3Mail SDK
Send emails to recipients who have granted access to your dApp.
Initializing Web3Mail SDK
import { getWeb3Provider, IExecWeb3mail } from '@iexec/web3mail'
// The PRIVATE_KEY of the Ethereum address that users granted access to - 'authorizedUser'
const provider = getWeb3Provider(process.env.PRIVATE_KEY)
const web3mail = new IExecWeb3mail(provider)
Fetching Contacts
Retrieve users who granted access:
// isUserStrict - enables fetching contacts who granted access exclusively to our dApp and no one else.
const contactsList = await web3mail.fetchMyContacts({ isUserStrict: true })
Sending Emails
Loop through contacts to send emails:
for (const { address: protectedDataAddress, owner } of contactsList) {
const content = `<div><p>Hello ${owner},</p><p>Here’s your update!</p></div>`
await web3mail.sendEmail({
protectedData: protectedDataAddress,
emailSubject: 'Your Update',
emailContent: content,
contentType: 'text/html',
senderName: 'Web3Mail Service',
workerpoolAddressOrEns: 'prod-v8-learn.main.pools.iexec.eth',
})
console.log(`Email sent to ${owner}`)
}
💡 Note: If you are hitting rate limits or get nonce related errors use a setTimeout to add a 1-2sec timeout between emails.
While in Development
For testing purposes, you can use a dev worker pool by setting workerpoolAddressOrEns
to prod-v8-learn.main.pools.iexec.eth
. This allows you to test email sending without RLC tokens.
Production Considerations
- Use a production worker pool for stability.
- Follow iExec’s guide for payment.
Validation
Verify protected data on the blockchain using iExec’s Explorer:
- Search for the user’s Ethereum address.
- View datasets under the
DATASETS
tab. - Make sure the dataset is stored on the blockchain.
You can find more articles, written by me on my website.
Top comments (0)