sdk-triggerx 0.1.15 → 0.1.17

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.
@@ -1,2 +1,3 @@
1
1
  import { TriggerXClient } from '../client';
2
- export declare const deleteJob: (client: TriggerXClient, jobId: string) => Promise<void>;
2
+ import { Signer } from 'ethers';
3
+ export declare const deleteJob: (client: TriggerXClient, jobId: string, signer: Signer, chainId: string) => Promise<void>;
@@ -1,16 +1,38 @@
1
1
  "use strict";
2
2
  // sdk-triggerx/src/api/deleteJob.ts
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
3
6
  Object.defineProperty(exports, "__esModule", { value: true });
4
7
  exports.deleteJob = void 0;
5
- const deleteJob = async (client, jobId) => {
8
+ const JobRegistry_json_1 = __importDefault(require("../contracts/abi/JobRegistry.json"));
9
+ const JobRegistry_1 = require("../contracts/JobRegistry");
10
+ const config_1 = require("../config");
11
+ const deleteJob = async (client, jobId, signer, chainId) => {
6
12
  const apiKey = client.getApiKey(); // Assuming you have a method to get the API key
13
+ const { jobRegistry: jobRegistryAddress } = config_1.CONTRACT_ADDRESSES_BY_CHAIN[chainId];
14
+ if (!jobRegistryAddress) {
15
+ throw new Error(`No contract address found for chain ID: ${chainId}`);
16
+ }
7
17
  try {
18
+ console.log(`Deleting job ${jobId} on chain ${chainId}...`);
19
+ // First delete on-chain
20
+ await (0, JobRegistry_1.deleteJobOnChain)({
21
+ jobId,
22
+ contractAddress: jobRegistryAddress,
23
+ abi: JobRegistry_json_1.default,
24
+ signer,
25
+ });
26
+ console.log('On-chain deletion successful, updating API...');
27
+ // Then update the API
8
28
  await client.put(`api/jobs/delete/${jobId}`, {}, {
9
29
  headers: {
10
30
  'Content-Type': 'application/json',
11
31
  'X-API-KEY': apiKey,
12
32
  },
33
+ timeout: 30000, // 30 second timeout
13
34
  });
35
+ console.log('API update successful');
14
36
  }
15
37
  catch (error) {
16
38
  console.error('Error deleting job:', error);
package/dist/api/jobs.js CHANGED
@@ -13,6 +13,10 @@ const JobRegistry_json_1 = __importDefault(require("../contracts/abi/JobRegistry
13
13
  const topupTg_1 = require("./topupTg");
14
14
  const checkTgBalance_1 = require("./checkTgBalance");
15
15
  const config_1 = require("../config");
16
+ const validation_1 = require("../utils/validation");
17
+ const errors_1 = require("../utils/errors");
18
+ const SafeWallet_1 = require("../contracts/safe/SafeWallet");
19
+ const SafeFactory_1 = require("../contracts/safe/SafeFactory");
16
20
  const JOB_ID = '300949528249665590178224313442040528409305273634097553067152835846309150732';
17
21
  const DYNAMIC_ARGS_URL = 'https://teal-random-koala-993.mypinata.cloud/ipfs/bafkreif426p7t7takzhw3g6we2h6wsvf27p5jxj3gaiynqf22p3jvhx4la';
18
22
  function toCreateJobDataFromTime(input, balances, userAddress, jobCostPrediction) {
@@ -120,13 +124,25 @@ function encodeJobType4or6Data(recurringJob, ipfsHash) {
120
124
  */
121
125
  async function createJob(client, params) {
122
126
  let { jobInput, signer, encodedData } = params;
127
+ // 0. Validate user input thoroughly before proceeding
128
+ try {
129
+ const argValue = jobInput.argType;
130
+ (0, validation_1.validateJobInput)(jobInput, argValue);
131
+ console.log('Job input validated successfully');
132
+ }
133
+ catch (err) {
134
+ if (err instanceof errors_1.ValidationError) {
135
+ return { success: false, error: `${err.field}: ${err.message}` };
136
+ }
137
+ return { success: false, error: err.message };
138
+ }
123
139
  // Use the API key from the client instance
124
140
  const apiKey = client.getApiKey();
125
141
  const userAddress = await signer.getAddress();
126
142
  // Resolve chain-specific addresses
127
143
  const network = await signer.provider?.getNetwork();
128
144
  const chainIdStr = network?.chainId ? network.chainId.toString() : undefined;
129
- const { jobRegistry } = (0, config_1.getChainAddresses)(chainIdStr);
145
+ const { jobRegistry, safeModule, safeFactory } = (0, config_1.getChainAddresses)(chainIdStr);
130
146
  const JOB_REGISTRY_ADDRESS = jobRegistry;
131
147
  if (!JOB_REGISTRY_ADDRESS) {
132
148
  return { success: false, error: 'JobRegistry address not configured for this chain. Update config mapping.' };
@@ -262,6 +278,46 @@ async function createJob(client, params) {
262
278
  const etherBalance = tokenBalanceWei / 1000n;
263
279
  // Patch jobInput with job_cost_prediction for downstream usage
264
280
  jobInput.jobCostPrediction = job_cost_prediction;
281
+ // --- Safe wallet integration (override target to module when selected) ---
282
+ const walletMode = jobInput.walletMode;
283
+ if (walletMode === 'safe') {
284
+ if (!safeModule) {
285
+ return { success: false, error: 'Safe Module address not configured for this chain.' };
286
+ }
287
+ // Enforce dynamic parameters coming from IPFS only
288
+ const dynUrl = jobInput.dynamicArgumentsScriptUrl;
289
+ if (!dynUrl) {
290
+ return { success: false, error: 'Safe jobs require dynamicArgumentsScriptUrl (IPFS) for parameters.' };
291
+ }
292
+ // Resolve or create Safe wallet
293
+ const providedSafeAddress = jobInput.safeAddress;
294
+ let safeAddressToUse = providedSafeAddress;
295
+ if (!safeAddressToUse) {
296
+ if (!safeFactory) {
297
+ return { success: false, error: 'Safe Factory address not configured for this chain.' };
298
+ }
299
+ safeAddressToUse = await (0, SafeFactory_1.createSafeWalletForUser)(safeFactory, signer, userAddress);
300
+ }
301
+ // Validate Safe has single owner and owner matches signer
302
+ await (0, SafeWallet_1.ensureSingleOwnerAndMatchSigner)(safeAddressToUse, signer.provider, userAddress);
303
+ // Ensure module is enabled on Safe
304
+ await (0, SafeWallet_1.enableSafeModule)(safeAddressToUse, signer, safeModule);
305
+ // Override target for job to Safe Module and function to execJobFromHub
306
+ targetContractAddress = safeModule; // ensure target contract is SAFE_MODULE_ADDRESS only
307
+ jobInput.targetContractAddress = safeModule;
308
+ jobInput.targetFunction = 'execJobFromHub(address,address,uint256,bytes,uint8)';
309
+ jobInput.abi = JSON.stringify([
310
+ { "type": "function", "name": "execJobFromHub", "stateMutability": "nonpayable", "inputs": [
311
+ { "name": "safeAddress", "type": "address" },
312
+ { "name": "actionTarget", "type": "address" },
313
+ { "name": "actionValue", "type": "uint256" },
314
+ { "name": "actionData", "type": "bytes" },
315
+ { "name": "operation", "type": "uint8" }
316
+ ], "outputs": [{ "type": "bool", "name": "success" }] }
317
+ ]);
318
+ // Note: Parameters will be dynamically provided via IPFS script; do not set static arguments here
319
+ jobInput.arguments = undefined;
320
+ }
265
321
  const jobId = await (0, JobRegistry_1.createJobOnChain)({
266
322
  jobTitle: jobTitle,
267
323
  jobType,
package/dist/client.js CHANGED
@@ -12,6 +12,7 @@ class TriggerXClient {
12
12
  this.client = axios_1.default.create({
13
13
  baseURL: 'https://data.triggerx.network', //'http://localhost:9002', //'https://data.triggerx.network',
14
14
  headers: { 'Authorization': `Bearer ${this.apiKey}` }, // Set the API key here
15
+ timeout: 30000, // 30 second timeout
15
16
  ...config,
16
17
  });
17
18
  }
package/dist/config.d.ts CHANGED
@@ -5,9 +5,15 @@ export interface SDKConfig {
5
5
  export declare const CONTRACT_ADDRESSES_BY_CHAIN: Record<string, {
6
6
  gasRegistry: string;
7
7
  jobRegistry: string;
8
+ safeFactory?: string;
9
+ safeModule?: string;
10
+ safeSingleton?: string;
8
11
  }>;
9
12
  export declare function getConfig(): SDKConfig;
10
13
  export declare function getChainAddresses(chainId: string | number | undefined): {
11
14
  gasRegistry: string;
12
15
  jobRegistry: string;
16
+ safeFactory: string | undefined;
17
+ safeModule: string | undefined;
18
+ safeSingleton: string | undefined;
13
19
  };
package/dist/config.js CHANGED
@@ -8,12 +8,32 @@ exports.getChainAddresses = getChainAddresses;
8
8
  // Contract addresses per chain
9
9
  // Keyed by chainId as string to avoid bigint conversions throughout the SDK
10
10
  exports.CONTRACT_ADDRESSES_BY_CHAIN = {
11
- // OP Sepolia? (used in examples) 11155420 - preserves previous defaults
11
+ // TESTNET CONFIGURATIONS
12
+ // OP Sepolia (11155420) - Optimism Sepolia Testnet
12
13
  '11155420': {
13
- gasRegistry: '0x204F9278D6BB7714D7A40842423dFd5A27cC1b88',
14
+ gasRegistry: '0x664CB20BCEEc9416D290AC820e5446e61a5c75e4',
14
15
  jobRegistry: '0x476ACc7949a95e31144cC84b8F6BC7abF0967E4b',
15
16
  },
16
- // Arbitrum One (42161)
17
+ // Ethereum Sepolia (11155111) - Ethereum Sepolia Testnet
18
+ '11155111': {
19
+ gasRegistry: '0x664CB20BCEEc9416D290AC820e5446e61a5c75e4',
20
+ jobRegistry: '0x476ACc7949a95e31144cC84b8F6BC7abF0967E4b',
21
+ },
22
+ // Arbitrum Sepolia (421614) - Arbitrum Sepolia Testnet
23
+ '421614': {
24
+ gasRegistry: '0x664CB20BCEEc9416D290AC820e5446e61a5c75e4',
25
+ jobRegistry: '0x476ACc7949a95e31144cC84b8F6BC7abF0967E4b',
26
+ safeFactory: '0x383D4a61D0B069D02cA2Db5A82003b9561d56e19',
27
+ safeModule: '0xca3a0c43Ac9E4FcB76C774F330fD31D4098EEacD',
28
+ // safeSingleton can be provided per deployment (Safe or SafeL2)
29
+ },
30
+ // Base Sepolia (84532) - Base Sepolia Testnet
31
+ '84532': {
32
+ gasRegistry: '0x664CB20BCEEc9416D290AC820e5446e61a5c75e4',
33
+ jobRegistry: '0x476ACc7949a95e31144cC84b8F6BC7abF0967E4b',
34
+ },
35
+ // MAINNET CONFIGURATIONS
36
+ // Arbitrum One (42161) - Mainnet
17
37
  '42161': {
18
38
  gasRegistry: '0x93dDB2307F3Af5df85F361E5Cddd898Acd3d132d',
19
39
  jobRegistry: '0xAf1189aFd1F1880F09AeC3Cbc32cf415c735C710',
@@ -32,5 +52,8 @@ function getChainAddresses(chainId) {
32
52
  return {
33
53
  gasRegistry: mapped ? mapped.gasRegistry : '',
34
54
  jobRegistry: mapped ? mapped.jobRegistry : '',
55
+ safeFactory: mapped ? mapped.safeFactory : '',
56
+ safeModule: mapped ? mapped.safeModule : '',
57
+ safeSingleton: mapped ? mapped.safeSingleton : '',
35
58
  };
36
59
  }
@@ -9,4 +9,11 @@ export interface CreateJobOnChainParams {
9
9
  abi: any;
10
10
  signer: Signer;
11
11
  }
12
+ export interface DeleteJobOnChainParams {
13
+ jobId: string;
14
+ contractAddress: string;
15
+ abi: any;
16
+ signer: Signer;
17
+ }
12
18
  export declare function createJobOnChain({ jobTitle, jobType, timeFrame, targetContractAddress, encodedData, contractAddress, abi, signer, }: CreateJobOnChainParams): Promise<string>;
19
+ export declare function deleteJobOnChain({ jobId, contractAddress, abi, signer, }: DeleteJobOnChainParams): Promise<void>;
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createJobOnChain = createJobOnChain;
4
+ exports.deleteJobOnChain = deleteJobOnChain;
4
5
  const ethers_1 = require("ethers");
5
6
  async function createJobOnChain({ jobTitle, jobType, timeFrame, targetContractAddress, encodedData, contractAddress, abi, signer, }) {
6
7
  const contract = new ethers_1.ethers.Contract(contractAddress, abi, signer);
@@ -22,3 +23,8 @@ async function createJobOnChain({ jobTitle, jobType, timeFrame, targetContractAd
22
23
  }
23
24
  throw new Error('Job ID not found in contract events');
24
25
  }
26
+ async function deleteJobOnChain({ jobId, contractAddress, abi, signer, }) {
27
+ const contract = new ethers_1.ethers.Contract(contractAddress, abi.abi, signer);
28
+ const tx = await contract.deleteJob(jobId);
29
+ await tx.wait();
30
+ }
@@ -1 +1,4 @@
1
+ export * from './JobRegistry';
2
+ export * from './safe/SafeFactory';
3
+ export * from './safe/SafeWallet';
1
4
  export * from './TriggerXContract';
@@ -14,4 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./JobRegistry"), exports);
18
+ // Safe-related helpers
19
+ __exportStar(require("./safe/SafeFactory"), exports);
20
+ __exportStar(require("./safe/SafeWallet"), exports);
17
21
  __exportStar(require("./TriggerXContract"), exports);
@@ -0,0 +1,3 @@
1
+ import { Signer } from 'ethers';
2
+ export declare const TRIGGERX_SAFE_FACTORY_ABI: string[];
3
+ export declare function createSafeWalletForUser(factoryAddress: string, signer: Signer, user: string): Promise<string>;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TRIGGERX_SAFE_FACTORY_ABI = void 0;
4
+ exports.createSafeWalletForUser = createSafeWalletForUser;
5
+ const ethers_1 = require("ethers");
6
+ // Minimal ABI for TriggerXSafeFactory
7
+ exports.TRIGGERX_SAFE_FACTORY_ABI = [
8
+ "function createSafeWallet(address user) returns (address)",
9
+ "function latestSafeWallet(address user) view returns (address)",
10
+ "function getSafeWallets(address user) view returns (address[])",
11
+ "function predictSafeAddress(address user) view returns (address)",
12
+ "event SafeWalletCreated(address indexed user, address indexed safeWallet, uint256 saltNonce)"
13
+ ];
14
+ async function createSafeWalletForUser(factoryAddress, signer, user) {
15
+ const factory = new ethers_1.ethers.Contract(factoryAddress, exports.TRIGGERX_SAFE_FACTORY_ABI, signer);
16
+ const tx = await factory.createSafeWallet(user);
17
+ const receipt = await tx.wait();
18
+ // Try to fetch from event; fallback to latestSafeWallet
19
+ const evt = receipt.logs
20
+ .map((l) => {
21
+ try {
22
+ return factory.interface.parseLog(l);
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ })
28
+ .find((e) => e && e.name === 'SafeWalletCreated');
29
+ if (evt && evt.args && evt.args.safeWallet) {
30
+ return evt.args.safeWallet;
31
+ }
32
+ return await factory.latestSafeWallet(user);
33
+ }
@@ -0,0 +1,6 @@
1
+ import { ethers, Signer } from 'ethers';
2
+ export declare const SAFE_ABI: string[];
3
+ export declare const SAFE_TX_TYPEHASH: string;
4
+ export declare function isSafeModuleEnabled(safeAddress: string, provider: ethers.Provider, moduleAddress: string): Promise<boolean>;
5
+ export declare function ensureSingleOwnerAndMatchSigner(safeAddress: string, provider: ethers.Provider, signerAddress: string): Promise<void>;
6
+ export declare function enableSafeModule(safeAddress: string, signer: Signer, moduleAddress: string): Promise<void>;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SAFE_TX_TYPEHASH = exports.SAFE_ABI = void 0;
4
+ exports.isSafeModuleEnabled = isSafeModuleEnabled;
5
+ exports.ensureSingleOwnerAndMatchSigner = ensureSingleOwnerAndMatchSigner;
6
+ exports.enableSafeModule = enableSafeModule;
7
+ const ethers_1 = require("ethers");
8
+ // Minimal Safe ABI fragments used in this SDK
9
+ exports.SAFE_ABI = [
10
+ // module checks
11
+ "function isModuleEnabled(address module) view returns (bool)",
12
+ // EIP-712 domain separator for Safe
13
+ "function domainSeparator() view returns (bytes32)",
14
+ // Safe nonce
15
+ "function nonce() view returns (uint256)",
16
+ // Exec transaction (self-call to enable module)
17
+ "function execTransaction(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,bytes signatures) returns (bool)",
18
+ // Owners and threshold, to validate single signer safes
19
+ "function getOwners() view returns (address[])",
20
+ "function getThreshold() view returns (uint256)",
21
+ ];
22
+ // Safe EIP-712 typehash for transactions
23
+ exports.SAFE_TX_TYPEHASH = ethers_1.ethers.id("SafeTx(address to,uint256 value,bytes data,uint8 operation,uint256 safeTxGas,uint256 baseGas,uint256 gasPrice,address gasToken,address refundReceiver,uint256 _nonce)");
24
+ async function isSafeModuleEnabled(safeAddress, provider, moduleAddress) {
25
+ const safe = new ethers_1.ethers.Contract(safeAddress, exports.SAFE_ABI, provider);
26
+ return await safe.isModuleEnabled(moduleAddress);
27
+ }
28
+ async function ensureSingleOwnerAndMatchSigner(safeAddress, provider, signerAddress) {
29
+ const safe = new ethers_1.ethers.Contract(safeAddress, exports.SAFE_ABI, provider);
30
+ const [owners, threshold] = await Promise.all([
31
+ safe.getOwners(),
32
+ safe.getThreshold(),
33
+ ]);
34
+ if (Number(threshold) !== 1) {
35
+ throw new Error('Safe wallet must have threshold 1');
36
+ }
37
+ // Check if signerAddress matches any of the Safe owners
38
+ const normalizedSigner = signerAddress.toLowerCase();
39
+ const normalizedOwners = owners.map((owner) => owner.toLowerCase());
40
+ if (!normalizedOwners.includes(normalizedSigner)) {
41
+ throw new Error('Signer is not an owner of the Safe wallet');
42
+ }
43
+ if (owners[0].toLowerCase() !== signerAddress.toLowerCase()) {
44
+ throw new Error('Signer must be the sole owner of the Safe wallet');
45
+ }
46
+ }
47
+ // Enable provided module on the Safe by crafting and signing a Safe execTransaction
48
+ async function enableSafeModule(safeAddress, signer, moduleAddress) {
49
+ const provider = signer.provider;
50
+ if (!provider)
51
+ throw new Error('Signer provider is required');
52
+ const safeProxy = new ethers_1.ethers.Contract(safeAddress, exports.SAFE_ABI, provider);
53
+ // If already enabled, exit early
54
+ const already = await safeProxy.isModuleEnabled(moduleAddress);
55
+ if (already)
56
+ return;
57
+ const safeNonce = await safeProxy.nonce();
58
+ const iface = new ethers_1.ethers.Interface(exports.SAFE_ABI);
59
+ const data = iface.encodeFunctionData('enableModule', [moduleAddress]);
60
+ const to = safeAddress;
61
+ const value = 0n;
62
+ const operation = 0; // CALL
63
+ const safeTxGas = 0n;
64
+ const baseGas = 0n;
65
+ const gasPrice = 0n;
66
+ const gasToken = ethers_1.ethers.ZeroAddress;
67
+ const refundReceiver = ethers_1.ethers.ZeroAddress;
68
+ // Calculate Safe transaction hash per EIP-712
69
+ const safeTxHash = ethers_1.ethers.keccak256(ethers_1.ethers.AbiCoder.defaultAbiCoder().encode([
70
+ 'bytes32', 'address', 'uint256', 'bytes32', 'uint8',
71
+ 'uint256', 'uint256', 'uint256', 'address', 'address', 'uint256'
72
+ ], [
73
+ exports.SAFE_TX_TYPEHASH, to, value, ethers_1.ethers.keccak256(data), operation,
74
+ safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, safeNonce
75
+ ]));
76
+ const domainSeparator = await safeProxy.domainSeparator();
77
+ const txHash = ethers_1.ethers.keccak256(ethers_1.ethers.solidityPacked(['bytes1', 'bytes1', 'bytes32', 'bytes32'], ['0x19', '0x01', domainSeparator, safeTxHash]));
78
+ const rawSignature = await signer.signMessage(ethers_1.ethers.getBytes(txHash));
79
+ const sig = ethers_1.ethers.Signature.from(rawSignature);
80
+ const adjustedV = sig.v + 4; // EthSign type
81
+ const signature = ethers_1.ethers.concat([sig.r, sig.s, ethers_1.ethers.toBeHex(adjustedV, 1)]);
82
+ const safeWithSigner = new ethers_1.ethers.Contract(safeAddress, exports.SAFE_ABI, signer);
83
+ const tx = await safeWithSigner.execTransaction(to, value, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, signature);
84
+ await tx.wait();
85
+ const check = await safeProxy.isModuleEnabled(moduleAddress);
86
+ if (!check) {
87
+ throw new Error('Module verification failed');
88
+ }
89
+ }
package/dist/index.d.ts CHANGED
@@ -2,6 +2,11 @@ export * from './client';
2
2
  export * from './config';
3
3
  export * from './types';
4
4
  export * from './api/jobs';
5
- export { createJobOnChain } from './contracts/JobRegistry';
5
+ export * from './api/getjob';
6
+ export * from './api/getUserData';
7
+ export * from './api/checkTgBalance';
8
+ export * from './api/topupTg';
9
+ export * from './api/deleteJob';
10
+ export * from './api/getJobDataById';
6
11
  export * from './contracts';
7
12
  export * from './utils/errors';
package/dist/index.js CHANGED
@@ -14,12 +14,16 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.createJobOnChain = void 0;
18
17
  __exportStar(require("./client"), exports);
19
18
  __exportStar(require("./config"), exports);
20
19
  __exportStar(require("./types"), exports);
21
20
  __exportStar(require("./api/jobs"), exports);
22
- var JobRegistry_1 = require("./contracts/JobRegistry");
23
- Object.defineProperty(exports, "createJobOnChain", { enumerable: true, get: function () { return JobRegistry_1.createJobOnChain; } });
21
+ __exportStar(require("./api/getjob"), exports);
22
+ __exportStar(require("./api/getUserData"), exports);
23
+ __exportStar(require("./api/checkTgBalance"), exports);
24
+ __exportStar(require("./api/topupTg"), exports);
25
+ __exportStar(require("./api/deleteJob"), exports);
26
+ __exportStar(require("./api/getJobDataById"), exports);
27
+ // export { createJobOnChain } from './contracts/JobRegistry';
24
28
  __exportStar(require("./contracts"), exports);
25
29
  __exportStar(require("./utils/errors"), exports);
package/dist/types.d.ts CHANGED
@@ -17,6 +17,7 @@ export declare enum ArgType {
17
17
  Static = "static",
18
18
  Dynamic = "dynamic"
19
19
  }
20
+ export type WalletMode = 'regular' | 'safe';
20
21
  export type CreateJobInput = (TimeBasedJobInput & {
21
22
  jobType: JobType.Time;
22
23
  argType: ArgType.Static | ArgType.Dynamic;
@@ -43,6 +44,8 @@ export interface TimeBasedJobInput {
43
44
  arguments?: string[];
44
45
  dynamicArgumentsScriptUrl?: string;
45
46
  autotopupTG?: boolean;
47
+ walletMode?: WalletMode;
48
+ safeAddress?: string;
46
49
  }
47
50
  export interface EventBasedJobInput {
48
51
  jobTitle: string;
@@ -60,6 +63,8 @@ export interface EventBasedJobInput {
60
63
  arguments?: string[];
61
64
  dynamicArgumentsScriptUrl?: string;
62
65
  autotopupTG?: boolean;
66
+ walletMode?: WalletMode;
67
+ safeAddress?: string;
63
68
  }
64
69
  export interface ConditionBasedJobInput {
65
70
  jobTitle: string;
@@ -79,6 +84,8 @@ export interface ConditionBasedJobInput {
79
84
  arguments?: string[];
80
85
  dynamicArgumentsScriptUrl?: string;
81
86
  autotopupTG?: boolean;
87
+ walletMode?: WalletMode;
88
+ safeAddress?: string;
82
89
  }
83
90
  export interface CreateJobData {
84
91
  job_id: string;
@@ -2,3 +2,7 @@ export declare class TriggerXError extends Error {
2
2
  constructor(message: string);
3
3
  }
4
4
  export declare function wrapError(error: unknown): TriggerXError;
5
+ export declare class ValidationError extends Error {
6
+ field: string;
7
+ constructor(field: string, message: string);
8
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TriggerXError = void 0;
3
+ exports.ValidationError = exports.TriggerXError = void 0;
4
4
  exports.wrapError = wrapError;
5
5
  class TriggerXError extends Error {
6
6
  constructor(message) {
@@ -15,3 +15,11 @@ function wrapError(error) {
15
15
  }
16
16
  return new TriggerXError('Unknown error');
17
17
  }
18
+ class ValidationError extends Error {
19
+ constructor(field, message) {
20
+ super(message);
21
+ this.name = 'ValidationError';
22
+ this.field = field;
23
+ }
24
+ }
25
+ exports.ValidationError = ValidationError;
@@ -0,0 +1,5 @@
1
+ import { TimeBasedJobInput, EventBasedJobInput, ConditionBasedJobInput } from '../types';
2
+ export declare function validateTimeBasedJobInput(input: TimeBasedJobInput, argType: 'static' | 'dynamic' | 1 | 2 | undefined): void;
3
+ export declare function validateEventBasedJobInput(input: EventBasedJobInput, argType: 'static' | 'dynamic' | 1 | 2 | undefined): void;
4
+ export declare function validateConditionBasedJobInput(input: ConditionBasedJobInput, argType: 'static' | 'dynamic' | 1 | 2 | undefined): void;
5
+ export declare function validateJobInput(jobInput: TimeBasedJobInput | EventBasedJobInput | ConditionBasedJobInput, argType?: 'static' | 'dynamic' | 1 | 2): void;
@@ -0,0 +1,200 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateTimeBasedJobInput = validateTimeBasedJobInput;
4
+ exports.validateEventBasedJobInput = validateEventBasedJobInput;
5
+ exports.validateConditionBasedJobInput = validateConditionBasedJobInput;
6
+ exports.validateJobInput = validateJobInput;
7
+ const ethers_1 = require("ethers");
8
+ const errors_1 = require("./errors");
9
+ function isNonEmptyString(value) {
10
+ return typeof value === 'string' && value.trim().length > 0;
11
+ }
12
+ function isValidUrl(value) {
13
+ try {
14
+ // eslint-disable-next-line no-new
15
+ new URL(value);
16
+ return true;
17
+ }
18
+ catch {
19
+ return false;
20
+ }
21
+ }
22
+ function buildFunctionSignature(name, inputs) {
23
+ const types = (inputs || []).map((i) => i.type).join(',');
24
+ return `${name}(${types})`;
25
+ }
26
+ function parseAbi(abiString) {
27
+ if (!isNonEmptyString(abiString))
28
+ return null;
29
+ try {
30
+ const parsed = JSON.parse(abiString);
31
+ return Array.isArray(parsed) ? parsed : null;
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ }
37
+ function validateContractBasics(address, abi, targetFunction, fieldPrefix = 'contract') {
38
+ if (!isNonEmptyString(address)) {
39
+ throw new errors_1.ValidationError(`${fieldPrefix}Address`, 'Contract address is required.');
40
+ }
41
+ if (!ethers_1.ethers.isAddress(address)) {
42
+ throw new errors_1.ValidationError(`${fieldPrefix}Address`, 'Invalid contract address.');
43
+ }
44
+ if (!isNonEmptyString(abi)) {
45
+ throw new errors_1.ValidationError(`${fieldPrefix}ABI`, 'Contract ABI must be provided.');
46
+ }
47
+ if (!isNonEmptyString(targetFunction)) {
48
+ throw new errors_1.ValidationError(`${fieldPrefix}Target`, 'Target function must be selected.');
49
+ }
50
+ }
51
+ function validateStaticArguments(abiString, targetFunction, args, fieldPrefix = 'contract') {
52
+ const abi = parseAbi(abiString);
53
+ if (!abi) {
54
+ // If ABI is invalid JSON, surface a clear error
55
+ throw new errors_1.ValidationError(`${fieldPrefix}ABI`, 'Contract ABI must be valid JSON array.');
56
+ }
57
+ const fnItem = abi.find((item) => item && item.type === 'function' && buildFunctionSignature(item.name, item.inputs) === targetFunction);
58
+ if (!fnItem)
59
+ return; // If we cannot find it, skip strict arg validation
60
+ const inputs = Array.isArray(fnItem.inputs) ? fnItem.inputs : [];
61
+ if (inputs.length === 0)
62
+ return;
63
+ if (!args || args.length < inputs.length) {
64
+ throw new errors_1.ValidationError(`${fieldPrefix}Args`, 'All function arguments are required for static argument type.');
65
+ }
66
+ const missing = inputs.findIndex((_, idx) => !isNonEmptyString(args[idx]));
67
+ if (missing !== -1) {
68
+ throw new errors_1.ValidationError(`${fieldPrefix}Args`, 'All function arguments are required for static argument type.');
69
+ }
70
+ }
71
+ function validateTimeBasedJobInput(input, argType) {
72
+ if (!isNonEmptyString(input.jobTitle)) {
73
+ throw new errors_1.ValidationError('jobTitle', 'Job title is required.');
74
+ }
75
+ if (!input.timeFrame || input.timeFrame <= 0) {
76
+ throw new errors_1.ValidationError('timeframe', 'Timeframe must be a positive number of seconds.');
77
+ }
78
+ if (!isNonEmptyString(input.timezone)) {
79
+ throw new errors_1.ValidationError('timezone', 'Timezone is required.');
80
+ }
81
+ if (!isNonEmptyString(input.chainId)) {
82
+ throw new errors_1.ValidationError('chainId', 'Chain ID is required.');
83
+ }
84
+ // Schedule-specific required fields
85
+ if (input.scheduleType === 'interval') {
86
+ if (input.timeInterval === undefined || input.timeInterval === null || input.timeInterval <= 0) {
87
+ throw new errors_1.ValidationError('timeInterval', 'timeInterval is required and must be > 0 when scheduleType is interval.');
88
+ }
89
+ if (input.timeInterval > input.timeFrame) {
90
+ throw new errors_1.ValidationError('timeInterval', 'Time interval cannot exceed the timeframe.');
91
+ }
92
+ }
93
+ else if (input.scheduleType === 'cron') {
94
+ if (!isNonEmptyString(input.cronExpression)) {
95
+ throw new errors_1.ValidationError('cronExpression', 'cronExpression is required when scheduleType is cron.');
96
+ }
97
+ }
98
+ else if (input.scheduleType === 'specific') {
99
+ if (!isNonEmptyString(input.specificSchedule)) {
100
+ throw new errors_1.ValidationError('specificSchedule', 'specificSchedule is required when scheduleType is specific.');
101
+ }
102
+ }
103
+ else {
104
+ throw new errors_1.ValidationError('scheduleType', 'scheduleType must be one of interval | cron | specific.');
105
+ }
106
+ validateContractBasics(input.targetContractAddress, input.abi, input.targetFunction, 'contract');
107
+ // Arg type checks
108
+ const isDynamic = argType === 'dynamic' || argType === 2;
109
+ if (isDynamic) {
110
+ if (!isNonEmptyString(input.dynamicArgumentsScriptUrl) || !isValidUrl(input.dynamicArgumentsScriptUrl)) {
111
+ throw new errors_1.ValidationError('contractIpfs', 'IPFS Code URL is required and must be valid for dynamic argument type.');
112
+ }
113
+ }
114
+ else {
115
+ validateStaticArguments(input.abi, input.targetFunction, input.arguments, 'contract');
116
+ }
117
+ }
118
+ function validateEventBasedJobInput(input, argType) {
119
+ if (!isNonEmptyString(input.jobTitle)) {
120
+ throw new errors_1.ValidationError('jobTitle', 'Job title is required.');
121
+ }
122
+ if (!input.timeFrame || input.timeFrame <= 0) {
123
+ throw new errors_1.ValidationError('timeframe', 'Timeframe must be a positive number of seconds.');
124
+ }
125
+ if (!isNonEmptyString(input.timezone)) {
126
+ throw new errors_1.ValidationError('timezone', 'Timezone is required.');
127
+ }
128
+ if (!isNonEmptyString(input.chainId)) {
129
+ throw new errors_1.ValidationError('chainId', 'Chain ID is required.');
130
+ }
131
+ if (!isNonEmptyString(input.triggerChainId)) {
132
+ throw new errors_1.ValidationError('triggerChainId', 'Trigger chain ID is required.');
133
+ }
134
+ validateContractBasics(input.triggerContractAddress, input.abi, input.triggerEvent, 'eventContract');
135
+ validateContractBasics(input.targetContractAddress, input.abi, input.targetFunction, 'contract');
136
+ const isDynamic = argType === 'dynamic' || argType === 2;
137
+ if (isDynamic) {
138
+ if (!isNonEmptyString(input.dynamicArgumentsScriptUrl) || !isValidUrl(input.dynamicArgumentsScriptUrl)) {
139
+ throw new errors_1.ValidationError('contractIpfs', 'IPFS Code URL is required and must be valid for dynamic argument type.');
140
+ }
141
+ }
142
+ else {
143
+ validateStaticArguments(input.abi, input.targetFunction, input.arguments, 'contract');
144
+ }
145
+ }
146
+ function validateConditionBasedJobInput(input, argType) {
147
+ if (!isNonEmptyString(input.jobTitle)) {
148
+ throw new errors_1.ValidationError('jobTitle', 'Job title is required.');
149
+ }
150
+ if (!input.timeFrame || input.timeFrame <= 0) {
151
+ throw new errors_1.ValidationError('timeframe', 'Timeframe must be a positive number of seconds.');
152
+ }
153
+ if (!isNonEmptyString(input.timezone)) {
154
+ throw new errors_1.ValidationError('timezone', 'Timezone is required.');
155
+ }
156
+ if (!isNonEmptyString(input.chainId)) {
157
+ throw new errors_1.ValidationError('chainId', 'Chain ID is required.');
158
+ }
159
+ // Condition fields
160
+ if (!isNonEmptyString(input.conditionType)) {
161
+ throw new errors_1.ValidationError('contractConditionType', 'Condition type is required.');
162
+ }
163
+ if (!isNonEmptyString(input.valueSourceType)) {
164
+ throw new errors_1.ValidationError('contractSourceType', 'Value source type is required.');
165
+ }
166
+ if (!isNonEmptyString(input.valueSourceUrl) || !isValidUrl(input.valueSourceUrl)) {
167
+ throw new errors_1.ValidationError('contractSourceUrl', 'Source URL is required and must be valid.');
168
+ }
169
+ if (input.conditionType === 'between') {
170
+ if (input.upperLimit === undefined || input.lowerLimit === undefined || input.upperLimit === null || input.lowerLimit === null) {
171
+ throw new errors_1.ValidationError('contractLimits', 'Both upper and lower limits are required.');
172
+ }
173
+ }
174
+ else {
175
+ if (input.upperLimit === undefined || input.upperLimit === null) {
176
+ throw new errors_1.ValidationError('contractLimits', 'Value is required.');
177
+ }
178
+ }
179
+ validateContractBasics(input.targetContractAddress, input.abi, input.targetFunction, 'contract');
180
+ const isDynamic = argType === 'dynamic' || argType === 2;
181
+ if (isDynamic) {
182
+ if (!isNonEmptyString(input.dynamicArgumentsScriptUrl) || !isValidUrl(input.dynamicArgumentsScriptUrl)) {
183
+ throw new errors_1.ValidationError('contractIpfs', 'IPFS Code URL is required and must be valid for dynamic argument type.');
184
+ }
185
+ }
186
+ else {
187
+ validateStaticArguments(input.abi, input.targetFunction, input.arguments, 'contract');
188
+ }
189
+ }
190
+ function validateJobInput(jobInput, argType) {
191
+ if ('scheduleType' in jobInput) {
192
+ validateTimeBasedJobInput(jobInput, argType);
193
+ }
194
+ else if ('triggerChainId' in jobInput) {
195
+ validateEventBasedJobInput(jobInput, argType);
196
+ }
197
+ else {
198
+ validateConditionBasedJobInput(jobInput, argType);
199
+ }
200
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sdk-triggerx",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "description": "SDK for interacting with the TriggerX backend and smart contracts.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",