sdk-triggerx 0.1.30 → 0.1.32
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 +174 -108
- package/dist/api/topup.d.ts +20 -0
- package/dist/api/topup.js +102 -0
- package/dist/api/topupTg.js +4 -2
- package/dist/api/withdraw.d.ts +26 -0
- package/dist/api/withdraw.js +89 -0
- package/dist/api/withdrawTg.js +24 -2
- package/dist/config.js +5 -5
- package/dist/contracts/JobRegistry.js +5 -5
- package/dist/contracts/abi/GasRegistry.json +268 -423
- package/dist/contracts/abi/JobRegistry.json +710 -1524
- package/dist/contracts/contractUtils.d.ts +13 -0
- package/dist/contracts/contractUtils.js +41 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -2
- package/dist/types.d.ts +32 -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,134 @@ 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
|
+
// Prepare parameters for /api/fees
|
|
427
|
+
const ipfs_url = jobInput.dynamicArgumentsScriptUrl || '';
|
|
428
|
+
const task_definition_id = jobType; // use inferred jobType as task definition id
|
|
429
|
+
const target_chain_id = chainIdStr || '';
|
|
430
|
+
const target_contract_address = jobInput.targetContractAddress || '';
|
|
431
|
+
const target_function = jobInput.targetFunction || '';
|
|
432
|
+
const abi = jobInput.abi || '';
|
|
433
|
+
const args = jobInput.arguments ? JSON.stringify(jobInput.arguments) : '';
|
|
434
|
+
let job_cost_prediction = 0n;
|
|
435
|
+
try {
|
|
436
|
+
const feeRes = await client.get('/api/fees', {
|
|
437
|
+
params: {
|
|
438
|
+
ipfs_url,
|
|
439
|
+
task_definition_id,
|
|
440
|
+
target_chain_id,
|
|
441
|
+
target_contract_address,
|
|
442
|
+
target_function,
|
|
443
|
+
abi,
|
|
444
|
+
args,
|
|
401
445
|
}
|
|
446
|
+
});
|
|
447
|
+
// The API returns total fee in wei: { total_fee: "<wei>" } or nested under data
|
|
448
|
+
let totalFeeRaw;
|
|
449
|
+
if (feeRes && feeRes.total_fee !== undefined) {
|
|
450
|
+
totalFeeRaw = feeRes.total_fee;
|
|
402
451
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
452
|
+
else if (feeRes && feeRes.data && feeRes.data.total_fee !== undefined) {
|
|
453
|
+
totalFeeRaw = feeRes.data.total_fee;
|
|
454
|
+
}
|
|
455
|
+
if (totalFeeRaw === undefined) {
|
|
456
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ApiError('Invalid response from /api/fees: missing total_fee', { response: feeRes }), 'API error');
|
|
457
|
+
}
|
|
458
|
+
// Support both number and string representations of wei
|
|
459
|
+
let totalFeeWei;
|
|
460
|
+
if (typeof totalFeeRaw === 'string') {
|
|
461
|
+
totalFeeWei = BigInt(totalFeeRaw);
|
|
462
|
+
}
|
|
463
|
+
else if (typeof totalFeeRaw === 'number') {
|
|
464
|
+
totalFeeWei = BigInt(Math.floor(totalFeeRaw));
|
|
465
|
+
}
|
|
466
|
+
else {
|
|
467
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ApiError('Invalid total_fee type from /api/fees', { totalFeeRaw }), 'API error');
|
|
407
468
|
}
|
|
408
|
-
//
|
|
409
|
-
//
|
|
410
|
-
job_cost_prediction =
|
|
469
|
+
// Convert wei to token units (formatted ether) so it matches tgBalance units
|
|
470
|
+
// job_cost_prediction = Number(ethers.formatEther(totalFeeWei));
|
|
471
|
+
job_cost_prediction = totalFeeWei;
|
|
411
472
|
}
|
|
412
|
-
|
|
473
|
+
catch (err) {
|
|
474
|
+
const httpStatusCode = (0, errors_1.extractHttpStatusCode)(err);
|
|
475
|
+
const errorCode = (0, errors_1.determineErrorCode)(err, httpStatusCode);
|
|
476
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ApiError('Failed to fetch job cost prediction', { originalError: err, jobType }, httpStatusCode), 'API error');
|
|
477
|
+
}
|
|
478
|
+
job_cost_prediction = job_cost_prediction * BigInt(noOfExecutions);
|
|
479
|
+
let requiredETHwei = job_cost_prediction; // this is in wei
|
|
413
480
|
// Ask user if they want to proceed
|
|
414
481
|
// Since this is a library, we can't prompt in Node.js directly.
|
|
415
482
|
// We'll throw an error with the fee and let the caller handle the prompt/confirmation.
|
|
416
483
|
// 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;
|
|
484
|
+
// Check if the user has enough ETH to cover the job cost prediction
|
|
485
|
+
let ethBalanceWei, ethBalance;
|
|
420
486
|
try {
|
|
421
|
-
const balanceResult = await (0,
|
|
487
|
+
const balanceResult = await (0, checkBalance_1.checkEthBalance)(userAddress, chainIdStr);
|
|
422
488
|
if (!balanceResult.success || !balanceResult.data) {
|
|
423
|
-
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to check
|
|
489
|
+
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to check ETH balance', balanceResult.details), 'Balance check error');
|
|
424
490
|
}
|
|
425
|
-
|
|
426
|
-
|
|
491
|
+
ethBalanceWei = balanceResult.data.ethBalanceWei;
|
|
492
|
+
ethBalance = balanceResult.data.ethBalance;
|
|
427
493
|
}
|
|
428
494
|
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
|
-
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to top up TG balance', topupResult.details), 'Top-up error');
|
|
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');
|
|
495
|
+
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to check ETH balance', { originalError: err }), 'Balance check error');
|
|
496
|
+
}
|
|
497
|
+
// Check if user has enabled auto topup
|
|
498
|
+
// For each job type, autotopupETH should be present in jobInput
|
|
499
|
+
// Support autotopupTG for backward compatibility
|
|
500
|
+
const autoTopupETH = jobInput.autotopupETH === true || jobInput.autotopupTG === true;
|
|
501
|
+
if (!autoTopupETH) {
|
|
502
|
+
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.`, {
|
|
503
|
+
required: requiredETHwei,
|
|
504
|
+
current: ethBalanceWei,
|
|
505
|
+
autoTopupEnabled: false
|
|
506
|
+
}), 'Insufficient balance');
|
|
507
|
+
}
|
|
508
|
+
else {
|
|
509
|
+
// autotopupETH is true, automatically top up
|
|
510
|
+
const requiredETH = requiredETHwei;
|
|
511
|
+
try {
|
|
512
|
+
const topupResult = await (0, topup_1.depositEth)(requiredETHwei, signer);
|
|
513
|
+
if (!topupResult.success) {
|
|
514
|
+
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to deposit ETH balance', topupResult.details), 'Top-up error');
|
|
454
515
|
}
|
|
455
516
|
}
|
|
517
|
+
catch (err) {
|
|
518
|
+
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to deposit ETH balance', { originalError: err, requiredETH }), 'Top-up error');
|
|
519
|
+
}
|
|
456
520
|
}
|
|
457
521
|
// Compute balances to store with the job
|
|
458
|
-
const tokenBalanceWei =
|
|
459
|
-
const etherBalance = tokenBalanceWei
|
|
522
|
+
const tokenBalanceWei = ethBalanceWei;
|
|
523
|
+
const etherBalance = tokenBalanceWei;
|
|
460
524
|
// Patch jobInput with job_cost_prediction for downstream usage
|
|
461
|
-
jobInput.jobCostPrediction =
|
|
525
|
+
jobInput.jobCostPrediction = Number(ethers_1.ethers.formatEther(tokenBalanceWei)); // this is in ether
|
|
462
526
|
let jobId;
|
|
463
527
|
try {
|
|
464
528
|
jobId = await (0, JobRegistry_1.createJobOnChain)({
|
|
@@ -468,7 +532,7 @@ async function createJob(client, params) {
|
|
|
468
532
|
targetContractAddress: targetContractAddress,
|
|
469
533
|
encodedData: encodedData || '',
|
|
470
534
|
contractAddress: JOB_REGISTRY_ADDRESS,
|
|
471
|
-
abi: JobRegistry_json_1.default
|
|
535
|
+
abi: JobRegistry_json_1.default,
|
|
472
536
|
signer,
|
|
473
537
|
});
|
|
474
538
|
}
|
|
@@ -479,13 +543,16 @@ async function createJob(client, params) {
|
|
|
479
543
|
let jobData;
|
|
480
544
|
const balances = { etherBalance, tokenBalanceWei };
|
|
481
545
|
if ('scheduleType' in jobInput) {
|
|
482
|
-
jobData = toCreateJobDataFromTime(jobInput, balances, userAddress, job_cost_prediction);
|
|
546
|
+
jobData = toCreateJobDataFromTime(jobInput, balances, userAddress, Number(ethers_1.ethers.formatEther(job_cost_prediction)));
|
|
483
547
|
}
|
|
484
548
|
else if ('triggerChainId' in jobInput) {
|
|
485
|
-
jobData = toCreateJobDataFromEvent(jobInput, balances, userAddress, job_cost_prediction);
|
|
549
|
+
jobData = toCreateJobDataFromEvent(jobInput, balances, userAddress, Number(ethers_1.ethers.formatEther(job_cost_prediction)));
|
|
550
|
+
}
|
|
551
|
+
else if (jobType === 7) {
|
|
552
|
+
jobData = toCreateJobDataFromCustomScript(jobInput, balances, userAddress, Number(ethers_1.ethers.formatEther(job_cost_prediction)));
|
|
486
553
|
}
|
|
487
554
|
else {
|
|
488
|
-
jobData = toCreateJobDataFromCondition(jobInput, balances, userAddress, job_cost_prediction);
|
|
555
|
+
jobData = toCreateJobDataFromCondition(jobInput, balances, userAddress, Number(ethers_1.ethers.formatEther(job_cost_prediction)));
|
|
489
556
|
}
|
|
490
557
|
// 3. Set the job_id from contract
|
|
491
558
|
jobData.job_id = jobId;
|
|
@@ -497,7 +564,6 @@ async function createJob(client, params) {
|
|
|
497
564
|
ether_balance: typeof jobData.ether_balance === 'bigint' ? Number(jobData.ether_balance) : Number(jobData.ether_balance),
|
|
498
565
|
token_balance: typeof jobData.token_balance === 'bigint' ? Number(jobData.token_balance) : Number(jobData.token_balance),
|
|
499
566
|
};
|
|
500
|
-
// console.log('jobDataForApi', jobDataForApi);
|
|
501
567
|
const res = await client.post('/api/jobs', [jobDataForApi], {
|
|
502
568
|
headers: { 'Content-Type': 'application/json', 'X-API-KEY': apiKey },
|
|
503
569
|
});
|