@hashgraph/hedera-wallet-connect
Version:
A library to facilitate integrating Hedera with WalletConnect
291 lines (290 loc) • 11.6 kB
JavaScript
import { CoreHelperUtil } from '@reown/appkit';
import { isReownName } from '@reown/appkit-common';
import { AdapterBlueprint, WcHelpersUtil } from '@reown/appkit-controllers';
import { LedgerId } from '@hashgraph/sdk';
import { BrowserProvider, Contract, formatUnits, JsonRpcSigner, parseUnits } from 'ethers';
import { HederaConnector } from './connectors';
import { hederaNamespace, getAccountBalance, HederaChainDefinition } from './utils';
import { createLogger } from '../lib/shared/logger';
export class HederaAdapter extends AdapterBlueprint {
constructor(params) {
var _a, _b;
if (params.namespace !== hederaNamespace && params.namespace !== 'eip155') {
throw new Error('Namespace must be "hedera" or "eip155"');
}
if (params.namespace == 'eip155') {
if ((_a = params.networks) === null || _a === void 0 ? void 0 : _a.some((n) => n.chainNamespace != 'eip155')) {
throw new Error('Invalid networks for eip155 namespace');
}
}
else {
if ((_b = params.networks) === null || _b === void 0 ? void 0 : _b.some((n) => n.chainNamespace != hederaNamespace)) {
throw new Error('Invalid networks for hedera namespace');
}
}
super(Object.assign({}, params));
this.logger = createLogger('HederaAdapter');
// Override getCaipNetworks to return appropriate networks based on namespace
this.getCaipNetworks = (namespace) => {
const targetNamespace = namespace || this.namespace;
if (targetNamespace === 'eip155') {
// Return EIP155 Hedera networks
return [HederaChainDefinition.EVM.Mainnet, HederaChainDefinition.EVM.Testnet];
}
else if (targetNamespace === hederaNamespace) {
// Return native Hedera networks
return [HederaChainDefinition.Native.Mainnet, HederaChainDefinition.Native.Testnet];
}
else {
// Return all Hedera networks if no specific namespace is requested
return [
HederaChainDefinition.EVM.Mainnet,
HederaChainDefinition.EVM.Testnet,
HederaChainDefinition.Native.Mainnet,
HederaChainDefinition.Native.Testnet,
];
}
};
}
async setUniversalProvider(universalProvider) {
this.addConnector(new HederaConnector({
provider: universalProvider,
caipNetworks: this.getCaipNetworks() || [],
namespace: this.namespace,
}));
}
async connect(params) {
this.logger.debug('connect called with params:', params);
// Get the WalletConnect connector and ensure it connects with proper namespaces
const connector = this.getWalletConnectConnector();
if (connector && 'connectWalletConnect' in connector) {
this.logger.debug('Calling HederaConnector.connectWalletConnect');
await connector.connectWalletConnect();
}
else {
this.logger.warn('HederaConnector not found or connectWalletConnect method missing');
}
return Promise.resolve({
id: 'WALLET_CONNECT',
type: 'WALLET_CONNECT',
chainId: Number(params.chainId),
provider: this.provider,
address: '',
});
}
async disconnect(_params) {
try {
const connector = this.getWalletConnectConnector();
await connector.disconnect();
}
catch (error) {
this.logger.warn('disconnect - error', error);
}
return { connections: [] };
}
async getAccounts({ namespace, }) {
var _a, _b, _c, _d;
const provider = this.provider;
const addresses = (((_d = (_c = (_b = (_a = provider === null || provider === void 0 ? void 0 : provider.session) === null || _a === void 0 ? void 0 : _a.namespaces) === null || _b === void 0 ? void 0 : _b[namespace]) === null || _c === void 0 ? void 0 : _c.accounts) === null || _d === void 0 ? void 0 : _d.map((account) => {
const [, , address] = account.split(':');
return address;
}).filter((address, index, self) => self.indexOf(address) === index)) || []);
return Promise.resolve({
accounts: addresses.map((address) => CoreHelperUtil.createAccount(namespace, address, 'eoa')),
});
}
async syncConnectors() {
return Promise.resolve();
}
async syncConnections(_params) {
return Promise.resolve();
}
async getBalance(params) {
const { address, caipNetwork } = params;
if (!caipNetwork) {
return Promise.resolve({
balance: '0',
decimals: 0,
symbol: '',
});
}
const accountBalance = await getAccountBalance(caipNetwork.testnet ? LedgerId.TESTNET : LedgerId.MAINNET, address);
return Promise.resolve({
balance: accountBalance
? formatUnits(accountBalance.hbars.toTinybars().toString(), 8).toString()
: '0',
decimals: caipNetwork.nativeCurrency.decimals,
symbol: caipNetwork.nativeCurrency.symbol,
});
}
async signMessage(params) {
const { provider, message, address } = params;
if (!provider) {
throw new Error('Provider is undefined');
}
const hederaProvider = provider;
let signature = '';
if (this.namespace === hederaNamespace) {
const response = await hederaProvider.hedera_signMessage({
signerAccountId: address,
message,
});
signature = response.signatureMap;
}
else {
signature = await hederaProvider.eth_signMessage(message, address);
}
return { signature };
}
async estimateGas(params) {
const { provider, caipNetwork, address } = params;
if (this.namespace !== 'eip155') {
throw new Error('Namespace is not eip155');
}
if (!provider) {
throw new Error('Provider is undefined');
}
const hederaProvider = provider;
const result = await hederaProvider.eth_estimateGas({
data: params.data,
to: params.to,
address: address,
}, address, Number(caipNetwork === null || caipNetwork === void 0 ? void 0 : caipNetwork.id));
return { gas: result };
}
async sendTransaction(params) {
var _a;
if (!params.provider) {
throw new Error('Provider is undefined');
}
const hederaProvider = params.provider;
if (this.namespace == 'eip155') {
const tx = await hederaProvider.eth_sendTransaction({
value: params.value,
to: params.to,
data: params.data,
gas: params.gas,
gasPrice: params.gasPrice,
address: params.address,
}, params.address, Number((_a = params.caipNetwork) === null || _a === void 0 ? void 0 : _a.id));
return { hash: tx };
}
else {
throw new Error('Namespace is not eip155');
}
}
async writeContract(params) {
if (!params.provider) {
throw new Error('Provider is undefined');
}
if (this.namespace !== 'eip155') {
throw new Error('Namespace is not eip155');
}
const { provider, caipNetwork, caipAddress, abi, tokenAddress, method, args } = params;
const browserProvider = new BrowserProvider(provider, Number(caipNetwork === null || caipNetwork === void 0 ? void 0 : caipNetwork.id));
const signer = new JsonRpcSigner(browserProvider, caipAddress);
const contract = new Contract(tokenAddress, abi, signer);
if (!contract || !method) {
throw new Error('Contract method is undefined');
}
const contractMethod = contract[method];
if (contractMethod) {
const result = await contractMethod(...args);
return { hash: result };
}
else
throw new Error('Contract method is undefined');
}
async getEnsAddress(params) {
if (this.namespace !== 'eip155') {
throw new Error('Namespace is not eip155');
}
const { name, caipNetwork } = params;
if (caipNetwork) {
if (isReownName(name)) {
return {
address: (await WcHelpersUtil.resolveReownName(name)) || false,
};
}
}
return { address: false };
}
parseUnits(params) {
return parseUnits(params.value, params.decimals);
}
formatUnits(params) {
return formatUnits(params.value, params.decimals);
}
async getCapabilities(params) {
var _a, _b;
if (this.namespace !== 'eip155') {
throw new Error('Namespace is not eip155');
}
const provider = this.provider;
if (!provider) {
throw new Error('Provider is undefined');
}
const walletCapabilitiesString = (_b = (_a = provider.session) === null || _a === void 0 ? void 0 : _a.sessionProperties) === null || _b === void 0 ? void 0 : _b['capabilities'];
if (walletCapabilitiesString) {
try {
const walletCapabilities = JSON.parse(walletCapabilitiesString);
const accountCapabilities = walletCapabilities[params];
if (accountCapabilities) {
return accountCapabilities;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}
catch (error) {
throw new Error('Error parsing wallet capabilities');
}
}
return await provider.request({
method: 'wallet_getCapabilities',
params: [params],
});
}
// Not supported
async getProfile() {
return Promise.resolve({
profileImage: '',
profileName: '',
});
}
// Not supported
async grantPermissions() {
return Promise.resolve({});
}
// Not supported
async revokePermissions() {
return Promise.resolve('0x');
}
async syncConnection(params) {
return Promise.resolve({
id: 'WALLET_CONNECT',
type: 'WALLET_CONNECT',
chainId: params.chainId,
provider: this.provider,
address: '',
});
}
async switchNetwork(params) {
const { caipNetwork } = params;
const connector = this.getWalletConnectConnector();
connector.provider.setDefaultChain(caipNetwork.caipNetworkId);
}
getWalletConnectConnector() {
const connector = this.connectors.find((c) => c.type == 'WALLET_CONNECT');
if (!connector) {
throw new Error('WalletConnectConnector not found');
}
return connector;
}
getWalletConnectProvider() {
const connector = this.connectors.find((c) => c.type === 'WALLET_CONNECT');
const provider = connector === null || connector === void 0 ? void 0 : connector.provider;
return provider;
}
async walletGetAssets(_params) {
return Promise.resolve({});
}
}