@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
JavaScript
;
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