@pushchain/ui-kit
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.
251 lines • 10.8 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.PhantomProvider = void 0;
const ethers_1 = require("ethers");
const BaseWalletProvider_1 = require("../BaseWalletProvider");
const wallet_types_1 = require("../../../types/wallet.types");
const web3_js_1 = require("@solana/web3.js");
const viem_1 = require("viem");
const bytes_1 = require("@coral-xyz/anchor/dist/cjs/utils/bytes");
const chains_1 = require("viem/chains");
const phantomMobile_1 = require("./phantomMobile");
class PhantomProvider extends BaseWalletProvider_1.BaseWalletProvider {
constructor() {
super('Phantom', 'https://www.phantom.app/img/logo.png', [
wallet_types_1.ChainType.ETHEREUM,
wallet_types_1.ChainType.SOLANA,
]);
}
connectedChainType = null;
isInstalled = async () => {
return (typeof window !== 'undefined' &&
(typeof window.phantom !== 'undefined' || (0, phantomMobile_1.isMobileDevice)()));
};
connectEthereum = async () => {
if (!window.phantom?.ethereum) {
throw new Error('Phantom not installed for Ethereum');
}
const provider = window.phantom?.ethereum;
const accounts = await provider.request({ method: 'eth_requestAccounts' });
const rawAddress = accounts[0];
const checksumAddress = (0, ethers_1.getAddress)(rawAddress);
const chainId = await this.getChainId(wallet_types_1.ChainType.ETHEREUM);
this.connectedChainType = wallet_types_1.ChainType.ETHEREUM;
const caipAddress = this.formatAddress(checksumAddress, wallet_types_1.ChainType.ETHEREUM, chainId);
return caipAddress;
};
connectSolana = async () => {
if (!window.phantom?.solana) {
if ((0, phantomMobile_1.shouldOpenPhantomMobileBrowser)(wallet_types_1.ChainType.SOLANA)) {
(0, phantomMobile_1.openPhantomMobileBrowser)(wallet_types_1.ChainType.SOLANA);
return new Promise(() => undefined);
}
throw new Error('Phantom not installed for Solana');
}
const provider = window.phantom?.solana;
const accounts = await provider.connect();
const chainId = await this.getChainId(wallet_types_1.ChainType.SOLANA);
this.connectedChainType = wallet_types_1.ChainType.SOLANA;
const caipAddress = this.formatAddress(accounts.publicKey.toString(), wallet_types_1.ChainType.SOLANA, chainId);
return caipAddress;
};
connect = async (chainType) => {
let account;
if (!chainType || chainType === wallet_types_1.ChainType.SOLANA) {
account = this.connectSolana();
}
else if (chainType === wallet_types_1.ChainType.ETHEREUM) {
account = this.connectEthereum();
}
if (!account) {
throw new Error('Error in connecting to phantom');
}
return account;
};
getChainId = async (chainType) => {
if (chainType === wallet_types_1.ChainType.ETHEREUM) {
const provider = window.phantom?.ethereum;
if (!provider)
throw new Error('No Phantom Ethereum wallet connected');
const chainId = await provider.request({ method: 'eth_chainId' });
return parseInt(chainId, 16);
}
else if (chainType === wallet_types_1.ChainType.SOLANA) {
const provider = window.phantom?.solana;
if (!provider)
throw new Error('No Phantom Solana wallet connected');
return provider.chainId || 1;
}
throw new Error('No Phantom wallet connected');
};
signMessage = async (message) => {
const isInstalled = await this.isInstalled();
if (!isInstalled) {
throw new Error('No Phantom wallet installed');
}
if (this.connectedChainType === wallet_types_1.ChainType.SOLANA &&
window.phantom?.solana) {
try {
const provider = window.phantom?.solana;
const signedMessage = await provider.signMessage(message, 'utf8');
return signedMessage.signature;
}
catch (error) {
console.error('Phantom Solana signing error:', error);
throw error;
}
}
else if (this.connectedChainType === wallet_types_1.ChainType.ETHEREUM &&
window.phantom?.ethereum) {
try {
const provider = window.phantom?.ethereum;
const accounts = await provider.request({
method: 'eth_accounts',
});
if (!accounts || accounts.length === 0) {
throw new Error('No connected account');
}
const hexMessage = (0, viem_1.bytesToHex)(message);
const signature = await provider.request({
method: 'personal_sign',
params: [hexMessage, accounts[0]],
});
return (0, viem_1.hexToBytes)(signature);
}
catch (error) {
console.error('Phantom Ethereum signing error:', error);
throw error;
}
}
else {
throw new Error('No Phantom wallet connected');
}
};
signAndSendTransaction = async (txn) => {
const isInstalled = await this.isInstalled();
if (!isInstalled) {
throw new Error('No Phantom wallet installed');
}
if (this.connectedChainType === wallet_types_1.ChainType.SOLANA &&
window.phantom?.solana) {
try {
const provider = window.phantom?.solana;
// VersionedTransaction.deserialize handles both legacy and v0 transactions.
// Check the deserialized message version to decide the appropriate type
// for Phantom's signAndSendTransaction.
const deserialized = web3_js_1.VersionedTransaction.deserialize(txn);
let transaction;
if (deserialized.message.version === 'legacy') {
transaction = web3_js_1.Transaction.from(txn);
}
else {
transaction = deserialized;
}
const signedTransaction = await provider.signAndSendTransaction(transaction);
const signature = typeof signedTransaction === 'string'
? signedTransaction
: signedTransaction.signature;
return bytes_1.bs58.decode(signature);
}
catch (error) {
console.error('Phantom Solana signing error:', error);
throw error;
}
}
else if (this.connectedChainType === wallet_types_1.ChainType.ETHEREUM &&
window.phantom?.ethereum) {
try {
const provider = window.phantom?.ethereum;
const accounts = await provider.request({
method: 'eth_accounts',
});
if (!accounts || accounts.length === 0) {
throw new Error('No connected account');
}
const hex = (0, viem_1.bytesToHex)(txn);
const parsed = (0, viem_1.parseTransaction)(hex);
const txParams = {
from: accounts[0],
to: parsed.to,
value: parsed.value ? '0x' + parsed.value.toString(16) : undefined,
data: parsed.data,
gas: parsed.gas ? '0x' + parsed.gas.toString(16) : undefined,
maxPriorityFeePerGas: parsed.maxPriorityFeePerGas
? '0x' + parsed.maxPriorityFeePerGas.toString(16)
: undefined,
maxFeePerGas: parsed.maxFeePerGas
? '0x' + parsed.maxFeePerGas.toString(16)
: undefined,
};
const signature = await provider.request({
method: 'eth_sendTransaction',
params: [txParams],
});
return (0, viem_1.hexToBytes)(signature);
}
catch (error) {
console.error('Phantom Ethereum signing error:', error);
throw error;
}
}
else {
throw new Error('No Phantom wallet connected');
}
};
signTypedData = async (typedData) => {
const isInstalled = await this.isInstalled();
if (!isInstalled) {
throw new Error('No Phantom wallet installed');
}
if (this.connectedChainType === wallet_types_1.ChainType.SOLANA &&
window.phantom?.solana) {
throw new Error('signTypedData is not implemented for this provider');
}
else if (this.connectedChainType === wallet_types_1.ChainType.ETHEREUM &&
window.phantom?.ethereum) {
try {
const walletClient = (0, viem_1.createWalletClient)({
chain: chains_1.sepolia,
transport: (0, viem_1.custom)(window.ethereum),
});
const accounts = await walletClient.request({
method: 'eth_accounts',
});
if (!accounts || accounts.length === 0) {
throw new Error('No connected account');
}
const signature = await walletClient.signTypedData({
account: accounts[0],
...typedData,
});
return (0, viem_1.hexToBytes)(signature);
}
catch (error) {
console.error('Phantom Ethereum signing error:', error);
throw error;
}
}
else {
throw new Error('No Phantom wallet connected');
}
};
disconnect = async () => {
const isInstalled = await this.isInstalled();
if (!isInstalled)
return;
if (this.connectedChainType === wallet_types_1.ChainType.SOLANA &&
window.phantom?.solana) {
const provider = window.phantom?.solana;
this.connectedChainType = null;
await provider.disconnect();
}
if (this.connectedChainType === wallet_types_1.ChainType.ETHEREUM &&
window.phantom?.ethereum) {
this.connectedChainType = null;
//TOOD: find how to disconnect ethereum
}
return Promise.resolve();
};
}
exports.PhantomProvider = PhantomProvider;
//# sourceMappingURL=phantom.js.map