@pushchain/core 2.0.1 → 2.0.3
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/package.json +4 -3
- package/src/lib/constants/abi/erc20.evm.d.ts +36 -0
- package/src/lib/constants/abi/erc20.evm.js +26 -0
- package/src/lib/constants/abi/erc20.evm.js.map +1 -0
- package/src/lib/constants/abi/index.d.ts +3 -0
- package/src/lib/constants/abi/index.js +7 -1
- package/src/lib/constants/abi/index.js.map +1 -1
- package/src/lib/constants/abi/pushsolanagateway.json +1004 -0
- package/src/lib/constants/abi/universalGatewayV0.evm.d.ts +31 -0
- package/src/lib/constants/abi/universalGatewayV0.evm.js +141 -0
- package/src/lib/constants/abi/universalGatewayV0.evm.js.map +1 -0
- package/src/lib/constants/index.d.ts +1 -0
- package/src/lib/constants/index.js.map +1 -1
- package/src/lib/constants/tokens.d.ts +50 -0
- package/src/lib/constants/tokens.js +167 -0
- package/src/lib/constants/tokens.js.map +1 -0
- package/src/lib/generated/uexecutor/v1/query.d.ts +23 -0
- package/src/lib/generated/uexecutor/v1/query.js +79 -0
- package/src/lib/generated/uexecutor/v1/query.js.map +1 -0
- package/src/lib/generated/uexecutor/v1/types.d.ts +91 -0
- package/src/lib/generated/uexecutor/v1/types.js +856 -0
- package/src/lib/generated/uexecutor/v1/types.js.map +1 -0
- package/src/lib/generated/v1/tx.d.ts +4 -4
- package/src/lib/generated/v1/tx.js +1 -1
- package/src/lib/index.d.ts +1 -0
- package/src/lib/index.js.map +1 -1
- package/src/lib/orchestrator/orchestrator.d.ts +18 -0
- package/src/lib/orchestrator/orchestrator.js +720 -4
- package/src/lib/orchestrator/orchestrator.js.map +1 -1
- package/src/lib/orchestrator/orchestrator.types.d.ts +13 -0
- package/src/lib/progress-hook/progress-hook.js +66 -0
- package/src/lib/progress-hook/progress-hook.js.map +1 -1
- package/src/lib/progress-hook/progress-hook.types.d.ts +9 -0
- package/src/lib/progress-hook/progress-hook.types.js +12 -0
- package/src/lib/progress-hook/progress-hook.types.js.map +1 -1
- package/src/lib/push-chain/push-chain.d.ts +17 -0
- package/src/lib/push-chain/push-chain.js +190 -0
- package/src/lib/push-chain/push-chain.js.map +1 -1
- package/src/lib/push-client/push-client.d.ts +5 -0
- package/src/lib/push-client/push-client.js +15 -0
- package/src/lib/push-client/push-client.js.map +1 -1
- package/src/lib/utils.d.ts +98 -2
- package/src/lib/utils.js +264 -8
- package/src/lib/utils.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,555 @@ 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)) {
|
|
83
|
+
throw new Error('Funds bridging is only supported on Ethereum Sepolia 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
|
+
try {
|
|
157
|
+
const receipt = yield evmClient.publicClient.getTransactionReceipt({
|
|
158
|
+
hash: txHash,
|
|
159
|
+
});
|
|
160
|
+
const gatewayLogs = (receipt.logs || []).filter((l) => (l.address || '').toLowerCase() ===
|
|
161
|
+
gatewayAddress.toLowerCase());
|
|
162
|
+
const firstLog = (gatewayLogs[0] || ((_a = receipt.logs) === null || _a === void 0 ? void 0 : _a[0]));
|
|
163
|
+
const logIndexVal = (_b = firstLog === null || firstLog === void 0 ? void 0 : firstLog.logIndex) !== null && _b !== void 0 ? _b : 0;
|
|
164
|
+
const logIndexStr = typeof logIndexVal === 'bigint'
|
|
165
|
+
? logIndexVal.toString()
|
|
166
|
+
: String(logIndexVal);
|
|
167
|
+
const sourceChain = `${chain_1.VM_NAMESPACE[vm]}:${chain_1.CHAIN_INFO[this.universalSigner.account.chain].chainId}`;
|
|
168
|
+
// ID = sha256("${sourceChain}:${txHash}:${logIndex}") as hex string (no 0x)
|
|
169
|
+
const idInput = `${sourceChain}:${txHash}:${logIndexStr}`;
|
|
170
|
+
const idHex = (0, viem_1.sha256)((0, viem_1.stringToBytes)(idInput)).slice(2);
|
|
171
|
+
// Fetch UniversalTx via gRPC with a brief retry window
|
|
172
|
+
let universalTxObj;
|
|
173
|
+
for (let attempt = 0; attempt < 10; attempt++) {
|
|
174
|
+
try {
|
|
175
|
+
const universalTxResp = yield this.pushClient.getUniversalTxById(idHex);
|
|
176
|
+
universalTxObj = universalTxResp === null || universalTxResp === void 0 ? void 0 : universalTxResp.universalTx;
|
|
177
|
+
if (universalTxObj)
|
|
178
|
+
break;
|
|
179
|
+
}
|
|
180
|
+
catch (_d) {
|
|
181
|
+
// ignore and retry
|
|
182
|
+
}
|
|
183
|
+
yield new Promise((r) => setTimeout(r, 1500));
|
|
184
|
+
}
|
|
185
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_06, (universalTxObj === null || universalTxObj === void 0 ? void 0 : universalTxObj.universalStatus) ||
|
|
186
|
+
(universalTxObj === null || universalTxObj === void 0 ? void 0 : universalTxObj.universal_status));
|
|
187
|
+
}
|
|
188
|
+
catch (_e) {
|
|
189
|
+
// Best-effort; do not fail flow if PC query is unavailable
|
|
190
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_06);
|
|
191
|
+
}
|
|
192
|
+
const tx = yield evmClient.getTransaction(txHash);
|
|
193
|
+
return yield this.transformToUniversalTxResponse(tx);
|
|
194
|
+
}
|
|
195
|
+
else {
|
|
196
|
+
// SVM path (Solana Devnet)
|
|
197
|
+
const svmClient = new svm_client_1.SvmClient({ rpcUrls });
|
|
198
|
+
const programId = new web3_js_1.PublicKey(abi_1.SVM_GATEWAY_IDL.metadata.address);
|
|
199
|
+
const [configPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('config')], programId);
|
|
200
|
+
const [vaultPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('vault')], programId);
|
|
201
|
+
const userPk = new web3_js_1.PublicKey(this.universalSigner.account.address);
|
|
202
|
+
let txSignature;
|
|
203
|
+
// SVM-specific RevertSettings: bytes must be a Buffer
|
|
204
|
+
const revertSvm = {
|
|
205
|
+
fundRecipient: userPk,
|
|
206
|
+
revertMsg: Buffer.alloc(0),
|
|
207
|
+
};
|
|
208
|
+
if (execute.funds.token.mechanism === 'native') {
|
|
209
|
+
// Native SOL funds-only
|
|
210
|
+
const recipientPk = userPk; // must be non-default per program checks
|
|
211
|
+
console.log('recipientPk', recipientPk);
|
|
212
|
+
console.log('bridgeAmount', bridgeAmount);
|
|
213
|
+
console.log('userPk', userPk);
|
|
214
|
+
console.log('configPda', configPda);
|
|
215
|
+
console.log('vaultPda', vaultPda);
|
|
216
|
+
console.log('systemProgram', web3_js_1.SystemProgram.programId);
|
|
217
|
+
console.log('programId', programId.toBase58());
|
|
218
|
+
console.log('SVM_GATEWAY_IDL', abi_1.SVM_GATEWAY_IDL);
|
|
219
|
+
console.log('sendFundsNative', 'sendFundsNative');
|
|
220
|
+
console.log('args', [
|
|
221
|
+
recipientPk,
|
|
222
|
+
bridgeAmount,
|
|
223
|
+
{ fundRecipient: userPk, revertMsg: new Uint8Array([]) },
|
|
224
|
+
]);
|
|
225
|
+
console.log('signer', this.universalSigner);
|
|
226
|
+
console.log('accounts', {
|
|
227
|
+
config: configPda,
|
|
228
|
+
vault: vaultPda,
|
|
229
|
+
user: userPk,
|
|
230
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
231
|
+
});
|
|
232
|
+
txSignature = yield svmClient.writeContract({
|
|
233
|
+
abi: abi_1.SVM_GATEWAY_IDL,
|
|
234
|
+
address: programId.toBase58(),
|
|
235
|
+
functionName: 'sendFundsNative',
|
|
236
|
+
args: [recipientPk, bridgeAmount, revertSvm],
|
|
237
|
+
signer: this.universalSigner,
|
|
238
|
+
accounts: {
|
|
239
|
+
config: configPda,
|
|
240
|
+
vault: vaultPda,
|
|
241
|
+
user: userPk,
|
|
242
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
243
|
+
},
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
else if (execute.funds.token.mechanism === 'approve') {
|
|
247
|
+
// SPL token funds-only (requires pre-existing ATAs)
|
|
248
|
+
const mintPk = new web3_js_1.PublicKey(execute.funds.token.address);
|
|
249
|
+
const [whitelistPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('whitelist')], programId);
|
|
250
|
+
// Associated Token Accounts
|
|
251
|
+
const TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
|
|
252
|
+
const ASSOCIATED_TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL');
|
|
253
|
+
const userAta = web3_js_1.PublicKey.findProgramAddressSync([
|
|
254
|
+
userPk.toBuffer(),
|
|
255
|
+
TOKEN_PROGRAM_ID.toBuffer(),
|
|
256
|
+
mintPk.toBuffer(),
|
|
257
|
+
], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
|
|
258
|
+
const vaultAta = web3_js_1.PublicKey.findProgramAddressSync([
|
|
259
|
+
vaultPda.toBuffer(),
|
|
260
|
+
TOKEN_PROGRAM_ID.toBuffer(),
|
|
261
|
+
mintPk.toBuffer(),
|
|
262
|
+
], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
|
|
263
|
+
const recipientPk = userPk; // must be non-default per program checks
|
|
264
|
+
txSignature = yield svmClient.writeContract({
|
|
265
|
+
abi: abi_1.SVM_GATEWAY_IDL,
|
|
266
|
+
address: programId.toBase58(),
|
|
267
|
+
functionName: 'sendFunds',
|
|
268
|
+
args: [recipientPk, mintPk, bridgeAmount, revertSvm],
|
|
269
|
+
signer: this.universalSigner,
|
|
270
|
+
accounts: {
|
|
271
|
+
config: configPda,
|
|
272
|
+
vault: vaultPda,
|
|
273
|
+
tokenWhitelist: whitelistPda,
|
|
274
|
+
userTokenAccount: userAta,
|
|
275
|
+
gatewayTokenAccount: vaultAta,
|
|
276
|
+
user: userPk,
|
|
277
|
+
bridgeToken: mintPk,
|
|
278
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
279
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
}
|
|
283
|
+
else {
|
|
284
|
+
throw new Error('Unsupported token mechanism on Solana');
|
|
285
|
+
}
|
|
286
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_03);
|
|
287
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_02, txSignature, bridgeAmount, execute.funds.token.decimals, symbol);
|
|
288
|
+
yield svmClient.waitForConfirmations({
|
|
289
|
+
txSignature,
|
|
290
|
+
confirmations: chain_1.CHAIN_INFO[chain].confirmations,
|
|
291
|
+
timeoutMs: chain_1.CHAIN_INFO[chain].timeout,
|
|
292
|
+
});
|
|
293
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_06);
|
|
294
|
+
// Build a minimal UniversalTxResponse for SVM origin
|
|
295
|
+
const chainId = chain_1.CHAIN_INFO[chain].chainId;
|
|
296
|
+
const origin = `${chain_1.VM_NAMESPACE[vm]}:${chainId}:${this.universalSigner.account.address}`;
|
|
297
|
+
const universalTxResponse = {
|
|
298
|
+
hash: txSignature,
|
|
299
|
+
origin,
|
|
300
|
+
blockNumber: BigInt(0),
|
|
301
|
+
blockHash: '',
|
|
302
|
+
transactionIndex: 0,
|
|
303
|
+
chainId,
|
|
304
|
+
from: this.universalSigner.account.address,
|
|
305
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
306
|
+
nonce: 0,
|
|
307
|
+
data: '0x',
|
|
308
|
+
value: BigInt(0),
|
|
309
|
+
gasLimit: BigInt(0),
|
|
310
|
+
gasPrice: undefined,
|
|
311
|
+
maxFeePerGas: undefined,
|
|
312
|
+
maxPriorityFeePerGas: undefined,
|
|
313
|
+
accessList: [],
|
|
314
|
+
wait: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
315
|
+
return ({
|
|
316
|
+
hash: txSignature,
|
|
317
|
+
blockNumber: BigInt(0),
|
|
318
|
+
blockHash: '',
|
|
319
|
+
transactionIndex: 0,
|
|
320
|
+
from: this.universalSigner.account.address,
|
|
321
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
322
|
+
contractAddress: null,
|
|
323
|
+
gasPrice: BigInt(0),
|
|
324
|
+
gasUsed: BigInt(0),
|
|
325
|
+
cumulativeGasUsed: BigInt(0),
|
|
326
|
+
logs: [],
|
|
327
|
+
logsBloom: '0x',
|
|
328
|
+
status: 1,
|
|
329
|
+
raw: {
|
|
330
|
+
from: this.universalSigner.account.address,
|
|
331
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
332
|
+
},
|
|
333
|
+
});
|
|
334
|
+
}),
|
|
335
|
+
type: '99',
|
|
336
|
+
typeVerbose: 'universal',
|
|
337
|
+
signature: { r: '0x0', s: '0x0', v: 0 },
|
|
338
|
+
raw: {
|
|
339
|
+
from: this.universalSigner.account.address,
|
|
340
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
341
|
+
nonce: 0,
|
|
342
|
+
data: '0x',
|
|
343
|
+
value: BigInt(0),
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
return universalTxResponse;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
// Bridge funds + execute payload. Support:
|
|
351
|
+
// - EVM (Sepolia): ERC-20 approve path + native gas via msg.value
|
|
352
|
+
// - SVM (Solana Devnet): SPL or native SOL with gas_amount
|
|
353
|
+
const { chain, evmClient, gatewayAddress } = this.ensureSepoliaGateway();
|
|
354
|
+
// Default token to native ETH if none provided
|
|
355
|
+
if (!execute.funds.token) {
|
|
356
|
+
const available = tokens_1.MOVEABLE_TOKENS[chain] || [];
|
|
357
|
+
const vm = chain_1.CHAIN_INFO[chain].vm;
|
|
358
|
+
const preferredSymbol = vm === enums_1.VM.EVM ? 'ETH' : vm === enums_1.VM.SVM ? 'SOL' : undefined;
|
|
359
|
+
const nativeToken = preferredSymbol
|
|
360
|
+
? available.find((t) => t.symbol === preferredSymbol)
|
|
361
|
+
: undefined;
|
|
362
|
+
if (!nativeToken) {
|
|
363
|
+
throw new Error('Native token not configured for this chain');
|
|
364
|
+
}
|
|
365
|
+
execute.funds.token = nativeToken;
|
|
366
|
+
}
|
|
367
|
+
const mechanism = execute.funds.token.mechanism;
|
|
368
|
+
const { deployed, nonce } = yield this.getUeaStatusAndNonce();
|
|
369
|
+
const { payload: universalPayload } = yield this.buildGatewayPayloadAndGas(execute, nonce);
|
|
370
|
+
// Compute required gas funding on Push Chain and current UEA balance
|
|
371
|
+
const gasEstimate = execute.gasLimit || BigInt(1e7);
|
|
372
|
+
const gasPrice = yield this.pushClient.getGasPrice();
|
|
373
|
+
const requiredGasFee = gasEstimate * gasPrice;
|
|
374
|
+
const payloadValue = (_c = execute.value) !== null && _c !== void 0 ? _c : BigInt(0);
|
|
375
|
+
const requiredFunds = requiredGasFee + payloadValue;
|
|
376
|
+
const ueaAddress = this.computeUEAOffchain();
|
|
377
|
+
const [ueaBalance] = yield Promise.all([
|
|
378
|
+
this.pushClient.getBalance(ueaAddress),
|
|
379
|
+
]);
|
|
380
|
+
// Determine USD to deposit via gateway (8 decimals) with caps: min=$1, max=$10
|
|
381
|
+
const oneUsd = push_chain_1.PushChain.utils.helpers.parseUnits('1', 8);
|
|
382
|
+
const tenUsd = push_chain_1.PushChain.utils.helpers.parseUnits('10', 8);
|
|
383
|
+
const deficit = requiredFunds > ueaBalance ? requiredFunds - ueaBalance : BigInt(0);
|
|
384
|
+
let depositUsd = deficit > BigInt(0) ? this.pushClient.pushToUSDC(deficit) : oneUsd;
|
|
385
|
+
if (depositUsd < oneUsd)
|
|
386
|
+
depositUsd = oneUsd;
|
|
387
|
+
if (depositUsd > tenUsd)
|
|
388
|
+
throw new Error('Deposit value exceeds max $10 worth of native token');
|
|
389
|
+
// Convert USD(8) -> native wei using the same pricing path as fee locking
|
|
390
|
+
const nativeTokenUsdPrice = yield new price_fetch_1.PriceFetch(this.rpcUrls).getPrice(chain); // 8 decimals
|
|
391
|
+
const oneEthWei = push_chain_1.PushChain.utils.helpers.parseUnits('1', 18);
|
|
392
|
+
const nativeAmount = (depositUsd * oneEthWei) / nativeTokenUsdPrice;
|
|
393
|
+
const revertCFG = {
|
|
394
|
+
fundRecipient: this.universalSigner.account
|
|
395
|
+
.address,
|
|
396
|
+
revertMsg: '0x',
|
|
397
|
+
};
|
|
398
|
+
const bridgeAmount = execute.funds.amount;
|
|
399
|
+
const symbol = execute.funds.token.symbol;
|
|
400
|
+
// Funds Flow: Preparing funds transfer
|
|
401
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_01, bridgeAmount, execute.funds.token.decimals, symbol);
|
|
402
|
+
if (chain_1.CHAIN_INFO[this.universalSigner.account.chain].vm === enums_1.VM.EVM) {
|
|
403
|
+
const tokenAddr = execute.funds.token.address;
|
|
404
|
+
if (mechanism !== 'approve') {
|
|
405
|
+
throw new Error('Only ERC-20 tokens are supported for funds+payload on EVM; native and permit2 are not supported yet');
|
|
406
|
+
}
|
|
407
|
+
yield this.ensureErc20Allowance(evmClient, tokenAddr, gatewayAddress, 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
|
+
const ueaAddress = this.computeUEAOffchain();
|
|
415
|
+
const eip712Signature = yield this.signUniversalPayload(universalPayload,
|
|
416
|
+
// execute.to
|
|
417
|
+
ueaAddress);
|
|
418
|
+
const eip712SignatureHex = typeof eip712Signature === 'string'
|
|
419
|
+
? eip712Signature
|
|
420
|
+
: (0, viem_1.bytesToHex)(eip712Signature);
|
|
421
|
+
const signatureData = (0, viem_1.keccak256)(eip712SignatureHex);
|
|
422
|
+
txHash = yield evmClient.writeContract({
|
|
423
|
+
abi: abi_1.UNIVERSAL_GATEWAY_V0,
|
|
424
|
+
address: gatewayAddress,
|
|
425
|
+
functionName: 'sendTxWithFunds',
|
|
426
|
+
args: [
|
|
427
|
+
tokenAddr,
|
|
428
|
+
bridgeAmount,
|
|
429
|
+
universalPayload,
|
|
430
|
+
revertCFG,
|
|
431
|
+
signatureData,
|
|
432
|
+
],
|
|
433
|
+
signer: this.universalSigner,
|
|
434
|
+
value: nativeAmount,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
// SVM funds+payload path
|
|
439
|
+
const svmClient = new svm_client_1.SvmClient({
|
|
440
|
+
rpcUrls: this.rpcUrls[enums_1.CHAIN.SOLANA_DEVNET] ||
|
|
441
|
+
chain_1.CHAIN_INFO[enums_1.CHAIN.SOLANA_DEVNET].defaultRPC,
|
|
442
|
+
});
|
|
443
|
+
const programId = new web3_js_1.PublicKey(abi_1.SVM_GATEWAY_IDL.metadata.address);
|
|
444
|
+
const [configPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('config')], programId);
|
|
445
|
+
const [vaultPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('vault')], programId);
|
|
446
|
+
const [whitelistPda] = web3_js_1.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('whitelist')], programId);
|
|
447
|
+
const userPk = new web3_js_1.PublicKey(this.universalSigner.account.address);
|
|
448
|
+
const priceUpdatePk = new web3_js_1.PublicKey('7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE');
|
|
449
|
+
const isNative = mechanism === 'native' || execute.funds.token.symbol === 'SOL';
|
|
450
|
+
const revertSvm2 = {
|
|
451
|
+
fundRecipient: userPk,
|
|
452
|
+
revertMsg: Buffer.alloc(0),
|
|
453
|
+
};
|
|
454
|
+
if (isNative) {
|
|
455
|
+
// Native SOL as bridge + gas
|
|
456
|
+
txHash = yield svmClient.writeContract({
|
|
457
|
+
abi: abi_1.SVM_GATEWAY_IDL,
|
|
458
|
+
address: programId.toBase58(),
|
|
459
|
+
functionName: 'sendTxWithFunds',
|
|
460
|
+
args: [
|
|
461
|
+
web3_js_1.PublicKey.default, // bridge_token = default for native SOL
|
|
462
|
+
bridgeAmount,
|
|
463
|
+
universalPayload,
|
|
464
|
+
revertSvm2,
|
|
465
|
+
nativeAmount,
|
|
466
|
+
Buffer.alloc(32),
|
|
467
|
+
],
|
|
468
|
+
signer: this.universalSigner,
|
|
469
|
+
accounts: {
|
|
470
|
+
config: configPda,
|
|
471
|
+
vault: vaultPda,
|
|
472
|
+
tokenWhitelist: whitelistPda,
|
|
473
|
+
userTokenAccount: web3_js_1.PublicKey.default, // not used for native
|
|
474
|
+
gatewayTokenAccount: web3_js_1.PublicKey.default, // not used for native
|
|
475
|
+
user: userPk,
|
|
476
|
+
priceUpdate: priceUpdatePk,
|
|
477
|
+
bridgeToken: web3_js_1.PublicKey.default,
|
|
478
|
+
tokenProgram: new web3_js_1.PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'),
|
|
479
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
480
|
+
},
|
|
481
|
+
});
|
|
482
|
+
}
|
|
483
|
+
else {
|
|
484
|
+
// SPL token as bridge + native SOL lamports as gas_amount
|
|
485
|
+
const mintPk = new web3_js_1.PublicKey(execute.funds.token.address);
|
|
486
|
+
const TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA');
|
|
487
|
+
const ASSOCIATED_TOKEN_PROGRAM_ID = new web3_js_1.PublicKey('ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL');
|
|
488
|
+
const userAta = web3_js_1.PublicKey.findProgramAddressSync([
|
|
489
|
+
userPk.toBuffer(),
|
|
490
|
+
TOKEN_PROGRAM_ID.toBuffer(),
|
|
491
|
+
mintPk.toBuffer(),
|
|
492
|
+
], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
|
|
493
|
+
const vaultAta = web3_js_1.PublicKey.findProgramAddressSync([
|
|
494
|
+
vaultPda.toBuffer(),
|
|
495
|
+
TOKEN_PROGRAM_ID.toBuffer(),
|
|
496
|
+
mintPk.toBuffer(),
|
|
497
|
+
], ASSOCIATED_TOKEN_PROGRAM_ID)[0];
|
|
498
|
+
txHash = yield svmClient.writeContract({
|
|
499
|
+
abi: abi_1.SVM_GATEWAY_IDL,
|
|
500
|
+
address: programId.toBase58(),
|
|
501
|
+
functionName: 'sendTxWithFunds',
|
|
502
|
+
args: [
|
|
503
|
+
mintPk,
|
|
504
|
+
bridgeAmount,
|
|
505
|
+
universalPayload,
|
|
506
|
+
revertSvm2,
|
|
507
|
+
nativeAmount,
|
|
508
|
+
Buffer.alloc(32),
|
|
509
|
+
],
|
|
510
|
+
signer: this.universalSigner,
|
|
511
|
+
accounts: {
|
|
512
|
+
config: configPda,
|
|
513
|
+
vault: vaultPda,
|
|
514
|
+
tokenWhitelist: whitelistPda,
|
|
515
|
+
userTokenAccount: userAta,
|
|
516
|
+
gatewayTokenAccount: vaultAta,
|
|
517
|
+
user: userPk,
|
|
518
|
+
priceUpdate: priceUpdatePk,
|
|
519
|
+
bridgeToken: mintPk,
|
|
520
|
+
tokenProgram: TOKEN_PROGRAM_ID,
|
|
521
|
+
systemProgram: web3_js_1.SystemProgram.programId,
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
catch (err) {
|
|
528
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_04);
|
|
529
|
+
throw err;
|
|
530
|
+
}
|
|
531
|
+
// Payload Flow: Verification Success
|
|
532
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_03);
|
|
533
|
+
// Funds Flow: Funds lock submitted
|
|
534
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_02, txHash, bridgeAmount, execute.funds.token.decimals, symbol);
|
|
535
|
+
// Awaiting confirmations
|
|
536
|
+
if (chain_1.CHAIN_INFO[this.universalSigner.account.chain].vm === enums_1.VM.EVM) {
|
|
537
|
+
yield this.waitForEvmConfirmationsWithCountdown(evmClient, txHash, 14, 300000);
|
|
538
|
+
}
|
|
539
|
+
else {
|
|
540
|
+
const svmClient = new svm_client_1.SvmClient({
|
|
541
|
+
rpcUrls: this.rpcUrls[enums_1.CHAIN.SOLANA_DEVNET] ||
|
|
542
|
+
chain_1.CHAIN_INFO[enums_1.CHAIN.SOLANA_DEVNET].defaultRPC,
|
|
543
|
+
});
|
|
544
|
+
yield svmClient.waitForConfirmations({
|
|
545
|
+
txSignature: txHash,
|
|
546
|
+
confirmations: chain_1.CHAIN_INFO[enums_1.CHAIN.SOLANA_DEVNET].confirmations,
|
|
547
|
+
timeoutMs: chain_1.CHAIN_INFO[enums_1.CHAIN.SOLANA_DEVNET].timeout,
|
|
548
|
+
});
|
|
549
|
+
}
|
|
550
|
+
// Funds Flow: Confirmed on origin
|
|
551
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_06);
|
|
552
|
+
// If UEA is not deployed yet, deploy it now using the gateway tx hash
|
|
553
|
+
yield this.sendUniversalTx(deployed, txHash);
|
|
554
|
+
if (chain_1.CHAIN_INFO[this.universalSigner.account.chain].vm === enums_1.VM.EVM) {
|
|
555
|
+
const evmTx = yield evmClient.getTransaction(txHash);
|
|
556
|
+
return yield this.transformToUniversalTxResponse(evmTx);
|
|
557
|
+
}
|
|
558
|
+
else {
|
|
559
|
+
const chainId = chain_1.CHAIN_INFO[chain].chainId;
|
|
560
|
+
const vm = chain_1.CHAIN_INFO[chain].vm;
|
|
561
|
+
const origin = `${chain_1.VM_NAMESPACE[vm]}:${chainId}:${this.universalSigner.account.address}`;
|
|
562
|
+
const universalTxResponse = {
|
|
563
|
+
hash: txHash,
|
|
564
|
+
origin,
|
|
565
|
+
blockNumber: BigInt(0),
|
|
566
|
+
blockHash: '',
|
|
567
|
+
transactionIndex: 0,
|
|
568
|
+
chainId,
|
|
569
|
+
from: this.universalSigner.account.address,
|
|
570
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
571
|
+
nonce: 0,
|
|
572
|
+
data: '0x',
|
|
573
|
+
value: BigInt(0),
|
|
574
|
+
gasLimit: BigInt(0),
|
|
575
|
+
gasPrice: undefined,
|
|
576
|
+
maxFeePerGas: undefined,
|
|
577
|
+
maxPriorityFeePerGas: undefined,
|
|
578
|
+
accessList: [],
|
|
579
|
+
wait: () => tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
580
|
+
return ({
|
|
581
|
+
hash: txHash,
|
|
582
|
+
blockNumber: BigInt(0),
|
|
583
|
+
blockHash: '',
|
|
584
|
+
transactionIndex: 0,
|
|
585
|
+
from: this.universalSigner.account.address,
|
|
586
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
587
|
+
contractAddress: null,
|
|
588
|
+
gasPrice: BigInt(0),
|
|
589
|
+
gasUsed: BigInt(0),
|
|
590
|
+
cumulativeGasUsed: BigInt(0),
|
|
591
|
+
logs: [],
|
|
592
|
+
logsBloom: '0x',
|
|
593
|
+
status: 1,
|
|
594
|
+
raw: {
|
|
595
|
+
from: this.universalSigner.account.address,
|
|
596
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
597
|
+
},
|
|
598
|
+
});
|
|
599
|
+
}),
|
|
600
|
+
type: '99',
|
|
601
|
+
typeVerbose: 'universal',
|
|
602
|
+
signature: { r: '0x0', s: '0x0', v: 0 },
|
|
603
|
+
raw: {
|
|
604
|
+
from: this.universalSigner.account.address,
|
|
605
|
+
to: '0x0000000000000000000000000000000000000000',
|
|
606
|
+
nonce: 0,
|
|
607
|
+
data: '0x',
|
|
608
|
+
value: BigInt(0),
|
|
609
|
+
},
|
|
610
|
+
};
|
|
611
|
+
return universalTxResponse;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
66
615
|
// Set default value for value if undefined
|
|
67
616
|
if (execute.value === undefined) {
|
|
68
617
|
execute.value = BigInt(0);
|
|
@@ -123,8 +672,9 @@ class Orchestrator {
|
|
|
123
672
|
if (!allowedChains.includes(this.universalSigner.account.chain)) {
|
|
124
673
|
throw new Error('Multicall is only enabled for Ethereum Sepolia and Solana Devnet');
|
|
125
674
|
}
|
|
126
|
-
//
|
|
127
|
-
|
|
675
|
+
// For multicall payloads, ExecuteParams.to must be the sentinel '0x'
|
|
676
|
+
// Note: This is distinct from each call's `to` inside MulticallCall[]
|
|
677
|
+
if (execute.to !== '0x') {
|
|
128
678
|
throw new Error('When using multicall, "to" must be a 0x-prefixed address');
|
|
129
679
|
}
|
|
130
680
|
// Normalize and validate calls
|
|
@@ -152,8 +702,12 @@ class Orchestrator {
|
|
|
152
702
|
else {
|
|
153
703
|
payloadData = (execute.data || '0x');
|
|
154
704
|
}
|
|
705
|
+
// Determine payload `to` value. For multicall sentinel '0x', encode as zero address.
|
|
706
|
+
const payloadTo = Array.isArray(execute.data)
|
|
707
|
+
? '0x0000000000000000000000000000000000000000'
|
|
708
|
+
: execute.to;
|
|
155
709
|
const universalPayload = JSON.parse(JSON.stringify({
|
|
156
|
-
to:
|
|
710
|
+
to: payloadTo,
|
|
157
711
|
value: execute.value,
|
|
158
712
|
data: payloadData,
|
|
159
713
|
gasLimit: execute.gasLimit || BigInt(1e7),
|
|
@@ -219,7 +773,19 @@ class Orchestrator {
|
|
|
219
773
|
return transactions[transactions.length - 1];
|
|
220
774
|
}
|
|
221
775
|
catch (err) {
|
|
222
|
-
|
|
776
|
+
const errMessage = err instanceof Error
|
|
777
|
+
? err.message
|
|
778
|
+
: typeof err === 'string'
|
|
779
|
+
? err
|
|
780
|
+
: (() => {
|
|
781
|
+
try {
|
|
782
|
+
return JSON.stringify(err);
|
|
783
|
+
}
|
|
784
|
+
catch (_a) {
|
|
785
|
+
return 'Unknown error';
|
|
786
|
+
}
|
|
787
|
+
})();
|
|
788
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_02, errMessage);
|
|
223
789
|
throw err;
|
|
224
790
|
}
|
|
225
791
|
});
|
|
@@ -365,6 +931,7 @@ class Orchestrator {
|
|
|
365
931
|
universalAccountId,
|
|
366
932
|
txHash: feeLockTxHash,
|
|
367
933
|
}));
|
|
934
|
+
// TODO: pchaind q uexecutor all-universal-tx --node https://rpc-testnet-donut-node1.push.org/
|
|
368
935
|
}
|
|
369
936
|
if (feeLockTxHash) {
|
|
370
937
|
msgs.push(this.pushClient.createMsgMintPC({
|
|
@@ -635,6 +1202,129 @@ class Orchestrator {
|
|
|
635
1202
|
}
|
|
636
1203
|
});
|
|
637
1204
|
}
|
|
1205
|
+
ensureErc20Allowance(evmClient, tokenAddress, spender, requiredAmount) {
|
|
1206
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1207
|
+
const chain = this.universalSigner.account.chain;
|
|
1208
|
+
const owner = this.universalSigner.account.address;
|
|
1209
|
+
const currentAllowance = yield evmClient.readContract({
|
|
1210
|
+
abi: abi_1.ERC20_EVM,
|
|
1211
|
+
address: tokenAddress,
|
|
1212
|
+
functionName: 'allowance',
|
|
1213
|
+
args: [owner, spender],
|
|
1214
|
+
});
|
|
1215
|
+
if (currentAllowance >= requiredAmount)
|
|
1216
|
+
return;
|
|
1217
|
+
// Some ERC-20s like USDT require setting allowance to 0 before changing
|
|
1218
|
+
// an existing non-zero allowance to a different non-zero value.
|
|
1219
|
+
if (currentAllowance > BigInt(0)) {
|
|
1220
|
+
this.printLog(`Resetting existing allowance from ${currentAllowance.toString()} to 0 for spender ${spender}`);
|
|
1221
|
+
const resetTxHash = yield evmClient.writeContract({
|
|
1222
|
+
abi: abi_1.ERC20_EVM,
|
|
1223
|
+
address: tokenAddress,
|
|
1224
|
+
functionName: 'approve',
|
|
1225
|
+
args: [spender, BigInt(0)],
|
|
1226
|
+
signer: this.universalSigner,
|
|
1227
|
+
});
|
|
1228
|
+
yield evmClient.waitForConfirmations({
|
|
1229
|
+
txHash: resetTxHash,
|
|
1230
|
+
confirmations: 1,
|
|
1231
|
+
timeoutMs: chain_1.CHAIN_INFO[chain].timeout,
|
|
1232
|
+
});
|
|
1233
|
+
}
|
|
1234
|
+
const setTxHash = yield evmClient.writeContract({
|
|
1235
|
+
abi: abi_1.ERC20_EVM,
|
|
1236
|
+
address: tokenAddress,
|
|
1237
|
+
functionName: 'approve',
|
|
1238
|
+
args: [spender, requiredAmount],
|
|
1239
|
+
signer: this.universalSigner,
|
|
1240
|
+
});
|
|
1241
|
+
yield evmClient.waitForConfirmations({
|
|
1242
|
+
txHash: setTxHash,
|
|
1243
|
+
confirmations: 1,
|
|
1244
|
+
timeoutMs: chain_1.CHAIN_INFO[chain].timeout,
|
|
1245
|
+
});
|
|
1246
|
+
try {
|
|
1247
|
+
const updated = yield evmClient.readContract({
|
|
1248
|
+
abi: abi_1.ERC20_EVM,
|
|
1249
|
+
address: tokenAddress,
|
|
1250
|
+
functionName: 'allowance',
|
|
1251
|
+
args: [owner, spender],
|
|
1252
|
+
});
|
|
1253
|
+
if (updated < requiredAmount) {
|
|
1254
|
+
this.printLog('Warning: allowance not updated yet; proceeding');
|
|
1255
|
+
}
|
|
1256
|
+
}
|
|
1257
|
+
catch (_a) {
|
|
1258
|
+
// ignore
|
|
1259
|
+
}
|
|
1260
|
+
});
|
|
1261
|
+
}
|
|
1262
|
+
/**
|
|
1263
|
+
* Ensures we're on Sepolia, returns EVM client and gateway address.
|
|
1264
|
+
*/
|
|
1265
|
+
ensureSepoliaGateway() {
|
|
1266
|
+
const chain = this.universalSigner.account.chain;
|
|
1267
|
+
if (chain !== enums_1.CHAIN.ETHEREUM_SEPOLIA) {
|
|
1268
|
+
throw new Error('Funds + payload bridging is only supported on Ethereum Sepolia for now');
|
|
1269
|
+
}
|
|
1270
|
+
const { defaultRPC, lockerContract } = chain_1.CHAIN_INFO[chain];
|
|
1271
|
+
const rpcUrls = this.rpcUrls[chain] || defaultRPC;
|
|
1272
|
+
const evmClient = new evm_client_1.EvmClient({ rpcUrls });
|
|
1273
|
+
const gatewayAddress = lockerContract;
|
|
1274
|
+
if (!gatewayAddress) {
|
|
1275
|
+
throw new Error('Universal Gateway address not configured');
|
|
1276
|
+
}
|
|
1277
|
+
return { chain, evmClient, gatewayAddress };
|
|
1278
|
+
}
|
|
1279
|
+
/**
|
|
1280
|
+
* Computes UEA and fetches its nonce if deployed; returns 0 otherwise.
|
|
1281
|
+
*/
|
|
1282
|
+
getUeaNonceForExecution() {
|
|
1283
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1284
|
+
const UEA = this.computeUEAOffchain();
|
|
1285
|
+
const [code] = yield Promise.all([
|
|
1286
|
+
this.pushClient.publicClient.getCode({ address: UEA }),
|
|
1287
|
+
]);
|
|
1288
|
+
return code !== undefined ? yield this.getUEANonce(UEA) : BigInt(0);
|
|
1289
|
+
});
|
|
1290
|
+
}
|
|
1291
|
+
/**
|
|
1292
|
+
* Returns UEA deployment status and nonce (0 if not deployed).
|
|
1293
|
+
*/
|
|
1294
|
+
getUeaStatusAndNonce() {
|
|
1295
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1296
|
+
const UEA = this.computeUEAOffchain();
|
|
1297
|
+
const [code] = yield Promise.all([
|
|
1298
|
+
this.pushClient.publicClient.getCode({ address: UEA }),
|
|
1299
|
+
]);
|
|
1300
|
+
const deployed = code !== undefined;
|
|
1301
|
+
const nonce = deployed ? yield this.getUEANonce(UEA) : BigInt(0);
|
|
1302
|
+
return { deployed, nonce };
|
|
1303
|
+
});
|
|
1304
|
+
}
|
|
1305
|
+
/**
|
|
1306
|
+
* Builds UniversalPayload for the gateway and computes the native gas deposit.
|
|
1307
|
+
*/
|
|
1308
|
+
buildGatewayPayloadAndGas(execute, nonce) {
|
|
1309
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1310
|
+
var _a, _b;
|
|
1311
|
+
const gasEstimate = execute.gasLimit || BigInt(1e7);
|
|
1312
|
+
const payloadValue = (_a = execute.value) !== null && _a !== void 0 ? _a : BigInt(0);
|
|
1313
|
+
const gasAmount = (_b = execute.value) !== null && _b !== void 0 ? _b : BigInt(0);
|
|
1314
|
+
const universalPayload = {
|
|
1315
|
+
to: execute.to,
|
|
1316
|
+
value: payloadValue,
|
|
1317
|
+
data: execute.data || '0x',
|
|
1318
|
+
gasLimit: gasEstimate,
|
|
1319
|
+
maxFeePerGas: execute.maxFeePerGas || BigInt(1e10),
|
|
1320
|
+
maxPriorityFeePerGas: execute.maxPriorityFeePerGas || BigInt(0),
|
|
1321
|
+
nonce,
|
|
1322
|
+
deadline: execute.deadline || BigInt(9999999999),
|
|
1323
|
+
vType: tx_1.VerificationType.universalTxVerification,
|
|
1324
|
+
};
|
|
1325
|
+
return { payload: universalPayload, gasAmount };
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
638
1328
|
/********************************** HELPER FUNCTIONS **************************************************/
|
|
639
1329
|
/**
|
|
640
1330
|
* Transforms a TransactionReceipt to UniversalTxReceipt format
|
|
@@ -855,6 +1545,32 @@ class Orchestrator {
|
|
|
855
1545
|
// invoke the user-provided callback
|
|
856
1546
|
this.progressHook(hookPayload);
|
|
857
1547
|
}
|
|
1548
|
+
// Emit countdown updates while waiting for EVM confirmations
|
|
1549
|
+
waitForEvmConfirmationsWithCountdown(evmClient, txHash, confirmations, timeoutMs) {
|
|
1550
|
+
return tslib_1.__awaiter(this, void 0, void 0, function* () {
|
|
1551
|
+
// initial emit
|
|
1552
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_03, confirmations);
|
|
1553
|
+
const start = Date.now();
|
|
1554
|
+
// Wait for receipt to get included block
|
|
1555
|
+
const receipt = yield evmClient.publicClient.waitForTransactionReceipt({
|
|
1556
|
+
hash: txHash,
|
|
1557
|
+
});
|
|
1558
|
+
const targetBlock = receipt.blockNumber + BigInt(confirmations);
|
|
1559
|
+
// Poll blocks and emit remaining confirmations
|
|
1560
|
+
// eslint-disable-next-line no-constant-condition
|
|
1561
|
+
while (true) {
|
|
1562
|
+
const currentBlock = yield evmClient.publicClient.getBlockNumber();
|
|
1563
|
+
if (currentBlock >= targetBlock)
|
|
1564
|
+
return;
|
|
1565
|
+
const remaining = Number(targetBlock - currentBlock);
|
|
1566
|
+
this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06_03, remaining);
|
|
1567
|
+
if (Date.now() - start > timeoutMs) {
|
|
1568
|
+
throw new Error(`Timeout: transaction ${txHash} not confirmed with ${confirmations} confirmations within ${timeoutMs} ms`);
|
|
1569
|
+
}
|
|
1570
|
+
yield new Promise((r) => setTimeout(r, 12000));
|
|
1571
|
+
}
|
|
1572
|
+
});
|
|
1573
|
+
}
|
|
858
1574
|
}
|
|
859
1575
|
exports.Orchestrator = Orchestrator;
|
|
860
1576
|
//# sourceMappingURL=orchestrator.js.map
|