sdk-triggerx 0.1.16 → 0.1.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -0
- package/dist/api/checkTgBalance.d.ts +9 -2
- package/dist/api/checkTgBalance.js +43 -24
- package/dist/api/deleteJob.d.ts +7 -1
- package/dist/api/deleteJob.js +29 -3
- package/dist/api/getJobDataById.d.ts +8 -1
- package/dist/api/getJobDataById.js +27 -3
- package/dist/api/getUserData.d.ts +8 -1
- package/dist/api/getUserData.js +27 -3
- package/dist/api/getjob.js +20 -2
- package/dist/api/jobs.js +141 -35
- package/dist/api/safeWallet.d.ts +8 -0
- package/dist/api/safeWallet.js +31 -0
- package/dist/api/topupTg.d.ts +8 -1
- package/dist/api/topupTg.js +39 -11
- package/dist/api/withdrawTg.d.ts +9 -2
- package/dist/api/withdrawTg.js +38 -13
- package/dist/config.d.ts +6 -0
- package/dist/config.js +6 -0
- package/dist/contracts/index.d.ts +3 -0
- package/dist/contracts/index.js +4 -0
- package/dist/contracts/safe/SafeFactory.d.ts +3 -0
- package/dist/contracts/safe/SafeFactory.js +33 -0
- package/dist/contracts/safe/SafeWallet.d.ts +6 -0
- package/dist/contracts/safe/SafeWallet.js +109 -0
- package/dist/types.d.ts +51 -9
- package/dist/utils/errors.d.ts +40 -1
- package/dist/utils/errors.js +227 -4
- package/dist/utils/validation.d.ts +5 -0
- package/dist/utils/validation.js +212 -0
- package/package.json +3 -3
package/dist/api/jobs.js
CHANGED
|
@@ -13,6 +13,9 @@ const JobRegistry_json_1 = __importDefault(require("../contracts/abi/JobRegistry
|
|
|
13
13
|
const topupTg_1 = require("./topupTg");
|
|
14
14
|
const checkTgBalance_1 = require("./checkTgBalance");
|
|
15
15
|
const config_1 = require("../config");
|
|
16
|
+
const validation_1 = require("../utils/validation");
|
|
17
|
+
const errors_1 = require("../utils/errors");
|
|
18
|
+
const SafeWallet_1 = require("../contracts/safe/SafeWallet");
|
|
16
19
|
const JOB_ID = '300949528249665590178224313442040528409305273634097553067152835846309150732';
|
|
17
20
|
const DYNAMIC_ARGS_URL = 'https://teal-random-koala-993.mypinata.cloud/ipfs/bafkreif426p7t7takzhw3g6we2h6wsvf27p5jxj3gaiynqf22p3jvhx4la';
|
|
18
21
|
function toCreateJobDataFromTime(input, balances, userAddress, jobCostPrediction) {
|
|
@@ -34,9 +37,9 @@ function toCreateJobDataFromTime(input, balances, userAddress, jobCostPrediction
|
|
|
34
37
|
cron_expression: input.scheduleType === 'cron' ? input.cronExpression : undefined,
|
|
35
38
|
specific_schedule: input.scheduleType === 'specific' ? input.specificSchedule : undefined,
|
|
36
39
|
target_chain_id: input.chainId,
|
|
37
|
-
target_contract_address: input.targetContractAddress,
|
|
38
|
-
target_function: input.targetFunction,
|
|
39
|
-
abi: input.abi,
|
|
40
|
+
target_contract_address: input.targetContractAddress || '',
|
|
41
|
+
target_function: input.targetFunction || '',
|
|
42
|
+
abi: input.abi || '',
|
|
40
43
|
arg_type: input.dynamicArgumentsScriptUrl ? 2 : 1,
|
|
41
44
|
arguments: input.arguments,
|
|
42
45
|
dynamic_arguments_script_url: input.dynamicArgumentsScriptUrl,
|
|
@@ -61,9 +64,9 @@ function toCreateJobDataFromEvent(input, balances, userAddress, jobCostPredictio
|
|
|
61
64
|
trigger_contract_address: input.triggerContractAddress,
|
|
62
65
|
trigger_event: input.triggerEvent,
|
|
63
66
|
target_chain_id: input.chainId,
|
|
64
|
-
target_contract_address: input.targetContractAddress,
|
|
65
|
-
target_function: input.targetFunction,
|
|
66
|
-
abi: input.abi,
|
|
67
|
+
target_contract_address: input.targetContractAddress || '',
|
|
68
|
+
target_function: input.targetFunction || '',
|
|
69
|
+
abi: input.abi || '',
|
|
67
70
|
arg_type: input.dynamicArgumentsScriptUrl ? 2 : 1,
|
|
68
71
|
arguments: input.arguments,
|
|
69
72
|
dynamic_arguments_script_url: input.dynamicArgumentsScriptUrl,
|
|
@@ -90,9 +93,9 @@ function toCreateJobDataFromCondition(input, balances, userAddress, jobCostPredi
|
|
|
90
93
|
value_source_type: input.valueSourceType,
|
|
91
94
|
value_source_url: input.valueSourceUrl,
|
|
92
95
|
target_chain_id: input.chainId,
|
|
93
|
-
target_contract_address: input.targetContractAddress,
|
|
94
|
-
target_function: input.targetFunction,
|
|
95
|
-
abi: input.abi,
|
|
96
|
+
target_contract_address: input.targetContractAddress || '',
|
|
97
|
+
target_function: input.targetFunction || '',
|
|
98
|
+
abi: input.abi || '',
|
|
96
99
|
arg_type: input.dynamicArgumentsScriptUrl ? 2 : 1,
|
|
97
100
|
arguments: input.arguments,
|
|
98
101
|
dynamic_arguments_script_url: input.dynamicArgumentsScriptUrl,
|
|
@@ -122,32 +125,102 @@ async function createJob(client, params) {
|
|
|
122
125
|
let { jobInput, signer, encodedData } = params;
|
|
123
126
|
// Use the API key from the client instance
|
|
124
127
|
const apiKey = client.getApiKey();
|
|
125
|
-
|
|
128
|
+
if (!apiKey) {
|
|
129
|
+
return (0, errors_1.createErrorResponse)(new errors_1.AuthenticationError('API key is required but not provided'), 'Authentication failed');
|
|
130
|
+
}
|
|
131
|
+
let userAddress;
|
|
132
|
+
try {
|
|
133
|
+
userAddress = await signer.getAddress();
|
|
134
|
+
}
|
|
135
|
+
catch (err) {
|
|
136
|
+
return (0, errors_1.createErrorResponse)(new errors_1.AuthenticationError('Failed to get signer address', { originalError: err }), 'Authentication failed');
|
|
137
|
+
}
|
|
126
138
|
// Resolve chain-specific addresses
|
|
127
|
-
|
|
139
|
+
let network;
|
|
140
|
+
try {
|
|
141
|
+
network = await signer.provider?.getNetwork();
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
return (0, errors_1.createErrorResponse)(new errors_1.NetworkError('Failed to get network information', { originalError: err }), 'Network error');
|
|
145
|
+
}
|
|
128
146
|
const chainIdStr = network?.chainId ? network.chainId.toString() : undefined;
|
|
129
|
-
const { jobRegistry } = (0, config_1.getChainAddresses)(chainIdStr);
|
|
147
|
+
const { jobRegistry, safeModule, safeFactory } = (0, config_1.getChainAddresses)(chainIdStr);
|
|
130
148
|
const JOB_REGISTRY_ADDRESS = jobRegistry;
|
|
131
149
|
if (!JOB_REGISTRY_ADDRESS) {
|
|
132
|
-
return
|
|
150
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ConfigurationError(`JobRegistry address not configured for chain ID: ${chainIdStr}`), 'Configuration error');
|
|
151
|
+
}
|
|
152
|
+
// If Safe mode, override target fields BEFORE validation so user doesn't need to provide them
|
|
153
|
+
const walletModePre = jobInput.walletMode;
|
|
154
|
+
if (walletModePre === 'safe') {
|
|
155
|
+
if (!safeModule) {
|
|
156
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ConfigurationError('Safe Module address not configured for this chain.'), 'Configuration error');
|
|
157
|
+
}
|
|
158
|
+
// If safeAddress is missing, require it (must be created by the user before this call)
|
|
159
|
+
if (!jobInput.safeAddress || typeof jobInput.safeAddress !== 'string' || !jobInput.safeAddress.trim()) {
|
|
160
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('safeAddress', 'safeAddress is required when walletMode is "safe". Call createSafeWallet first.'), 'Validation error');
|
|
161
|
+
}
|
|
162
|
+
const dynUrl = jobInput.dynamicArgumentsScriptUrl;
|
|
163
|
+
if (!dynUrl) {
|
|
164
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('dynamicArgumentsScriptUrl', 'Safe jobs require dynamicArgumentsScriptUrl (IPFS) for parameters.'), 'Validation error');
|
|
165
|
+
}
|
|
166
|
+
// Validate Safe has single owner and owner matches signer
|
|
167
|
+
try {
|
|
168
|
+
await (0, SafeWallet_1.ensureSingleOwnerAndMatchSigner)(jobInput.safeAddress, signer.provider, await signer.getAddress());
|
|
169
|
+
// Ensure module is enabled on Safe
|
|
170
|
+
await (0, SafeWallet_1.enableSafeModule)(jobInput.safeAddress, signer, safeModule);
|
|
171
|
+
}
|
|
172
|
+
catch (err) {
|
|
173
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ContractError('Failed to configure Safe wallet', { originalError: err, safeAddress: jobInput.safeAddress }), 'Contract error');
|
|
174
|
+
}
|
|
175
|
+
// Auto-set module target; user does not need to pass targetContractAddress in safe mode
|
|
176
|
+
jobInput.targetContractAddress = safeModule;
|
|
177
|
+
jobInput.targetFunction = 'execJobFromHub(address,address,uint256,bytes,uint8)';
|
|
178
|
+
jobInput.abi = JSON.stringify([
|
|
179
|
+
{
|
|
180
|
+
"type": "function", "name": "execJobFromHub", "stateMutability": "nonpayable", "inputs": [
|
|
181
|
+
{ "name": "safeAddress", "type": "address" },
|
|
182
|
+
{ "name": "actionTarget", "type": "address" },
|
|
183
|
+
{ "name": "actionValue", "type": "uint256" },
|
|
184
|
+
{ "name": "actionData", "type": "bytes" },
|
|
185
|
+
{ "name": "operation", "type": "uint8" }
|
|
186
|
+
], "outputs": [{ "type": "bool", "name": "success" }]
|
|
187
|
+
}
|
|
188
|
+
]);
|
|
189
|
+
// Ensure we don't carry static args in safe mode
|
|
190
|
+
jobInput.arguments = undefined;
|
|
191
|
+
}
|
|
192
|
+
// 0. Validate user input thoroughly before proceeding (after safe overrides)
|
|
193
|
+
try {
|
|
194
|
+
const argValue = jobInput.argType;
|
|
195
|
+
(0, validation_1.validateJobInput)(jobInput, argValue);
|
|
196
|
+
console.log('Job input validated successfully');
|
|
197
|
+
}
|
|
198
|
+
catch (err) {
|
|
199
|
+
if (err instanceof errors_1.ValidationError) {
|
|
200
|
+
return (0, errors_1.createErrorResponse)(err);
|
|
201
|
+
}
|
|
202
|
+
return (0, errors_1.createErrorResponse)(err, 'Job input validation failed');
|
|
133
203
|
}
|
|
134
|
-
let jobTitle
|
|
204
|
+
let jobTitle = '';
|
|
205
|
+
let timeFrame = 0;
|
|
206
|
+
let targetContractAddress = '';
|
|
207
|
+
let jobType = 0;
|
|
135
208
|
if ('jobTitle' in jobInput)
|
|
136
209
|
jobTitle = jobInput.jobTitle;
|
|
137
210
|
if ('timeFrame' in jobInput)
|
|
138
211
|
timeFrame = jobInput.timeFrame;
|
|
139
212
|
if ('targetContractAddress' in jobInput)
|
|
140
|
-
targetContractAddress = jobInput.targetContractAddress;
|
|
213
|
+
targetContractAddress = jobInput.targetContractAddress || '';
|
|
141
214
|
// Validate schedule-specific fields for time-based jobs
|
|
142
215
|
if ('scheduleType' in jobInput) {
|
|
143
216
|
if (jobInput.scheduleType === 'interval' && (jobInput.timeInterval === undefined || jobInput.timeInterval === null)) {
|
|
144
|
-
|
|
217
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('timeInterval', 'timeInterval is required when scheduleType is interval'), 'Validation error');
|
|
145
218
|
}
|
|
146
219
|
if (jobInput.scheduleType === 'cron' && !jobInput.cronExpression) {
|
|
147
|
-
|
|
220
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('cronExpression', 'cronExpression is required when scheduleType is cron'), 'Validation error');
|
|
148
221
|
}
|
|
149
222
|
if (jobInput.scheduleType === 'specific' && !jobInput.specificSchedule) {
|
|
150
|
-
|
|
223
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('specificSchedule', 'specificSchedule is required when scheduleType is specific'), 'Validation error');
|
|
151
224
|
}
|
|
152
225
|
}
|
|
153
226
|
// Infer jobType from jobInput
|
|
@@ -216,7 +289,7 @@ async function createJob(client, params) {
|
|
|
216
289
|
// Dynamic: call backend API to get fee
|
|
217
290
|
const ipfs_url = jobInput.dynamicArgumentsScriptUrl;
|
|
218
291
|
if (!ipfs_url) {
|
|
219
|
-
|
|
292
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('dynamicArgumentsScriptUrl', 'dynamicArgumentsScriptUrl is required for dynamic argType'), 'Validation error');
|
|
220
293
|
}
|
|
221
294
|
// Call backend API to get fee
|
|
222
295
|
let fee = 0;
|
|
@@ -230,11 +303,13 @@ async function createJob(client, params) {
|
|
|
230
303
|
fee = feeRes.data.total_fee;
|
|
231
304
|
}
|
|
232
305
|
else {
|
|
233
|
-
|
|
306
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ApiError('Invalid response from /api/fees: missing total_fee', { response: feeRes }), 'API error');
|
|
234
307
|
}
|
|
235
308
|
}
|
|
236
309
|
catch (err) {
|
|
237
|
-
|
|
310
|
+
const httpStatusCode = (0, errors_1.extractHttpStatusCode)(err);
|
|
311
|
+
const errorCode = (0, errors_1.determineErrorCode)(err, httpStatusCode);
|
|
312
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ApiError('Failed to fetch job cost prediction', { originalError: err, ipfs_url }, httpStatusCode), 'API error');
|
|
238
313
|
}
|
|
239
314
|
job_cost_prediction = fee * noOfExecutions;
|
|
240
315
|
}
|
|
@@ -243,18 +318,41 @@ async function createJob(client, params) {
|
|
|
243
318
|
// We'll throw an error with the fee and let the caller handle the prompt/confirmation.
|
|
244
319
|
// If you want to automate, you can add a `proceed` flag to params in the future.
|
|
245
320
|
// Check if the user has enough TG to cover the job cost prediction
|
|
246
|
-
|
|
321
|
+
let tgBalanceWei, tgBalance;
|
|
322
|
+
try {
|
|
323
|
+
const balanceResult = await (0, checkTgBalance_1.checkTgBalance)(signer);
|
|
324
|
+
if (!balanceResult.success || !balanceResult.data) {
|
|
325
|
+
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to check TG balance', balanceResult.details), 'Balance check error');
|
|
326
|
+
}
|
|
327
|
+
tgBalanceWei = balanceResult.data.tgBalanceWei;
|
|
328
|
+
tgBalance = balanceResult.data.tgBalance;
|
|
329
|
+
}
|
|
330
|
+
catch (err) {
|
|
331
|
+
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to check TG balance', { originalError: err }), 'Balance check error');
|
|
332
|
+
}
|
|
247
333
|
if (Number(tgBalance) < job_cost_prediction) {
|
|
248
334
|
// Check if user has enabled auto topup
|
|
249
335
|
// For each job type, autotopupTG should be present in jobInput
|
|
250
336
|
const autoTopupTG = jobInput.autotopupTG === true;
|
|
251
337
|
if (!autoTopupTG) {
|
|
252
|
-
|
|
338
|
+
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError(`Insufficient TG balance. Job cost prediction is ${job_cost_prediction}. Current TG balance: ${tgBalance}. Please set autotopupTG: true in jobInput.`, {
|
|
339
|
+
required: job_cost_prediction,
|
|
340
|
+
current: tgBalance,
|
|
341
|
+
autoTopupEnabled: false
|
|
342
|
+
}), 'Insufficient balance');
|
|
253
343
|
}
|
|
254
344
|
else {
|
|
255
345
|
// autotopupTG is true, automatically top up
|
|
256
346
|
const requiredTG = Math.ceil(job_cost_prediction); // 1 TG = 0.001 ETH
|
|
257
|
-
|
|
347
|
+
try {
|
|
348
|
+
const topupResult = await (0, topupTg_1.topupTg)(requiredTG, signer);
|
|
349
|
+
if (!topupResult.success) {
|
|
350
|
+
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to top up TG balance', topupResult.details), 'Top-up error');
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
catch (err) {
|
|
354
|
+
return (0, errors_1.createErrorResponse)(new errors_1.BalanceError('Failed to top up TG balance', { originalError: err, requiredTG }), 'Top-up error');
|
|
355
|
+
}
|
|
258
356
|
}
|
|
259
357
|
}
|
|
260
358
|
// Compute balances to store with the job
|
|
@@ -262,16 +360,22 @@ async function createJob(client, params) {
|
|
|
262
360
|
const etherBalance = tokenBalanceWei / 1000n;
|
|
263
361
|
// Patch jobInput with job_cost_prediction for downstream usage
|
|
264
362
|
jobInput.jobCostPrediction = job_cost_prediction;
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
363
|
+
let jobId;
|
|
364
|
+
try {
|
|
365
|
+
jobId = await (0, JobRegistry_1.createJobOnChain)({
|
|
366
|
+
jobTitle: jobTitle,
|
|
367
|
+
jobType,
|
|
368
|
+
timeFrame: timeFrame,
|
|
369
|
+
targetContractAddress: targetContractAddress,
|
|
370
|
+
encodedData: encodedData || '',
|
|
371
|
+
contractAddress: JOB_REGISTRY_ADDRESS,
|
|
372
|
+
abi: JobRegistry_json_1.default.abi,
|
|
373
|
+
signer,
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
catch (err) {
|
|
377
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ContractError('Failed to create job on chain', { originalError: err, jobTitle, jobType, timeFrame }), 'Contract error');
|
|
378
|
+
}
|
|
275
379
|
// 2. Convert input to CreateJobData
|
|
276
380
|
let jobData;
|
|
277
381
|
const balances = { etherBalance, tokenBalanceWei };
|
|
@@ -300,6 +404,8 @@ async function createJob(client, params) {
|
|
|
300
404
|
return { success: true, data: res };
|
|
301
405
|
}
|
|
302
406
|
catch (error) {
|
|
303
|
-
|
|
407
|
+
const httpStatusCode = (0, errors_1.extractHttpStatusCode)(error);
|
|
408
|
+
const errorCode = (0, errors_1.determineErrorCode)(error, httpStatusCode);
|
|
409
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ApiError('Failed to create job via API', { originalError: error, jobId }, httpStatusCode), 'API error');
|
|
304
410
|
}
|
|
305
411
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Signer } from 'ethers';
|
|
2
|
+
/**
|
|
3
|
+
* Creates a new Safe wallet for the user on the signer's network.
|
|
4
|
+
* @param signer ethers.Signer (must be connected to the correct network)
|
|
5
|
+
* @returns Promise<string> - the new Safe wallet address
|
|
6
|
+
* @throws If cannot resolve SafeFactory address from config, or provider/chain/network errors.
|
|
7
|
+
*/
|
|
8
|
+
export declare function createSafeWallet(signer: Signer): Promise<string>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createSafeWallet = createSafeWallet;
|
|
4
|
+
const config_1 = require("../config");
|
|
5
|
+
const SafeFactory_1 = require("../contracts/safe/SafeFactory");
|
|
6
|
+
const SafeWallet_1 = require("../contracts/safe/SafeWallet");
|
|
7
|
+
/**
|
|
8
|
+
* Creates a new Safe wallet for the user on the signer's network.
|
|
9
|
+
* @param signer ethers.Signer (must be connected to the correct network)
|
|
10
|
+
* @returns Promise<string> - the new Safe wallet address
|
|
11
|
+
* @throws If cannot resolve SafeFactory address from config, or provider/chain/network errors.
|
|
12
|
+
*/
|
|
13
|
+
async function createSafeWallet(signer) {
|
|
14
|
+
if (!signer.provider)
|
|
15
|
+
throw new Error('Signer must have a provider');
|
|
16
|
+
const network = await signer.provider.getNetwork();
|
|
17
|
+
if (!network?.chainId)
|
|
18
|
+
throw new Error('Could not get chainId from signer provider');
|
|
19
|
+
const chainId = network.chainId.toString();
|
|
20
|
+
const { safeFactory } = (0, config_1.getChainAddresses)(chainId);
|
|
21
|
+
if (!safeFactory)
|
|
22
|
+
throw new Error(`SafeFactory not configured for chain ${chainId}`);
|
|
23
|
+
const userAddr = await signer.getAddress();
|
|
24
|
+
const safeAddress = await (0, SafeFactory_1.createSafeWalletForUser)(safeFactory, signer, userAddr);
|
|
25
|
+
const { safeModule } = (0, config_1.getChainAddresses)(chainId);
|
|
26
|
+
if (!safeModule) {
|
|
27
|
+
throw new Error(`SafeModule not configured for chain ${chainId}`);
|
|
28
|
+
}
|
|
29
|
+
await (0, SafeWallet_1.enableSafeModule)(safeAddress, signer, safeModule);
|
|
30
|
+
return safeAddress;
|
|
31
|
+
}
|
package/dist/api/topupTg.d.ts
CHANGED
|
@@ -1,2 +1,9 @@
|
|
|
1
1
|
import { ethers } from 'ethers';
|
|
2
|
-
export declare const topupTg: (tgAmount: number, signer: ethers.Signer) => Promise<
|
|
2
|
+
export declare const topupTg: (tgAmount: number, signer: ethers.Signer) => Promise<{
|
|
3
|
+
success: boolean;
|
|
4
|
+
data?: any;
|
|
5
|
+
error?: string;
|
|
6
|
+
errorCode?: string;
|
|
7
|
+
errorType?: string;
|
|
8
|
+
details?: any;
|
|
9
|
+
}>;
|
package/dist/api/topupTg.js
CHANGED
|
@@ -7,17 +7,45 @@ exports.topupTg = void 0;
|
|
|
7
7
|
const ethers_1 = require("ethers");
|
|
8
8
|
const GasRegistry_json_1 = __importDefault(require("../contracts/abi/GasRegistry.json"));
|
|
9
9
|
const config_1 = require("../config");
|
|
10
|
+
const errors_1 = require("../utils/errors");
|
|
10
11
|
const topupTg = async (tgAmount, signer) => {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
12
|
+
// Validate inputs
|
|
13
|
+
if (!tgAmount || tgAmount <= 0) {
|
|
14
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('tgAmount', 'TG amount must be a positive number'), 'Validation error');
|
|
15
|
+
}
|
|
16
|
+
if (!signer) {
|
|
17
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('signer', 'Signer is required'), 'Validation error');
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const network = await signer.provider?.getNetwork();
|
|
21
|
+
const chainId = network?.chainId ? network.chainId.toString() : undefined;
|
|
22
|
+
const { gasRegistry } = (0, config_1.getChainAddresses)(chainId);
|
|
23
|
+
const gasRegistryContractAddress = gasRegistry;
|
|
24
|
+
if (!gasRegistryContractAddress) {
|
|
25
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ConfigurationError(`GasRegistry address not configured for chain ID: ${chainId}`), 'Configuration error');
|
|
26
|
+
}
|
|
27
|
+
const contract = new ethers_1.ethers.Contract(gasRegistryContractAddress, GasRegistry_json_1.default, signer);
|
|
28
|
+
// Each TG costs 0.001 ETH, so calculate the ETH required for the given TG amount
|
|
29
|
+
const amountInEthWei = tgAmount;
|
|
30
|
+
// const amountInEthWei = ethers.parseEther(amountInEth.toString());
|
|
31
|
+
const tx = await contract.purchaseTG(amountInEthWei, { value: amountInEthWei });
|
|
32
|
+
await tx.wait();
|
|
33
|
+
return { success: true, data: tx };
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
console.error('Error topping up TG:', error);
|
|
37
|
+
if (error instanceof Error) {
|
|
38
|
+
if (error.message.includes('network') || error.message.includes('timeout')) {
|
|
39
|
+
return (0, errors_1.createErrorResponse)(new errors_1.NetworkError('Network error during TG top-up', { originalError: error, tgAmount }), 'Network error');
|
|
40
|
+
}
|
|
41
|
+
else if (error.message.includes('contract') || error.message.includes('transaction')) {
|
|
42
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ContractError('Contract error during TG top-up', { originalError: error, tgAmount }), 'Contract error');
|
|
43
|
+
}
|
|
44
|
+
else if (error.message.includes('insufficient funds') || error.message.includes('balance')) {
|
|
45
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('balance', 'Insufficient funds for TG top-up', { originalError: error, tgAmount }), 'Validation error');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
return (0, errors_1.createErrorResponse)(error, 'Failed to top up TG');
|
|
49
|
+
}
|
|
22
50
|
};
|
|
23
51
|
exports.topupTg = topupTg;
|
package/dist/api/withdrawTg.d.ts
CHANGED
|
@@ -3,6 +3,13 @@ import { ethers } from 'ethers';
|
|
|
3
3
|
* Withdraw ETH in exchange for TG tokens.
|
|
4
4
|
* @param signer ethers.Signer instance
|
|
5
5
|
* @param amountTG The amount of TG tokens to withdraw (as a string or BigNumberish)
|
|
6
|
-
* @returns The transaction object
|
|
6
|
+
* @returns The transaction object or error response
|
|
7
7
|
*/
|
|
8
|
-
export declare const withdrawTg: (signer: ethers.Signer, amountTG: string | ethers.BigNumberish) => Promise<
|
|
8
|
+
export declare const withdrawTg: (signer: ethers.Signer, amountTG: string | ethers.BigNumberish) => Promise<{
|
|
9
|
+
success: boolean;
|
|
10
|
+
data?: any;
|
|
11
|
+
error?: string;
|
|
12
|
+
errorCode?: string;
|
|
13
|
+
errorType?: string;
|
|
14
|
+
details?: any;
|
|
15
|
+
}>;
|
package/dist/api/withdrawTg.js
CHANGED
|
@@ -7,25 +7,50 @@ exports.withdrawTg = void 0;
|
|
|
7
7
|
const ethers_1 = require("ethers");
|
|
8
8
|
const GasRegistry_json_1 = __importDefault(require("../contracts/abi/GasRegistry.json"));
|
|
9
9
|
const config_1 = require("../config");
|
|
10
|
+
const errors_1 = require("../utils/errors");
|
|
10
11
|
/**
|
|
11
12
|
* Withdraw ETH in exchange for TG tokens.
|
|
12
13
|
* @param signer ethers.Signer instance
|
|
13
14
|
* @param amountTG The amount of TG tokens to withdraw (as a string or BigNumberish)
|
|
14
|
-
* @returns The transaction object
|
|
15
|
+
* @returns The transaction object or error response
|
|
15
16
|
*/
|
|
16
17
|
const withdrawTg = async (signer, amountTG) => {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
if (!
|
|
22
|
-
|
|
18
|
+
// Validate inputs
|
|
19
|
+
if (!signer) {
|
|
20
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('signer', 'Signer is required'), 'Validation error');
|
|
21
|
+
}
|
|
22
|
+
if (!amountTG || (typeof amountTG === 'string' && amountTG.trim() === '') || Number(amountTG) <= 0) {
|
|
23
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('amountTG', 'Amount must be a positive number'), 'Validation error');
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const network = await signer.provider?.getNetwork();
|
|
27
|
+
const chainId = network?.chainId ? network.chainId.toString() : undefined;
|
|
28
|
+
const { gasRegistry } = (0, config_1.getChainAddresses)(chainId);
|
|
29
|
+
const gasRegistryContractAddress = gasRegistry;
|
|
30
|
+
if (!gasRegistryContractAddress) {
|
|
31
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ConfigurationError(`GasRegistry address not configured for chain ID: ${chainId}`), 'Configuration error');
|
|
32
|
+
}
|
|
33
|
+
const contract = new ethers_1.ethers.Contract(gasRegistryContractAddress, GasRegistry_json_1.default, signer);
|
|
34
|
+
// Assumes the contract has a function: claimEthForTg(uint256 amount)
|
|
35
|
+
const amountTGWei = ethers_1.ethers.parseEther(amountTG.toString());
|
|
36
|
+
const tx = await contract.claimETHForTG(amountTGWei);
|
|
37
|
+
await tx.wait();
|
|
38
|
+
return { success: true, data: tx };
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
console.error('Error withdrawing TG:', error);
|
|
42
|
+
if (error instanceof Error) {
|
|
43
|
+
if (error.message.includes('network') || error.message.includes('timeout')) {
|
|
44
|
+
return (0, errors_1.createErrorResponse)(new errors_1.NetworkError('Network error during TG withdrawal', { originalError: error, amountTG }), 'Network error');
|
|
45
|
+
}
|
|
46
|
+
else if (error.message.includes('contract') || error.message.includes('transaction')) {
|
|
47
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ContractError('Contract error during TG withdrawal', { originalError: error, amountTG }), 'Contract error');
|
|
48
|
+
}
|
|
49
|
+
else if (error.message.includes('insufficient') || error.message.includes('balance')) {
|
|
50
|
+
return (0, errors_1.createErrorResponse)(new errors_1.ValidationError('balance', 'Insufficient TG balance for withdrawal', { originalError: error, amountTG }), 'Validation error');
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return (0, errors_1.createErrorResponse)(error, 'Failed to withdraw TG');
|
|
23
54
|
}
|
|
24
|
-
const contract = new ethers_1.ethers.Contract(gasRegistryContractAddress, GasRegistry_json_1.default, signer);
|
|
25
|
-
// Assumes the contract has a function: claimEthForTg(uint256 amount)
|
|
26
|
-
const amountTGWei = ethers_1.ethers.parseEther(amountTG.toString());
|
|
27
|
-
const tx = await contract.claimETHForTG(amountTGWei);
|
|
28
|
-
await tx.wait();
|
|
29
|
-
return tx;
|
|
30
55
|
};
|
|
31
56
|
exports.withdrawTg = withdrawTg;
|
package/dist/config.d.ts
CHANGED
|
@@ -5,9 +5,15 @@ export interface SDKConfig {
|
|
|
5
5
|
export declare const CONTRACT_ADDRESSES_BY_CHAIN: Record<string, {
|
|
6
6
|
gasRegistry: string;
|
|
7
7
|
jobRegistry: string;
|
|
8
|
+
safeFactory?: string;
|
|
9
|
+
safeModule?: string;
|
|
10
|
+
safeSingleton?: string;
|
|
8
11
|
}>;
|
|
9
12
|
export declare function getConfig(): SDKConfig;
|
|
10
13
|
export declare function getChainAddresses(chainId: string | number | undefined): {
|
|
11
14
|
gasRegistry: string;
|
|
12
15
|
jobRegistry: string;
|
|
16
|
+
safeFactory: string | undefined;
|
|
17
|
+
safeModule: string | undefined;
|
|
18
|
+
safeSingleton: string | undefined;
|
|
13
19
|
};
|
package/dist/config.js
CHANGED
|
@@ -23,6 +23,9 @@ exports.CONTRACT_ADDRESSES_BY_CHAIN = {
|
|
|
23
23
|
'421614': {
|
|
24
24
|
gasRegistry: '0x664CB20BCEEc9416D290AC820e5446e61a5c75e4',
|
|
25
25
|
jobRegistry: '0x476ACc7949a95e31144cC84b8F6BC7abF0967E4b',
|
|
26
|
+
safeFactory: '0x383D4a61D0B069D02cA2Db5A82003b9561d56e19',
|
|
27
|
+
safeModule: '0xca3a0c43Ac9E4FcB76C774F330fD31D4098EEacD',
|
|
28
|
+
// safeSingleton can be provided per deployment (Safe or SafeL2)
|
|
26
29
|
},
|
|
27
30
|
// Base Sepolia (84532) - Base Sepolia Testnet
|
|
28
31
|
'84532': {
|
|
@@ -49,5 +52,8 @@ function getChainAddresses(chainId) {
|
|
|
49
52
|
return {
|
|
50
53
|
gasRegistry: mapped ? mapped.gasRegistry : '',
|
|
51
54
|
jobRegistry: mapped ? mapped.jobRegistry : '',
|
|
55
|
+
safeFactory: mapped ? mapped.safeFactory : '',
|
|
56
|
+
safeModule: mapped ? mapped.safeModule : '',
|
|
57
|
+
safeSingleton: mapped ? mapped.safeSingleton : '',
|
|
52
58
|
};
|
|
53
59
|
}
|
package/dist/contracts/index.js
CHANGED
|
@@ -14,4 +14,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
__exportStar(require("./JobRegistry"), exports);
|
|
18
|
+
// Safe-related helpers
|
|
19
|
+
__exportStar(require("./safe/SafeFactory"), exports);
|
|
20
|
+
__exportStar(require("./safe/SafeWallet"), exports);
|
|
17
21
|
__exportStar(require("./TriggerXContract"), exports);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TRIGGERX_SAFE_FACTORY_ABI = void 0;
|
|
4
|
+
exports.createSafeWalletForUser = createSafeWalletForUser;
|
|
5
|
+
const ethers_1 = require("ethers");
|
|
6
|
+
// Minimal ABI for TriggerXSafeFactory
|
|
7
|
+
exports.TRIGGERX_SAFE_FACTORY_ABI = [
|
|
8
|
+
"function createSafeWallet(address user) returns (address)",
|
|
9
|
+
"function latestSafeWallet(address user) view returns (address)",
|
|
10
|
+
"function getSafeWallets(address user) view returns (address[])",
|
|
11
|
+
"function predictSafeAddress(address user) view returns (address)",
|
|
12
|
+
"event SafeWalletCreated(address indexed user, address indexed safeWallet, uint256 saltNonce)"
|
|
13
|
+
];
|
|
14
|
+
async function createSafeWalletForUser(factoryAddress, signer, user) {
|
|
15
|
+
const factory = new ethers_1.ethers.Contract(factoryAddress, exports.TRIGGERX_SAFE_FACTORY_ABI, signer);
|
|
16
|
+
const tx = await factory.createSafeWallet(user);
|
|
17
|
+
const receipt = await tx.wait();
|
|
18
|
+
// Try to fetch from event; fallback to latestSafeWallet
|
|
19
|
+
const evt = receipt.logs
|
|
20
|
+
.map((l) => {
|
|
21
|
+
try {
|
|
22
|
+
return factory.interface.parseLog(l);
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
})
|
|
28
|
+
.find((e) => e && e.name === 'SafeWalletCreated');
|
|
29
|
+
if (evt && evt.args && evt.args.safeWallet) {
|
|
30
|
+
return evt.args.safeWallet;
|
|
31
|
+
}
|
|
32
|
+
return await factory.latestSafeWallet(user);
|
|
33
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ethers, Signer } from 'ethers';
|
|
2
|
+
export declare const SAFE_ABI: string[];
|
|
3
|
+
export declare const SAFE_TX_TYPEHASH: string;
|
|
4
|
+
export declare function isSafeModuleEnabled(safeAddress: string, provider: ethers.Provider, moduleAddress: string): Promise<boolean>;
|
|
5
|
+
export declare function ensureSingleOwnerAndMatchSigner(safeAddress: string, provider: ethers.Provider, signerAddress: string): Promise<void>;
|
|
6
|
+
export declare function enableSafeModule(safeAddress: string, signer: Signer, moduleAddress: string): Promise<void>;
|