sdk-triggerx 0.1.31 → 0.1.33
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 +9 -8
- package/dist/api/checkBalance.d.ts +33 -0
- package/dist/api/checkBalance.js +68 -0
- package/dist/api/jobs.d.ts +6 -2
- package/dist/api/jobs.js +181 -108
- package/dist/api/topup.d.ts +20 -0
- package/dist/api/topup.js +102 -0
- package/dist/api/withdraw.d.ts +26 -0
- package/dist/api/withdraw.js +89 -0
- package/dist/config.js +5 -5
- package/dist/contracts/abi/GasRegistry.json +268 -423
- package/dist/contracts/abi/JobRegistry.json +710 -1524
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/types.d.ts +34 -1
- package/dist/types.js +1 -0
- package/dist/utils/validation.d.ts +2 -2
- package/dist/utils/validation.js +63 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -104,7 +104,7 @@ const jobInput = {
|
|
|
104
104
|
isImua: false, // Optional feature flag
|
|
105
105
|
arguments: ['3'], // Target function args as strings
|
|
106
106
|
dynamicArgumentsScriptUrl: '', // If ArgType.Dynamic, provide IPFS/URL here
|
|
107
|
-
|
|
107
|
+
autotopupETH: true, // Auto top-up ETH in GasRegistry if balance is low
|
|
108
108
|
};
|
|
109
109
|
|
|
110
110
|
const signer = /* ethers.Signer instance */;
|
|
@@ -140,8 +140,8 @@ const jobInput = {
|
|
|
140
140
|
dynamicArgumentsScriptUrl: 'https://your-ipfs-gateway/ipfs/your-hash',
|
|
141
141
|
language:'', //Your code language exmampel-> language:'go',
|
|
142
142
|
|
|
143
|
-
// Optional helper to auto-top up
|
|
144
|
-
|
|
143
|
+
// Optional helper to auto-top up ETH in GasRegistry if low
|
|
144
|
+
autotopupETH: true,
|
|
145
145
|
};
|
|
146
146
|
|
|
147
147
|
const result = await createJob(client, { jobInput, signer });
|
|
@@ -180,7 +180,7 @@ const jobInput = {
|
|
|
180
180
|
dynamicArgumentsScriptUrl: 'https://your-ipfs-url', // Script URL for dynamic args
|
|
181
181
|
language:'', //Your code language exmampel-> language:'go',
|
|
182
182
|
isImua: false, // Optional feature flag
|
|
183
|
-
|
|
183
|
+
autotopupETH: true, // Auto top-up ETH in GasRegistry if balance is low
|
|
184
184
|
};
|
|
185
185
|
|
|
186
186
|
const result = await createJob(client, { jobInput, signer });
|
|
@@ -215,7 +215,7 @@ const jobInput = {
|
|
|
215
215
|
arguments: ['5'], // Target function args as strings
|
|
216
216
|
dynamicArgumentsScriptUrl: '', // If ArgType.Dynamic, provide IPFS/URL here
|
|
217
217
|
isImua: false, // Optional feature flag
|
|
218
|
-
|
|
218
|
+
autotopupETH: true, // Auto top-up ETH in GasRegistry if balance is low
|
|
219
219
|
};
|
|
220
220
|
|
|
221
221
|
const result = await createJob(client, { jobInput, signer });
|
|
@@ -266,7 +266,7 @@ const jobInput = {
|
|
|
266
266
|
data: '0x' // empty for simple ETH transfer
|
|
267
267
|
}
|
|
268
268
|
],
|
|
269
|
-
|
|
269
|
+
autotopupETH: true,
|
|
270
270
|
};
|
|
271
271
|
|
|
272
272
|
await createJob(client, { jobInput, signer });
|
|
@@ -330,7 +330,7 @@ const jobInput = {
|
|
|
330
330
|
data: swapData
|
|
331
331
|
}
|
|
332
332
|
],
|
|
333
|
-
|
|
333
|
+
autotopupETH: true,
|
|
334
334
|
};
|
|
335
335
|
|
|
336
336
|
await createJob(client, { jobInput, signer });
|
|
@@ -359,7 +359,7 @@ const jobInput = {
|
|
|
359
359
|
safeAddress: '0xYourSafeAddress',
|
|
360
360
|
dynamicArgumentsScriptUrl: 'https://your-ipfs-gateway/ipfs/your-hash',
|
|
361
361
|
language:'go', //Your code language exmampel-> language:'go',
|
|
362
|
-
|
|
362
|
+
autotopupETH: true,
|
|
363
363
|
};
|
|
364
364
|
|
|
365
365
|
await createJob(client, { jobInput, signer });
|
|
@@ -456,6 +456,7 @@ Includes:
|
|
|
456
456
|
data: string; // Encoded function call data (hex with 0x prefix)
|
|
457
457
|
}
|
|
458
458
|
```
|
|
459
|
+
- `autotopupETH` boolean flag on job inputs to automatically ensure sufficient ETH is deposited in the GasRegistry for job execution (legacy `autotopupTG` is still accepted but deprecated).
|
|
459
460
|
|
|
460
461
|
---
|
|
461
462
|
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Check ETH balance for a given signer using SDK-provided RPC
|
|
3
|
+
* This function uses our own RPC provider to ensure reliable connection
|
|
4
|
+
* even if the user's RPC fails
|
|
5
|
+
* @param signer - ethers.Signer instance (used to get wallet address)
|
|
6
|
+
* @param chainId - Optional chain ID. If not provided, will try to get from signer's provider
|
|
7
|
+
* @returns Balance information or error response
|
|
8
|
+
*/
|
|
9
|
+
export declare const checkEthBalance: (userAddress: string, chainId: string | number) => Promise<{
|
|
10
|
+
success: boolean;
|
|
11
|
+
data?: {
|
|
12
|
+
ethBalanceWei: bigint;
|
|
13
|
+
ethBalance: string;
|
|
14
|
+
};
|
|
15
|
+
error?: string;
|
|
16
|
+
errorCode?: string;
|
|
17
|
+
errorType?: string;
|
|
18
|
+
details?: any;
|
|
19
|
+
}>;
|
|
20
|
+
/**
|
|
21
|
+
* @deprecated Use checkEthBalance instead. This is an alias for backward compatibility.
|
|
22
|
+
*/
|
|
23
|
+
export declare const checkTgBalance: (userAddress: string, chainId: string | number) => Promise<{
|
|
24
|
+
success: boolean;
|
|
25
|
+
data?: {
|
|
26
|
+
ethBalanceWei: bigint;
|
|
27
|
+
ethBalance: string;
|
|
28
|
+
};
|
|
29
|
+
error?: string;
|
|
30
|
+
errorCode?: string;
|
|
31
|
+
errorType?: string;
|
|
32
|
+
details?: any;
|
|
33
|
+
}>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.checkTgBalance = exports.checkEthBalance = void 0;
|
|
7
|
+
const ethers_1 = require("ethers");
|
|
8
|
+
const GasRegistry_json_1 = __importDefault(require("../contracts/abi/GasRegistry.json"));
|
|
9
|
+
const contractUtils_1 = require("../contracts/contractUtils");
|
|
10
|
+
const errors_1 = require("../utils/errors");
|
|
11
|
+
/**
|
|
12
|
+
* Check ETH balance for a given signer using SDK-provided RPC
|
|
13
|
+
* This function uses our own RPC provider to ensure reliable connection
|
|
14
|
+
* even if the user's RPC fails
|
|
15
|
+
* @param signer - ethers.Signer instance (used to get wallet address)
|
|
16
|
+
* @param chainId - Optional chain ID. If not provided, will try to get from signer's provider
|
|
17
|
+
* @returns Balance information or error response
|
|
18
|
+
*/
|
|
19
|
+
const checkEthBalance = async (userAddress, chainId) => {
|
|
20
|
+
// Validate inputs
|
|
21
|
+
if (!userAddress || typeof userAddress !== 'string') {
|
|
22
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('userAddress', 'User address is required and must be a string'), 'Validation error');
|
|
23
|
+
}
|
|
24
|
+
if (!chainId) {
|
|
25
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('chainId', 'Chain ID is required'), 'Validation error');
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
// Resolve chain ID
|
|
29
|
+
const resolvedChainId = chainId.toString();
|
|
30
|
+
// Get contract address
|
|
31
|
+
let gasRegistryContractAddress;
|
|
32
|
+
try {
|
|
33
|
+
gasRegistryContractAddress = (0, contractUtils_1.getContractAddress)(resolvedChainId, 'gasRegistry');
|
|
34
|
+
}
|
|
35
|
+
catch (configError) {
|
|
36
|
+
if (configError instanceof errors_1.ConfigurationError) {
|
|
37
|
+
return (0, errors_1.createErrorResponse)(configError, 'Configuration error');
|
|
38
|
+
}
|
|
39
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ConfigurationError('Failed to get contract address', { originalError: configError }), 'Configuration error');
|
|
40
|
+
}
|
|
41
|
+
// Create contract instance with SDK RPC provider (read-only)
|
|
42
|
+
// This ensures we can read balance even if user's RPC fails
|
|
43
|
+
const contract = await (0, contractUtils_1.createContractWithSdkRpc)(gasRegistryContractAddress, GasRegistry_json_1.default, resolvedChainId);
|
|
44
|
+
// Read balance using our RPC provider
|
|
45
|
+
const ethBalanceWei = await contract.getBalance(userAddress);
|
|
46
|
+
// Convert from wei to ETH
|
|
47
|
+
const ethBalance = ethers_1.ethers.formatEther(ethBalanceWei);
|
|
48
|
+
console.log('ethBalance', ethBalance);
|
|
49
|
+
return { success: true, data: { ethBalanceWei, ethBalance } };
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
console.error('Error checking ETH balance:', error);
|
|
53
|
+
if (error instanceof Error) {
|
|
54
|
+
if (error.message.includes('network') || error.message.includes('timeout')) {
|
|
55
|
+
return (0, errors_1.createErrorResponse)(new errors_1.NetworkError('Network error during balance check', { originalError: error }), 'Network error');
|
|
56
|
+
}
|
|
57
|
+
else if (error.message.includes('contract') || error.message.includes('transaction')) {
|
|
58
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ContractError('Contract error during balance check', { originalError: error }), 'Contract error');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return (0, errors_1.createErrorResponse)(error, 'Failed to check ETH balance');
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
exports.checkEthBalance = checkEthBalance;
|
|
65
|
+
/**
|
|
66
|
+
* @deprecated Use checkEthBalance instead. This is an alias for backward compatibility.
|
|
67
|
+
*/
|
|
68
|
+
exports.checkTgBalance = exports.checkEthBalance;
|
package/dist/api/jobs.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { TriggerXClient } from '../client';
|
|
2
|
-
import { TimeBasedJobInput, EventBasedJobInput, ConditionBasedJobInput, CreateJobData, JobResponse } from '../types';
|
|
2
|
+
import { TimeBasedJobInput, EventBasedJobInput, ConditionBasedJobInput, CreateJobData, JobResponse, CustomScriptJobInput } from '../types';
|
|
3
3
|
import { Signer } from 'ethers';
|
|
4
4
|
export declare function toCreateJobDataFromTime(input: TimeBasedJobInput, balances: {
|
|
5
5
|
etherBalance: bigint;
|
|
@@ -13,8 +13,12 @@ export declare function toCreateJobDataFromCondition(input: ConditionBasedJobInp
|
|
|
13
13
|
etherBalance: bigint;
|
|
14
14
|
tokenBalanceWei: bigint;
|
|
15
15
|
}, userAddress: string, jobCostPrediction: number): CreateJobData;
|
|
16
|
+
export declare function toCreateJobDataFromCustomScript(input: CustomScriptJobInput, balances: {
|
|
17
|
+
etherBalance: bigint;
|
|
18
|
+
tokenBalanceWei: bigint;
|
|
19
|
+
}, userAddress: string, jobCostPrediction: number): CreateJobData;
|
|
16
20
|
export interface CreateJobParams {
|
|
17
|
-
jobInput: TimeBasedJobInput | EventBasedJobInput | ConditionBasedJobInput;
|
|
21
|
+
jobInput: TimeBasedJobInput | EventBasedJobInput | ConditionBasedJobInput | CustomScriptJobInput;
|
|
18
22
|
signer: Signer;
|
|
19
23
|
encodedData?: string;
|
|
20
24
|
}
|
package/dist/api/jobs.js
CHANGED
|
@@ -6,16 +6,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.toCreateJobDataFromTime = toCreateJobDataFromTime;
|
|
7
7
|
exports.toCreateJobDataFromEvent = toCreateJobDataFromEvent;
|
|
8
8
|
exports.toCreateJobDataFromCondition = toCreateJobDataFromCondition;
|
|
9
|
+
exports.toCreateJobDataFromCustomScript = toCreateJobDataFromCustomScript;
|
|
9
10
|
exports.createJob = createJob;
|
|
11
|
+
const types_1 = require("../types");
|
|
10
12
|
const JobRegistry_1 = require("../contracts/JobRegistry");
|
|
11
13
|
const ethers_1 = require("ethers");
|
|
12
14
|
const JobRegistry_json_1 = __importDefault(require("../contracts/abi/JobRegistry.json"));
|
|
13
|
-
const
|
|
14
|
-
const
|
|
15
|
+
const topup_1 = require("./topup");
|
|
16
|
+
const checkBalance_1 = require("./checkBalance");
|
|
15
17
|
const config_1 = require("../config");
|
|
16
18
|
const validation_1 = require("../utils/validation");
|
|
17
19
|
const errors_1 = require("../utils/errors");
|
|
18
20
|
const SafeWallet_1 = require("../contracts/safe/SafeWallet");
|
|
21
|
+
const JOB_ID = '300949528249665590178224313442040528409305273634097553067152835846309150732';
|
|
22
|
+
const DYNAMIC_ARGS_URL = 'https://teal-random-koala-993.mypinata.cloud/ipfs/bafkreif426p7t7takzhw3g6we2h6wsvf27p5jxj3gaiynqf22p3jvhx4la';
|
|
19
23
|
// Helper function to encode multisend batch transactions
|
|
20
24
|
function encodeMultisendData(transactions) {
|
|
21
25
|
// Multisend format: for each transaction, encode as:
|
|
@@ -53,7 +57,7 @@ function encodeMultisendData(transactions) {
|
|
|
53
57
|
}
|
|
54
58
|
function toCreateJobDataFromTime(input, balances, userAddress, jobCostPrediction) {
|
|
55
59
|
return {
|
|
56
|
-
job_id:
|
|
60
|
+
job_id: JOB_ID,
|
|
57
61
|
user_address: userAddress,
|
|
58
62
|
ether_balance: balances.etherBalance,
|
|
59
63
|
token_balance: balances.tokenBalanceWei,
|
|
@@ -76,16 +80,16 @@ function toCreateJobDataFromTime(input, balances, userAddress, jobCostPrediction
|
|
|
76
80
|
arg_type: input.dynamicArgumentsScriptUrl ? 2 : 1,
|
|
77
81
|
arguments: input.arguments,
|
|
78
82
|
dynamic_arguments_script_url: input.dynamicArgumentsScriptUrl,
|
|
79
|
-
is_imua: input.isImua ??
|
|
83
|
+
is_imua: input.isImua ?? true,
|
|
80
84
|
is_safe: input.walletMode === 'safe',
|
|
81
85
|
safe_name: input.safeName || '',
|
|
82
86
|
safe_address: input.safeAddress || '',
|
|
83
|
-
language: input.language || '
|
|
87
|
+
language: input.language || '',
|
|
84
88
|
};
|
|
85
89
|
}
|
|
86
90
|
function toCreateJobDataFromEvent(input, balances, userAddress, jobCostPrediction) {
|
|
87
91
|
return {
|
|
88
|
-
job_id:
|
|
92
|
+
job_id: JOB_ID,
|
|
89
93
|
user_address: userAddress,
|
|
90
94
|
ether_balance: balances.etherBalance,
|
|
91
95
|
token_balance: balances.tokenBalanceWei,
|
|
@@ -107,16 +111,16 @@ function toCreateJobDataFromEvent(input, balances, userAddress, jobCostPredictio
|
|
|
107
111
|
arg_type: input.dynamicArgumentsScriptUrl ? 2 : 1,
|
|
108
112
|
arguments: input.arguments,
|
|
109
113
|
dynamic_arguments_script_url: input.dynamicArgumentsScriptUrl,
|
|
110
|
-
is_imua: input.isImua ??
|
|
114
|
+
is_imua: input.isImua ?? true,
|
|
111
115
|
is_safe: input.walletMode === 'safe',
|
|
112
116
|
safe_name: input.safeName || '',
|
|
113
117
|
safe_address: input.safeAddress || '',
|
|
114
|
-
language: input.language || '
|
|
118
|
+
language: input.language || '',
|
|
115
119
|
};
|
|
116
120
|
}
|
|
117
121
|
function toCreateJobDataFromCondition(input, balances, userAddress, jobCostPrediction) {
|
|
118
122
|
return {
|
|
119
|
-
job_id:
|
|
123
|
+
job_id: JOB_ID,
|
|
120
124
|
user_address: userAddress,
|
|
121
125
|
ether_balance: balances.etherBalance,
|
|
122
126
|
token_balance: balances.tokenBalanceWei,
|
|
@@ -140,11 +144,40 @@ function toCreateJobDataFromCondition(input, balances, userAddress, jobCostPredi
|
|
|
140
144
|
arg_type: input.dynamicArgumentsScriptUrl ? 2 : 1,
|
|
141
145
|
arguments: input.arguments,
|
|
142
146
|
dynamic_arguments_script_url: input.dynamicArgumentsScriptUrl,
|
|
143
|
-
is_imua: input.isImua ??
|
|
147
|
+
is_imua: input.isImua ?? true,
|
|
144
148
|
is_safe: input.walletMode === 'safe',
|
|
145
149
|
safe_name: input.safeName || '',
|
|
146
150
|
safe_address: input.safeAddress || '',
|
|
147
|
-
language: input.language || '
|
|
151
|
+
language: input.language || '',
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
function toCreateJobDataFromCustomScript(input, balances, userAddress, jobCostPrediction) {
|
|
155
|
+
return {
|
|
156
|
+
job_id: JOB_ID,
|
|
157
|
+
user_address: userAddress,
|
|
158
|
+
ether_balance: balances.etherBalance,
|
|
159
|
+
token_balance: balances.tokenBalanceWei,
|
|
160
|
+
job_title: input.jobTitle,
|
|
161
|
+
task_definition_id: 7,
|
|
162
|
+
custom: true,
|
|
163
|
+
time_frame: input.timeFrame,
|
|
164
|
+
recurring: input.recurring ?? false,
|
|
165
|
+
job_cost_prediction: jobCostPrediction,
|
|
166
|
+
timezone: input.timezone,
|
|
167
|
+
created_chain_id: input.chainId,
|
|
168
|
+
target_chain_id: input.chainId,
|
|
169
|
+
target_contract_address: input.targetContractAddress || '',
|
|
170
|
+
target_function: input.targetFunction || '',
|
|
171
|
+
abi: input.abi || '',
|
|
172
|
+
arg_type: 2,
|
|
173
|
+
arguments: input.arguments,
|
|
174
|
+
dynamic_arguments_script_url: input.dynamicArgumentsScriptUrl,
|
|
175
|
+
is_imua: input.isImua ?? true,
|
|
176
|
+
is_safe: input.walletMode === 'safe',
|
|
177
|
+
safe_name: input.safeName || '',
|
|
178
|
+
safe_address: input.safeAddress || '',
|
|
179
|
+
language: input.language,
|
|
180
|
+
time_interval: input.timeInterval,
|
|
148
181
|
};
|
|
149
182
|
}
|
|
150
183
|
// --- Encoding helpers for different job types ---
|
|
@@ -160,6 +193,9 @@ function encodeJobType3or5Data(recurringJob) {
|
|
|
160
193
|
function encodeJobType4or6Data(recurringJob, ipfsHash) {
|
|
161
194
|
return ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(['bool', 'bytes32'], [recurringJob, ipfsHash]);
|
|
162
195
|
}
|
|
196
|
+
function encodeJobType7Data(timeInterval, ipfsHash, language) {
|
|
197
|
+
return ethers_1.ethers.AbiCoder.defaultAbiCoder().encode(['uint256', 'bytes32', 'string'], [timeInterval, ipfsHash, language]);
|
|
198
|
+
}
|
|
163
199
|
/**
|
|
164
200
|
* Create a job on the blockchain.
|
|
165
201
|
* @param client TriggerXClient instance
|
|
@@ -215,24 +251,16 @@ async function createJob(client, params) {
|
|
|
215
251
|
}
|
|
216
252
|
// Auto-set module target; user does not need to pass targetContractAddress in safe mode
|
|
217
253
|
jobInput.targetContractAddress = safeModule;
|
|
218
|
-
|
|
219
|
-
jobInput.targetFunction = 'execJobFromHub';
|
|
220
|
-
// ABI verified per provided interface and matches execJobFromHub
|
|
254
|
+
jobInput.targetFunction = 'execJobFromHub(address,address,uint256,bytes,uint8)';
|
|
221
255
|
jobInput.abi = JSON.stringify([
|
|
222
256
|
{
|
|
223
|
-
"inputs": [
|
|
224
|
-
{ "
|
|
225
|
-
{ "
|
|
226
|
-
{ "
|
|
227
|
-
{ "
|
|
228
|
-
{ "
|
|
229
|
-
],
|
|
230
|
-
"name": "execJobFromHub",
|
|
231
|
-
"outputs": [
|
|
232
|
-
{ "internalType": "bool", "name": "success", "type": "bool" }
|
|
233
|
-
],
|
|
234
|
-
"stateMutability": "nonpayable",
|
|
235
|
-
"type": "function"
|
|
257
|
+
"type": "function", "name": "execJobFromHub", "stateMutability": "nonpayable", "inputs": [
|
|
258
|
+
{ "name": "safeAddress", "type": "address" },
|
|
259
|
+
{ "name": "actionTarget", "type": "address" },
|
|
260
|
+
{ "name": "actionValue", "type": "uint256" },
|
|
261
|
+
{ "name": "actionData", "type": "bytes" },
|
|
262
|
+
{ "name": "operation", "type": "uint8" }
|
|
263
|
+
], "outputs": [{ "type": "bool", "name": "success" }]
|
|
236
264
|
}
|
|
237
265
|
]);
|
|
238
266
|
// Handle static vs dynamic safe wallet jobs
|
|
@@ -254,7 +282,7 @@ async function createJob(client, params) {
|
|
|
254
282
|
tx.to,
|
|
255
283
|
tx.value,
|
|
256
284
|
tx.data,
|
|
257
|
-
|
|
285
|
+
0 // CALL
|
|
258
286
|
];
|
|
259
287
|
}
|
|
260
288
|
else {
|
|
@@ -268,7 +296,7 @@ async function createJob(client, params) {
|
|
|
268
296
|
multisendCallOnly,
|
|
269
297
|
'0',
|
|
270
298
|
encodedMultisendData,
|
|
271
|
-
|
|
299
|
+
1 // DELEGATECALL
|
|
272
300
|
];
|
|
273
301
|
}
|
|
274
302
|
}
|
|
@@ -321,6 +349,13 @@ async function createJob(client, params) {
|
|
|
321
349
|
else {
|
|
322
350
|
jobType = jobInput.dynamicArgumentsScriptUrl ? 6 : 5; // Condition-based job
|
|
323
351
|
}
|
|
352
|
+
const jobTypeField = jobInput.jobType;
|
|
353
|
+
if (jobTypeField === types_1.JobType.CustomScript ||
|
|
354
|
+
jobTypeField === 'custom_script' ||
|
|
355
|
+
jobTypeField === 7 ||
|
|
356
|
+
jobTypeField === '7') {
|
|
357
|
+
jobType = 7;
|
|
358
|
+
}
|
|
324
359
|
// --- Generate encodedData if not provided ---
|
|
325
360
|
if (!encodedData) {
|
|
326
361
|
// Time-based jobs
|
|
@@ -343,6 +378,12 @@ async function createJob(client, params) {
|
|
|
343
378
|
encodedData = encodeJobType4or6Data(jobInput.recurring ?? false, ipfsBytes32);
|
|
344
379
|
}
|
|
345
380
|
}
|
|
381
|
+
// Custom script jobs
|
|
382
|
+
else if (jobType === 7) {
|
|
383
|
+
const customInput = jobInput;
|
|
384
|
+
const ipfsBytes32 = ethers_1.ethers.id(customInput.dynamicArgumentsScriptUrl);
|
|
385
|
+
encodedData = encodeJobType7Data(customInput.timeInterval ?? 0, ipfsBytes32, customInput.language);
|
|
386
|
+
}
|
|
346
387
|
// Condition-based jobs
|
|
347
388
|
else {
|
|
348
389
|
if (jobType === 3 || jobType === 5) {
|
|
@@ -354,111 +395,139 @@ async function createJob(client, params) {
|
|
|
354
395
|
}
|
|
355
396
|
}
|
|
356
397
|
}
|
|
357
|
-
// Handle job_cost_prediction
|
|
358
|
-
//
|
|
398
|
+
// Handle job_cost_prediction by always calling backend /api/fees
|
|
399
|
+
// The backend returns the total fee in wei; we convert it to token units (formatted ether)
|
|
400
|
+
// so it can be compared with ETH balance (which is also formatted via ethers.formatEther).
|
|
359
401
|
// Determine argType directly from user input
|
|
360
402
|
let argType = 1; // default to static
|
|
361
403
|
if ('argType' in jobInput) {
|
|
362
|
-
if (jobInput.argType === 'dynamic' || jobInput.argType === 2) {
|
|
404
|
+
if (jobInput.argType === 'dynamic' || jobInput.argType === 2 || jobInput.argType === types_1.ArgType.Dynamic) {
|
|
363
405
|
argType = 2;
|
|
364
406
|
}
|
|
365
407
|
else {
|
|
366
408
|
argType = 1;
|
|
367
409
|
}
|
|
368
410
|
}
|
|
411
|
+
if (jobType === 7) {
|
|
412
|
+
argType = 2;
|
|
413
|
+
jobInput.argType = types_1.ArgType.Dynamic;
|
|
414
|
+
}
|
|
369
415
|
//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
|
|
370
416
|
let noOfExecutions = 1;
|
|
371
417
|
if ('scheduleType' in jobInput) {
|
|
372
|
-
noOfExecutions = jobInput.timeFrame / (jobInput.timeInterval ??
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
if (argType === 2) {
|
|
379
|
-
// Dynamic: call backend API to get fee
|
|
380
|
-
const ipfs_url = jobInput.dynamicArgumentsScriptUrl;
|
|
381
|
-
if (!ipfs_url) {
|
|
382
|
-
return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('dynamicArgumentsScriptUrl', 'dynamicArgumentsScriptUrl is required for dynamic argType'), 'Validation error');
|
|
418
|
+
noOfExecutions = Math.ceil(jobInput.timeFrame / (jobInput.timeInterval ?? 1));
|
|
419
|
+
}
|
|
420
|
+
else if (jobType === 7) {
|
|
421
|
+
const customInterval = jobInput.timeInterval ?? 0;
|
|
422
|
+
if (customInterval > 0) {
|
|
423
|
+
noOfExecutions = Math.max(1, Math.floor(jobInput.timeFrame / customInterval));
|
|
383
424
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
425
|
+
}
|
|
426
|
+
let requiredETH;
|
|
427
|
+
let maxtotalFeeRaw;
|
|
428
|
+
// Prepare parameters for /api/fees
|
|
429
|
+
const ipfs_url = jobInput.dynamicArgumentsScriptUrl || '';
|
|
430
|
+
const task_definition_id = jobType; // use inferred jobType as task definition id
|
|
431
|
+
const target_chain_id = chainIdStr || '';
|
|
432
|
+
const target_contract_address = jobInput.targetContractAddress || '';
|
|
433
|
+
const target_function = jobInput.targetFunction || '';
|
|
434
|
+
const abi = jobInput.abi || '';
|
|
435
|
+
const args = jobInput.arguments ? JSON.stringify(jobInput.arguments) : '';
|
|
436
|
+
let job_cost_prediction = 0n;
|
|
437
|
+
try {
|
|
438
|
+
const feeRes = await client.get('/api/fees', {
|
|
439
|
+
params: {
|
|
440
|
+
ipfs_url,
|
|
441
|
+
task_definition_id,
|
|
442
|
+
target_chain_id,
|
|
443
|
+
target_contract_address,
|
|
444
|
+
target_function,
|
|
445
|
+
abi,
|
|
446
|
+
args,
|
|
401
447
|
}
|
|
448
|
+
});
|
|
449
|
+
// The API returns total fee in wei: { total_fee: "<wei>" } or nested under data
|
|
450
|
+
let totalFeeRaw;
|
|
451
|
+
if (feeRes && feeRes.current_total_fee !== undefined) {
|
|
452
|
+
totalFeeRaw = feeRes.current_total_fee;
|
|
402
453
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
454
|
+
else if (feeRes && feeRes.data && feeRes.data.current_total_fee !== undefined) {
|
|
455
|
+
totalFeeRaw = feeRes.data.current_total_fee;
|
|
456
|
+
}
|
|
457
|
+
maxtotalFeeRaw = feeRes.total_fee;
|
|
458
|
+
if (totalFeeRaw === undefined) {
|
|
459
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ApiError('Invalid response from /api/fees: missing total_fee', { response: feeRes }), 'API error');
|
|
460
|
+
}
|
|
461
|
+
// Support both number and string representations of wei
|
|
462
|
+
let totalFeeWei;
|
|
463
|
+
if (typeof totalFeeRaw === 'string') {
|
|
464
|
+
totalFeeWei = BigInt(totalFeeRaw);
|
|
465
|
+
}
|
|
466
|
+
else if (typeof totalFeeRaw === 'number') {
|
|
467
|
+
totalFeeWei = BigInt(Math.floor(totalFeeRaw));
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ApiError('Invalid total_fee type from /api/fees', { totalFeeRaw }), 'API error');
|
|
407
471
|
}
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
job_cost_prediction =
|
|
472
|
+
// Convert wei to token units (formatted ether) so it matches tgBalance units
|
|
473
|
+
// job_cost_prediction = Number(ethers.formatEther(totalFeeWei));
|
|
474
|
+
job_cost_prediction = totalFeeWei;
|
|
411
475
|
}
|
|
412
|
-
|
|
476
|
+
catch (err) {
|
|
477
|
+
const httpStatusCode = (0, errors_1.extractHttpStatusCode)(err);
|
|
478
|
+
const errorCode = (0, errors_1.determineErrorCode)(err, httpStatusCode);
|
|
479
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ApiError('Failed to fetch job cost prediction', { originalError: err, jobType }, httpStatusCode), 'API error');
|
|
480
|
+
}
|
|
481
|
+
job_cost_prediction = job_cost_prediction * BigInt(noOfExecutions);
|
|
482
|
+
let requiredETHwei = job_cost_prediction; // this is in wei
|
|
413
483
|
// Ask user if they want to proceed
|
|
414
484
|
// Since this is a library, we can't prompt in Node.js directly.
|
|
415
485
|
// We'll throw an error with the fee and let the caller handle the prompt/confirmation.
|
|
416
486
|
// If you want to automate, you can add a `proceed` flag to params in the future.
|
|
417
|
-
// Check if the user has enough
|
|
418
|
-
|
|
419
|
-
let tgBalanceWei, tgBalance;
|
|
487
|
+
// Check if the user has enough ETH to cover the job cost prediction
|
|
488
|
+
let ethBalanceWei, ethBalance;
|
|
420
489
|
try {
|
|
421
|
-
const balanceResult = await (0,
|
|
490
|
+
const balanceResult = await (0, checkBalance_1.checkEthBalance)(userAddress, chainIdStr);
|
|
422
491
|
if (!balanceResult.success || !balanceResult.data) {
|
|
423
|
-
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to check
|
|
492
|
+
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to check ETH balance', balanceResult.details), 'Balance check error');
|
|
424
493
|
}
|
|
425
|
-
|
|
426
|
-
|
|
494
|
+
ethBalanceWei = balanceResult.data.ethBalanceWei;
|
|
495
|
+
ethBalance = balanceResult.data.ethBalance;
|
|
427
496
|
}
|
|
428
497
|
catch (err) {
|
|
429
|
-
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to check
|
|
430
|
-
}
|
|
431
|
-
if
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
}
|
|
452
|
-
catch (err) {
|
|
453
|
-
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to top up TG balance', { originalError: err, requiredTG }), 'Top-up error');
|
|
498
|
+
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to check ETH balance', { originalError: err }), 'Balance check error');
|
|
499
|
+
}
|
|
500
|
+
// Check if user has enabled auto topup
|
|
501
|
+
// For each job type, autotopupETH should be present in jobInput
|
|
502
|
+
// Support autotopupTG for backward compatibility
|
|
503
|
+
const autoTopupETH = jobInput.autotopupETH === true || jobInput.autotopupTG === true;
|
|
504
|
+
if (!autoTopupETH) {
|
|
505
|
+
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError(`Insufficient ETH balance. Job cost prediction is ${requiredETHwei}. Current ETH balance: ${ethBalanceWei}. Please set autotopupETH: true in jobInput.`, {
|
|
506
|
+
required: requiredETHwei,
|
|
507
|
+
current: ethBalanceWei,
|
|
508
|
+
autoTopupEnabled: false
|
|
509
|
+
}), 'Insufficient balance');
|
|
510
|
+
}
|
|
511
|
+
else {
|
|
512
|
+
// autotopupETH is true, automatically top up
|
|
513
|
+
// Calculate requiredETH as 1.2x the predicted job cost (in wei) using bigint math to avoid precision loss,
|
|
514
|
+
// ensuring requiredETH is returned in the JobResponse (see types.ts).
|
|
515
|
+
requiredETH = (requiredETHwei * 12n) / 10n; // 1.2x in bigint
|
|
516
|
+
try {
|
|
517
|
+
const topupResult = await (0, topup_1.depositEth)(requiredETHwei, signer);
|
|
518
|
+
if (!topupResult.success) {
|
|
519
|
+
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to deposit ETH balance', topupResult.details), 'Top-up error');
|
|
454
520
|
}
|
|
455
521
|
}
|
|
522
|
+
catch (err) {
|
|
523
|
+
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to deposit ETH balance', { originalError: err, requiredETH }), 'Top-up error');
|
|
524
|
+
}
|
|
456
525
|
}
|
|
457
526
|
// Compute balances to store with the job
|
|
458
|
-
const tokenBalanceWei =
|
|
459
|
-
const etherBalance = tokenBalanceWei
|
|
527
|
+
const tokenBalanceWei = ethBalanceWei;
|
|
528
|
+
const etherBalance = tokenBalanceWei;
|
|
460
529
|
// Patch jobInput with job_cost_prediction for downstream usage
|
|
461
|
-
jobInput.jobCostPrediction =
|
|
530
|
+
jobInput.jobCostPrediction = Number(ethers_1.ethers.formatEther(tokenBalanceWei)); // this is in ether
|
|
462
531
|
let jobId;
|
|
463
532
|
try {
|
|
464
533
|
jobId = await (0, JobRegistry_1.createJobOnChain)({
|
|
@@ -468,7 +537,7 @@ async function createJob(client, params) {
|
|
|
468
537
|
targetContractAddress: targetContractAddress,
|
|
469
538
|
encodedData: encodedData || '',
|
|
470
539
|
contractAddress: JOB_REGISTRY_ADDRESS,
|
|
471
|
-
abi: JobRegistry_json_1.default
|
|
540
|
+
abi: JobRegistry_json_1.default,
|
|
472
541
|
signer,
|
|
473
542
|
});
|
|
474
543
|
}
|
|
@@ -479,13 +548,16 @@ async function createJob(client, params) {
|
|
|
479
548
|
let jobData;
|
|
480
549
|
const balances = { etherBalance, tokenBalanceWei };
|
|
481
550
|
if ('scheduleType' in jobInput) {
|
|
482
|
-
jobData = toCreateJobDataFromTime(jobInput, balances, userAddress, job_cost_prediction);
|
|
551
|
+
jobData = toCreateJobDataFromTime(jobInput, balances, userAddress, Number(ethers_1.ethers.formatEther(job_cost_prediction)));
|
|
483
552
|
}
|
|
484
553
|
else if ('triggerChainId' in jobInput) {
|
|
485
|
-
jobData = toCreateJobDataFromEvent(jobInput, balances, userAddress, job_cost_prediction);
|
|
554
|
+
jobData = toCreateJobDataFromEvent(jobInput, balances, userAddress, Number(ethers_1.ethers.formatEther(job_cost_prediction)));
|
|
555
|
+
}
|
|
556
|
+
else if (jobType === 7) {
|
|
557
|
+
jobData = toCreateJobDataFromCustomScript(jobInput, balances, userAddress, Number(ethers_1.ethers.formatEther(job_cost_prediction)));
|
|
486
558
|
}
|
|
487
559
|
else {
|
|
488
|
-
jobData = toCreateJobDataFromCondition(jobInput, balances, userAddress, job_cost_prediction);
|
|
560
|
+
jobData = toCreateJobDataFromCondition(jobInput, balances, userAddress, Number(ethers_1.ethers.formatEther(job_cost_prediction)));
|
|
489
561
|
}
|
|
490
562
|
// 3. Set the job_id from contract
|
|
491
563
|
jobData.job_id = jobId;
|
|
@@ -497,10 +569,11 @@ async function createJob(client, params) {
|
|
|
497
569
|
ether_balance: typeof jobData.ether_balance === 'bigint' ? Number(jobData.ether_balance) : Number(jobData.ether_balance),
|
|
498
570
|
token_balance: typeof jobData.token_balance === 'bigint' ? Number(jobData.token_balance) : Number(jobData.token_balance),
|
|
499
571
|
};
|
|
500
|
-
// console.log('jobDataForApi', jobDataForApi);
|
|
501
572
|
const res = await client.post('/api/jobs', [jobDataForApi], {
|
|
502
573
|
headers: { 'Content-Type': 'application/json', 'X-API-KEY': apiKey },
|
|
503
574
|
});
|
|
575
|
+
res.requiredETH = requiredETH;
|
|
576
|
+
res.maxtotalFeeRaw = maxtotalFeeRaw;
|
|
504
577
|
return { success: true, data: res };
|
|
505
578
|
}
|
|
506
579
|
catch (error) {
|