@pushchain/core 2.0.2 → 2.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/package.json +4 -3
  2. package/src/lib/constants/abi/erc20.evm.d.ts +36 -0
  3. package/src/lib/constants/abi/erc20.evm.js +26 -0
  4. package/src/lib/constants/abi/erc20.evm.js.map +1 -0
  5. package/src/lib/constants/abi/index.d.ts +3 -0
  6. package/src/lib/constants/abi/index.js +7 -1
  7. package/src/lib/constants/abi/index.js.map +1 -1
  8. package/src/lib/constants/abi/pushsolanagateway.json +1880 -0
  9. package/src/lib/constants/abi/universalGatewayV0.evm.d.ts +31 -0
  10. package/src/lib/constants/abi/universalGatewayV0.evm.js +141 -0
  11. package/src/lib/constants/abi/universalGatewayV0.evm.js.map +1 -0
  12. package/src/lib/constants/index.d.ts +1 -0
  13. package/src/lib/constants/index.js.map +1 -1
  14. package/src/lib/constants/tokens.d.ts +50 -0
  15. package/src/lib/constants/tokens.js +172 -0
  16. package/src/lib/constants/tokens.js.map +1 -0
  17. package/src/lib/generated/uexecutor/v1/query.d.ts +23 -0
  18. package/src/lib/generated/uexecutor/v1/query.js +79 -0
  19. package/src/lib/generated/uexecutor/v1/query.js.map +1 -0
  20. package/src/lib/generated/uexecutor/v1/types.d.ts +91 -0
  21. package/src/lib/generated/uexecutor/v1/types.js +856 -0
  22. package/src/lib/generated/uexecutor/v1/types.js.map +1 -0
  23. package/src/lib/generated/v1/tx.d.ts +4 -4
  24. package/src/lib/generated/v1/tx.js +1 -1
  25. package/src/lib/index.d.ts +1 -0
  26. package/src/lib/index.js.map +1 -1
  27. package/src/lib/orchestrator/orchestrator.d.ts +19 -0
  28. package/src/lib/orchestrator/orchestrator.js +796 -7
  29. package/src/lib/orchestrator/orchestrator.js.map +1 -1
  30. package/src/lib/orchestrator/orchestrator.types.d.ts +13 -0
  31. package/src/lib/progress-hook/progress-hook.js +61 -2
  32. package/src/lib/progress-hook/progress-hook.js.map +1 -1
  33. package/src/lib/progress-hook/progress-hook.types.d.ts +8 -0
  34. package/src/lib/progress-hook/progress-hook.types.js +11 -0
  35. package/src/lib/progress-hook/progress-hook.types.js.map +1 -1
  36. package/src/lib/push-chain/push-chain.d.ts +17 -0
  37. package/src/lib/push-chain/push-chain.js +190 -0
  38. package/src/lib/push-chain/push-chain.js.map +1 -1
  39. package/src/lib/push-client/push-client.d.ts +5 -0
  40. package/src/lib/push-client/push-client.js +15 -0
  41. package/src/lib/push-client/push-client.js.map +1 -1
  42. package/src/lib/utils.d.ts +98 -2
  43. package/src/lib/utils.js +264 -8
  44. package/src/lib/utils.js.map +1 -1
  45. package/src/lib/vm-client/svm-client.js +83 -4
  46. package/src/lib/vm-client/svm-client.js.map +1 -1
@@ -18,6 +18,7 @@ const anchor_1 = require("@coral-xyz/anchor");
18
18
  const progress_hook_types_1 = require("../progress-hook/progress-hook.types");
19
19
  const progress_hook_1 = tslib_1.__importDefault(require("../progress-hook/progress-hook"));
20
20
  const uea_evm_1 = require("../constants/abi/uea.evm");
