sdk-triggerx 0.1.17 โ†’ 0.1.18

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.
package/README.md CHANGED
@@ -50,6 +50,17 @@ const client = new TriggerXClient('YOUR_API_KEY');
50
50
  - `ArgType.Static`: Hardcoded values
51
51
  - `ArgType.Dynamic`: Runtime values fetched from a script
52
52
 
53
+ #### Wallet Modes
54
+
55
+ - `walletMode: 'regular'` (default): Executes jobs from the externally-owned account (EOA) signer.
56
+ - `walletMode: 'safe'`: Executes jobs via a Safe wallet using the TriggerX Safe Module.
57
+ - Requirements:
58
+ - Safe threshold must be 1 (single-signer) in this implementation.
59
+ - The signer must be an owner of the Safe.
60
+ - Parameters must be dynamic (provided by `dynamicArgumentsScriptUrl`).
61
+ - The SDK will auto-create a Safe if `safeAddress` is not provided and the chain is configured with a Safe Factory.
62
+ - The SDK will auto-enable the TriggerX Safe Module on the Safe if not already enabled.
63
+
53
64
  #### Supported Condition Types (for `conditionType`)
54
65
 
55
66
  - `greater_than`
@@ -97,6 +108,47 @@ console.log(result);
97
108
 
98
109
  ---
99
110
 
111
+ #### ๐Ÿงท Example: Time-based Dynamic Job via Safe Wallet (Arbitrum Sepolia)
112
+
113
+ ```ts
114
+ import { createJob, JobType, ArgType } from 'sdk-triggerx';
115
+
116
+ const jobInput = {
117
+ jobType: JobType.Time,
118
+ argType: ArgType.Dynamic,
119
+
120
+ jobTitle: 'My Safe Time Job',
121
+ timeFrame: 3600,
122
+ scheduleType: 'interval',
123
+ timeInterval: 300,
124
+ timezone: 'UTC',
125
+
126
+ // Arbitrum Sepolia
127
+ chainId: '421614',
128
+
129
+ // Safe mode (no target required โ€” SDK auto-sets module target/function/ABI)
130
+ walletMode: 'safe',
131
+ // Optional: provide an existing Safe; otherwise the SDK will create one for you
132
+ // safeAddress: '0xYourSafeAddress',
133
+
134
+ // Dynamic params must come from an IPFS/URL script
135
+ dynamicArgumentsScriptUrl: 'https://your-ipfs-gateway/ipfs/your-hash',
136
+
137
+ // Optional helper to auto-top up TG if low
138
+ autotopupTG: true,
139
+ };
140
+
141
+ const result = await createJob(client, { jobInput, signer });
142
+ console.log(result);
143
+ ```
144
+
145
+ Notes for Safe wallet mode:
146
+ - In Safe mode, you do NOT need to set `targetContractAddress`/`targetFunction`/`abi` โ€” the SDK sets these for the Safe Module and uses `execJobFromHub(address,address,uint256,bytes,uint8)` under the hood.
147
+ - Your action details (action target/value/data/op) must be produced by your IPFS script at execution time.
148
+ - No static arguments are allowed in Safe mode.
149
+
150
+ ---
151
+
100
152
  #### ๐Ÿ“Ÿ Example: Event-based Dynamic Job
101
153
 
