@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.
- 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 +1880 -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 +172 -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 +19 -0
- package/src/lib/orchestrator/orchestrator.js +796 -7
- 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 +61 -2
- package/src/lib/progress-hook/progress-hook.js.map +1 -1
- package/src/lib/progress-hook/progress-hook.types.d.ts +8 -0
- package/src/lib/progress-hook/progress-hook.types.js +11 -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
- package/src/lib/vm-client/svm-client.js +83 -4
- 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
|
|
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
|
|
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
|
-
|
|
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
|