21
+ const tokens_1 = require("../constants/tokens");
21
22
  class Orchestrator {
22
23
  constructor(universalSigner, pushNetwork, rpcUrls = {}, printTraces = false, progressHook) {
23
24
  this.universalSigner = universalSigner;
@@ -62,7 +63,574 @@ class Orchestrator {
62
63
  */
63
64
  execute(execute) {
64
65
  return tslib_1.__awaiter(this, void 0, void 0, function* () {
66
+ var _a, _b, _c;
65
67
  try {
68
+ // FUNDS_TX short-circuit: Bridge tokens from origin chain to Push Chain
69
+ // - EVM (Sepolia): UniversalGatewayV0
70
+ // - SVM (Solana Devnet): pushsolanagateway
71
+ if (execute.funds) {
72
+ if (!execute.data || execute.data === '0x') {
73
+ // Disallow user-provided `value` for funds-only bridging. The SDK derives
74
+ // origin-chain msg.value automatically from the funds input:
75
+ // - Native path: msg.value = bridgeAmount
76
+ // - ERC-20 path: msg.value = 0
77
+ if (execute.value !== undefined && execute.value !== BigInt(0)) {
78
+ throw new Error('Do not set `value` when using funds bridging; the SDK sets origin msg.value from `funds.amount` automatically');
79
+ }
80
+ const chain = this.universalSigner.account.chain;
81
+ const { vm } = chain_1.CHAIN_INFO[chain];
82
+ if (!(chain === enums_1.CHAIN.ETHEREUM_SEPOLIA || chain === enums_1.CHAIN.SOLANA_DEVNET)) {
83
+ throw new Error('Funds bridging is only supported on Ethereum Sepolia and Solana Devnet for now');
84
+ }
85
+ // Progress: Origin chain detected
86
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_01, chain);
87
+ const { defaultRPC, lockerContract } = chain_1.CHAIN_INFO[chain];
88
+ const rpcUrls = this.rpcUrls[chain] || defaultRPC;
89
+ // Resolve token: default to native token based on VM (ETH for EVM, SOL for SVM)
90
+ if (!execute.funds.token) {
91
+ const available = tokens_1.MOVEABLE_TOKENS[chain] || [];
92
+ const vm = chain_1.CHAIN_INFO[chain].vm;
93
+ const preferredSymbol = vm === enums_1.VM.EVM ? 'ETH' : vm === enums_1.VM.SVM ? 'SOL' : undefined;
94
+ const nativeToken = preferredSymbol
95
+ ? available.find((t) => t.symbol === preferredSymbol)
96
+ : undefined;
97
+ if (!nativeToken) {
98
+ throw new Error('Native token not configured for this chain');
99
+ }
100
+ execute.funds.token = nativeToken;
101
+ }
102
+ const amount = execute.funds.amount;
103
+ const symbol = execute.funds.token.symbol;
104
+ // Funds Flow: Preparing funds transfer
105
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_01, amount, execute.funds.token.decimals, symbol);
106
+ if (vm === enums_1.VM.EVM) {
107
+ const evmClient = new evm_client_1.EvmClient({ rpcUrls });
108
+ const gatewayAddress = lockerContract;
109
+ const tokenAddr = execute.funds.token.address;
110
+ // Approve gateway to pull tokens if ERC-20 (not native sentinel)
111
+ if (execute.funds.token.mechanism === 'approve') {
112
+ yield this.ensureErc20Allowance(evmClient, tokenAddr, gatewayAddress, amount);
113
+ }
114
+ else if (execute.funds.token.mechanism === 'permit2') {
115
+ throw new Error('Permit2 is not supported yet');
116
+ }
117
+ else if (execute.funds.token.mechanism === 'native') {
118
+ // Native flow uses msg.value == bridgeAmount and bridgeToken = address(0)
119
+ }
120
+ }
121
+ const bridgeAmount = amount;
122
+ const revertCFG = {
123
+ fundRecipient: this.universalSigner.account
124
+ .address,
125
+ revertMsg: '0x',
126
+ }; // typed by viem via ABI
127
+ if (vm === enums_1.VM.EVM) {
128
+ const evmClient = new evm_client_1.EvmClient({ rpcUrls });
129
+ const gatewayAddress = lockerContract;
130
+ const tokenAddr = execute.funds.token.address;
131
+ // Call UniversalGatewayV0.sendFunds(recipient, bridgeToken, bridgeAmount, revertCFG)
132
+ const recipient = execute.to; // funds to recipient on Push Chain
133
+ const isNative = execute.funds.token.mechanism === 'native';
134
+ const bridgeToken = execute.funds.token.mechanism === 'approve'
135
+ ? tokenAddr
136
+ : '0x0000000000000000000000000000000000000000';
137
+ let txHash;
138
+ try {
139
+ txHash = yield evmClient.writeContract({
140
+ abi: abi_1.UNIVERSAL_GATEWAY_V0,
141
+ address: gatewayAddress,
142
+ functionName: 'sendFunds',
143
+ args: [recipient, bridgeToken, bridgeAmount, revertCFG],
144
+ signer: this.universalSigner,
145
+ value: isNative ? bridgeAmount : BigInt(0),
146
+ });
147
+ }
148
+ catch (err) {
149
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_04);
150
+ throw err;
151
+ }
152
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_03);
153
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_02, txHash, bridgeAmount, execute.funds.token.decimals, symbol);
154
+ yield this.waitForEvmConfirmationsWithCountdown(evmClient, txHash, 14, 210000);
155
+ // After origin confirmations, query Push Chain for UniversalTx status
156
+ yield this.queryUniversalTxStatusFromEvmGatewayTx(evmClient, gatewayAddress, txHash, 'sendFunds');
157
+ const tx = yield evmClient.getTransaction(txHash);
158
+ return yield this.transformToUniversalTxResponse(tx);
159
+ }
160
+ else {
161
+ // SVM path (Solana Devnet)
162
+ const svmClient = new svm_client_1.SvmClient({ rpcUrls });
163
+ const programId = new web3_js_1.PublicKey(abi_1.SVM_GATEWAY_IDL.address);
164
+ const [configPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('config')], programId);
165
+ const [vaultPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('vault')], programId);
166
+ const [whitelistPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('whitelist')], programId);
167
+ const userPk = new web3_js_1.PublicKey(this.universalSigner.account.address);
168
+ let txSignature;
169
+ // SVM-specific RevertSettings: bytes must be a Buffer
170
+ const revertSvm = {
171
+ fundRecipient: userPk,
172
+ revertMsg: Buffer.from([]),
173
+ };
174
+ // New gateway expects EVM recipient as [u8; 20]
175
+ const recipientEvm20 = Array.from(Buffer.from(execute.to.slice(2).padStart(40, '0'), 'hex').subarray(0, 20));
176
+ if (execute.funds.token.mechanism === 'native') {
177
+ // Native SOL funds-only
178
+ // Compute a local whitelist PDA to avoid TS scope issues
179
+ const [whitelistPdaLocal] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('whitelist')], programId);
180
+ txSignature = yield svmClient.writeContract({
181
+ abi: abi_1.SVM_GATEWAY_IDL,
182
+ address: programId.toBase58(),
183
+ functionName: 'sendFunds', // -> unified sendFunds(recipient, bridge_token, bridge_amount, revert_cfg)
184
+ args: [
185
+ recipientEvm20,
186
+ web3_js_1.PublicKey.default,
187
+ bridgeAmount,
188
+ revertSvm,
189
+ ],
190
+ signer: this.universalSigner,
191
+ accounts: {
192
+ config: configPda,
193
+ vault: vaultPda,
194
+ user: userPk,
195
+ tokenWhitelist: whitelistPdaLocal,
196
+ userTokenAccount: userPk,
197
+ gatewayTokenAccount: vaultPda,
198
+ bridgeToken: web3_js_1.PublicKey.default,
199
+ tokenProgram: new web3_js_1.PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'),
200
+ systemProgram: web3_js_1.SystemProgram.programId,
201
+ },
202
+ });
203
+ }
204
+ else if (execute.funds.token.mechanism === 'approve') {
205
+ // SPL token funds-only (requires pre-existing ATAs)
206
+ const mintPk = new web3_js_1.PublicKey(execute.funds.token.address);
207
+ // Associated Token Accounts
208
+ const TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
209
+ const ASSOCIATED_TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL');
210
+ const userAta = web3_js_1.PublicKey.findProgramAddressSync([
211
+ userPk.toBuffer(),
212
+ TOKEN_PROGRAM_ID.toBuffer(),
213
+ mintPk.toBuffer(),
214
+ ], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
215
+ const vaultAta = web3_js_1.PublicKey.findProgramAddressSync([
216
+ vaultPda.toBuffer(),
217
+ TOKEN_PROGRAM_ID.toBuffer(),
218
+ mintPk.toBuffer(),
219
+ ], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
220
+ txSignature = yield svmClient.writeContract({
221
+ abi: abi_1.SVM_GATEWAY_IDL,
222
+ address: programId.toBase58(),
223
+ functionName: 'sendFunds',
224
+ args: [recipientEvm20, mintPk, bridgeAmount, revertSvm],
225
+ signer: this.universalSigner,
226
+ accounts: {
227
+ config: configPda,
228
+ vault: vaultPda,
229
+ tokenWhitelist: whitelistPda,
230
+ userTokenAccount: userAta,
231
+ gatewayTokenAccount: vaultAta,
232
+ user: userPk,
233
+ bridgeToken: mintPk,
234
+ tokenProgram: TOKEN_PROGRAM_ID,
235
+ systemProgram: web3_js_1.SystemProgram.programId,
236
+ },
237
+ });
238
+ }
239
+ else {
240
+ throw new Error('Unsupported token mechanism on Solana');
241
+ }
242
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_03);
243
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_02, txSignature, bridgeAmount, execute.funds.token.decimals, symbol);
244
+ yield svmClient.waitForConfirmations({
245
+ txSignature,
246
+ confirmations: chain_1.CHAIN_INFO[chain].confirmations,
247
+ timeoutMs: chain_1.CHAIN_INFO[chain].timeout,
248
+ });
249
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_06);
250
+ // Build a minimal UniversalTxResponse for SVM origin
251
+ const chainId = chain_1.CHAIN_INFO[chain].chainId;
252
+ const origin = `${chain_1.VM_NAMESPACE[vm]}:${chainId}:${this.universalSigner.account.address}`;
253
+ const universalTxResponse = {
254
+ hash: txSignature,
255
+ origin,
256
+ blockNumber: BigInt(0),
257
+ blockHash: '',
258
+ transactionIndex: 0,
259
+ chainId,
260
+ from: this.universalSigner.account.address,
261
+ to: '0x0000000000000000000000000000000000000000',
262
+ nonce: 0,
263
+ data: '0x',
264
+ value: BigInt(0),
265
+ gasLimit: BigInt(0),
266
+ gasPrice: undefined,
267
+ maxFeePerGas: undefined,
268
+ maxPriorityFeePerGas: undefined,
269
+ accessList: [],
270
+ wait: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
271
+ return ({
272
+ hash: txSignature,
273
+ blockNumber: BigInt(0),
274
+ blockHash: '',
275
+ transactionIndex: 0,
276
+ from: this.universalSigner.account.address,
277
+ to: '0x0000000000000000000000000000000000000000',
278
+ contractAddress: null,
279
+ gasPrice: BigInt(0),
280
+ gasUsed: BigInt(0),
281
+ cumulativeGasUsed: BigInt(0),
282
+ logs: [],
283
+ logsBloom: '0x',
284
+ status: 1,
285
+ raw: {
286
+ from: this.universalSigner.account.address,
287
+ to: '0x0000000000000000000000000000000000000000',
288
+ },
289
+ });
290
+ }),
291
+ type: '99',
292
+ typeVerbose: 'universal',
293
+ signature: { r: '0x0', s: '0x0', v: 0 },
294
+ raw: {
295
+ from: this.universalSigner.account.address,
296
+ to: '0x0000000000000000000000000000000000000000',
297
+ nonce: 0,
298
+ data: '0x',
299
+ value: BigInt(0),
300
+ },
301
+ };
302
+ return universalTxResponse;
303
+ }
304
+ }
305
+ else {
306
+ // Bridge funds + execute payload. Support:
307
+ // - EVM (Sepolia): ERC-20 approve path + native gas via msg.value
308
+ // - SVM (Solana Devnet): SPL or native SOL with gas_amount
309
+ const { chain, evmClient, gatewayAddress } = this.getOriginGatewayContext();
310
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_01, chain);
311
+ // Default token to native ETH if none provided
312
+ if (!execute.funds.token) {
313
+ const available = tokens_1.MOVEABLE_TOKENS[chain] || [];
314
+ const vm = chain_1.CHAIN_INFO[chain].vm;
315
+ const preferredSymbol = vm === enums_1.VM.EVM ? 'ETH' : vm === enums_1.VM.SVM ? 'SOL' : undefined;
316
+ const nativeToken = preferredSymbol
317
+ ? available.find((t) => t.symbol === preferredSymbol)
318
+ : undefined;
319
+ if (!nativeToken) {
320
+ throw new Error('Native token not configured for this chain');
321
+ }
322
+ execute.funds.token = nativeToken;
323
+ }
324
+ const mechanism = execute.funds.token.mechanism;
325
+ const { deployed, nonce } = yield this.getUeaStatusAndNonce();
326
+ const { payload: universalPayload } = yield this.buildGatewayPayloadAndGas(execute, nonce);
327
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_02_01);
328
+ // Compute required gas funding on Push Chain and current UEA balance
329
+ const gasEstimate = execute.gasLimit || BigInt(1e7);
330
+ const gasPrice = yield this.pushClient.getGasPrice();
331
+ const requiredGasFee = gasEstimate * gasPrice;
332
+ const payloadValue = (_a = execute.value) !== null && _a !== void 0 ? _a : BigInt(0);
333
+ const requiredFunds = requiredGasFee + payloadValue;
334
+ const ueaAddress = this.computeUEAOffchain();
335
+ const [ueaBalance] = yield Promise.all([
336
+ this.pushClient.getBalance(ueaAddress),
337
+ ]);
338
+ // UEA resolved (address, deployment status, balance, nonce)
339
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_02, ueaAddress, deployed);
340
+ // Determine USD to deposit via gateway (8 decimals) with caps: min=$1, max=$10
341
+ const oneUsd = push_chain_1.PushChain.utils.helpers.parseUnits('1', 8);
342
+ const tenUsd = push_chain_1.PushChain.utils.helpers.parseUnits('10', 8);
343
+ const deficit = requiredFunds > ueaBalance ? requiredFunds - ueaBalance : BigInt(0);
344
+ let depositUsd = deficit > BigInt(0) ? this.pushClient.pushToUSDC(deficit) : oneUsd;
345
+ if (depositUsd < oneUsd)
346
+ depositUsd = oneUsd;
347
+ if (depositUsd > tenUsd)
348
+ throw new Error('Deposit value exceeds max $10 worth of native token');
349
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_02_02, depositUsd);
350
+ // If SVM, clamp depositUsd to on-chain Config caps
351
+ if (chain_1.CHAIN_INFO[chain].vm === enums_1.VM.SVM) {
352
+ const svmClient = new svm_client_1.SvmClient({
353
+ rpcUrls: this.rpcUrls[enums_1.CHAIN.SOLANA_DEVNET] ||
354
+ chain_1.CHAIN_INFO[enums_1.CHAIN.SOLANA_DEVNET].defaultRPC,
355
+ });
356
+ const programId = new web3_js_1.PublicKey(abi_1.SVM_GATEWAY_IDL.address);
357
+ const [configPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('config')], programId);
358
+ try {
359
+ const cfg = yield svmClient.readContract({
360
+ abi: abi_1.SVM_GATEWAY_IDL,
361
+ address: abi_1.SVM_GATEWAY_IDL.address,
362
+ functionName: 'config',
363
+ args: [configPda.toBase58()],
364
+ });
365
+ const minField = (_b = cfg.minCapUniversalTxUsd) !== null && _b !== void 0 ? _b : cfg.min_cap_universal_tx_usd;
366
+ const maxField = (_c = cfg.maxCapUniversalTxUsd) !== null && _c !== void 0 ? _c : cfg.max_cap_universal_tx_usd;
367
+ const minCapUsd = BigInt(minField.toString());
368
+ const maxCapUsd = BigInt(maxField.toString());
369
+ if (depositUsd < minCapUsd)
370
+ depositUsd = minCapUsd;
371
+ // Add 20% safety margin to avoid BelowMinCap due to price drift
372
+ const withMargin = (minCapUsd * BigInt(12)) / BigInt(10);
373
+ if (depositUsd < withMargin)
374
+ depositUsd = withMargin;
375
+ if (depositUsd > maxCapUsd)
376
+ depositUsd = maxCapUsd;
377
+ }
378
+ catch (_d) {
379
+ // best-effort; fallback to previous bounds if read fails
380
+ }
381
+ }
382
+ // Convert USD(8) -> native units using pricing path
383
+ const nativeTokenUsdPrice = yield new price_fetch_1.PriceFetch(this.rpcUrls).getPrice(chain); // 8 decimals
384
+ const nativeDecimals = chain_1.CHAIN_INFO[chain].vm === enums_1.VM.SVM ? 9 : 18;
385
+ const oneNativeUnit = push_chain_1.PushChain.utils.helpers.parseUnits('1', nativeDecimals);
386
+ // Ceil division to avoid rounding below min USD on-chain
387
+ let nativeAmount = (depositUsd * oneNativeUnit + (nativeTokenUsdPrice - BigInt(1))) /
388
+ nativeTokenUsdPrice;
389
+ // Add 1 unit safety to avoid BelowMinCap from rounding differences
390
+ nativeAmount = nativeAmount + BigInt(1);
391
+ const revertCFG = {
392
+ fundRecipient: this.universalSigner.account
393
+ .address,
394
+ revertMsg: '0x',
395
+ };
396
+ const bridgeAmount = execute.funds.amount;
397
+ const symbol = execute.funds.token.symbol;
398
+ // Funds Flow: Preparing funds transfer
399
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_01, bridgeAmount, execute.funds.token.decimals, symbol);
400
+ if (chain_1.CHAIN_INFO[this.universalSigner.account.chain].vm === enums_1.VM.EVM) {
401
+ const tokenAddr = execute.funds.token.address;
402
+ if (mechanism !== 'approve') {
403
+ throw new Error('Only ERC-20 tokens are supported for funds+payload on EVM; native and permit2 are not supported yet');
404
+ }
405
+ const evmClientEvm = evmClient;
406
+ const gatewayAddressEvm = gatewayAddress;
407
+ yield this.ensureErc20Allowance(evmClientEvm, tokenAddr, gatewayAddressEvm, bridgeAmount);
408
+ }
409
+ let txHash;
410
+ try {
411
+ if (chain_1.CHAIN_INFO[this.universalSigner.account.chain].vm === enums_1.VM.EVM) {
412
+ const tokenAddr = execute.funds.token.address;
413
+ // Compute EIP-712 signature for the universal payload and hash to bytes32
414
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_01);
415
+ const ueaAddress = this.computeUEAOffchain();
416
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_02, ueaAddress, deployed);
417
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_01);
418
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_02);
419
+ const eip712Signature = yield this.signUniversalPayload(universalPayload, ueaAddress);
420
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_03);
421
+ const eip712SignatureHex = typeof eip712Signature === 'string'
422
+ ? eip712Signature
423
+ : (0, viem_1.bytesToHex)(eip712Signature);
424
+ const evmClientEvm = evmClient;
425
+ const gatewayAddressEvm = gatewayAddress;
426
+ txHash = yield evmClientEvm.writeContract({
427
+ abi: abi_1.UNIVERSAL_GATEWAY_V0,
428
+ address: gatewayAddressEvm,
429
+ functionName: 'sendTxWithFunds',
430
+ args: [
431
+ tokenAddr,
432
+ bridgeAmount,
433
+ universalPayload,
434
+ revertCFG,
435
+ eip712SignatureHex,
436
+ ],
437
+ signer: this.universalSigner,
438
+ value: nativeAmount,
439
+ });
440
+ }
441
+ else {
442
+ // SVM funds+payload path
443
+ const svmClient = new svm_client_1.SvmClient({
444
+ rpcUrls: this.rpcUrls[enums_1.CHAIN.SOLANA_DEVNET] ||
445
+ chain_1.CHAIN_INFO[enums_1.CHAIN.SOLANA_DEVNET].defaultRPC,
446
+ });
447
+ const programId = new web3_js_1.PublicKey(abi_1.SVM_GATEWAY_IDL.address);
448
+ const [configPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('config')], programId);
449
+ const [vaultPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('vault')], programId);
450
+ // whitelistPda already computed above
451
+ const userPk = new web3_js_1.PublicKey(this.universalSigner.account.address);
452
+ const priceUpdatePk = new web3_js_1.PublicKey('7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE');
453
+ const isNative = mechanism === 'native' || execute.funds.token.symbol === 'SOL';
454
+ const revertSvm2 = {
455
+ fundRecipient: userPk,
456
+ revertMsg: Buffer.from([]),
457
+ };
458
+ if (isNative) {
459
+ // Native SOL as bridge + gas
460
+ const [whitelistPdaLocal] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('whitelist')], programId);
461
+ txHash = yield svmClient.writeContract({
462
+ abi: abi_1.SVM_GATEWAY_IDL,
463
+ address: programId.toBase58(),
464
+ functionName: 'sendTxWithFunds',
465
+ args: [
466
+ web3_js_1.PublicKey.default, // bridge_token = default for native SOL
467
+ bridgeAmount,
468
+ universalPayload,
469
+ revertSvm2,
470
+ nativeAmount,
471
+ Buffer.alloc(32),
472
+ ],
473
+ signer: this.universalSigner,
474
+ accounts: {
475
+ config: configPda,
476
+ vault: vaultPda,
477
+ tokenWhitelist: whitelistPdaLocal,
478
+ userTokenAccount: userPk, // for native SOL, can be any valid account
479
+ gatewayTokenAccount: vaultPda, // for native SOL, can be any valid account
480
+ user: userPk,
481
+ priceUpdate: priceUpdatePk,
482
+ bridgeToken: web3_js_1.PublicKey.default,
483
+ tokenProgram: new web3_js_1.PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'),
484
+ systemProgram: web3_js_1.SystemProgram.programId,
485
+ },
486
+ });
487
+ }
488
+ else {
489
+ // SPL token as bridge + native SOL lamports as gas_amount
490
+ const mintPk = new web3_js_1.PublicKey(execute.funds.token.address);
491
+ const TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
492
+ const ASSOCIATED_TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL');
493
+ const userAta = web3_js_1.PublicKey.findProgramAddressSync([
494
+ userPk.toBuffer(),
495
+ TOKEN_PROGRAM_ID.toBuffer(),
496
+ mintPk.toBuffer(),
497
+ ], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
498
+ const vaultAta = web3_js_1.PublicKey.findProgramAddressSync([
499
+ vaultPda.toBuffer(),
500
+ TOKEN_PROGRAM_ID.toBuffer(),
501
+ mintPk.toBuffer(),
502
+ ], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
503
+ const [whitelistPdaLocal] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('whitelist')], programId);
504
+ txHash = yield svmClient.writeContract({
505
+ abi: abi_1.SVM_GATEWAY_IDL,
506
+ address: programId.toBase58(),
507
+ functionName: 'sendTxWithFunds',
508
+ args: [
509
+ mintPk,
510
+ bridgeAmount,
511
+ universalPayload,
512
+ revertSvm2,
513
+ nativeAmount,
514
+ Buffer.alloc(32),
515
+ ],
516
+ signer: this.universalSigner,
517
+ accounts: {
518
+ config: configPda,
519
+ vault: vaultPda,
520
+ tokenWhitelist: whitelistPdaLocal,
521
+ userTokenAccount: userAta,
522
+ gatewayTokenAccount: vaultAta,
523
+ user: userPk,
524
+ priceUpdate: priceUpdatePk,
525
+ bridgeToken: mintPk,
526
+ tokenProgram: TOKEN_PROGRAM_ID,
527
+ systemProgram: web3_js_1.SystemProgram.programId,
528
+ },
529
+ });
530
+ }
531
+ }
532
+ }
533
+ catch (err) {
534
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_04);
535
+ throw err;
536
+ }
537
+ // Payload Flow: Verification Success
538
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_03);
539
+ // Funds Flow: Funds lock submitted
540
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_02, txHash, bridgeAmount, execute.funds.token.decimals, symbol);
541
+ // Awaiting confirmations
542
+ if (chain_1.CHAIN_INFO[this.universalSigner.account.chain].vm === enums_1.VM.EVM) {
543
+ const evmClientEvm = evmClient;
544
+ yield this.waitForEvmConfirmationsWithCountdown(evmClientEvm, txHash, 14, 300000);
545
+ }
546
+ else {
547
+ const svmClient = new svm_client_1.SvmClient({
548
+ rpcUrls: this.rpcUrls[enums_1.CHAIN.SOLANA_DEVNET] ||
549
+ chain_1.CHAIN_INFO[enums_1.CHAIN.SOLANA_DEVNET].defaultRPC,
550
+ });
551
+ yield svmClient.waitForConfirmations({
552
+ txSignature: txHash,
553
+ confirmations: chain_1.CHAIN_INFO[enums_1.CHAIN.SOLANA_DEVNET].confirmations,
554
+ timeoutMs: chain_1.CHAIN_INFO[enums_1.CHAIN.SOLANA_DEVNET].timeout,
555
+ });
556
+ }
557
+ // Funds Flow: Confirmed on origin
558
+ if (chain_1.CHAIN_INFO[this.universalSigner.account.chain].vm === enums_1.VM.EVM) {
559
+ yield this.sendUniversalTx(deployed, txHash);
560
+ }
561
+ else {
562
+ // skip
563
+ }
564
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_06);
565
+ // After sending Cosmos tx to Push Chain, query UniversalTx status (Sepolia only)
566
+ if (chain_1.CHAIN_INFO[this.universalSigner.account.chain].vm === enums_1.VM.EVM) {
567
+ const evmClientEvm = evmClient;
568
+ const gatewayAddressEvm = gatewayAddress;
569
+ yield this.queryUniversalTxStatusFromEvmGatewayTx(evmClientEvm, gatewayAddressEvm, txHash, 'sendTxWithFunds');
570
+ }
571
+ if (chain_1.CHAIN_INFO[this.universalSigner.account.chain].vm === enums_1.VM.EVM) {
572
+ const evmClientEvm = evmClient;
573
+ const evmTx = yield evmClientEvm.getTransaction(txHash);
574
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_07, bridgeAmount, execute.funds.token.decimals, symbol);
575
+ return yield this.transformToUniversalTxResponse(evmTx);
576
+ }
577
+ else {
578
+ const chainId = chain_1.CHAIN_INFO[chain].chainId;
579
+ const vm = chain_1.CHAIN_INFO[chain].vm;
580
+ const origin = `${chain_1.VM_NAMESPACE[vm]}:${chainId}:${this.universalSigner.account.address}`;
581
+ const universalTxResponse = {
582
+ hash: txHash,
583
+ origin,
584
+ blockNumber: BigInt(0),
585
+ blockHash: '',
586
+ transactionIndex: 0,
587
+ chainId,
588
+ from: this.universalSigner.account.address,
589
+ to: '0x0000000000000000000000000000000000000000',
590
+ nonce: 0,
591
+ data: '0x',
592
+ value: BigInt(0),
593
+ gasLimit: BigInt(0),
594
+ gasPrice: undefined,
595
+ maxFeePerGas: undefined,
596
+ maxPriorityFeePerGas: undefined,
597
+ accessList: [],
598
+ wait: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
599
+ return ({
600
+ hash: txHash,
601
+ blockNumber: BigInt(0),
602
+ blockHash: '',
603
+ transactionIndex: 0,
604
+ from: this.universalSigner.account.address,
605
+ to: '0x0000000000000000000000000000000000000000',
606
+ contractAddress: null,
607
+ gasPrice: BigInt(0),
608
+ gasUsed: BigInt(0),
609
+ cumulativeGasUsed: BigInt(0),
610
+ logs: [],
611
+ logsBloom: '0x',
612
+ status: 1,
613
+ raw: {
614
+ from: this.universalSigner.account.address,
615
+ to: '0x0000000000000000000000000000000000000000',
616
+ },
617
+ });
618
+ }),
619
+ type: '99',
620
+ typeVerbose: 'universal',
621
+ signature: { r: '0x0', s: '0x0', v: 0 },
622
+ raw: {
623
+ from: this.universalSigner.account.address,
624
+ to: '0x0000000000000000000000000000000000000000',
625
+ nonce: 0,
626
+ data: '0x',
627
+ value: BigInt(0),
628
+ },
629
+ };
630
+ return universalTxResponse;
631
+ }
632
+ }
633
+ }
66
634
  // Set default value for value if undefined
