@iexec/web3mail 1.6.0-nightly-b2a0e93 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/utils/subgraphQuery.js +1 -2
  2. package/dist/utils/subgraphQuery.js.map +1 -1
  3. package/dist/utils/validators.d.ts +35 -0
  4. package/dist/utils/validators.js +45 -1
  5. package/dist/utils/validators.js.map +1 -1
  6. package/dist/web3mail/IExecWeb3mail.d.ts +4 -2
  7. package/dist/web3mail/IExecWeb3mail.js +24 -1
  8. package/dist/web3mail/IExecWeb3mail.js.map +1 -1
  9. package/dist/web3mail/fetchMyContacts.js +2 -1
  10. package/dist/web3mail/fetchMyContacts.js.map +1 -1
  11. package/dist/web3mail/fetchUserContacts.js +1 -1
  12. package/dist/web3mail/fetchUserContacts.js.map +1 -1
  13. package/dist/web3mail/internalTypes.d.ts +4 -4
  14. package/dist/web3mail/prepareEmailCampaign.d.ts +4 -0
  15. package/dist/web3mail/prepareEmailCampaign.js +106 -0
  16. package/dist/web3mail/prepareEmailCampaign.js.map +1 -0
  17. package/dist/web3mail/sendEmail.d.ts +2 -2
  18. package/dist/web3mail/sendEmail.js +191 -135
  19. package/dist/web3mail/sendEmail.js.map +1 -1
  20. package/dist/web3mail/sendEmailCampaign.d.ts +4 -0
  21. package/dist/web3mail/sendEmailCampaign.js +43 -0
  22. package/dist/web3mail/sendEmailCampaign.js.map +1 -0
  23. package/dist/web3mail/types.d.ts +83 -24
  24. package/package.json +3 -4
  25. package/src/utils/subgraphQuery.ts +1 -2
  26. package/src/utils/validators.ts +68 -1
  27. package/src/web3mail/IExecWeb3mail.ts +38 -5
  28. package/src/web3mail/fetchMyContacts.ts +2 -1
  29. package/src/web3mail/fetchUserContacts.ts +7 -5
  30. package/src/web3mail/internalTypes.ts +5 -3
  31. package/src/web3mail/prepareEmailCampaign.ts +170 -0
  32. package/src/web3mail/sendEmail.ts +246 -150
  33. package/src/web3mail/sendEmailCampaign.ts +69 -0
  34. package/src/web3mail/types.ts +88 -26
@@ -1,15 +1,18 @@
1
1
  import { Buffer } from 'buffer';
2
2
  import {
3
+ CALLBACK_WEB3MAIL,
3
4
  DEFAULT_CONTENT_TYPE,
4
5
  MAX_DESIRED_APP_ORDER_PRICE,
5
6
  MAX_DESIRED_DATA_ORDER_PRICE,
6
7
  MAX_DESIRED_WORKERPOOL_ORDER_PRICE,
7
8
  } from '../config/config.js';
8
9
  import { handleIfProtocolError, WorkflowError } from '../utils/errors.js';
10
+ import { generateSecureUniqueId } from '../utils/generateUniqueId.js';
9
11
  import * as ipfs from '../utils/ipfs-service.js';
10
12
  import { checkProtectedDataValidity } from '../utils/subgraphQuery.js';
