sdk-triggerx 0.1.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 (105) hide show
  1. package/.eslintrc.json +16 -0
  2. package/README.md +249 -0
  3. package/dist/api/jobs.d.ts +15 -0
  4. package/dist/api/jobs.js +138 -0
  5. package/dist/api/tasks.d.ts +4 -0
  6. package/dist/api/tasks.js +13 -0
  7. package/dist/client.d.ts +7 -0
  8. package/dist/client.js +27 -0
  9. package/dist/config.d.ts +6 -0
  10. package/dist/config.js +15 -0
  11. package/dist/contracts/JobRegistry.d.ts +12 -0
  12. package/dist/contracts/JobRegistry.js +26 -0
  13. package/dist/contracts/TriggerXContract.d.ts +6 -0
  14. package/dist/contracts/TriggerXContract.js +14 -0
  15. package/dist/contracts/abi/JobRegistry.json +1554 -0
  16. package/dist/contracts/index.d.ts +1 -0
  17. package/dist/contracts/index.js +17 -0
  18. package/dist/index.d.ts +8 -0
  19. package/dist/index.js +26 -0
  20. package/dist/src/api/checkTgBalance.d.ts +2 -0
  21. package/dist/src/api/checkTgBalance.js +35 -0
  22. package/dist/src/api/deleteJob.d.ts +2 -0
  23. package/dist/src/api/deleteJob.js +20 -0
  24. package/dist/src/api/getJobDataById.d.ts +3 -0
  25. package/dist/src/api/getJobDataById.js +21 -0
  26. package/dist/src/api/getUserData.d.ts +3 -0
  27. package/dist/src/api/getUserData.js +21 -0
  28. package/dist/src/api/getjob.d.ts +8 -0
  29. package/dist/src/api/getjob.js +23 -0
  30. package/dist/src/api/jobs.d.ts +18 -0
  31. package/dist/src/api/jobs.js +270 -0
  32. package/dist/src/api/tasks.d.ts +4 -0
  33. package/dist/src/api/tasks.js +13 -0
  34. package/dist/src/api/topupTg.d.ts +2 -0
  35. package/dist/src/api/topupTg.js +19 -0
  36. package/dist/src/api/withdrawTg.d.ts +8 -0
  37. package/dist/src/api/withdrawTg.js +27 -0
  38. package/dist/src/client.d.ts +10 -0
  39. package/dist/src/client.js +37 -0
  40. package/dist/src/config.d.ts +6 -0
  41. package/dist/src/config.js +15 -0
  42. package/dist/src/contracts/JobRegistry.d.ts +12 -0
  43. package/dist/src/contracts/JobRegistry.js +24 -0
  44. package/dist/src/contracts/TriggerXContract.d.ts +6 -0
  45. package/dist/src/contracts/TriggerXContract.js +14 -0
  46. package/dist/src/contracts/abi/GasRegistry.json +607 -0
  47. package/dist/src/contracts/abi/JobRegistry.json +1554 -0
  48. package/dist/src/contracts/index.d.ts +1 -0
  49. package/dist/src/contracts/index.js +17 -0
  50. package/dist/src/index.d.ts +7 -0
  51. package/dist/src/index.js +25 -0
  52. package/dist/src/types.d.ts +246 -0
  53. package/dist/src/types.js +15 -0
  54. package/dist/src/utils/errors.d.ts +4 -0
  55. package/dist/src/utils/errors.js +17 -0
  56. package/dist/test/createJobExample.d.ts +1 -0
  57. package/dist/test/createJobExample.js +77 -0
  58. package/dist/test/deleteJob.test.d.ts +1 -0
  59. package/dist/test/deleteJob.test.js +22 -0
  60. package/dist/test/example.test.d.ts +1 -0
  61. package/dist/test/example.test.js +11 -0
  62. package/dist/test/getJobDataById.test.d.ts +1 -0
  63. package/dist/test/getJobDataById.test.js +23 -0
  64. package/dist/test/getUserData.test.d.ts +1 -0
  65. package/dist/test/getUserData.test.js +22 -0
  66. package/dist/test/getjob.test.d.ts +1 -0
  67. package/dist/test/getjob.test.js +21 -0
  68. package/dist/test/testTgFunctions.d.ts +1 -0
  69. package/dist/test/testTgFunctions.js +56 -0
  70. package/dist/types.d.ts +134 -0
  71. package/dist/types.js +15 -0
  72. package/dist/utils/errors.d.ts +4 -0
  73. package/dist/utils/errors.js +17 -0
  74. package/jest.config.js +7 -0
  75. package/package.json +32 -0
  76. package/scripts/deploy.ts +9 -0
  77. package/src/api/checkTgBalance.ts +27 -0
  78. package/src/api/deleteJob.ts +22 -0
  79. package/src/api/getJobDataById.ts +24 -0
  80. package/src/api/getUserData.ts +24 -0
  81. package/src/api/getjob.ts +26 -0
  82. package/src/api/jobs.ts +303 -0
  83. package/src/api/topupTg.ts +17 -0
  84. package/src/api/withdrawTg.ts +25 -0
  85. package/src/client.ts +38 -0
  86. package/src/config.ts +16 -0
  87. package/src/contracts/JobRegistry.ts +45 -0
  88. package/src/contracts/TriggerXContract.ts +14 -0
  89. package/src/contracts/abi/.gitkeep +1 -0
  90. package/src/contracts/abi/GasRegistry.json +607 -0
  91. package/src/contracts/abi/JobRegistry.json +1554 -0
  92. package/src/contracts/index.ts +1 -0
  93. package/src/index.ts +7 -0
  94. package/src/types.ts +262 -0
  95. package/src/utils/errors.ts +13 -0
  96. package/test/createJobExample.ts +84 -0
  97. package/test/deleteJob.test.ts +25 -0
  98. package/test/example.test.d.ts +1 -0
  99. package/test/example.test.js +11 -0
  100. package/test/example.test.ts +10 -0
  101. package/test/getJobDataById.test.ts +27 -0
  102. package/test/getUserData.test.ts +25 -0
  103. package/test/getjob.test.ts +23 -0
  104. package/test/testTgFunctions.ts +56 -0
  105. package/tsconfig.json +16 -0