67
635
  if (execute.value === undefined) {
68
636
  execute.value = BigInt(0);
@@ -103,7 +671,7 @@ class Orchestrator {
103
671
  ]);
104
672
  const isUEADeployed = code !== undefined;
105
673
  const nonce = isUEADeployed ? yield this.getUEANonce(UEA) : BigInt(0);
106
- this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_02, UEA, isUEADeployed, funds, nonce);
674
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_02, UEA, isUEADeployed);
107
675
  /**
108
676
  * Compute Universal Payload Hash
109
677
  */
@@ -111,7 +679,7 @@ class Orchestrator {
111
679
  if (feeLockTxHash && !feeLockTxHash.startsWith('0x')) {
112
680
  // decode svm base58
113
681
  const decoded = anchor_1.utils.bytes.bs58.decode(feeLockTxHash);
114
- feeLockTxHash = (0, viem_1.bytesToHex)(Uint8Array.from(decoded));
682
+ feeLockTxHash = (0, viem_1.bytesToHex)(new Uint8Array(decoded));
115
683
  }
116
684
  // Fee locking is required if UEA is not deployed OR insufficient funds
117
685
  const feeLockingRequired = (!isUEADeployed || funds < requiredFunds) && !feeLockTxHash;
@@ -224,7 +792,19 @@ class Orchestrator {
224
792
  return transactions[transactions.length - 1];
225
793
  }
226
794
  catch (err) {
227
- this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_02, err);
795
+ const errMessage = err instanceof Error
796
+ ? err.message
797
+ : typeof err === 'string'
798
+ ? err
799
+ : (() => {
800
+ try {
801
+ return JSON.stringify(err);
802
+ }
803
+ catch (_a) {
804
+ return 'Unknown error';
805
+ }
806
+ })();
807
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_02, errMessage);
228
808
  throw err;