11
13
  import {
12
14
  addressOrEnsSchema,
15
+ addressSchema,
13
16
  booleanSchema,
14
17
  contentTypeSchema,
15
18
  emailContentSchema,
@@ -19,11 +22,14 @@ import {
19
22
  senderNameSchema,
20
23
  throwIfMissing,
21
24
  } from '../utils/validators.js';
25
+ import {
26
+ checkUserVoucher,
27
+ filterWorkerpoolOrders,
28
+ } from './sendEmail.models.js';
22
29
  import { SendEmailParams, SendEmailResponse } from './types.js';
23
30
  import {
24
31
  DappAddressConsumer,
25
32
  DappWhitelistAddressConsumer,
26
- DataProtectorConsumer,
27
33
  IExecConsumer,
28
34
  IpfsGatewayConfigConsumer,
29
35
  IpfsNodeConfigConsumer,
@@ -35,9 +41,9 @@ export type SendEmail = typeof sendEmail;
35
41
  export const sendEmail = async ({
36
42
  graphQLClient = throwIfMissing(),
37
43
  iexec = throwIfMissing(),
38
- dataProtector = throwIfMissing(),
39
- workerpoolAddressOrEns = throwIfMissing(),
44
+ workerpoolAddressOrEns,
40
45
  dappAddressOrENS,
46
+ dappWhitelistAddress,
41
47
  ipfsNode,
42
48
  ipfsGateway,
43
49
  emailSubject,
@@ -49,8 +55,6 @@ export const sendEmail = async ({
49
55
  workerpoolMaxPrice = MAX_DESIRED_WORKERPOOL_ORDER_PRICE,
50
56
  senderName,
51
57
  protectedData,
52
- grantedAccess,
53
- maxProtectedDataPerTask,
54
58
  useVoucher = false,
55
59
  }: IExecConsumer &
56
60
  SubgraphConsumer &
@@ -58,53 +62,203 @@ export const sendEmail = async ({
58
62
  DappWhitelistAddressConsumer &
59
63
  IpfsNodeConfigConsumer &
60
64
  IpfsGatewayConfigConsumer &
61
- SendEmailParams &
62
- DataProtectorConsumer): Promise<SendEmailResponse<SendEmailParams>> => {
65
+ SendEmailParams): Promise<SendEmailResponse> => {
66
+ const vDatasetAddress = addressOrEnsSchema()
67
+ .required()
68
+ .label('protectedData')
69
+ .validateSync(protectedData);
70
+
71
+ const vEmailSubject = emailSubjectSchema()
72
+ .required()
73
+ .label('emailSubject')
74
+ .validateSync(emailSubject);
75
+
76
+ const vEmailContent = emailContentSchema()
77
+ .required()
78
+ .label('emailContent')
79
+ .validateSync(emailContent);
80
+
81
+ const vContentType = contentTypeSchema()
82
+ .required()
83
+ .label('contentType')
84
+ .validateSync(contentType);
85
+
86
+ const vSenderName = senderNameSchema()
87
+ .label('senderName')
88
+ .validateSync(senderName);
89
+
90
+ const vLabel = labelSchema().label('label').validateSync(label);
91
+
92
+ const vWorkerpoolAddressOrEns = addressOrEnsSchema()
93
+ .required()
94
+ .label('WorkerpoolAddressOrEns')
95
+ .validateSync(workerpoolAddressOrEns);
96
+
97
+ const vDappAddressOrENS = addressOrEnsSchema()
98
+ .required()
99
+ .label('dappAddressOrENS')
100
+ .validateSync(dappAddressOrENS);
101
+
102
+ const vDappWhitelistAddress = addressSchema()
103
+ .required()
104
+ .label('dappWhitelistAddress')
105
+ .validateSync(dappWhitelistAddress);
106
+
107
+ const vDataMaxPrice = positiveNumberSchema()
108
+ .label('dataMaxPrice')
109
+ .validateSync(dataMaxPrice);
110
+
111
+ const vAppMaxPrice = positiveNumberSchema()
112
+ .label('appMaxPrice')
113
+ .validateSync(appMaxPrice);
114
+
115
+ const vWorkerpoolMaxPrice = positiveNumberSchema()
116
+ .label('workerpoolMaxPrice')
117
+ .validateSync(workerpoolMaxPrice);
118
+
119
+ const vUseVoucher = booleanSchema()
120
+ .label('useVoucher')
121
+ .validateSync(useVoucher);
122
+
123
+ // Check protected data schema through subgraph
124
+ const isValidProtectedData = await checkProtectedDataValidity(
125
+ graphQLClient,
126
+ vDatasetAddress
127
+ );
128
+
129
+ if (!isValidProtectedData) {
130
+ throw new Error(
131
+ 'This protected data does not contain "email:string" in its schema.'
132
+ );
133
+ }
134
+
135
+ const requesterAddress = await iexec.wallet.getAddress();
136
+
137
+ let userVoucher;
138
+ if (vUseVoucher) {
139
+ try {
140
+ userVoucher = await iexec.voucher.showUserVoucher(requesterAddress);
141
+ checkUserVoucher({ userVoucher });
142
+ } catch (err) {
143
+ if (err?.message?.startsWith('No Voucher found for address')) {
144
+ throw new Error(
145
+ 'Oops, it seems your wallet is not associated with any voucher. Check on https://builder.iex.ec/'
146
+ );
147
+ }
148
+ throw err;
149
+ }
150
+ }
151
+
63
152
  try {
64
- const vUseVoucher = booleanSchema()
65
- .label('useVoucher')
66
- .validateSync(useVoucher);
67
- const vWorkerpoolAddressOrEns = addressOrEnsSchema()
68
- .required()
69
- .label('WorkerpoolAddressOrEns')
70
- .validateSync(workerpoolAddressOrEns);
71
- const vSenderName = senderNameSchema()
72
- .label('senderName')
73
- .validateSync(senderName);
74
- const vEmailSubject = emailSubjectSchema()
75
- .required()
76
- .label('emailSubject')
77
- .validateSync(emailSubject);
78
- const vEmailContent = emailContentSchema()
79
- .required()
80
- .label('emailContent')
81
- .validateSync(emailContent);
82
- const vContentType = contentTypeSchema()
83
- .required()
84
- .label('contentType')
85
- .validateSync(contentType);
86
- const vLabel = labelSchema().label('label').validateSync(label);
87
-
88
- const vDappAddressOrENS = addressOrEnsSchema()
89
- .required()
90
- .label('dappAddressOrENS')
91
- .validateSync(dappAddressOrENS);
92
- // TODO: remove this once we have a way to pass appWhitelist to processProtectedData function
93
- // const vDappWhitelistAddress = addressSchema()
94
- // .required()
95
- // .label('dappWhitelistAddress')
96
- // .validateSync(dappWhitelistAddress);
97
- const vDataMaxPrice = positiveNumberSchema()
98
- .label('dataMaxPrice')
99
- .validateSync(dataMaxPrice);
100
- const vAppMaxPrice = positiveNumberSchema()
101
- .label('appMaxPrice')
102
- .validateSync(appMaxPrice);
103
- const vWorkerpoolMaxPrice = positiveNumberSchema()
104
- .label('workerpoolMaxPrice')
105
- .validateSync(workerpoolMaxPrice);
106
-
107
- // Encrypt email content
153
+ const [
154
+ datasetorderForApp,
155
+ datasetorderForWhitelist,
156
+ apporder,
157
+ workerpoolorder,
158
+ ] = await Promise.all([
159
+ // Fetch dataset order for web3mail app
160
+ iexec.orderbook
161
+ .fetchDatasetOrderbook({
162
+ dataset: vDatasetAddress,
163
+ app: dappAddressOrENS,
164
+ requester: requesterAddress,
165
+ })
166
+ .then((datasetOrderbook) => {
167
+ const desiredPriceDataOrderbook = datasetOrderbook.orders.filter(
168
+ (order) => order.order.datasetprice <= vDataMaxPrice
169
+ );
170
+ return desiredPriceDataOrderbook[0]?.order; // may be undefined
171
+ }),
172
+
173
+ // Fetch dataset order for web3mail whitelist
174
+ iexec.orderbook
175
+ .fetchDatasetOrderbook({
176
+ dataset: vDatasetAddress,
177
+ app: vDappWhitelistAddress,
178
+ requester: requesterAddress,
179
+ })
180
+ .then((datasetOrderbook) => {
181
+ const desiredPriceDataOrderbook = datasetOrderbook.orders.filter(
182
+ (order) => order.order.datasetprice <= vDataMaxPrice
183
+ );
184
+ return desiredPriceDataOrderbook[0]?.order; // may be undefined
185
+ }),
186
+
187
+ // Fetch app order
188
+ iexec.orderbook
189
+ .fetchAppOrderbook({
190
+ app: dappAddressOrENS,
191
+ minTag: ['tee', 'scone'],
192
+ maxTag: ['tee', 'scone'],
193
+ workerpool: workerpoolAddressOrEns,
194
+ })
195
+ .then((appOrderbook) => {
196
+ const desiredPriceAppOrderbook = appOrderbook.orders.filter(
197
+ (order) => order.order.appprice <= vAppMaxPrice
198
+ );
199
+ const desiredPriceAppOrder = desiredPriceAppOrderbook[0]?.order;
200
+ if (!desiredPriceAppOrder) {
201
+ throw new Error('No App order found for the desired price');
202
+ }
203
+ return desiredPriceAppOrder;
204
+ }),
205
+
206
+ // Fetch workerpool order for App or AppWhitelist
207
+ Promise.all([
208
+ // for app
209
+ iexec.orderbook.fetchWorkerpoolOrderbook({
210
+ workerpool: workerpoolAddressOrEns,
211
+ app: vDappAddressOrENS,
212
+ dataset: vDatasetAddress,
213
+ requester: requesterAddress, // public orders + user specific orders
214
+ isRequesterStrict: useVoucher, // If voucher, we only want user specific orders
215
+ minTag: ['tee', 'scone'],
216
+ maxTag: ['tee', 'scone'],
217
+ category: 0,
218
+ }),
219
+ // for app whitelist
220
+ iexec.orderbook.fetchWorkerpoolOrderbook({
221
+ workerpool: workerpoolAddressOrEns,
222
+ app: vDappWhitelistAddress,
223
+ dataset: vDatasetAddress,
224
+ requester: requesterAddress, // public orders + user specific orders
225
+ isRequesterStrict: useVoucher, // If voucher, we only want user specific orders
226
+ minTag: ['tee', 'scone'],
227
+ maxTag: ['tee', 'scone'],
228
+ category: 0,
229
+ }),
230
+ ]).then(
231
+ ([workerpoolOrderbookForApp, workerpoolOrderbookForAppWhitelist]) => {
232
+ const desiredPriceWorkerpoolOrder = filterWorkerpoolOrders({
233
+ workerpoolOrders: [
234
+ ...workerpoolOrderbookForApp.orders,
235
+ ...workerpoolOrderbookForAppWhitelist.orders,
236
+ ],
237
+ workerpoolMaxPrice: vWorkerpoolMaxPrice,
238
+ useVoucher: vUseVoucher,
239
+ userVoucher,
240
+ });
241
+
242
+ if (!desiredPriceWorkerpoolOrder) {
243
+ throw new Error('No Workerpool order found for the desired price');
244
+ }
245
+
246
+ return desiredPriceWorkerpoolOrder;
247
+ }
248
+ ),
249
+ ]);
250
+
251
+ if (!workerpoolorder) {
252
+ throw new Error('No Workerpool order found for the desired price');
253
+ }
254
+
255
+ const datasetorder = datasetorderForApp || datasetorderForWhitelist;
256
+ if (!datasetorder) {
257
+ throw new Error('No Dataset order found for the desired price');
258
+ }
259
+
260
+ // Push requester secrets
261
+ const requesterSecretId = generateSecureUniqueId(16);
108
262
  const emailContentEncryptionKey = iexec.dataset.generateEncryptionKey();
109
263
  const encryptedFile = await iexec.dataset
110
264
  .encrypt(Buffer.from(vEmailContent, 'utf8'), emailContentEncryptionKey)
@@ -115,11 +269,10 @@ export const sendEmail = async ({
115
269
  });
116
270
  });
117
271
 
118
- // Push email message to IPFS
119
272
  const cid = await ipfs
120
273
  .add(encryptedFile, {
121
- ipfsNode,
122
- ipfsGateway,
274
+ ipfsNode: ipfsNode,
275
+ ipfsGateway: ipfsGateway,
123
276
  })
124
277
  .catch((e) => {
125
278
  throw new WorkflowError({
@@ -127,118 +280,61 @@ export const sendEmail = async ({
127
280
  errorCause: e,
128
281
  });
129
282
  });
283
+
130
284
  const multiaddr = `/ipfs/${cid}`;
131
285
 
132
- // Prepare secrets for the requester
133
- // Use a positive integer as secret ID (required by iexec)
134
- // Using "1" as a fixed ID for the requester secret
135
- const requesterSecretId = 1;
136
- const secrets = {
137
- [requesterSecretId]: JSON.stringify({
138
- senderName: vSenderName,
286
+ await iexec.secrets.pushRequesterSecret(
287
+ requesterSecretId,
288
+ JSON.stringify({
139
289
  emailSubject: vEmailSubject,
140
290
  emailContentMultiAddr: multiaddr,
141
291
  contentType: vContentType,
292
+ senderName: vSenderName,
142
293
  emailContentEncryptionKey,
143
- }),
144
- };
145
- // Bulk processing
146
- if (grantedAccess) {
147
- const vMaxProtectedDataPerTask = positiveNumberSchema()
148
- .label('maxProtectedDataPerTask')
149
- .validateSync(maxProtectedDataPerTask);
150
-
151
- const bulkRequest = await dataProtector.prepareBulkRequest({
152
- app: vDappAddressOrENS,
153
- appMaxPrice: vAppMaxPrice,
154
- workerpoolMaxPrice: vWorkerpoolMaxPrice,
155
- workerpool: vWorkerpoolAddressOrEns,
156
- args: vLabel,
157
- inputFiles: [],
158
- secrets,
159
- bulkAccesses: grantedAccess,
160
- maxProtectedDataPerTask: vMaxProtectedDataPerTask,
161
- });
162
- const processBulkRequestResponse = await dataProtector.processBulkRequest(
163
- {
164
- bulkRequest: bulkRequest.bulkRequest,
165
- useVoucher: vUseVoucher,
166
- workerpool: vWorkerpoolAddressOrEns,
167
- }
168
- );
169
- return {
170
- tasks: processBulkRequestResponse.tasks.map((task) => ({
171
- taskId: task.taskId,
172
- dealId: task.dealId,
173
- bulkIndex: task.bulkIndex,
174
- })),
175
- } as unknown as SendEmailResponse<SendEmailParams>;
176
- }
177
-
178
- // Single processing mode - protectedData is required
179
- const vDatasetAddress = addressOrEnsSchema()
180
- .required()
181
- .label('protectedData')
182
- .validateSync(protectedData);
183
- // Check protected data validity through subgraph
184
- const isValidProtectedData = await checkProtectedDataValidity(
185
- graphQLClient,
186
- vDatasetAddress
294
+ useCallback: true,
295
+ })
187
296
  );
188
- if (!isValidProtectedData) {
189
- throw new Error(
190
- 'This protected data does not contain "email:string" in its schema.'
191
- );
192
- }
193
297
 
194
- // Use processProtectedData from dataprotector
195
- const result = await dataProtector.processProtectedData({
196
- defaultWorkerpool: vWorkerpoolAddressOrEns,
197
- protectedData: vDatasetAddress,
298
+ const requestorderToSign = await iexec.order.createRequestorder({
198
299
  app: vDappAddressOrENS,
199
- // userWhitelist: vDappWhitelistAddress, // Removed due to bug in dataprotector v2.0.0-beta.20
200
- dataMaxPrice: vDataMaxPrice,
201
- appMaxPrice: vAppMaxPrice,
202
- workerpoolMaxPrice: vWorkerpoolMaxPrice,
300
+ category: workerpoolorder.category,
301
+ dataset: vDatasetAddress,
302
+ datasetmaxprice: datasetorder.datasetprice,
303
+ appmaxprice: apporder.appprice,
304
+ workerpoolmaxprice: workerpoolorder.workerpoolprice,
305
+ tag: ['tee', 'scone'],
203
306
  workerpool: vWorkerpoolAddressOrEns,
204
- args: vLabel,
205
- inputFiles: [],
206
- secrets,
207
- useVoucher: vUseVoucher,
208
- waitForResult: false,
307
+ callback: CALLBACK_WEB3MAIL,
308
+ params: {
309
+ iexec_secrets: {
310
+ 1: requesterSecretId,
311
+ },
312
+ iexec_args: vLabel,
313
+ },
209
314
  });
210
315
 
316
+ const requestorder = await iexec.order.signRequestorder(requestorderToSign);
317
+
318
+ // Match orders and compute task ID
319
+ const { dealid: dealId } = await iexec.order.matchOrders(
320
+ {
321
+ apporder: apporder,
322
+ datasetorder: datasetorder,
323
+ workerpoolorder: workerpoolorder,
324
+ requestorder: requestorder,
325
+ },
326
+ { useVoucher: vUseVoucher }
327
+ );
328
+
329
+ const taskId = await iexec.deal.computeTaskId(dealId, 0);
330
+
211
331
  return {
212
- taskId: result.taskId,
213
- } as unknown as SendEmailResponse<SendEmailParams>;
332
+ taskId,
333
+ dealId,
334
+ };
214
335
  } catch (error) {
215
- // Protocol error detected, re-throwing as-is
216
- if ((error as any)?.isProtocolError === true) {
217
- throw error;
218
- }
219
-
220
- // Handle protocol errors - this will throw if it's an ApiCallError
221
- // handleIfProtocolError transforms ApiCallError into a WorkflowError with isProtocolError=true
222
336
  handleIfProtocolError(error);
223
337
 
224
- // If we reach here, it's not a protocol error
225
- // Check if it's a WorkflowError from processProtectedData by checking the message
226
- const isProcessProtectedDataError =
227
- error instanceof Error &&
228
- error.message === 'Failed to process protected data';
229
-
230
- if (isProcessProtectedDataError) {
231
- const cause = (error as any)?.cause;
232
- // Return unwrapped cause (the actual Error object)
233
- // error.cause should be an Error, but ensure it is
234
- const unwrappedCause = cause instanceof Error ? cause : error;
235
- throw new WorkflowError({
236
- message: 'Failed to sendEmail',
237
- errorCause: unwrappedCause,
238
- });
239
- }
240
-
241
- // For all other errors
242
338
  throw new WorkflowError({
243
339
  message: 'Failed to sendEmail',
244
340
  errorCause: error,
@@ -0,0 +1,69 @@
1
+ import { NULL_ADDRESS } from 'iexec/utils';
2
+ import { ValidationError } from 'yup';
3
+ import { handleIfProtocolError, WorkflowError } from '../utils/errors.js';
4
+ import {
5
+ addressOrEnsSchema,
6
+ campaignRequestSchema,
7
+ throwIfMissing,
8
+ } from '../utils/validators.js';
9
+ import {
10
+ CampaignRequest,
11
+ SendEmailCampaignParams,
12
+ SendEmailCampaignResponse,
13
+ } from './types.js';
14
+ import { DataProtectorConsumer } from './internalTypes.js';
15
+
16
+ export type SendEmailCampaign = typeof sendEmailCampaign;
17
+
18
+ export const sendEmailCampaign = async ({
19
+ dataProtector = throwIfMissing(),
20
+ workerpoolAddressOrEns = throwIfMissing(),
21
+ campaignRequest,
22
+ }: DataProtectorConsumer &
23
+ SendEmailCampaignParams): Promise<SendEmailCampaignResponse> => {
24
+ const vCampaignRequest = campaignRequestSchema()
25
+ .required()
26
+ .label('campaignRequest')
27
+ .validateSync(campaignRequest) as CampaignRequest;
28
+
29
+ const vWorkerpoolAddressOrEns = addressOrEnsSchema()
30
+ .required()
31
+ .label('workerpoolAddressOrEns')
32
+ .validateSync(workerpoolAddressOrEns);
33
+
34
+ if (
35
+ vCampaignRequest.workerpool !== NULL_ADDRESS &&
36
+ vCampaignRequest.workerpool.toLowerCase() !==
37
+ vWorkerpoolAddressOrEns.toLowerCase()
38
+ ) {
39
+ throw new ValidationError(
40
+ "workerpoolAddressOrEns doesn't match campaignRequest workerpool"
41
+ );
42
+ }
43
+
44
+ try {
45
+ // Process the prepared bulk request
46
+ const processBulkRequestResponse = await dataProtector.processBulkRequest({
47
+ bulkRequest: vCampaignRequest,
48
+ workerpool: vWorkerpoolAddressOrEns,
49
+ waitForResult: false,
50
+ });
51
+
52
+ return processBulkRequestResponse;
53
+ } catch (error) {
54
+ // Protocol error detected, re-throwing as-is
55
+ if ((error as any)?.isProtocolError === true) {
56
+ throw error;
57
+ }
58
+
59
+ // Handle protocol errors - this will throw if it's an ApiCallError
60
+ // handleIfProtocolError transforms ApiCallError into a WorkflowError with isProtocolError=true
61
+ handleIfProtocolError(error);
62
+
63
+ // For all other errors
64
+ throw new WorkflowError({
65
+ message: 'Failed to sendEmailCampaign',
66
+ errorCause: error,
67
+ });
68
+ }
69
+ };
@@ -1,5 +1,6 @@
1
1
  import { EnhancedWallet } from 'iexec';
2
2
  import { IExecConfigOptions } from 'iexec/IExecConfig';
3
+ import type { BulkRequest } from '@iexec/dataprotector';
3
4
 
4
5
  export type Web3SignerProvider = EnhancedWallet;
5
6
 
@@ -11,6 +12,22 @@ export type Address = string;
11
12
 
12
13
  export type TimeStamp = string;
13
14
 
15
+ /**
16
+ * request to send email in bulk
17
+ *
18
+ * use `prepareEmailCampaign()` to create a `CampaignRequest`
19
+ *
20
+ * then use `sendEmailCampaign()` to send the campaign
21
+ */
22
+ export type CampaignRequest = BulkRequest;
23
+
24
+ /**
25
+ * authorization signed by the data owner granting access to this contact
26
+ *
27
+ * `GrantedAccess` are obtained by fetching contacts (e.g. `fetchMyContacts()` or `fetchUserContacts()`)
28
+ *
29
+ * `GrantedAccess` can be consumed for email campaigns (e.g. `prepareEmailCampaign()` then `sendEmailCampaign()`)
30
+ */
14
31
  export type GrantedAccess = {
15
32
  dataset: string;
16
33
  datasetprice: string;
@@ -38,14 +55,7 @@ export type Contact = {
38
55
  export type SendEmailParams = {
39
56
  emailSubject: string;
40
57
  emailContent: string;
41
- protectedData?: Address;
42
- /**
43
- * Granted access to process.
44
- * use prepareBulkRequest of dataprotector to create a bulk request.
45
- * if not provided, the single message will be processed.
46
- */
47
- grantedAccess?: GrantedAccess[];
48
- maxProtectedDataPerTask?: number;
58
+ protectedData: Address;
49
59
  contentType?: string;
50
60
  senderName?: string;
51
61
  label?: string;
@@ -61,6 +71,9 @@ export type FetchMyContactsParams = {
61
71
  * Get contacts for this specific user only
62
72
  */
63
73
  isUserStrict?: boolean;
74
+ /**
75
+ * If true, returns only contacts with bulk processing access grants
76
+ */
64
77
  bulkOnly?: boolean;
65
78
  };
66
79
 
@@ -71,27 +84,17 @@ export type FetchUserContactsParams = {
71
84
  userAddress: Address;
72
85
  } & FetchMyContactsParams;
73
86
 
74
- type SendEmailSingleResponse = {
87
+ export type SendEmailResponse = {
88
+ /**
89
+ * ID of the task
90
+ */
75
91
  taskId: string;
92
+ /**
93
+ * ID of the deal containing the task
94
+ */
95
+ dealId: string;
76
96
  };
77
97
 
78
- type SendEmailBulkResponse = {
79
- tasks: {
80
- bulkIndex: number;
81
- taskId: string;
82
- dealId: string;
83
- }[];
84
- };
85
-
86
- export type SendEmailResponse<Params = { protectedData: Address }> =
87
- Params extends {
88
- grantedAccess: GrantedAccess[];
89
- }
90
- ? SendEmailBulkResponse
91
- : never & Params extends { protectedData: Address }
92
- ? SendEmailSingleResponse
93
- : never;
94
-
95
98
  /**
96
99
  * Configuration options for Web3Mail.
97
100
  */
@@ -139,3 +142,62 @@ export type Web3MailConfigOptions = {
139
142
  */
140
143
  allowExperimentalNetworks?: boolean;
141
144
  };
145
+
146
+ export type PrepareEmailCampaignParams = {
147
+ /**
148
+ * List of `GrantedAccess` to contacts to send emails to in bulk.
149
+ *
150
+ * use `fetchMyContacts({ bulkOnly: true })` to get granted accesses.
151
+ */
152
+ grantedAccesses: GrantedAccess[];
153
+ maxProtectedDataPerTask?: number;
154
+ senderName?: string;
155
+ emailSubject: string;
156
+ emailContent: string;
157
+ contentType?: string;
158
+ label?: string;
159
+ workerpoolAddressOrEns?: AddressOrENS;
160
+ dataMaxPrice?: number;
161
+ appMaxPrice?: number;
162
+ workerpoolMaxPrice?: number;
163
+ };
164
+
165
+ export type PrepareEmailCampaignResponse = {
166
+ /**
167
+ * The prepared campaign request
168
+ *
169
+ * Use this in `sendEmailCampaign()` to start or continue sending the campaign
170
+ */
171
+ campaignRequest: CampaignRequest;
172
+ };
173
+
174
+ export type SendEmailCampaignParams = {
175
+ /**
176
+ * The prepared campaign request from `prepareEmailCampaign()`
177
+ */
178
+ campaignRequest: CampaignRequest;
179
+ /**
180
+ * Workerpool address or ENS to use for processing
181
+ */
182
+ workerpoolAddressOrEns?: AddressOrENS;
183
+ };
184
+
185
+ export type SendEmailCampaignResponse = {
186
+ /**
187
+ * List of tasks created for the campaign
188
+ */
189
+ tasks: Array<{
190
+ /**
191
+ * ID of the task
192
+ */
193
+ taskId: string;
194
+ /**
195
+ * ID of the deal containing the task
196
+ */
197
+ dealId: string;
198
+ /**
199
+ * Index of the task in the bulk request
200
+ */
201
+ bulkIndex: number;
202
+ }>;
203
+ };