UNPKG

@pushchain/core

Version:

Push Chain is a true universal L1 that is 100% EVM compatible. It allows developers to deploy once and make their apps instantly compatible with users from all other L1s (Ethereum, Solana, etc) with zero on-chain code change.

286 lines 15.3 kB
"use strict"; var _a; Object.defineProperty(exports, "__esModule", { value: true }); exports.PushChain = void 0; const tslib_1 = require("tslib"); const constants_1 = require("../constants"); const enums_1 = require("../constants/enums"); const chain_1 = require("../constants/chain"); const orchestrator_1 = require("../orchestrator/orchestrator"); const signer_1 = require("../universal/signer"); const utils_1 = require("../utils"); const anchor_1 = require("@coral-xyz/anchor"); const viem_1 = require("viem"); const evm_client_1 = require("../vm-client/evm-client"); const tokens_1 = require("../constants/tokens"); /** * @class PushChain * * Entry point to interact with Push Chain in your application. * Provides access to cross-chain execution, utilities, and signer abstraction. */ class PushChain { /** * Helper function to check if input is UniversalAccount (read-only) or UniversalSigner */ static isUniversalAccount(input) { return !('signMessage' in input) && !('signAndSendTransaction' in input); } constructor(orchestrator, universalSigner, blockExplorers, isReadMode) { var _b, _c, _d, _e, _f, _g; this.orchestrator = orchestrator; this.universalSigner = universalSigner; this.blockExplorers = blockExplorers; this.isReadMode = isReadMode; /** * @method reinitialize * Reinitializes the PushChain SDK with a new universal signer and optional config. * * @param universalSigner * @param options - Optional settings to configure the SDK instance. * - network: PushChain network to target (e.g., TESTNET_DONUT, MAINNET). * - rpcUrls: Custom RPC URLs mapped by chain IDs. * - printTraces: Whether to print internal trace logs for debugging. * * @returns A new initialized instance of PushChain. */ this.reinitialize = (universalSigner, options) => tslib_1.__awaiter(this, void 0, void 0, function* () { var _b, _c, _d, _e, _f; const mergedOptions = { network: (_b = options === null || options === void 0 ? void 0 : options.network) !== null && _b !== void 0 ? _b : this.orchestrator.getNetwork(), rpcUrls: (_c = options === null || options === void 0 ? void 0 : options.rpcUrls) !== null && _c !== void 0 ? _c : this.orchestrator.getRpcUrls(), blockExplorers: (_d = options === null || options === void 0 ? void 0 : options.blockExplorers) !== null && _d !== void 0 ? _d : this.blockExplorers, printTraces: (_e = options === null || options === void 0 ? void 0 : options.printTraces) !== null && _e !== void 0 ? _e : this.orchestrator.getPrintTraces(), progressHook: (_f = options === null || options === void 0 ? void 0 : options.progressHook) !== null && _f !== void 0 ? _f : this.orchestrator.getProgressHook(), }; return _a.createInstance(universalSigner, mergedOptions); }); this.orchestrator = orchestrator; this.universal = { get origin() { return orchestrator.getUOA(); }, get account() { return orchestrator.computeUEAOffchain(); }, sendTransaction: (...args) => { if (this.isReadMode) { throw new Error('Read only mode cannot call sendTransaction function'); } return orchestrator.execute.bind(orchestrator)(...args); }, signMessage: (data) => tslib_1.__awaiter(this, void 0, void 0, function* () { if (this.isReadMode) { throw new Error('Read only mode cannot call signMessage function'); } const sigBytes = yield universalSigner.signMessage(data); const chain = universalSigner.account.chain; if (chain_1.CHAIN_INFO[chain].vm === enums_1.VM.EVM) { return (0, viem_1.bytesToHex)(sigBytes); } else if (chain_1.CHAIN_INFO[chain].vm === enums_1.VM.SVM) { return anchor_1.utils.bytes.bs58.encode(sigBytes); } return (0, viem_1.bytesToHex)(sigBytes); }), signTypedData: (...args) => tslib_1.__awaiter(this, void 0, void 0, function* () { if (typeof universalSigner.signTypedData !== 'function') { throw new Error('Typed data signing not supported'); } const signBytes = yield universalSigner.signTypedData(...args); return (0, viem_1.bytesToHex)(signBytes); }), }; this.explorer = { getTransactionUrl: (txHash) => { return `https://donut.push.network/tx/${txHash}`; }, listUrls: () => { var _b; return { urls: (_b = blockExplorers[enums_1.CHAIN.PUSH_TESTNET_DONUT]) !== null && _b !== void 0 ? _b : [] }; }, }; // Derive moveable/payable tokens for the current origin chain const originChain = universalSigner.account.chain; const toTokenMap = (arr) => (arr !== null && arr !== void 0 ? arr : []).reduce((acc, t) => { acc[t.symbol] = t; return acc; }, {}); const moveableList = (_d = (_c = (_b = tokens_1.MOVEABLE_TOKENS[originChain]) !== null && _b !== void 0 ? _b : tokens_1.MOVEABLE_TOKENS[enums_1.CHAIN.ETHEREUM_MAINNET]) !== null && _c !== void 0 ? _c : tokens_1.MOVEABLE_TOKENS[enums_1.CHAIN.ETHEREUM_SEPOLIA]) !== null && _d !== void 0 ? _d : []; const payableList = (_g = (_f = (_e = tokens_1.PAYABLE_TOKENS[originChain]) !== null && _e !== void 0 ? _e : tokens_1.PAYABLE_TOKENS[enums_1.CHAIN.ETHEREUM_MAINNET]) !== null && _f !== void 0 ? _f : tokens_1.PAYABLE_TOKENS[enums_1.CHAIN.ETHEREUM_SEPOLIA]) !== null && _g !== void 0 ? _g : []; this.moveable = { token: new tokens_1.MoveableTokenAccessor(toTokenMap(moveableList)), }; this.payable = { token: new tokens_1.PayableTokenAccessor(toTokenMap(payableList)), }; this.funds = { getConversionQuote: (amountIn_1, _b) => tslib_1.__awaiter(this, [amountIn_1, _b], void 0, function* (amountIn, { from, to, }) { var _c, _d, _e; const originChain = universalSigner.account.chain; if (originChain !== enums_1.CHAIN.ETHEREUM_SEPOLIA) { throw new Error('getConversionQuote is only supported on Ethereum Sepolia for now'); } if (!from) { throw new Error('from token is required'); } if (!to) { throw new Error('to token is required'); } // Resolve RPCs from client config, falling back to defaults const rpcUrls = orchestrator.getRpcUrls()[originChain] || chain_1.CHAIN_INFO[originChain].defaultRPC; const evm = new evm_client_1.EvmClient({ rpcUrls }); // Minimal ABIs and Uniswap V3 addresses sourced from chain config const factoryFromConfig = (_c = chain_1.CHAIN_INFO[originChain].dex) === null || _c === void 0 ? void 0 : _c.uniV3Factory; const quoterFromConfig = (_d = chain_1.CHAIN_INFO[originChain].dex) === null || _d === void 0 ? void 0 : _d.uniV3QuoterV2; if (!factoryFromConfig || !quoterFromConfig) { throw new Error('Uniswap V3 addresses not configured for this chain'); } const UNISWAP_V3_FACTORY = factoryFromConfig; const UNISWAP_V3_QUOTER_V2 = quoterFromConfig; const factoryAbi = (0, viem_1.parseAbi)([ 'function getPool(address tokenA, address tokenB, uint24 fee) view returns (address)', ]); const quoterAbi = (0, viem_1.parseAbi)([ 'function quoteExactInputSingle((address tokenIn, address tokenOut, uint256 amountIn, uint24 fee, uint160 sqrtPriceLimitX96) params) returns (uint256 amountOut, uint160 sqrtPriceX96After, uint32 initializedTicksCrossed, uint256 gasEstimate)', ]); const poolAbi = (0, viem_1.parseAbi)([ 'function liquidity() view returns (uint128)', ]); const feeTiers = [100, 500, 3000, 10000]; let bestAmountOut = BigInt(0); let bestFee = null; for (const fee of feeTiers) { // Find pool address for this fee tier const poolAddress = yield evm.readContract({ abi: factoryAbi, address: UNISWAP_V3_FACTORY, functionName: 'getPool', args: [from.address, to.address, fee], }); const isZero = !poolAddress || poolAddress.toLowerCase() === '0x0000000000000000000000000000000000000000'; if (isZero) continue; // Skip uninitialized/empty pools to avoid Quoter reverts try { const liquidity = yield evm.readContract({ abi: poolAbi, address: poolAddress, functionName: 'liquidity', args: [], }); if (!liquidity || liquidity === BigInt(0)) { continue; } } catch (_f) { // If we can't read liquidity, skip this pool/fee tier continue; } // Quote exact input single for this fee tier; catch reverts due to empty/uninitialized pools try { const result = yield evm.readContract({ abi: quoterAbi, address: UNISWAP_V3_QUOTER_V2, functionName: 'quoteExactInputSingle', args: [ { tokenIn: from.address, tokenOut: to.address, amountIn, fee, sqrtPriceLimitX96: BigInt(0), }, ], }); const amountOut = (_e = result === null || result === void 0 ? void 0 : result[0]) !== null && _e !== void 0 ? _e : BigInt(0); if (amountOut > bestAmountOut) { bestAmountOut = amountOut; bestFee = fee; } } catch (_g) { // try next fee } } if (!bestFee) { throw new Error('No direct Uniswap V3 pool found for the given token pair on common fee tiers'); } // Compute normalized rate: tokenOut per tokenIn const amountInHuman = parseFloat(utils_1.Utils.helpers.formatUnits(amountIn, { decimals: from.decimals })); const amountOutHuman = parseFloat(utils_1.Utils.helpers.formatUnits(bestAmountOut, { decimals: to.decimals })); const rate = amountInHuman > 0 ? amountOutHuman / amountInHuman : 0; return { amountIn: amountIn.toString(), amountOut: bestAmountOut.toString(), rate, route: [from.symbol, to.symbol], timestamp: Date.now(), }; }), }; } /** * @private * Internal method to create a PushChain instance with the given parameters. * Used by both initialize and reinitialize methods to avoid code duplication. */ static createInstance(universalSigner, options) { return tslib_1.__awaiter(this, void 0, void 0, function* () { var _b, _c, _d, _e; const isReadOnly = _a.isUniversalAccount(universalSigner); // If it's a UniversalAccount (read-only), create a dummy signer for the orchestrator const validatedUniversalSigner = isReadOnly ? (0, signer_1.createUniversalSigner)({ account: universalSigner, signMessage: () => tslib_1.__awaiter(this, void 0, void 0, function* () { throw new Error('Read only mode cannot call signMessage function'); }), signAndSendTransaction: () => tslib_1.__awaiter(this, void 0, void 0, function* () { throw new Error('Read only mode cannot call signAndSendTransaction function'); }), }) : (0, signer_1.createUniversalSigner)(universalSigner); const blockExplorers = (_b = options === null || options === void 0 ? void 0 : options.blockExplorers) !== null && _b !== void 0 ? _b : { [enums_1.CHAIN.PUSH_TESTNET_DONUT]: ['https://donut.push.network'], }; const orchestrator = new orchestrator_1.Orchestrator( /** * Ensures the signer conforms to the UniversalSigner interface. */ validatedUniversalSigner, (_c = options === null || options === void 0 ? void 0 : options.network) !== null && _c !== void 0 ? _c : enums_1.PUSH_NETWORK.TESTNET_DONUT, (_d = options === null || options === void 0 ? void 0 : options.rpcUrls) !== null && _d !== void 0 ? _d : {}, (_e = options === null || options === void 0 ? void 0 : options.printTraces) !== null && _e !== void 0 ? _e : false, options === null || options === void 0 ? void 0 : options.progressHook); return new _a(orchestrator, validatedUniversalSigner, blockExplorers, isReadOnly); }); } } exports.PushChain = PushChain; _a = PushChain; /** * @static * Constants for the PushChain SDK. */ PushChain.CONSTANTS = constants_1.CONSTANTS; /** * @static * Utility functions for encoding, hashing, and data formatting. */ PushChain.utils = utils_1.Utils; /** * @method initialize * Initializes the PushChain SDK with a universal signer and optional config. * * @param universalSigner * @param options - Optional settings to configure the SDK instance. * - network: PushChain network to target (e.g., TESTNET_DONUT, MAINNET). * - rpcUrls: Custom RPC URLs mapped by chain IDs. * - printTraces: Whether to print internal trace logs for debugging. * * @returns An initialized instance of PushChain. */ PushChain.initialize = (universalSigner, options) => tslib_1.__awaiter(void 0, void 0, void 0, function* () { return _a.createInstance(universalSigner, options); }); //# sourceMappingURL=push-chain.js.map