@pushchain/core
Version:
## Overview
814 lines • 38 kB
JavaScript
"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