229
809
  }
230
810
  });
@@ -286,7 +866,7 @@ class Orchestrator {
286
866
  systemProgram: web3_js_1.SystemProgram.programId,
287
867
  },
288
868
  });
289
- return anchor_1.utils.bytes.bs58.decode(txHash);
869
+ return new Uint8Array(anchor_1.utils.bytes.bs58.decode(txHash));
290
870
  }
291
871
  default:
292
872
  throw new Error(`Unsupported VM type: ${vm}`);
@@ -353,7 +933,7 @@ class Orchestrator {
353
933
  owner: vm === enums_1.VM.EVM
354
934
  ? address
355
935
  : vm === enums_1.VM.SVM
356
- ? (0, viem_1.bytesToHex)(anchor_1.utils.bytes.bs58.decode(address))
936
+ ? (0, viem_1.bytesToHex)(new Uint8Array(anchor_1.utils.bytes.bs58.decode(address)))
357
937
  : address,
358
938
  };
359
939
  const { cosmosAddress: signer } = this.pushClient.getSignerAddress();
@@ -370,6 +950,7 @@ class Orchestrator {
370
950
  universalAccountId,
371
951
  txHash: feeLockTxHash,
372
952
  }));
953
+ // TODO: pchaind q uexecutor all-universal-tx --node https://rpc-testnet-donut-node1.push.org/
373
954
  }
