@web3auth/no-modal
Version:
Multi chain wallet aggregator for web3Auth
176 lines (170 loc) • 7.58 kB
JavaScript
import _objectSpread from '@babel/runtime/helpers/objectSpread2';
import _defineProperty from '@babel/runtime/helpers/defineProperty';
import { getBase64EncodedWireTransaction, getBase64Decoder, getBase58Encoder, getTransactionDecoder, getBase64Encoder } from '@solana/kit';
import { SolanaSignTransaction, SolanaSignMessage, SolanaSignAndSendTransaction } from '@solana/wallet-standard-features';
import { StandardEvents, StandardDisconnect, StandardConnect } from '@wallet-standard/features';
import { SOLANA_METHOD_TYPES } from '@web3auth/ws-embed';
import { WEB3AUTH_ICON } from '../../base/wallet/index.js';
import { WalletLoginError } from '../../base/errors/index.js';
import { getSolanaChainByChainConfig } from '../../base/wallet/solana.js';
import { SOLANA_CAIP_CHAIN_MAP } from '../../base/constants.js';
const base58Encoder = getBase58Encoder();
const base64Decoder = getBase64Decoder();
const base64Encoder = getBase64Encoder();
const transactionDecoder = getTransactionDecoder();
const ACCOUNT_FEATURES = [SolanaSignAndSendTransaction, SolanaSignMessage, SolanaSignTransaction];
function solanaWalletChainsFromConfigs(solanaChainConfigs) {
const ids = solanaChainConfigs.map(getSolanaChainByChainConfig).filter(id => id != null);
const unique = [...new Set(ids)];
return unique;
}
const SOLANA_PROVIDER_HEX_CHAIN_IDS = new Set(Object.keys(SOLANA_CAIP_CHAIN_MAP).map(id => id.toLowerCase()));
/**
* AuthSolanaWallet implements the Wallet Standard interface, wrapping a JRPC provider.
* Used by AuthConnector so consumers get a standards-compliant Wallet from `connection.solanaWallet`.
*
* {@link Wallet.accounts} is synchronous in the Wallet Standard; it returns `[]` until accounts have been loaded
* (`null` internally). The first async operation ({@link StandardConnect}, sign, etc.) calls `ensureAccountsLoaded`, which
* requires {@link IProvider.chainId} to be a configured Solana network (see {@link SOLANA_CAIP_CHAIN_MAP}) before {@link SOLANA_METHOD_TYPES.GET_ACCOUNTS}.
*/
class AuthSolanaWallet {
/**
* @param solanaChainConfigs - All configured Solana {@link CustomChainConfig} entries (same namespace).
*/
constructor(_provider, solanaChainConfigs) {
this._provider = _provider;
_defineProperty(this, "version", "1.0.0");
_defineProperty(this, "name", "Web3Auth");
_defineProperty(this, "icon", WEB3AUTH_ICON);
_defineProperty(this, "chains", void 0);
_defineProperty(this, "features", void 0);
_defineProperty(this, "_accounts", null);
_defineProperty(this, "_listeners", {});
this.chains = solanaWalletChainsFromConfigs(solanaChainConfigs);
this.features = {
[StandardConnect]: {
version: "1.0.0",
connect: async () => {
await this.ensureAccountsLoaded();
return {
accounts: this.accounts
};
}
},
[StandardDisconnect]: {
version: "1.0.0",
disconnect: async () => {}
},
[StandardEvents]: {
version: "1.0.0",
on: (event, listener) => {
var _this$_listeners, _this$_listeners$even;
((_this$_listeners$even = (_this$_listeners = this._listeners)[event]) !== null && _this$_listeners$even !== void 0 ? _this$_listeners$even : _this$_listeners[event] = new Set()).add(listener);
return () => this._listeners[event].delete(listener);
}
},
[SolanaSignAndSendTransaction]: {
version: "1.0.0",
supportedTransactionVersions: ["legacy", 0],
signAndSendTransaction: async (...inputs) => {
await this.ensureAccountsLoaded();
return Promise.all(inputs.map(async input => {
const base64Tx = base64Decoder.decode(input.transaction);
const signature = await this._provider.request({
method: SOLANA_METHOD_TYPES.SEND_TRANSACTION,
params: {
message: base64Tx
}
});
return {
signature: new Uint8Array(base58Encoder.encode(signature))
};
}));
}
},
[SolanaSignMessage]: {
version: "1.0.0",
signMessage: async (...inputs) => {
await this.ensureAccountsLoaded();
return Promise.all(inputs.map(async input => {
const message = new TextDecoder().decode(input.message);
const signature = await this._provider.request({
method: SOLANA_METHOD_TYPES.SIGN_MESSAGE,
params: {
data: message,
from: input.account.address
}
});
return {
signedMessage: new Uint8Array(input.message),
signature: new Uint8Array(base58Encoder.encode(signature))
};
}));
}
},
[SolanaSignTransaction]: {
version: "1.0.0",
supportedTransactionVersions: ["legacy", 0],
signTransaction: async (...inputs) => {
await this.ensureAccountsLoaded();
return Promise.all(inputs.map(async input => {
const base64Tx = base64Decoder.decode(input.transaction);
const signatureBase58 = await this._provider.request({
method: SOLANA_METHOD_TYPES.SIGN_TRANSACTION,
params: {
message: base64Tx
}
});
const sigBytes = new Uint8Array(base58Encoder.encode(signatureBase58));
const decodedTx = transactionDecoder.decode(input.transaction);
const signedTx = _objectSpread(_objectSpread({}, decodedTx), {}, {
signatures: _objectSpread(_objectSpread({}, decodedTx.signatures), {}, {
[input.account.address]: sigBytes
})
});
const signedBase64 = getBase64EncodedWireTransaction(signedTx);
return {
signedTransaction: new Uint8Array(base64Encoder.encode(signedBase64))
};
}));
}
}
};
}
/**
* Wallet Standard requires a synchronous getter; RPC runs on first async wallet operation instead.
*/
get accounts() {
var _this$_accounts;
return (_this$_accounts = this._accounts) !== null && _this$_accounts !== void 0 ? _this$_accounts : [];
}
/** Throws if the embed is not on Solana; otherwise loads accounts once via {@link SOLANA_METHOD_TYPES.GET_ACCOUNTS}. */
async ensureAccountsLoaded() {
var _await$this$_provider;
// assert solana chain
const chainId = this._provider.chainId;
if (!SOLANA_PROVIDER_HEX_CHAIN_IDS.has(chainId.toLowerCase())) throw WalletLoginError.unsupportedOperation(`Solana wallet operations require the embedded provider to be on a Solana network (current chainId: ${chainId}). Switch chain first.`);
if (this._accounts !== null) return;
const addresses = (_await$this$_provider = await this._provider.request({
method: SOLANA_METHOD_TYPES.GET_ACCOUNTS
})) !== null && _await$this$_provider !== void 0 ? _await$this$_provider : [];
const accountChains = this.chains;
this._accounts = addresses.map(address => ({
address,
publicKey: new Uint8Array(base58Encoder.encode(address)),
chains: accountChains.length ? accountChains : [],
features: ACCOUNT_FEATURES
}));
this.emitChange({
accounts: this.accounts
});
}
emitChange(properties) {
const listeners = this._listeners.change;
if (!listeners) return;
listeners.forEach(listener => {
listener(properties);
});
}
}
export { AuthSolanaWallet };