@@ -0,0 +1,134 @@
1
+ export interface Task {
2
+ id: string;
3
+ name: string;
4
+ status: 'pending' | 'completed' | 'failed';
5
+ createdAt: string;
6
+ }
7
+ export interface ApiResponse<T> {
8
+ data: T;
9
+ error?: string;
10
+ }
11
+ export declare enum JobType {
12
+ Time = "time",
13
+ Event = "event",
14
+ Condition = "condition"
15
+ }
16
+ export declare enum ArgType {
17
+ Static = "static",
18
+ Dynamic = "dynamic"
19
+ }
20
+ export type CreateJobInput = (TimeBasedJobInput & {
21
+ jobType: JobType.Time;
22
+ argType: ArgType.Static | ArgType.Dynamic;
23
+ }) | (EventBasedJobInput & {
24
+ jobType: JobType.Event;
25
+ argType: ArgType.Static | ArgType.Dynamic;
26
+ }) | (ConditionBasedJobInput & {
27
+ jobType: JobType.Condition;
28
+ argType: ArgType.Static | ArgType.Dynamic;
29
+ });
30
+ export interface TimeBasedJobInput {
31
+ userAddress: string;
32
+ etherBalance: BigInt | number;
33
+ tokenBalance: BigInt | number;
34
+ jobTitle: string;
35
+ timeFrame: number;
36
+ scheduleType: 'cron' | 'specific' | 'interval';
37
+ timeInterval?: number;
38
+ cronExpression?: string;
39
+ specificSchedule?: string;
40
+ timezone: string;
41
+ recurring?: boolean;
42
+ jobCostPrediction: number;
43
+ createdChainId: string;
44
+ targetChainId: string;
45
+ targetContractAddress: string;
46
+ targetFunction: string;
47
+ abi: string;
48
+ isImua?: boolean;
49
+ arguments?: string[];
50
+ dynamicArgumentsScriptUrl?: string;
51
+ }
52
+ export interface EventBasedJobInput {
53
+ userAddress: string;
54
+ etherBalance: BigInt | number;
55
+ tokenBalance: BigInt | number;
56
+ jobTitle: string;
57
+ timeFrame: number;
58
+ triggerChainId: string;
59
+ triggerContractAddress: string;
60
+ triggerEvent: string;
61
+ timezone: string;
62
+ recurring?: boolean;
63
+ jobCostPrediction: number;
64
+ createdChainId: string;
65
+ targetChainId: string;
66
+ targetContractAddress: string;
67
+ targetFunction: string;
68
+ abi: string;
69
+ isImua?: boolean;
70
+ arguments?: string[];
71
+ dynamicArgumentsScriptUrl?: string;
72
+ }
73
+ export interface ConditionBasedJobInput {
74
+ userAddress: string;
75
+ etherBalance: BigInt | number;
76
+ tokenBalance: BigInt | number;
77
+ jobTitle: string;
78
+ timeFrame: number;
79
+ conditionType: string;
80
+ upperLimit: number;
81
+ lowerLimit: number;
82
+ valueSourceType: string;
83
+ valueSourceUrl: string;
84
+ timezone: string;
85
+ recurring?: boolean;
86
+ jobCostPrediction: number;
87
+ createdChainId: string;
88
+ targetChainId: string;
89
+ targetContractAddress: string;
90
+ targetFunction: string;
91
+ abi: string;
92
+ isImua?: boolean;
93
+ arguments?: string[];
94
+ dynamicArgumentsScriptUrl?: string;
95
+ }
96
+ export interface CreateJobData {
97
+ job_id: string;
98
+ user_address: string;
99
+ ether_balance: BigInt | number;
100
+ token_balance: BigInt | number;
101
+ job_title: string;
102
+ task_definition_id: number;
103
+ custom: boolean;
104
+ time_frame: number;
105
+ recurring: boolean;
106
+ job_cost_prediction: number;
107
+ timezone: string;
108
+ created_chain_id: string;
109
+ schedule_type?: string;
110
+ time_interval?: number;
111
+ cron_expression?: string;
112
+ specific_schedule?: string;
113
+ trigger_chain_id?: string;
114
+ trigger_contract_address?: string;
115
+ trigger_event?: string;
116
+ condition_type?: string;
117
+ upper_limit?: number;
118
+ lower_limit?: number;
119
+ value_source_type?: string;
120
+ value_source_url?: string;
121
+ target_chain_id: string;
122
+ target_contract_address: string;
123
+ target_function: string;
124
+ abi: string;
125
+ arg_type: number;
126
+ arguments?: string[];
127
+ dynamic_arguments_script_url?: string;
128
+ is_imua: boolean;
129
+ }
130
+ export interface JobResponse {
131
+ success: boolean;
132
+ data?: any;
133
+ error?: string;
134
+ }
package/dist/types.js ADDED
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ArgType = exports.JobType = void 0;
4
+ // Enums for job type and argument type
5
+ var JobType;
6
+ (function (JobType) {
7
+ JobType["Time"] = "time";
8
+ JobType["Event"] = "event";
9
+ JobType["Condition"] = "condition";
10
+ })(JobType || (exports.JobType = JobType = {}));
11
+ var ArgType;
12
+ (function (ArgType) {
13
+ ArgType["Static"] = "static";
14
+ ArgType["Dynamic"] = "dynamic";
15
+ })(ArgType || (exports.ArgType = ArgType = {}));
@@ -0,0 +1,4 @@
1
+ export declare class TriggerXError extends Error {
2
+ constructor(message: string);
3
+ }
4
+ export declare function wrapError(error: unknown): TriggerXError;
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TriggerXError = void 0;
4
+ exports.wrapError = wrapError;
5
+ class TriggerXError extends Error {
6
+ constructor(message) {
7
+ super(message);
8
+ this.name = 'TriggerXError';
9
+ }
10
+ }
11
+ exports.TriggerXError = TriggerXError;
12
+ function wrapError(error) {
13
+ if (error instanceof Error) {
14
+ return new TriggerXError(error.message);
15
+ }
16
+ return new TriggerXError('Unknown error');
17
+ }
package/jest.config.js ADDED
@@ -0,0 +1,7 @@
1
+ module.exports = {
2
+ preset: 'ts-jest',
3
+ testEnvironment: 'node',
4
+ testMatch: ['**/test/**/*.test.ts'],
5
+ moduleFileExtensions: ['ts', 'js', 'json'],
6
+ roots: ['<rootDir>/src', '<rootDir>/test'],
7
+ };
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "sdk-triggerx",
3
+ "version": "0.1.0",
4
+ "description": "SDK for interacting with the TriggerX backend and smart contracts.",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "test": "jest",
10
+ "lint": "eslint src --ext .ts"
11
+ },
12
+ "author": "",
13
+ "license": "MIT",
14
+ "directories": {
15
+ "test": "test"
16
+ },
17
+ "keywords": [],
18
+ "dependencies": {
19
+ "axios": "^1.11.0",
20
+ "dotenv": "^17.2.1",
21
+ "ethers": "^6.15.0"
22
+ },
23
+ "devDependencies": {
24
+ "@types/jest": "^30.0.0",
25
+ "@typescript-eslint/eslint-plugin": "^8.38.0",
26
+ "@typescript-eslint/parser": "^8.38.0",
27
+ "eslint": "^9.32.0",
28
+ "jest": "^30.0.5",
29
+ "ts-jest": "^29.4.0",
30
+ "typescript": "^5.8.3"
31
+ }
32
+ }
@@ -0,0 +1,9 @@
1
+ // scripts/deploy.ts
2
+ // Script for deploying/testing contracts
3
+
4
+ async function main() {
5
+ // TODO: Implement contract deployment logic
6
+ console.log('Deploy script placeholder');
7
+ }
8
+
9
+ main();
@@ -0,0 +1,27 @@
1
+ import { ethers } from 'ethers';
2
+ import gasRegistryAbi from '../contracts/abi/GasRegistry.json';
3
+
4
+ export const checkTgBalance = async (signer: ethers.Signer) => {
5
+ const gasRegistryContractAddress = process.env.GAS_REGISTRY_CONTRACT_ADDRESS as string || '0x85ea3eB894105bD7e7e2A8D34cf66C8E8163CD2a';
6
+ if (!gasRegistryContractAddress) {
7
+ throw new Error('GAS_REGISTRY_CONTRACT_ADDRESS is not set in the environment variables');
8
+ }
9
+ const contract = new ethers.Contract(gasRegistryContractAddress, gasRegistryAbi, signer);
10
+ const address = await signer.getAddress();
11
+ const balance = await contract.balances(address);
12
+ // balance is likely an array or object with ethSpent and TGbalance, both in wei
13
+ // We'll convert TGbalance from wei to ETH
14
+ // If balance is an array: [ethSpent, TGbalance]
15
+ // If balance is an object: { ethSpent, TGbalance }
16
+ let tgBalanceWei;
17
+ if (Array.isArray(balance)) {
18
+ tgBalanceWei = balance[1];
19
+ } else if (balance && balance.TGbalance !== undefined) {
20
+ tgBalanceWei = balance.TGbalance;
21
+ } else {
22
+ throw new Error('Unexpected balance format');
23
+ }
24
+ const tgBalanceEth = ethers.formatEther(tgBalanceWei);
25
+ console.log('tgBalanceEth', tgBalanceEth);
26
+ return tgBalanceEth;
27
+ };
@@ -0,0 +1,22 @@
1
+ // sdk-triggerx/src/api/deleteJob.ts
2
+
3
+ import { TriggerXClient } from '../client';
4
+
5
+ export const deleteJob = async (client: TriggerXClient, jobId: string): Promise<void> => {
6
+ const apiKey = client.getApiKey(); // Assuming you have a method to get the API key
7
+
8
+ try {
9
+ await client.put<void>(
10
+ `api/jobs/delete/${jobId}`, {},
11
+ {
12
+ headers: {
13
+ 'Content-Type': 'application/json',
14
+ 'X-API-KEY': apiKey,
15
+ },
16
+ }
17
+ );
18
+ } catch (error) {
19
+ console.error('Error deleting job:', error);
20
+ throw error; // Rethrow the error for handling in the calling function
21
+ }
22
+ };
@@ -0,0 +1,24 @@
1
+ // sdk-triggerx/src/api/getJobDataById.ts
2
+
3
+ import { TriggerXClient } from '../client';
4
+ import { JobDataAPI } from '../types';
5
+
6
+ export const getJobDataById = async (client: TriggerXClient, jobId: string): Promise<JobDataAPI> => {
7
+ const apiKey = client.getApiKey(); // Assuming you have a method to get the API key
8
+
9
+ try {
10
+ const response = await client.get<JobDataAPI>(
11
+ `/api/jobs/${jobId}`,
12
+ {
13
+ headers: {
14
+ 'Content-Type': 'application/json',
15
+ 'X-API-KEY': apiKey,
16
+ },
17
+ }
18
+ );
19
+ return response; // Assuming the response data matches the JobDataAPI interface
20
+ } catch (error) {
21
+ console.error('Error fetching job data by ID:', error);
22
+ throw error; // Rethrow the error for handling in the calling function
23
+ }
24
+ };
@@ -0,0 +1,24 @@
1
+ // sdk-triggerx/src/api/getUserData.ts
2
+
3
+ import { TriggerXClient } from '../client';
4
+ import { UserData } from '../types';
5
+
6
+ export const getUserData = async (client: TriggerXClient, address: string): Promise<UserData> => {
7
+ const apiKey = client.getApiKey(); // Assuming you have a method to get the API key
8
+
9
+ try {
10
+ const response = await client.get<UserData>(
11
+ `api/users/${address}`,
12
+ {
13
+ headers: {
14
+ 'Content-Type': 'application/json',
15
+ 'X-API-KEY': apiKey,
16
+ },
17
+ }
18
+ );
19
+ return response; // Assuming the response data matches the UserData interface
20
+ } catch (error) {
21
+ console.error('Error fetching user data:', error);
22
+ throw error; // Rethrow the error for handling in the calling function
23
+ }
24
+ };
@@ -0,0 +1,26 @@
1
+ import { TriggerXClient } from '../client';
2
+ import { JobResponseUser, JobResponseAPI } from '../types';
3
+
4
+ /**
5
+ * Fetch job data for a given API key by calling the backend endpoint.
6
+ * @param client TriggerXClient instance
7
+ * @returns JobResponse containing job data or error
8
+ */
9
+ export async function getJobData(client: TriggerXClient): Promise<JobResponseUser> {
10
+ try {
11
+
12
+ const apiKey = client.getApiKey(); // Assuming you have a method to get the API key
13
+ const data = await client.get<JobResponseUser>(
14
+ '/api/jobs/by-apikey',
15
+ {
16
+ headers: {
17
+ 'Content-Type': 'application/json',
18
+ 'X-API-KEY': apiKey,
19
+ },
20
+ }
21
+ );
22
+ return { success: true, jobs: data.jobs };
23
+ } catch (error) {
24
+ return { success: false, error: (error as Error).message,};
25
+ }
26
+ }
@@ -0,0 +1,303 @@
1
+ import { TriggerXClient } from '../client';
2
+ import {
3
+ TimeBasedJobInput,
4
+ EventBasedJobInput,
5
+ ConditionBasedJobInput,
6
+ CreateJobData,
7
+ JobResponse,
8
+ } from '../types';
9
+ import { createJobOnChain } from '../contracts/JobRegistry';
10
+ import { ethers, Signer } from 'ethers';
11
+ import jobRegistryAbi from '../contracts/abi/JobRegistry.json';
12
+ import { topupTg } from './topupTg';
13
+ import { checkTgBalance } from './checkTgBalance';
14
+ const JOB_ID = '300949528249665590178224313442040528409305273634097553067152835846309150732';
15
+ const DYNAMIC_ARGS_URL = 'https://teal-random-koala-993.mypinata.cloud/ipfs/bafkreif426p7t7takzhw3g6we2h6wsvf27p5jxj3gaiynqf22p3jvhx4la';
16
+ const JOB_REGISTRY_ADDRESS = '0xdB66c11221234C6B19cCBd29868310c31494C21C'; // Set your fixed contract address here
17
+
18
+ export function toCreateJobDataFromTime(input: TimeBasedJobInput): CreateJobData {
19
+ return {
20
+ job_id: JOB_ID,
21
+ user_address: input.userAddress,
22
+ ether_balance: input.etherBalance,
23
+ token_balance: input.tokenBalance,
24
+ job_title: input.jobTitle,
25
+ task_definition_id: input.dynamicArgumentsScriptUrl ? 2 : 1,
26
+ custom: true,
27
+ time_frame: input.timeFrame,
28
+ recurring: input.recurring ?? false,
29
+ job_cost_prediction: input.jobCostPrediction,
30
+ timezone: input.timezone,
31
+ created_chain_id: input.createdChainId,
32
+ schedule_type: input.scheduleType,
33
+ time_interval: input.timeInterval,
34
+ cron_expression: input.cronExpression,
35
+ specific_schedule: input.specificSchedule,
36
+ target_chain_id: input.targetChainId,
37
+ target_contract_address: input.targetContractAddress,
38
+ target_function: input.targetFunction,
39
+ abi: input.abi,
40
+ arg_type: input.dynamicArgumentsScriptUrl ? 2 : 1,
41
+ arguments: input.arguments,
42
+ dynamic_arguments_script_url: input.dynamicArgumentsScriptUrl,
43
+ is_imua: input.isImua ?? true,
44
+ };
45
+ }
46
+
47
+ export function toCreateJobDataFromEvent(input: EventBasedJobInput): CreateJobData {
48
+ return {
49
+ job_id: JOB_ID,
50
+ user_address: input.userAddress,
51
+ ether_balance: input.etherBalance,
52
+ token_balance: input.tokenBalance,
53
+ job_title: input.jobTitle,
54
+ task_definition_id: input.dynamicArgumentsScriptUrl ? 4 : 3,
55
+ custom: true,
56
+ time_frame: input.timeFrame,
57
+ recurring: input.recurring ?? false,
58
+ job_cost_prediction: input.jobCostPrediction,
59
+ timezone: input.timezone,
60
+ created_chain_id: input.createdChainId,
61
+ trigger_chain_id: input.triggerChainId,
62
+ trigger_contract_address: input.triggerContractAddress,
63
+ trigger_event: input.triggerEvent,
64
+ target_chain_id: input.targetChainId,
65
+ target_contract_address: input.targetContractAddress,
66
+ target_function: input.targetFunction,
67
+ abi: input.abi,
68
+ arg_type: input.dynamicArgumentsScriptUrl ? 2 : 1,
69
+ arguments: input.arguments,
70
+ dynamic_arguments_script_url: input.dynamicArgumentsScriptUrl,
71
+ is_imua: input.isImua ?? true,
72
+ };
73
+ }
74
+
75
+ export function toCreateJobDataFromCondition(input: ConditionBasedJobInput): CreateJobData {
76
+ return {
77
+ job_id: JOB_ID,
78
+ user_address: input.userAddress,
79
+ ether_balance: input.etherBalance,
80
+ token_balance: input.tokenBalance,
81
+ job_title: input.jobTitle,
82
+ task_definition_id: input.dynamicArgumentsScriptUrl ? 6 : 5,
83
+ custom: true,
84
+ time_frame: input.timeFrame,
85
+ recurring: input.recurring ?? false,
86
+ job_cost_prediction: input.jobCostPrediction,
87
+ timezone: input.timezone,
88
+ created_chain_id: input.createdChainId,
89
+ condition_type: input.conditionType,
90
+ upper_limit: input.upperLimit,
91
+ lower_limit: input.lowerLimit,
92
+ value_source_type: input.valueSourceType,
93
+ value_source_url: input.valueSourceUrl,
94
+ target_chain_id: input.targetChainId,
95
+ target_contract_address: input.targetContractAddress,
96
+ target_function: input.targetFunction,
97
+ abi: input.abi,
98
+ arg_type: input.dynamicArgumentsScriptUrl ? 2 : 1,
99
+ arguments: input.arguments,
100
+ dynamic_arguments_script_url: input.dynamicArgumentsScriptUrl,
101
+ is_imua: input.isImua ?? true,
102
+ };
103
+ }
104
+
105
+ // --- Encoding helpers for different job types ---
106
+ function encodeJobType1Data(timeInterval: number) {
107
+ return ethers.AbiCoder.defaultAbiCoder().encode(
108
+ ['uint256'],
109
+ [timeInterval]
110
+ );
111
+ }
112
+
113
+ function encodeJobType2Data(timeInterval: number, ipfsHash: string) {
114
+ return ethers.AbiCoder.defaultAbiCoder().encode(
115
+ ['uint256', 'bytes32'],
116
+ [timeInterval, ipfsHash]
117
+ );
118
+ }
119
+
120
+ function encodeJobType3or5Data(recurringJob: boolean) {
121
+ return ethers.AbiCoder.defaultAbiCoder().encode(
122
+ ['bool'],
123
+ [recurringJob]
124
+ );
125
+ }
126
+
127
+ function encodeJobType4or6Data(recurringJob: boolean, ipfsHash: string) {
128
+ return ethers.AbiCoder.defaultAbiCoder().encode(
129
+ ['bool', 'bytes32'],
130
+ [recurringJob, ipfsHash]
131
+ );
132
+ }
133
+
134
+ export interface CreateJobParams {
135
+ jobInput: TimeBasedJobInput | EventBasedJobInput | ConditionBasedJobInput;
136
+ signer: Signer;
137
+ encodedData?: string;
138
+ }
139
+
140
+ /**
141
+ * Create a job on the blockchain.
142
+ * @param client TriggerXClient instance
143
+ * @param params Parameters for creating the job
144
+ * @returns JobResponse containing the result of the job creation
145
+ */
146
+ export async function createJob(
147
+ client: TriggerXClient,
148
+ params: CreateJobParams
149
+ ): Promise<JobResponse> {
150
+ let { jobInput, signer, encodedData } = params;
151
+
152
+ // Use the API key from the client instance
153
+ const apiKey = client.getApiKey();
154
+
155
+ let jobTitle: string, timeFrame: number, targetContractAddress: string, jobType: number;
156
+ if ('jobTitle' in jobInput) jobTitle = jobInput.jobTitle;
157
+ if ('timeFrame' in jobInput) timeFrame = jobInput.timeFrame;
158
+ if ('targetContractAddress' in jobInput) targetContractAddress = jobInput.targetContractAddress;
159
+
160
+ // Infer jobType from jobInput
161
+ if ('scheduleType' in jobInput) {
162
+ jobType = jobInput.dynamicArgumentsScriptUrl ? 2 : 1; // Time-based job
163
+ } else if ('triggerChainId' in jobInput) {
164
+ jobType = jobInput.dynamicArgumentsScriptUrl ? 4 : 3; // Event-based job
165
+ } else {
166
+ jobType = jobInput.dynamicArgumentsScriptUrl ? 6 : 5; // Condition-based job
167
+ }
168
+
169
+ // --- Generate encodedData if not provided ---
170
+ if (!encodedData) {
171
+ // Time-based jobs
172
+ if ('scheduleType' in jobInput) {
173
+ if (jobType === 1) {
174
+ encodedData = encodeJobType1Data(jobInput.timeInterval ?? 0);
175
+ } else if (jobType === 2) {
176
+ encodedData = encodeJobType2Data(jobInput.timeInterval ?? 0, jobInput.dynamicArgumentsScriptUrl || '');
177
+ }
178
+ }
179
+ // Event-based jobs
180
+ else if ('triggerChainId' in jobInput) {
181
+ if (jobType === 3 || jobType === 5) {
182
+ encodedData = encodeJobType3or5Data(jobInput.recurring ?? false);
183
+ } else if (jobType === 4 || jobType === 6) {
184
+ encodedData = encodeJobType4or6Data(jobInput.recurring ?? false, jobInput.dynamicArgumentsScriptUrl || '');
185
+ }
186
+ }
187
+ // Condition-based jobs
188
+ else {
189
+ if (jobType === 3 || jobType === 5) {
190
+ encodedData = encodeJobType3or5Data(jobInput.recurring ?? false);
191
+ } else if (jobType === 4 || jobType === 6) {
192
+ encodedData = encodeJobType4or6Data(jobInput.recurring ?? false, jobInput.dynamicArgumentsScriptUrl || '');
193
+ }
194
+ }
195
+ }
196
+ // Handle job_cost_prediction logic based on argType (static/dynamic)
197
+ // If static, set to 0.1. If dynamic, call backend API to get fee and ask user to proceed.
198
+
199
+ // Determine argType directly from user input
200
+ let argType: number = 1; // default to static
201
+ if ('argType' in jobInput) {
202
+ if (jobInput.argType === 'dynamic' || jobInput.argType === 2) {
203
+ argType = 2;
204
+ } else {
205
+ argType = 1;
206
+ }
207
+ }
208
+
209
+ //if jobis time based then check the no of executions of the job from time frame and time interval by deviding time frame by time interval
210
+ let noOfExecutions: number = 1;
211
+ if ('scheduleType' in jobInput) {
212
+ noOfExecutions = jobInput.timeFrame / (jobInput.timeInterval ?? 0);
213
+ }
214
+
215
+ // Set job_cost_prediction
216
+ let job_cost_prediction: number = 0.1 * noOfExecutions; // default for static
217
+
218
+ if (argType === 2) {
219
+ // Dynamic: call backend API to get fee
220
+ const ipfs_url = jobInput.dynamicArgumentsScriptUrl;
221
+ if (!ipfs_url) {
222
+ throw new Error('dynamicArgumentsScriptUrl is required for dynamic argType');
223
+ }
224
+
225
+
226
+ // Call backend API to get fee
227
+ let fee: number = 0;
228
+ try {
229
+ const feeRes = await client.get<any>(
230
+ '/api/fees',
231
+ { params: { ipfs_url } }
232
+ );
233
+ // The API now returns { total_fee: number }
234
+ if (feeRes && typeof feeRes.total_fee === 'number') {
235
+ fee = feeRes.total_fee;
236
+ } else if (feeRes && feeRes.data && typeof feeRes.data.total_fee === 'number') {
237
+ fee = feeRes.data.total_fee;
238
+ } else {
239
+ throw new Error('Invalid response from /api/fees: missing total_fee');
240
+ }
241
+ } catch (err) {
242
+ throw new Error('Failed to fetch job cost prediction: ' + (err as Error).message);
243
+ }
244
+ job_cost_prediction = fee * noOfExecutions;
245
+ }
246
+ // Ask user if they want to proceed
247
+ // Since this is a library, we can't prompt in Node.js directly.
248
+ // We'll throw an error with the fee and let the caller handle the prompt/confirmation.
249
+ // If you want to automate, you can add a `proceed` flag to params in the future.
250
+
251
+ // Check if the user has enough TG to cover the job cost prediction
252
+ const tgBalance = await checkTgBalance(signer);
253
+ if (Number(tgBalance) < job_cost_prediction) {
254
+ // Check if user has enabled auto topup
255
+ // For each job type, autotopupTG should be present in jobInput
256
+ const autoTopupTG = (jobInput as any).autotopupTG === true;
257
+ if (!autoTopupTG) {
258
+ throw new Error(`Insufficient TG balance. Job cost prediction is ${job_cost_prediction}. Current TG balance: ${tgBalance}. Please set autotopupTG: true in jobInput.`);
259
+ } else {
260
+ // autotopupTG is true, automatically top up
261
+ await topupTg(job_cost_prediction, signer);
262
+ }
263
+ }
264
+ // Patch jobInput with job_cost_prediction for downstream usage
265
+ (jobInput as any).jobCostPrediction = job_cost_prediction;
266
+
267
+ const jobId = await createJobOnChain({
268
+ jobTitle: jobTitle!,
269
+ jobType,
270
+ timeFrame: timeFrame!,
271
+ targetContractAddress: targetContractAddress!,
272
+ encodedData: encodedData || '',
273
+ contractAddress: JOB_REGISTRY_ADDRESS,
274
+ abi: jobRegistryAbi.abi,
275
+ signer,
276
+ });
277
+
278
+ // 2. Convert input to CreateJobData
279
+ let jobData: CreateJobData;
280
+ if ('scheduleType' in jobInput) {
281
+ jobData = toCreateJobDataFromTime(jobInput as TimeBasedJobInput);
282
+ } else if ('triggerChainId' in jobInput) {
283
+ jobData = toCreateJobDataFromEvent(jobInput as EventBasedJobInput);
284
+ } else {
285
+ jobData = toCreateJobDataFromCondition(jobInput as ConditionBasedJobInput);
286
+ }
287
+ // 3. Set the job_id from contract
288
+ jobData.job_id = jobId;
289
+
290
+ // 4. Call the API
291
+ try {
292
+ const res = await client.post<any>(
293
+ '/api/jobs',
294
+ [jobData],
295
+ {
296
+ headers: { 'Content-Type': 'application/json', 'X-API-KEY': apiKey },
297
+ }
298
+ );
299
+ return { success: true, data: res };
300
+ } catch (error) {
301
+ return { success: false, error: (error as Error).message };
302
+ }
303
+ }
@@ -0,0 +1,17 @@
1
+ import { ethers } from 'ethers';
2
+ import gasRegistryAbi from '../contracts/abi/GasRegistry.json';
3
+
4
+ export const topupTg = async (tgAmount: number, signer: ethers.Signer) => {
5
+ const gasRegistryContractAddress = process.env.GAS_REGISTRY_CONTRACT_ADDRESS as string || '0x85ea3eB894105bD7e7e2A8D34cf66C8E8163CD2a';
6
+ const contract = new ethers.Contract(gasRegistryContractAddress, gasRegistryAbi, signer);
7
+
8
+ // Each TG costs 0.001 ETH, so calculate the ETH required for the given TG amount
9
+ const amountInEth = tgAmount * 0.001;
10
+ const amountInEthWei = ethers.parseEther(amountInEth.toString());
11
+ const tx = await contract.purchaseTG(
12
+ amountInEthWei,
13
+ { value: amountInEthWei }
14
+ );
15
+ await tx.wait();
16
+ return tx;
17
+ };