374
955
  if (feeLockTxHash) {
375
956
  msgs.push(this.pushClient.createMsgMintPC({
@@ -518,7 +1099,7 @@ class Orchestrator {
518
1099
  owner: vm === enums_1.VM.EVM
519
1100
  ? address
520
1101
  : vm === enums_1.VM.SVM
521
- ? (0, viem_1.bytesToHex)(anchor_1.utils.bytes.bs58.decode(address))
1102
+ ? (0, viem_1.bytesToHex)(new Uint8Array(anchor_1.utils.bytes.bs58.decode(address)))
522
1103
  : address,
523
1104
  },
524
1105
  ],
@@ -542,7 +1123,7 @@ class Orchestrator {
542
1123
  ownerKey = address;
543
1124
  }
544
1125
  else if (chain_1.CHAIN_INFO[chain].vm === enums_1.VM.SVM) {
545
- ownerKey = (0, viem_1.bytesToHex)(anchor_1.utils.bytes.bs58.decode(address));
1126
+ ownerKey = (0, viem_1.bytesToHex)(new Uint8Array(anchor_1.utils.bytes.bs58.decode(address)));
546
1127
  }
547
1128
  else {
548
1129
  throw new Error(`Unsupported VM type: ${chain_1.CHAIN_INFO[chain].vm}`);
@@ -640,6 +1221,134 @@ class Orchestrator {
640
1221
  }
641
1222
  });
642
1223
  }
1224
+ ensureErc20Allowance(evmClient, tokenAddress, spender, requiredAmount) {
1225
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
1226
+ const chain = this.universalSigner.account.chain;
1227
+ const owner = this.universalSigner.account.address;
1228
+ const currentAllowance = yield evmClient.readContract({
1229
+ abi: abi_1.ERC20_EVM,
1230
+ address: tokenAddress,
1231
+ functionName: 'allowance',
1232
+ args: [owner, spender],
1233
+ });
1234
+ if (currentAllowance >= requiredAmount)
1235
+ return;
1236
+ // Some ERC-20s like USDT require setting allowance to 0 before changing
1237
+ // an existing non-zero allowance to a different non-zero value.
1238
+ if (currentAllowance > BigInt(0)) {
1239
+ this.printLog(`Resetting existing allowance from ${currentAllowance.toString()} to 0 for spender ${spender}`);
1240
+ const resetTxHash = yield evmClient.writeContract({
1241
+ abi: abi_1.ERC20_EVM,
1242
+ address: tokenAddress,
1243
+ functionName: 'approve',
1244
+ args: [spender, BigInt(0)],
1245
+ signer: this.universalSigner,
1246
+ });
1247
+ yield evmClient.waitForConfirmations({
1248
+ txHash: resetTxHash,
1249
+ confirmations: 1,
1250
+ timeoutMs: chain_1.CHAIN_INFO[chain].timeout,
1251
+ });
1252
+ }
1253
+ const setTxHash = yield evmClient.writeContract({
1254
+ abi: abi_1.ERC20_EVM,
1255
+ address: tokenAddress,
1256
+ functionName: 'approve',
1257
+ args: [spender, requiredAmount],
1258
+ signer: this.universalSigner,
1259
+ });
1260
+ yield evmClient.waitForConfirmations({
1261
+ txHash: setTxHash,
1262
+ confirmations: 1,
1263
+ timeoutMs: chain_1.CHAIN_INFO[chain].timeout,
1264
+ });
1265
+ try {
1266
+ const updated = yield evmClient.readContract({
1267
+ abi: abi_1.ERC20_EVM,
1268
+ address: tokenAddress,
1269
+ functionName: 'allowance',
1270
+ args: [owner, spender],
1271
+ });
1272
+ if (updated < requiredAmount) {
1273
+ this.printLog('Warning: allowance not updated yet; proceeding');
1274
+ }
1275
+ }
1276
+ catch (_a) {
1277
+ // ignore
1278
+ }
1279
+ });
1280
+ }
1281
+ /**
1282
+ * Ensures we're on Sepolia, returns EVM client and gateway address.
1283
+ */
1284
+ getOriginGatewayContext() {
1285
+ const chain = this.universalSigner.account.chain;
1286
+ if (chain !== enums_1.CHAIN.ETHEREUM_SEPOLIA && chain !== enums_1.CHAIN.SOLANA_DEVNET) {
1287
+ throw new Error('Funds + payload bridging is only supported on Ethereum Sepolia and Solana Devnet for now');
1288
+ }
1289
+ // For EVM (Sepolia), return client and gateway address. For SVM (Solana Devnet), only chain is needed here.
1290
+ if (chain_1.CHAIN_INFO[chain].vm === enums_1.VM.EVM) {
1291
+ const { defaultRPC, lockerContract } = chain_1.CHAIN_INFO[chain];
1292
+ const rpcUrls = this.rpcUrls[chain] || defaultRPC;
1293
+ const evmClient = new evm_client_1.EvmClient({ rpcUrls });
1294
+ const gatewayAddress = lockerContract;
1295
+ if (!gatewayAddress) {
1296
+ throw new Error('Universal Gateway address not configured');
1297
+ }
1298
+ return { chain, evmClient, gatewayAddress };
1299
+ }
1300
+ // SVM path (Solana Devnet) does not require evmClient/gatewayAddress
1301
+ return { chain };
1302
+ }
1303
+ /**
1304
+ * Computes UEA and fetches its nonce if deployed; returns 0 otherwise.
1305
+ */
1306
+ getUeaNonceForExecution() {
1307
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
1308
+ const UEA = this.computeUEAOffchain();
1309
+ const [code] = yield Promise.all([
1310
+ this.pushClient.publicClient.getCode({ address: UEA }),
1311
+ ]);
1312
+ return code !== undefined ? yield this.getUEANonce(UEA) : BigInt(0);
1313
+ });
1314
+ }
1315
+ /**
1316
+ * Returns UEA deployment status and nonce (0 if not deployed).
1317
+ */
1318
+ getUeaStatusAndNonce() {
1319
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
1320
+ const UEA = this.computeUEAOffchain();
1321
+ const [code] = yield Promise.all([
1322
+ this.pushClient.publicClient.getCode({ address: UEA }),
1323
+ ]);
1324
+ const deployed = code !== undefined;
1325
+ const nonce = deployed ? yield this.getUEANonce(UEA) : BigInt(0);
1326
+ return { deployed, nonce };
1327
+ });
1328
+ }
1329
+ /**
1330
+ * Builds UniversalPayload for the gateway and computes the native gas deposit.
1331
+ */
1332
+ buildGatewayPayloadAndGas(execute, nonce) {
1333
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
1334
+ var _a, _b;
1335
+ const gasEstimate = execute.gasLimit || BigInt(1e7);
1336
+ const payloadValue = (_a = execute.value) !== null && _a !== void 0 ? _a : BigInt(0);
1337
+ const gasAmount = (_b = execute.value) !== null && _b !== void 0 ? _b : BigInt(0);
1338
+ const universalPayload = {
1339
+ to: execute.to,
1340
+ value: payloadValue,
1341
+ data: execute.data || '0x',
1342
+ gasLimit: gasEstimate,
1343
+ maxFeePerGas: execute.maxFeePerGas || BigInt(1e10),
1344
+ maxPriorityFeePerGas: execute.maxPriorityFeePerGas || BigInt(0),
1345
+ nonce,
1346
+ deadline: execute.deadline || BigInt(9999999999),
1347
+ vType: tx_1.VerificationType.signedVerification,
1348
+ };
1349
+ return { payload: universalPayload, gasAmount };
1350
+ });
1351
+ }
643
1352
  /********************************** HELPER FUNCTIONS **************************************************/
