UNPKG

@pushchain/core

Version:
814 lines 38 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Orchestrator = void 0; const tslib_1 = require("tslib"); const viem_1 = require("viem"); const push_chain_1 = require("../push-chain/push-chain"); const enums_1 = require("../constants/enums"); const evm_client_1 = require("../vm-client/evm-client"); const chain_1 = require("../constants/chain"); const abi_1 = require("../constants/abi"); const push_client_1 = require("../push-client/push-client"); const svm_client_1 = require("../vm-client/svm-client"); const web3_js_1 = require("@solana/web3.js"); const anchor = tslib_1.__importStar(require("@coral-xyz/anchor")); const tx_1 = require("../generated/v1/tx"); const price_fetch_1 = require("../price-fetch/price-fetch"); const anchor_1 = require("@coral-xyz/anchor"); const progress_hook_types_1 = require("../progress-hook/progress-hook.types"); const progress_hook_1 = tslib_1.__importDefault(require("../progress-hook/progress-hook")); const uea_evm_1 = require("../constants/abi/uea.evm"); class Orchestrator { constructor(universalSigner, pushNetwork, rpcUrls = {}, printTraces = false, progressHook) { this.universalSigner = universalSigner; this.pushNetwork = pushNetwork; this.rpcUrls = rpcUrls; this.printTraces = printTraces; this.progressHook = progressHook; let pushChain; if (pushNetwork === enums_1.PUSH_NETWORK.MAINNET) { pushChain = enums_1.CHAIN.PUSH_MAINNET; } else if (pushNetwork === enums_1.PUSH_NETWORK.TESTNET_DONUT || pushNetwork === enums_1.PUSH_NETWORK.TESTNET) { pushChain = enums_1.CHAIN.PUSH_TESTNET_DONUT; } else { pushChain = enums_1.CHAIN.PUSH_LOCALNET; } const pushChainRPCs = this.rpcUrls[pushChain] || chain_1.CHAIN_INFO[pushChain].defaultRPC; this.pushClient = new push_client_1.PushClient({ rpcUrls: pushChainRPCs, network: pushNetwork, }); } /** * Read-only accessors for current Orchestrator configuration */ getNetwork() { return this.pushNetwork; } getRpcUrls() { return this.rpcUrls; } getPrintTraces() { return this.printTraces; } getProgressHook() { return this.progressHook; } /** * Executes an interaction on Push Chain */ execute(execute) { return tslib_1.__awaiter(this, void 0, void 0, function* () { try { // Validate fundGas property - must not be set for now if (execute.fundGas) { throw new Error('Unsupported token'); } const chain = this.universalSigner.account.chain; this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_01, chain); this.validateMainnetConnection(chain); /** * Push to Push Tx */ if (this.isPushChain(chain)) { this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06); const tx = yield this.sendPushTx(execute); this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_01, [tx]); return tx; } /** * Fetch Gas details and estimate cost of execution */ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_02_01); const gasEstimate = execute.gasLimit || BigInt(1e7); const gasPrice = yield this.pushClient.getGasPrice(); const requiredGasFee = gasEstimate * gasPrice; const requiredFunds = requiredGasFee + execute.value; this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_02_02, requiredFunds); /** * Fetch UEA Details */ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_01); const UEA = this.computeUEAOffchain(); const [code, funds] = yield Promise.all([ this.pushClient.publicClient.getCode({ address: UEA }), this.pushClient.getBalance(UEA), ]); const isUEADeployed = code !== undefined; const nonce = isUEADeployed ? yield this.getUEANonce(UEA) : BigInt(0); this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_03_02, UEA, isUEADeployed, funds, nonce); /** * Compute Universal Payload Hash */ let feeLockTxHash = execute.feeLockTxHash; if (feeLockTxHash && !feeLockTxHash.startsWith('0x')) { // decode svm base58 feeLockTxHash = (0, viem_1.bytesToHex)(anchor_1.utils.bytes.bs58.decode(feeLockTxHash)); } // Fee locking is required if UEA is not deployed OR insufficient funds const feeLockingRequired = (!isUEADeployed || funds < requiredFunds) && !feeLockTxHash; const universalPayload = JSON.parse(JSON.stringify({ to: execute.to, value: execute.value, data: execute.data || '0x', gasLimit: execute.gasLimit || BigInt(1e7), maxFeePerGas: execute.maxFeePerGas || BigInt(1e10), maxPriorityFeePerGas: execute.maxPriorityFeePerGas || BigInt(0), nonce, deadline: execute.deadline || BigInt(9999999999), vType: feeLockingRequired ? tx_1.VerificationType.universalTxVerification : tx_1.VerificationType.signedVerification, }, this.bigintReplacer)); const executionHash = this.computeExecutionHash({ verifyingContract: UEA, payload: universalPayload, }); /** * Prepare verification data by either signature or fund locking */ let verificationData; /** * 1. UEA deployed + sufficient funds: No fee locking needed * 2. UEA deployed + insufficient funds: Lock requiredFunds * 3. UEA not deployed + sufficient funds: Lock 0.001 PC (for deployment) * 4. UEA not deployed + insufficient funds: Lock requiredFunds */ if (!feeLockingRequired) { /** * Sign Universal Payload */ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_01, executionHash); const signature = yield this.signUniversalPayload(universalPayload, UEA); verificationData = (0, viem_1.bytesToHex)(signature); this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_04_02, verificationData); } else { /** * Fee Locking */ const fundDifference = requiredFunds - funds; const fixedPushAmount = push_chain_1.PushChain.utils.helpers.parseUnits('0.001', 18); // Minimum lock 0.001 Push tokens const lockAmount = funds < requiredFunds ? fundDifference : fixedPushAmount; const lockAmountInUSD = this.pushClient.pushToUSDC(lockAmount); this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_05_01, lockAmount); const feeLockTxHashBytes = yield this.lockFee(lockAmountInUSD, executionHash); feeLockTxHash = (0, viem_1.bytesToHex)(feeLockTxHashBytes); verificationData = (0, viem_1.bytesToHex)(feeLockTxHashBytes); /** * Waiting for Confirmations */ const { vm } = chain_1.CHAIN_INFO[chain]; this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_05_02, vm === enums_1.VM.SVM ? anchor_1.utils.bytes.bs58.encode(feeLockTxHashBytes) : feeLockTxHash, chain_1.CHAIN_INFO[chain].confirmations); yield this.waitForLockerFeeConfirmation(feeLockTxHashBytes); this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_05_03); } /** * Broadcasting Tx to PC */ this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_06); const transactions = yield this.sendUniversalTx(isUEADeployed, feeLockTxHash, universalPayload, verificationData); this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_01, transactions); return transactions[transactions.length - 1]; } catch (err) { this.executeProgressHook(progress_hook_types_1.PROGRESS_HOOK.SEND_TX_99_02, err); throw err; } }); } /** * Locks a fee on the origin chain by interacting with the chain's fee-locker contract. * * @param amount - Fee amount in USDC (8 Decimals) * @param executionHash - Optional execution payload hash (default: zeroHash) * @returns Transaction hash of the locking transaction */ lockFee(amount_1) { return tslib_1.__awaiter(this, arguments, void 0, function* (amount, // USD with 8 decimals executionHash = viem_1.zeroHash) { const chain = this.universalSigner.account.chain; const { lockerContract, vm, defaultRPC } = chain_1.CHAIN_INFO[chain]; if (!lockerContract) { throw new Error(`Locker contract not configured for chain: ${chain}`); } const rpcUrls = this.rpcUrls[chain] || defaultRPC; switch (vm) { case enums_1.VM.EVM: { // Run price fetching and client creation in parallel const [nativeTokenUsdPrice, evmClient] = yield Promise.all([ new price_fetch_1.PriceFetch(this.rpcUrls).getPrice(chain), // 8 decimals Promise.resolve(new evm_client_1.EvmClient({ rpcUrls })), ]); const nativeDecimals = 18; // ETH, MATIC, etc. const nativeAmount = (amount * BigInt(Math.pow(10, nativeDecimals))) / nativeTokenUsdPrice; const txHash = yield evmClient.writeContract({ abi: abi_1.FEE_LOCKER_EVM, address: lockerContract, functionName: 'addFunds', args: [executionHash], signer: this.universalSigner, value: nativeAmount, }); return (0, viem_1.hexToBytes)(txHash); } case enums_1.VM.SVM: { // Run price fetching, client creation, and PDA computation in parallel const [nativeTokenUsdPrice, svmClient, [lockerPda]] = yield Promise.all([ new price_fetch_1.PriceFetch(this.rpcUrls).getPrice(chain), // 8 decimals Promise.resolve(new svm_client_1.SvmClient({ rpcUrls })), Promise.resolve(anchor.web3.PublicKey.findProgramAddressSync([(0, viem_1.stringToBytes)('locker')], new web3_js_1.PublicKey(lockerContract))), ]); const nativeDecimals = 9; // SOL lamports const nativeAmount = (amount * BigInt(Math.pow(10, nativeDecimals))) / nativeTokenUsdPrice; const txHash = yield svmClient.writeContract({ abi: abi_1.FEE_LOCKER_SVM, address: lockerContract, functionName: 'addFunds', args: [nativeAmount, (0, viem_1.toBytes)(executionHash)], signer: this.universalSigner, accounts: { locker: lockerPda, user: new web3_js_1.PublicKey(this.universalSigner.account.address), priceUpdate: new web3_js_1.PublicKey('7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE'), systemProgram: web3_js_1.SystemProgram.programId, }, }); return anchor_1.utils.bytes.bs58.decode(txHash); } default: throw new Error(`Unsupported VM type: ${vm}`); } }); } signUniversalPayload(universalPayload, verifyingContract, version) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const chain = this.universalSigner.account.chain; const { vm, chainId } = chain_1.CHAIN_INFO[chain]; switch (vm) { case enums_1.VM.EVM: { if (!this.universalSigner.signTypedData) { throw new Error('signTypedData is not defined'); } return this.universalSigner.signTypedData({ domain: { version: version || '0.1.0', chainId: Number(chainId), verifyingContract, }, types: { UniversalPayload: [ { name: 'to', type: 'address' }, { name: 'value', type: 'uint256' }, { name: 'data', type: 'bytes' }, { name: 'gasLimit', type: 'uint256' }, { name: 'maxFeePerGas', type: 'uint256' }, { name: 'maxPriorityFeePerGas', type: 'uint256' }, { name: 'nonce', type: 'uint256' }, { name: 'deadline', type: 'uint256' }, { name: 'vType', type: 'uint8' }, ], }, primaryType: 'UniversalPayload', message: universalPayload, }); } case enums_1.VM.SVM: { const digest = this.computeExecutionHash({ verifyingContract, payload: universalPayload, version: version || '0.1.0', }); return this.universalSigner.signMessage((0, viem_1.stringToBytes)(digest)); } default: { throw new Error(`Unsupported VM type: ${vm}`); } } }); } /** * Sends a custom Cosmos tx to Push Chain (gasless) to execute user intent. */ sendUniversalTx(isUEADeployed, feeLockTxHash, universalPayload, verificationData) { return tslib_1.__awaiter(this, void 0, void 0, function* () { var _a, _b; const { chain, address } = this.universalSigner.account; const { vm, chainId } = chain_1.CHAIN_INFO[chain]; const universalAccountId = { chainNamespace: chain_1.VM_NAMESPACE[vm], chainId: chainId, owner: vm === enums_1.VM.EVM ? address : vm === enums_1.VM.SVM ? (0, viem_1.bytesToHex)(anchor_1.utils.bytes.bs58.decode(address)) : address, }; const { cosmosAddress: signer } = this.pushClient.getSignerAddress(); const msgs = []; if (!isUEADeployed) { /** * @dev - fee should be locked for UEA deployment to avoid spamming */ if (!feeLockTxHash) { throw new Error('UEA cannot be deployed without fee locking'); } msgs.push(this.pushClient.createMsgDeployUEA({ signer, universalAccountId, txHash: feeLockTxHash, })); } if (feeLockTxHash) { msgs.push(this.pushClient.createMsgMintPC({ signer, universalAccountId, txHash: feeLockTxHash, })); } if (universalPayload && verificationData) { msgs.push(this.pushClient.createMsgExecutePayload({ signer, universalAccountId, universalPayload, verificationData, })); } const txBody = yield this.pushClient.createCosmosTxBody(msgs); const txRaw = yield this.pushClient.signCosmosTx(txBody); const tx = yield this.pushClient.broadcastCosmosTx(txRaw); if (tx.code !== 0) { throw new Error(tx.rawLog); } const ethTxHashes = (_b = (_a = tx.events) === null || _a === void 0 ? void 0 : _a.filter((e) => e.type === 'ethereum_tx').flatMap((e) => { var _a; return (_a = e.attributes) === null || _a === void 0 ? void 0 : _a.filter((attr) => attr.key === 'ethereumTxHash').map((attr) => attr.value); })) !== null && _b !== void 0 ? _b : []; if (ethTxHashes.length === 0) { throw new Error('No ethereumTxHash found in transaction events'); } // 🔗 Fetch all corresponding EVM transactions in parallel const evmTxs = yield Promise.all(ethTxHashes.map((hash) => tslib_1.__awaiter(this, void 0, void 0, function* () { return yield this.pushClient.getTransaction(hash); }))); return yield Promise.all(evmTxs.map((tx) => this.transformToUniversalTxResponse(tx))); }); } /** * Sends a EVM trx on Push Chain * @dev - Only to be used from universal signer is on Push chain * @param execute * @returns Cosmos Tx Response for a given Evm Tx */ sendPushTx(execute) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const txHash = yield this.pushClient.sendTransaction({ to: execute.to, data: execute.data || '0x', value: execute.value, signer: this.universalSigner, }); const txResponse = yield this.pushClient.getTransaction(txHash); return yield this.transformToUniversalTxResponse(txResponse); }); } /** * Computes the EIP-712 digest hash for the UniversalPayload structure. * This is the message that should be signed by the user's wallet (e.g., Solana signer). * * The resulting hash is equivalent to: * keccak256("\x19\x01" || domainSeparator || structHash) * * @param chainId - EVM chain ID of the destination chain (Push Chain) * @param verifyingContract - Address of the verifying contract (i.e., the user's UEA smart wallet) * @param version - Optional EIP-712 domain version (default: '0.1.0') * @param payload - Execution details encoded into the UniversalPayload struct * @returns keccak256 digest to be signed by the user */ computeExecutionHash({ verifyingContract, payload, version = '0.1.0', }) { const chain = this.universalSigner.account.chain; const { vm, chainId } = chain_1.CHAIN_INFO[chain]; // 1. Type hash const typeHash = (0, viem_1.keccak256)((0, viem_1.toBytes)('UniversalPayload(address to,uint256 value,bytes data,uint256 gasLimit,uint256 maxFeePerGas,uint256 maxPriorityFeePerGas,uint256 nonce,uint256 deadline,uint8 vType)')); // 2. Domain separator const domainTypeHash = (0, viem_1.keccak256)((0, viem_1.toBytes)(vm === enums_1.VM.EVM ? 'EIP712Domain(string version,uint256 chainId,address verifyingContract)' : 'EIP712Domain_SVM(string version,string chainId,address verifyingContract)')); const domainSeparator = (0, viem_1.keccak256)((0, viem_1.encodeAbiParameters)([ { name: 'typeHash', type: 'bytes32' }, { name: 'version', type: 'bytes32' }, { name: 'chainId', type: vm === enums_1.VM.EVM ? 'uint256' : 'string' }, { name: 'verifyingContract', type: 'address' }, ], [ domainTypeHash, (0, viem_1.keccak256)((0, viem_1.toBytes)(version)), vm === enums_1.VM.EVM ? BigInt(chainId) : chainId, verifyingContract, ])); // 3. Struct hash const structHash = (0, viem_1.keccak256)((0, viem_1.encodeAbiParameters)([ { name: 'typeHash', type: 'bytes32' }, { name: 'to', type: 'address' }, { name: 'value', type: 'uint256' }, { name: 'data', type: 'bytes32' }, { name: 'gasLimit', type: 'uint256' }, { name: 'maxFeePerGas', type: 'uint256' }, { name: 'maxPriorityFeePerGas', type: 'uint256' }, { name: 'nonce', type: 'uint256' }, { name: 'deadline', type: 'uint256' }, { name: 'vType', type: 'uint8' }, ], [ typeHash, payload.to, BigInt(payload.value), (0, viem_1.keccak256)(payload.data), BigInt(payload.gasLimit), BigInt(payload.maxFeePerGas), BigInt(payload.maxPriorityFeePerGas), BigInt(payload.nonce), BigInt(payload.deadline), payload.vType, ])); // 4. Final digest return (0, viem_1.keccak256)((0, viem_1.encodePacked)(['string', 'bytes32', 'bytes32'], ['\x19\x01', domainSeparator, structHash])); } /** * Computes UEA for given UniversalAccount * @dev - This fn calls a view fn of Factory Contract * @dev - Don't use this fn in production - only used for testing * @returns UEA Address with Deployment Status */ computeUEA() { return tslib_1.__awaiter(this, void 0, void 0, function* () { const { chain, address } = this.universalSigner.account; const { vm, chainId } = chain_1.CHAIN_INFO[chain]; if (this.isPushChain(chain)) { throw new Error('UEA cannot be computed for a Push Chain Address'); } const computedAddress = yield this.pushClient.readContract({ address: this.pushClient.pushChainInfo.factoryAddress, abi: abi_1.FACTORY_V1, functionName: 'computeUEA', args: [ { chainNamespace: chain_1.VM_NAMESPACE[vm], chainId: chainId, /** * @dev - Owner should be in bytes * for eth - convert hex to bytes * for sol - convert base64 to bytes * for others - not defined yet */ owner: vm === enums_1.VM.EVM ? address : vm === enums_1.VM.SVM ? (0, viem_1.bytesToHex)(anchor_1.utils.bytes.bs58.decode(address)) : address, }, ], }); const byteCode = yield this.pushClient.publicClient.getCode({ address: computedAddress, }); return { address: computedAddress, deployed: byteCode !== undefined }; }); } computeUEAOffchain() { const { chain, address } = this.universalSigner.account; const { vm, chainId } = chain_1.CHAIN_INFO[chain]; // If already an on-chain Push EOA, just return it if (this.isPushChain(chain)) { return address; } // 1) Figure out the external‐chain ownerKey bytes let ownerKey; if (chain_1.CHAIN_INFO[chain].vm === enums_1.VM.EVM) { ownerKey = address; } else if (chain_1.CHAIN_INFO[chain].vm === enums_1.VM.SVM) { ownerKey = (0, viem_1.bytesToHex)(anchor_1.utils.bytes.bs58.decode(address)); } else { throw new Error(`Unsupported VM type: ${chain_1.CHAIN_INFO[chain].vm}`); } // Step 1: Recreate the salt: keccak256(abi.encode(UniversalAccount)) const encodedAccountId = (0, viem_1.encodeAbiParameters)([ { type: 'tuple', components: [ { name: 'chainNamespace', type: 'string' }, { name: 'chainId', type: 'string' }, { name: 'owner', type: 'bytes' }, ], }, ], [{ chainNamespace: chain_1.VM_NAMESPACE[vm], chainId, owner: ownerKey }]); const salt = (0, viem_1.keccak256)(encodedAccountId); // Step 2: Clone Minimal Proxy bytecode const minimalProxyRuntimeCode = ('0x3d602d80600a3d3981f3' + '363d3d373d3d3d363d73' + chain_1.UEA_PROXY[this.pushNetwork].toLowerCase().replace(/^0x/, '') + '5af43d82803e903d91602b57fd5bf3'); // Step 3: Get init code hash (used by CREATE2) const initCodeHash = (0, viem_1.keccak256)(minimalProxyRuntimeCode); // Step 4: Predict the address using standard CREATE2 formula const ueaAddress = (0, viem_1.getCreate2Address)({ from: this.pushClient.pushChainInfo.factoryAddress, salt, bytecodeHash: initCodeHash, }); return ueaAddress; } /** * @dev - Although as of now nonce var is same in evm & svm so switch conditions does not matter * @param address UEA address * @returns UEA current nonce */ getUEANonce(address) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const chain = this.universalSigner.account.chain; const { vm } = chain_1.CHAIN_INFO[chain]; switch (vm) { case enums_1.VM.EVM: { return this.pushClient.readContract({ address, abi: uea_evm_1.UEA_EVM, functionName: 'nonce', }); } case enums_1.VM.SVM: { return this.pushClient.readContract({ address, abi: abi_1.UEA_SVM, functionName: 'nonce', }); } default: { throw new Error(`Unsupported VM type: ${vm}`); } } }); } // TODO: Fix this fn - It needs to get UOA for a given UEA getUOA() { return { chain: this.universalSigner.account.chain, address: this.universalSigner.account.address, }; } waitForLockerFeeConfirmation(txHashBytes) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const chain = this.universalSigner.account.chain; const { vm, defaultRPC, confirmations, timeout } = chain_1.CHAIN_INFO[chain]; const rpcUrls = this.rpcUrls[chain] || defaultRPC; switch (vm) { case enums_1.VM.EVM: { const evmClient = new evm_client_1.EvmClient({ rpcUrls }); yield evmClient.waitForConfirmations({ txHash: (0, viem_1.bytesToHex)(txHashBytes), confirmations, timeoutMs: timeout, }); return; } case enums_1.VM.SVM: { const svmClient = new svm_client_1.SvmClient({ rpcUrls }); yield svmClient.waitForConfirmations({ txSignature: anchor_1.utils.bytes.bs58.encode(txHashBytes), confirmations, timeoutMs: timeout, }); return; } default: throw new Error(`Unsupported VM for tx confirmation: ${vm}`); } }); } /********************************** HELPER FUNCTIONS **************************************************/ /** * Transforms a TransactionReceipt to UniversalTxReceipt format */ transformToUniversalTxReceipt(receipt, // TransactionReceipt from viem originalTxResponse) { return { // 1. Identity hash: receipt.transactionHash, // 2. Block Info blockNumber: receipt.blockNumber, blockHash: receipt.blockHash, transactionIndex: receipt.transactionIndex, // 3. Execution Context from: originalTxResponse.from, to: originalTxResponse.to, contractAddress: receipt.contractAddress || null, // 4. Gas & Usage gasPrice: originalTxResponse.gasPrice || BigInt(0), gasUsed: receipt.gasUsed, cumulativeGasUsed: receipt.cumulativeGasUsed, // 5. Logs logs: receipt.logs || [], logsBloom: receipt.logsBloom || '0x', // 6. Outcome status: receipt.status === 'success' ? 1 : 0, // 7. Raw raw: originalTxResponse.raw || { from: originalTxResponse.from, to: originalTxResponse.to, }, }; } /** * Transforms a TxResponse to the new UniversalTxResponse format */ transformToUniversalTxResponse(tx) { return tslib_1.__awaiter(this, void 0, void 0, function* () { const chain = this.universalSigner.account.chain; const { vm, chainId } = chain_1.CHAIN_INFO[chain]; let from; let to; let value; let data; let rawTransactionData; const ueaOrigin = yield push_chain_1.PushChain.utils.account.convertExecutorToOriginAccount(tx.to); let originAddress; if (ueaOrigin.exists) { if (!ueaOrigin.account) { throw new Error('UEA origin account is null'); } originAddress = ueaOrigin.account.address; from = (0, viem_1.getAddress)(tx.to); let decoded; if (tx.input !== '0x') { decoded = (0, viem_1.decodeFunctionData)({ abi: uea_evm_1.UEA_EVM, data: tx.input, }); if (!(decoded === null || decoded === void 0 ? void 0 : decoded.args)) { throw new Error('Failed to decode function data'); } const universalPayload = decoded === null || decoded === void 0 ? void 0 : decoded.args[0]; to = universalPayload.to; value = BigInt(universalPayload.value); data = universalPayload.data; rawTransactionData = { from: (0, viem_1.getAddress)(tx.from), to: (0, viem_1.getAddress)(tx.to), nonce: tx.nonce, data: tx.input, value: tx.value, }; } else { to = (0, viem_1.getAddress)(tx.to); value = tx.value; data = tx.input; rawTransactionData = { from: (0, viem_1.getAddress)(tx.from), to: (0, viem_1.getAddress)(tx.to), nonce: tx.nonce, data: tx.input, value: tx.value, }; } } else { originAddress = (0, viem_1.getAddress)(tx.from); from = (0, viem_1.getAddress)(tx.from); to = (0, viem_1.getAddress)(tx.to); value = tx.value; data = tx.input; rawTransactionData = { from: (0, viem_1.getAddress)(tx.from), to: (0, viem_1.getAddress)(tx.to), nonce: tx.nonce, data: tx.input, value: tx.value, }; } const origin = `${chain_1.VM_NAMESPACE[vm]}:${chainId}:${originAddress}`; // Create signature from transaction r, s, v values let signature; try { signature = { r: tx.r || '0x0', s: tx.s || '0x0', v: typeof tx.v === 'bigint' ? Number(tx.v) : tx.v || 0, yParity: tx.yParity, }; } catch (_a) { // Fallback signature if parsing fails signature = { r: '0x0000000000000000000000000000000000000000000000000000000000000000', s: '0x0000000000000000000000000000000000000000000000000000000000000000', v: 0, yParity: 0, }; } // Determine transaction type and typeVerbose let type = '99'; // universal let typeVerbose = 'universal'; if (tx.type !== undefined) { const txType = tx.type; if (txType === 'eip1559') { type = '2'; typeVerbose = 'eip1559'; } else if (txType === 'eip2930') { type = '1'; typeVerbose = 'eip2930'; } else if (txType === 'legacy') { type = '0'; typeVerbose = 'legacy'; } else if (txType == 'eip4844') { type = '3'; typeVerbose = 'eip4844'; } } const universalTxResponse = { // 1. Identity hash: tx.hash, origin, // 2. Block Info blockNumber: tx.blockNumber || BigInt(0), blockHash: tx.blockHash || '', transactionIndex: tx.transactionIndex || 0, chainId, // 3. Execution Context from: from, // UEA (executor) address, checksummed for EVM to: to || '', nonce: tx.nonce, // 4. Payload data, // perceived calldata (was input) value, // 5. Gas gasLimit: tx.gas || BigInt(0), // (was gas) gasPrice: tx.gasPrice, maxFeePerGas: tx.maxFeePerGas, maxPriorityFeePerGas: tx.maxPriorityFeePerGas, accessList: Array.isArray(tx.accessList) ? [...tx.accessList] : [], // 6. Utilities wait: () => tslib_1.__awaiter(this, void 0, void 0, function* () { const receipt = yield tx.wait(); return this.transformToUniversalTxReceipt(receipt, universalTxResponse); }), // 7. Metadata type, typeVerbose, signature, // 8. Raw Universal Fields raw: rawTransactionData, }; return universalTxResponse; }); } // eslint-disable-next-line @typescript-eslint/no-explicit-any bigintReplacer(_key, value) { return typeof value === 'bigint' ? value.toString() // convert BigInt to string : value; } /** * Checks if the given chain belongs to the Push Chain ecosystem. * Used to differentiate logic for Push-native interactions vs external chains. * * @param chain - The chain identifier (e.g., PUSH_MAINNET, PUSH_TESTNET_DONUT) * @returns True if the chain is a Push chain, false otherwise. */ isPushChain(chain) { return (chain === enums_1.CHAIN.PUSH_MAINNET || chain === enums_1.CHAIN.PUSH_TESTNET_DONUT || chain === enums_1.CHAIN.PUSH_LOCALNET); } validateMainnetConnection(chain) { const isMainnet = [enums_1.CHAIN.ETHEREUM_MAINNET, enums_1.CHAIN.SOLANA_MAINNET].includes(chain); if (isMainnet && this.pushClient.pushChainInfo.chainId !== chain_1.CHAIN_INFO[enums_1.CHAIN.PUSH_MAINNET].chainId) { throw new Error('Mainnet chains can only interact with Push Mainnet'); } } printLog(log) { if (this.printTraces) { console.log(`[${this.constructor.name}] ${log}`); } } executeProgressHook(hookId, ...args) { const hookEntry = progress_hook_1.default[hookId]; const hookPayload = hookEntry(...args); this.printLog(hookPayload.message); if (!this.progressHook) return; // invoke the user-provided callback this.progressHook(hookPayload); } } exports.Orchestrator = Orchestrator; //# sourceMappingURL=orchestrator.js.map