102
154
  ```ts
@@ -165,6 +217,49 @@ console.log(result);
165
217
 
166
218
  ---
167
219
 
220
+ ### ๐Ÿ›ก๏ธ Safe Wallet (Gnosis Safe) Flow
221
+
222
+ To create jobs that use Safe wallets (`walletMode: 'safe'`), you must first create a Safe and obtain its address. The Safe MUST exist before the job can be created.
223
+
224
+ #### 1๏ธโƒฃ Create a Safe wallet for your user
225
+
226
+ ```ts
227
+ import { createSafeWallet } from 'sdk-triggerx/api/safeWallet'; // <--- new dedicated helper!
228
+ const safeAddress = await createSafeWallet(signer /* ethers.Signer */);
229
+ console.log('Your Safe address:', safeAddress);
230
+ ```
231
+
232
+ - Use this new address in safe job creation. All safe wallet creation belongs in this flow.
233
+ - You can use the returned address in any number of jobs for this user.
234
+ - This MUST be done before you attempt to create any job with `walletMode: 'safe'`.
235
+ - All safe wallet creation helpers are now in the dedicated `api/safeWallet` module so your file structure stays clean.
236
+
237
+ #### 2๏ธโƒฃ Create a job using the Safe (walletMode: 'safe')
238
+
239
+ ```ts
240
+ const jobInput = {
241
+ jobType: JobType.Time,
242
+ argType: ArgType.Dynamic, // required in safe mode
243
+ jobTitle: 'Safe Job',
244
+ timeFrame: 3600,
245
+ scheduleType: 'interval',
246
+ timeInterval: 300,
247
+ timezone: 'UTC',
248
+ chainId: '421614',
249
+ walletMode: 'safe',
250
+ safeAddress, // <---- required and must come from step 1
251
+ dynamicArgumentsScriptUrl: 'https://your-ipfs-gateway/ipfs/your-hash',
252
+ autotopupTG: true,
253
+ };
254
+ // ...
255
+ await createJob(client, { jobInput, signer }); // as normal
256
+ ```
257
+
258
+ - The `safeAddress` property is now required for jobs using `walletMode: 'safe'`.
259
+ - The SDK will no longer auto-create a Safe wallet for you; you must explicitly create and pass it in your job input.
260
+
261
+ ---
262
+
168
263
  ### 3. Fetch Job Data
169
264
 
170
265
  #### ๐Ÿ” Get All Jobs
@@ -1,5 +1,12 @@
1
1
  import { ethers } from 'ethers';
2
2
  export declare const checkTgBalance: (signer: ethers.Signer) => Promise<{
3
- tgBalanceWei: bigint;
4
- tgBalance: string;
3
+ success: boolean;
4
+ data?: {
5
+ tgBalanceWei: bigint;
6
+ tgBalance: string;
7
+ };
8
+ error?: string;
9
+ errorCode?: string;
10
+ errorType?: string;
11
+ details?: any;
5
12
  }>;
@@ -7,33 +7,52 @@ exports.checkTgBalance = void 0;
7
7
  const ethers_1 = require("ethers");
8
8
  const GasRegistry_json_1 = __importDefault(require("../contracts/abi/GasRegistry.json"));
9
9
  const config_1 = require("../config");
10
+ const errors_1 = require("../utils/errors");
10
11
  const checkTgBalance = async (signer) => {
11
- const network = await signer.provider?.getNetwork();
12
- const chainId = network?.chainId ? network.chainId.toString() : undefined;
13
- const { gasRegistry } = (0, config_1.getChainAddresses)(chainId);
14
- const gasRegistryContractAddress = gasRegistry;
15
- if (!gasRegistryContractAddress) {
16
- throw new Error('GasRegistry address not configured for this chain. Update config mapping.');
12
+ // Validate inputs
13
+ if (!signer) {
14
+ return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('signer', 'Signer is required'), 'Validation error');
17
15
  }
18
- const contract = new ethers_1.ethers.Contract(gasRegistryContractAddress, GasRegistry_json_1.default, signer);
19
- const address = await signer.getAddress();
20
- const balance = await contract.balances(address);
21
- // balance is likely an array or object with ethSpent and TGbalance, both in wei
22
- // We'll convert TGbalance from wei to ETH
23
- // If balance is an array: [ethSpent, TGbalance]
24
- // If balance is an object: { ethSpent, TGbalance }
25
- let tgBalanceWei;
26
- if (Array.isArray(balance)) {
27
- tgBalanceWei = balance[1];
16
+ try {
17
+ const network = await signer.provider?.getNetwork();
18
+ const chainId = network?.chainId ? network.chainId.toString() : undefined;
19
+ const { gasRegistry } = (0, config_1.getChainAddresses)(chainId);
20
+ const gasRegistryContractAddress = gasRegistry;
21
+ if (!gasRegistryContractAddress) {
22
+ return (0, errors_1.createErrorResponse)(new errors_1.ConfigurationError(`GasRegistry address not configured for chain ID: ${chainId}`), 'Configuration error');
23
+ }
24
+ const contract = new ethers_1.ethers.Contract(gasRegistryContractAddress, GasRegistry_json_1.default, signer);
25
+ const address = await signer.getAddress();
26
+ const balance = await contract.balances(address);
27
+ // balance is likely an array or object with ethSpent and TGbalance, both in wei
28
+ // We'll convert TGbalance from wei to ETH
29
+ // If balance is an array: [ethSpent, TGbalance]
30
+ // If balance is an object: { ethSpent, TGbalance }
31
+ let tgBalanceWei;
32
+ if (Array.isArray(balance)) {
33
+ tgBalanceWei = balance[1];
34
+ }
35
+ else if (balance && balance.TGbalance !== undefined) {
36
+ tgBalanceWei = balance.TGbalance;
37
+ }
38
+ else {
39
+ return (0, errors_1.createErrorResponse)(new errors_1.ContractError('Unexpected balance format from contract', { balance }), 'Contract error');
40
+ }
41
+ const tgBalance = ethers_1.ethers.formatEther(tgBalanceWei);
42
+ console.log('tgBalanceEth', tgBalance);
43
+ return { success: true, data: { tgBalanceWei, tgBalance } };
28
44
  }
29
- else if (balance && balance.TGbalance !== undefined) {
30
- tgBalanceWei = balance.TGbalance;
45
+ catch (error) {
46
+ console.error('Error checking TG balance:', error);
47
+ if (error instanceof Error) {
48
+ if (error.message.includes('network') || error.message.includes('timeout')) {
49
+ return (0, errors_1.createErrorResponse)(new errors_1.NetworkError('Network error during balance check', { originalError: error }), 'Network error');
50
+ }
51
+ else if (error.message.includes('contract') || error.message.includes('transaction')) {
52
+ return (0, errors_1.createErrorResponse)(new errors_1.ContractError('Contract error during balance check', { originalError: error }), 'Contract error');
53
+ }
54
+ }
55
+ return (0, errors_1.createErrorResponse)(error, 'Failed to check TG balance');
31
56
  }
32
- else {
33
- throw new Error('Unexpected balance format');
34
- }
35
- const tgBalance = ethers_1.ethers.formatEther(tgBalanceWei);
36
- console.log('tgBalanceEth', tgBalance);
37
- return { tgBalanceWei, tgBalance };
38
57
  };
39
58
  exports.checkTgBalance = checkTgBalance;
@@ -1,3 +1,9 @@
1
1
  import { TriggerXClient } from '../client';
2
2
  import { Signer } from 'ethers';
3
- export declare const deleteJob: (client: TriggerXClient, jobId: string, signer: Signer, chainId: string) => Promise<void>;
3
+ export declare const deleteJob: (client: TriggerXClient, jobId: string, signer: Signer, chainId: string) => Promise<{
4
+ success: boolean;
5
+ error?: string;
6
+ errorCode?: string;
7
+ errorType?: string;
8
+ details?: any;
9
+ }>;
@@ -8,11 +8,22 @@ exports.deleteJob = void 0;
8
8
  const JobRegistry_json_1 = __importDefault(require("../contracts/abi/JobRegistry.json"));
9
9
  const JobRegistry_1 = require("../contracts/JobRegistry");
10
10
  const config_1 = require("../config");
11
+ const errors_1 = require("../utils/errors");
11
12
  const deleteJob = async (client, jobId, signer, chainId) => {
12
- const apiKey = client.getApiKey(); // Assuming you have a method to get the API key
13
+ // Validate inputs
14
+ if (!jobId || typeof jobId !== 'string') {
15
+ return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('jobId', 'Job ID is required and must be a string'), 'Validation error');
16
+ }
17
+ if (!chainId || typeof chainId !== 'string') {
18
+ return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('chainId', 'Chain ID is required and must be a string'), 'Validation error');
19
+ }
20
+ const apiKey = client.getApiKey();
21
+ if (!apiKey) {
22
+ return (0, errors_1.createErrorResponse)(new errors_1.AuthenticationError('API key is required but not provided'), 'Authentication error');
23
+ }
13
24
  const { jobRegistry: jobRegistryAddress } = config_1.CONTRACT_ADDRESSES_BY_CHAIN[chainId];
14
25
  if (!jobRegistryAddress) {
15
- throw new Error(`No contract address found for chain ID: ${chainId}`);
26
+ return (0, errors_1.createErrorResponse)(new errors_1.ConfigurationError(`No contract address found for chain ID: ${chainId}`), 'Configuration error');
16
27
  }
17
28
  try {
18
29
  console.log(`Deleting job ${jobId} on chain ${chainId}...`);
@@ -33,10 +44,25 @@ const deleteJob = async (client, jobId, signer, chainId) => {
33
44
  timeout: 30000, // 30 second timeout
34
45
  });
35
46
  console.log('API update successful');
47
+ return { success: true };
36
48
  }
37
49
  catch (error) {
38
50
  console.error('Error deleting job:', error);
39
- throw error; // Rethrow the error for handling in the calling function
51
+ // Determine error type based on the error
52
+ const httpStatusCode = (0, errors_1.extractHttpStatusCode)(error);
53
+ const errorCode = (0, errors_1.determineErrorCode)(error, httpStatusCode);
54
+ if (error instanceof Error) {
55
+ if (error.message.includes('network') || error.message.includes('timeout')) {
56
+ return (0, errors_1.createErrorResponse)(new errors_1.NetworkError('Network error during job deletion', { originalError: error, jobId, chainId }, httpStatusCode), 'Network error');
57
+ }
58
+ else if (error.message.includes('contract') || error.message.includes('transaction')) {
59
+ return (0, errors_1.createErrorResponse)(new errors_1.ContractError('Contract error during job deletion', { originalError: error, jobId, chainId }, httpStatusCode), 'Contract error');
60
+ }
61
+ else if (error.message.includes('API') || error.message.includes('response')) {
62
+ return (0, errors_1.createErrorResponse)(new errors_1.ApiError('API error during job deletion', { originalError: error, jobId, chainId }, httpStatusCode), 'API error');
63
+ }
64
+ }
65
+ return (0, errors_1.createErrorResponse)(error, 'Failed to delete job');
40
66
  }
41
67
  };
42
68
  exports.deleteJob = deleteJob;
@@ -1,3 +1,10 @@
1
1
  import { TriggerXClient } from '../client';
2
2
  import { JobDataWithTasks } from '../types';
3
- export declare const getJobDataById: (client: TriggerXClient, jobId: string) => Promise<JobDataWithTasks>;
3
+ export declare const getJobDataById: (client: TriggerXClient, jobId: string) => Promise<{
4
+ success: boolean;
5
+ data?: JobDataWithTasks;
6
+ error?: string;
7
+ errorCode?: string;
8
+ errorType?: string;
9
+ details?: any;
10
+ }>;
@@ -2,8 +2,16 @@
2
2
  // sdk-triggerx/src/api/getJobDataById.ts
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.getJobDataById = void 0;
5
+ const errors_1 = require("../utils/errors");
5
6
  const getJobDataById = async (client, jobId) => {
6
- const apiKey = client.getApiKey(); // Assuming you have a method to get the API key
7
+ // Validate inputs
8
+ if (!jobId || typeof jobId !== 'string') {
9
+ return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('jobId', 'Job ID is required and must be a string'), 'Validation error');
10
+ }
11
+ const apiKey = client.getApiKey();
12
+ if (!apiKey) {
13
+ return (0, errors_1.createErrorResponse)(new errors_1.AuthenticationError('API key is required but not provided'), 'Authentication error');
14
+ }
7
15
  try {
8
16
  // First, fetch the job data
9
17
  const jobResponse = await client.get(`/api/jobs/${jobId}`, {
@@ -24,11 +32,27 @@ const getJobDataById = async (client, jobId) => {
24
32
  jobData: jobResponse,
25
33
  taskData: taskResponse
26
34
  };
27
- return combinedResponse;
35
+ return { success: true, data: combinedResponse };
28
36
  }
29
37
  catch (error) {
30
38
  console.error('Error fetching job data and task data by ID:', error);
31
- throw error; // Rethrow the error for handling in the calling function
39
+ const httpStatusCode = (0, errors_1.extractHttpStatusCode)(error);
40
+ const errorCode = (0, errors_1.determineErrorCode)(error, httpStatusCode);
41
+ if (error instanceof Error) {
42
+ if (error.message.includes('network') || error.message.includes('timeout')) {
43
+ return (0, errors_1.createErrorResponse)(new errors_1.NetworkError('Network error while fetching job data', { originalError: error, jobId }, httpStatusCode), 'Network error');
44
+ }
45
+ else if (error.message.includes('404') || error.message.includes('not found')) {
46
+ return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('jobId', 'Job not found', { originalError: error, jobId }, 404), 'Validation error');
47
+ }
48
+ else if (error.message.includes('401') || error.message.includes('unauthorized')) {
49
+ return (0, errors_1.createErrorResponse)(new errors_1.AuthenticationError('Unauthorized access to job data', { originalError: error, jobId }, 401), 'Authentication error');
50
+ }
51
+ else if (error.message.includes('API') || error.message.includes('response')) {
52
+ return (0, errors_1.createErrorResponse)(new errors_1.ApiError('API error while fetching job data', { originalError: error, jobId }, httpStatusCode), 'API error');
53
+ }
54
+ }
55
+ return (0, errors_1.createErrorResponse)(error, 'Failed to fetch job data');
32
56
  }
33
57
  };
34
58
  exports.getJobDataById = getJobDataById;
@@ -1,3 +1,10 @@
1
1
  import { TriggerXClient } from '../client';
2
2
  import { UserData } from '../types';
3
- export declare const getUserData: (client: TriggerXClient, address: string) => Promise<UserData>;
3
+ export declare const getUserData: (client: TriggerXClient, address: string) => Promise<{
4
+ success: boolean;
5
+ data?: UserData;
6
+ error?: string;
7
+ errorCode?: string;
8
+ errorType?: string;
9
+ details?: any;
10
+ }>;
@@ -2,8 +2,16 @@
2
2
  // sdk-triggerx/src/api/getUserData.ts
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.getUserData = void 0;
5
+ const errors_1 = require("../utils/errors");
5
6
  const getUserData = async (client, address) => {
6
- const apiKey = client.getApiKey(); // Assuming you have a method to get the API key
7
+ // Validate inputs
8
+ if (!address || typeof address !== 'string') {
9
+ return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('address', 'User address is required and must be a string'), 'Validation error');
10
+ }
11
+ const apiKey = client.getApiKey();
12
+ if (!apiKey) {
13
+ return (0, errors_1.createErrorResponse)(new errors_1.AuthenticationError('API key is required but not provided'), 'Authentication error');
14
+ }
7
15
  try {
8
16
  const response = await client.get(`api/users/${address}`, {
9
17
  headers: {
@@ -11,11 +19,27 @@ const getUserData = async (client, address) => {
11
19
  'X-API-KEY': apiKey,
12
20
  },
13
21
  });
14
- return response; // Assuming the response data matches the UserData interface
22
+ return { success: true, data: response };
15
23
  }
16
24
  catch (error) {
17
25
  console.error('Error fetching user data:', error);
18
- throw error; // Rethrow the error for handling in the calling function
26
+ const httpStatusCode = (0, errors_1.extractHttpStatusCode)(error);
27
+ const errorCode = (0, errors_1.determineErrorCode)(error, httpStatusCode);
28
+ if (error instanceof Error) {
29
+ if (error.message.includes('network') || error.message.includes('timeout')) {
30
+ return (0, errors_1.createErrorResponse)(new errors_1.NetworkError('Network error while fetching user data', { originalError: error, address }, httpStatusCode), 'Network error');
31
+ }
32
+ else if (error.message.includes('404') || error.message.includes('not found')) {
33
+ return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('address', 'User not found', { originalError: error, address }, 404), 'Validation error');
34
+ }
35
+ else if (error.message.includes('401') || error.message.includes('unauthorized')) {
36
+ return (0, errors_1.createErrorResponse)(new errors_1.AuthenticationError('Unauthorized access to user data', { originalError: error, address }, 401), 'Authentication error');
37
+ }
38
+ else if (error.message.includes('API') || error.message.includes('response')) {
39
+ return (0, errors_1.createErrorResponse)(new errors_1.ApiError('API error while fetching user data', { originalError: error, address }, httpStatusCode), 'API error');
40
+ }
41
+ }
42
+ return (0, errors_1.createErrorResponse)(error, 'Failed to fetch user data');
19
43
  }
20
44
  };
21
45
  exports.getUserData = getUserData;
@@ -1,14 +1,18 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.getJobData = getJobData;
4
+ const errors_1 = require("../utils/errors");
4
5
  /**
5
6
  * Fetch job data for a given API key by calling the backend endpoint.
6
7
  * @param client TriggerXClient instance
7
8
  * @returns JobResponse containing job data or error
8
9
  */
9
10
  async function getJobData(client) {
11
+ const apiKey = client.getApiKey();
12
+ if (!apiKey) {
13
+ return (0, errors_1.createErrorResponse)(new errors_1.AuthenticationError('API key is required but not provided'), 'Authentication error');
14
+ }
10
15
  try {
11
- const apiKey = client.getApiKey(); // Assuming you have a method to get the API key
12
16
  const data = await client.get('/api/jobs/by-apikey', {
13
17
  headers: {
14
18
  'Content-Type': 'application/json',
@@ -18,6 +22,20 @@ async function getJobData(client) {
18
22
  return { success: true, jobs: data.jobs };
19
23
  }
20
24
  catch (error) {
21
- return { success: false, error: error.message, };
25
+ console.error('Error fetching job data:', error);
26
+ const httpStatusCode = (0, errors_1.extractHttpStatusCode)(error);
27
+ const errorCode = (0, errors_1.determineErrorCode)(error, httpStatusCode);
28
+ if (error instanceof Error) {
29
+ if (error.message.includes('network') || error.message.includes('timeout')) {
30
+ return (0, errors_1.createErrorResponse)(new errors_1.NetworkError('Network error while fetching job data', { originalError: error }, httpStatusCode), 'Network error');
31
+ }
32
+ else if (error.message.includes('401') || error.message.includes('unauthorized')) {
33
+ return (0, errors_1.createErrorResponse)(new errors_1.AuthenticationError('Unauthorized access to job data', { originalError: error }, 401), 'Authentication error');
34
+ }
35
+ else if (error.message.includes('API') || error.message.includes('response')) {
36
+ return (0, errors_1.createErrorResponse)(new errors_1.ApiError('API error while fetching job data', { originalError: error }, httpStatusCode), 'API error');
37
+ }
38
+ }
39
+ return (0, errors_1.createErrorResponse)(error, 'Failed to fetch job data');
22
40
  }
23
41
  }