644
1353
  /**
645
1354
  * Transforms a TransactionReceipt to UniversalTxReceipt format
@@ -860,6 +1569,86 @@ class Orchestrator {
860
1569
  // invoke the user-provided callback
861
1570
  this.progressHook(hookPayload);
862
1571
  }
1572
+ // Query Push Chain for UniversalTx status given an EVM gateway tx
1573
+ queryUniversalTxStatusFromEvmGatewayTx(evmClient, gatewayAddress, txHash, fromBranch) {
1574
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
1575
+ var _a, _b;
1576
+ try {
1577
+ const receipt = yield evmClient.publicClient.getTransactionReceipt({
1578
+ hash: txHash,
1579
+ });
1580
+ const gatewayLogs = (receipt.logs || []).filter((l) => (l.address || '').toLowerCase() === gatewayAddress.toLowerCase());
1581
+ const logIndexToUse = fromBranch === 'sendTxWithFunds' ? 1 : 0;
1582
+ const firstLog = (gatewayLogs[logIndexToUse] ||
1583
+ ((_a = receipt.logs) === null || _a === void 0 ? void 0 : _a[logIndexToUse]));
1584
+ const logIndexVal = (_b = firstLog === null || firstLog === void 0 ? void 0 : firstLog.logIndex) !== null && _b !== void 0 ? _b : 0;
1585
+ const logIndexStr = typeof logIndexVal === 'bigint'
1586
+ ? logIndexVal.toString()
1587
+ : String(logIndexVal);
1588
+ const chain = this.universalSigner.account.chain;
1589
+ const { vm } = chain_1.CHAIN_INFO[chain];
1590
+ const sourceChain = `${chain_1.VM_NAMESPACE[vm]}:${chain_1.CHAIN_INFO[chain].chainId}`;
1591
+ // ID = sha256("${sourceChain}:${txHash}:${logIndex}") as hex string (no 0x)
1592
+ const idInput = `${sourceChain}:${txHash}:${logIndexStr}`;
1593
+ const idHex = (0, viem_1.sha256)((0, viem_1.stringToBytes)(idInput)).slice(2);
1594
+ // Fetch UniversalTx via gRPC with a brief retry window
1595
+ let universalTxObj;
1596
+ for (let attempt = 0; attempt < 10; attempt++) {
1597
+ try {
1598
+ const universalTxResp = yield this.pushClient.getUniversalTxById(idHex);
1599
+ universalTxObj = universalTxResp === null || universalTxResp === void 0 ? void 0 : universalTxResp.universalTx;
1600
+ if (universalTxObj)
1601
+ break;
1602
+ }
1603
+ catch (error) {
1604
+ // ignore and retry
1605
+ console.log(error);
1606
+ }
1607
+ yield new Promise((r) => setTimeout(r, 1500));
1608
+ }
1609
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_06, (universalTxObj === null || universalTxObj === void 0 ? void 0 : universalTxObj.universalStatus) || (universalTxObj === null || universalTxObj === void 0 ? void 0 : universalTxObj.universal_status));
1610
+ this.printLog(`UniversalTx fetched via gRPC: ${JSON.stringify({
1611
+ gatewayTx: txHash,
1612
+ id: idHex,
1613
+ status: (universalTxObj === null || universalTxObj === void 0 ? void 0 : universalTxObj.universalStatus) ||
1614
+ (universalTxObj === null || universalTxObj === void 0 ? void 0 : universalTxObj.universal_status),
1615
+ }, this.bigintReplacer, 2)}`);
1616
+ return universalTxObj;
1617
+ }
1618
+ catch (_c) {
1619
+ // Best-effort; do not fail flow if PC query is unavailable
1620
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_06);
1621
+ return undefined;
1622
+ }
1623
+ });
1624
+ }
1625
+ // Emit countdown updates while waiting for EVM confirmations
1626
+ waitForEvmConfirmationsWithCountdown(evmClient, txHash, confirmations, timeoutMs) {
1627
+ return tslib_1.__awaiter(this, void 0, void 0, function* () {
1628
+ // initial emit
1629
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_03, confirmations);
1630
+ const start = Date.now();
1631
+ // Wait for receipt to get included block
1632
+ const receipt = yield evmClient.publicClient.waitForTransactionReceipt({
1633
+ hash: txHash,
1634
+ });
1635
+ const targetBlock = receipt.blockNumber + BigInt(confirmations);
1636
+ // Poll blocks and emit remaining confirmations
1637
+ // eslint-disable-next-line no-constant-condition
1638
+ while (true) {
1639
+ const currentBlock = yield evmClient.publicClient.getBlockNumber();
1640
+ if (currentBlock >= targetBlock)
1641
+ return;
1642
+ const remaining = Number(targetBlock - currentBlock);
1643
+ const completed = Math.max(1, confirmations - remaining + 1);
1644
+ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_04, completed, confirmations);
1645
+ if (Date.now() - start > timeoutMs) {
1646
+ throw new Error(`Timeout: transaction ${txHash} not confirmed with ${confirmations} confirmations within ${timeoutMs} ms`);
1647
+ }
1648
+ yield new Promise((r) => setTimeout(r, 12000));
1649
+ }
1650
+ });
1651
+ }
863
1652
  }
864
1653
  exports.Orchestrator = Orchestrator;
865
1654
  //# sourceMappingURL=orchestrator.js.map