diff --git a/wallets/core/src/namespaces/common/mod.ts b/wallets/core/src/namespaces/common/mod.ts index d312779fbe..282204b70b 100644 --- a/wallets/core/src/namespaces/common/mod.ts +++ b/wallets/core/src/namespaces/common/mod.ts @@ -6,6 +6,7 @@ export { intoConnectionFinished, recommended as afterRecommended, } from './after.js'; +export { ChangeAccountSubscriberBuilder } from './hooks/changeAccountSubscriber.js'; export { connectAndUpdateStateForMultiNetworks, connectAndUpdateStateForSingleNetwork, diff --git a/wallets/core/src/namespaces/solana/actions.ts b/wallets/core/src/namespaces/solana/actions.ts index 6fb01bb49b..45ee37d976 100644 --- a/wallets/core/src/namespaces/solana/actions.ts +++ b/wallets/core/src/namespaces/solana/actions.ts @@ -1,14 +1,10 @@ import type { ProviderAPI, SolanaActions } from './types.js'; import type { Context } from '../../hub/namespaces/mod.js'; -import type { CaipAccount } from '../../types/accounts.js'; import type { FunctionWithContext } from '../../types/actions.js'; -import { AccountId } from 'caip'; - import { recommended as commonRecommended } from '../common/actions.js'; -import { CAIP_NAMESPACE, CAIP_SOLANA_CHAIN_ID } from './constants.js'; -import { getAccounts } from './utils.js'; +import { formatAccountsToCAIP, getAccounts } from './utils.js'; export const recommended = [...commonRecommended]; @@ -25,15 +21,6 @@ export function connect( ); } - return result.accounts.map( - (account) => - AccountId.format({ - address: account, - chainId: { - namespace: CAIP_NAMESPACE, - reference: CAIP_SOLANA_CHAIN_ID, - }, - }) as CaipAccount - ); + return formatAccountsToCAIP(result.accounts); }; } diff --git a/wallets/core/src/namespaces/solana/mod.ts b/wallets/core/src/namespaces/solana/mod.ts index 3d699c7891..63094ed604 100644 --- a/wallets/core/src/namespaces/solana/mod.ts +++ b/wallets/core/src/namespaces/solana/mod.ts @@ -4,6 +4,7 @@ export * as after from './after.js'; export * as and from './and.js'; export * as before from './before.js'; export * as builders from './builders.js'; +export * as utils from './utils.js'; export type { ProviderAPI, SolanaActions } from './types.js'; export { CAIP_NAMESPACE, CAIP_SOLANA_CHAIN_ID } from './constants.js'; diff --git a/wallets/provider-metamask/package.json b/wallets/provider-metamask/package.json index eb58d3d3a9..d0a66b0762 100644 --- a/wallets/provider-metamask/package.json +++ b/wallets/provider-metamask/package.json @@ -24,9 +24,17 @@ "@rango-dev/signer-evm": "^0.40.0", "@rango-dev/wallets-core": "^0.50.0", "@rango-dev/wallets-shared": "^0.51.0", - "rango-types": "^0.1.89" + "@rango-dev/signer-solana": "^0.44.0", + "@wallet-standard/app": "^1.1.0", + "rango-types": "^0.1.89", + "bs58": "^5.0.0" + }, + "devDependencies": { + "@wallet-standard/features": "^1.1.0", + "@wallet-standard/base": "^1.1.0", + "@solana/wallet-standard-features": "^1.3.0" }, "publishConfig": { "access": "public" } -} \ No newline at end of file +} diff --git a/wallets/provider-metamask/readme.md b/wallets/provider-metamask/readme.md index d31fe7197d..d01f0eec07 100644 --- a/wallets/provider-metamask/readme.md +++ b/wallets/provider-metamask/readme.md @@ -7,13 +7,19 @@ More about implementation status can be found [here](../readme.md). ## Implementation notes/limitations ### Group - -#### 🚧 Solana -MetaMask supports both **EVM** and **Solana**, but this integration currently supports **only EVM** and **Solana** is under construction. +MetaMask supports both **EVM** and **Solana**. ### Feature -All implemented features are working correctly. +#### ⚠️ Switch Account + +In MetaMask, you can have only one active account at a time, which may belong to either the Solana or EVM namespace. When you connect to MetaMask, it connects to the currently active accountβ€”this account could belong to either namespace. Additionally, MetaMask may also establish a random connection to another account from the opposite namespace. + +When a user switches accounts in MetaMask, the wallet emits an update event tied to the currently active namespaceβ€”either EVM or Solana. +The switchAccount notification is scoped to that active namespace, meaning only the relevant provider will receive the update. + +Regardless of which namespace triggers the event, MetaMask always executes transactions using the correct account for the selected chain. +This ensures consistent behavior and prevents cross-namespace conflicts between Solana and EVM contexts. --- diff --git a/wallets/provider-metamask/src/actions/solana.ts b/wallets/provider-metamask/src/actions/solana.ts new file mode 100644 index 0000000000..4085c026ee --- /dev/null +++ b/wallets/provider-metamask/src/actions/solana.ts @@ -0,0 +1,42 @@ +import type { WalletStandardSolanaInstance } from '../types.js'; +import type { Context, FunctionWithContext } from '@rango-dev/wallets-core'; + +import { + type SolanaActions, + utils, +} from '@rango-dev/wallets-core/namespaces/solana'; + +function connect( + getInstance: () => WalletStandardSolanaInstance +): FunctionWithContext { + return async () => { + const solanaInstance = getInstance(); + const connectResult = await solanaInstance.features[ + 'standard:connect' + ].connect(); + return utils.formatAccountsToCAIP( + connectResult.accounts.map((account) => account.address) + ); + }; +} +function canEagerConnect(getInstance: () => WalletStandardSolanaInstance) { + return async () => { + const solanaInstance = getInstance(); + + if (!solanaInstance) { + throw new Error( + 'Trying to eagerly connect to your Solana wallet, but it seems that its instance is not available.' + ); + } + + try { + const result = await solanaInstance.features['standard:connect'].connect({ + silent: true, + }); + return !!result.accounts.length; + } catch { + return false; + } + }; +} +export const solanaActions = { connect, canEagerConnect }; diff --git a/wallets/provider-metamask/src/builders/solana.ts b/wallets/provider-metamask/src/builders/solana.ts new file mode 100644 index 0000000000..b23297317d --- /dev/null +++ b/wallets/provider-metamask/src/builders/solana.ts @@ -0,0 +1,27 @@ +import type { WalletStandardSolanaInstance } from '../types.js'; +import type { StandardEventsChangeProperties } from '@wallet-standard/features'; + +import { ChangeAccountSubscriberBuilder } from '@rango-dev/wallets-core/namespaces/common'; +import { utils } from '@rango-dev/wallets-core/namespaces/solana'; + +// Hooks +const changeAccountSubscriber = ( + getInstance: () => WalletStandardSolanaInstance +) => + new ChangeAccountSubscriberBuilder< + StandardEventsChangeProperties, + WalletStandardSolanaInstance + >() + .getInstance(getInstance) + .validateEventPayload((accounts) => !!accounts.accounts?.length) + .format(async (_, event) => + utils.formatAccountsToCAIP( + event.accounts!.map((account) => account.address) + ) + ) + .addEventListener((instance, callback) => { + instance.features['standard:events'].on('change', callback); + }) + .removeEventListener((_, __) => {}); + +export const solanaBuilders = { changeAccountSubscriber }; diff --git a/wallets/provider-metamask/src/constants.ts b/wallets/provider-metamask/src/constants.ts index e04c2caa1d..f83fee9897 100644 --- a/wallets/provider-metamask/src/constants.ts +++ b/wallets/provider-metamask/src/constants.ts @@ -1,11 +1,16 @@ import { type ProviderMetadata } from '@rango-dev/wallets-core'; -import { type BlockchainMeta, evmBlockchains } from 'rango-types'; +import { + type BlockchainMeta, + evmBlockchains, + solanaBlockchain, +} from 'rango-types'; import getSigners from './signer.js'; import { getInstanceOrThrow } from './utils.js'; export const WALLET_ID = 'metamask'; - +export const WALLET_STANDARD_NAME = 'MetaMask'; +export const SOLANA_WALLET_STANDARD_MAINNET = 'solana:mainnet'; export const metadata: ProviderMetadata = { name: 'MetaMask', icon: 'https://raw.githubusercontent.com/rango-exchange/assets/main/wallets/metamask/icon.svg', @@ -31,6 +36,13 @@ export const metadata: ProviderMetadata = { getSupportedChains: (allBlockchains: BlockchainMeta[]) => evmBlockchains(allBlockchains), }, + { + label: 'Solana', + value: 'Solana', + id: 'SOLANA', + getSupportedChains: (allBlockchains: BlockchainMeta[]) => + solanaBlockchain(allBlockchains), + }, ], }, }, diff --git a/wallets/provider-metamask/src/legacy/index.ts b/wallets/provider-metamask/src/legacy/index.ts index 8538439e88..29cefa8b04 100644 --- a/wallets/provider-metamask/src/legacy/index.ts +++ b/wallets/provider-metamask/src/legacy/index.ts @@ -20,7 +20,7 @@ import { import { evmBlockchains } from 'rango-types'; import signer from '../signer.js'; -import { metamask as metamask_instance, type Provider } from '../utils.js'; +import { metamask as metamask_instance } from '../utils.js'; const WALLET = WalletTypes.META_MASK; @@ -49,7 +49,8 @@ export const subscribe: Subscribe = subscribeToEvm; export const switchNetwork: SwitchNetwork = switchNetworkForEvm; export const canSwitchNetworkTo: CanSwitchNetwork = canSwitchNetworkToEvm; - +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type Provider = any; export const getSigners: (provider: Provider) => Promise = signer; diff --git a/wallets/provider-metamask/src/namespaces/evm.ts b/wallets/provider-metamask/src/namespaces/evm.ts index 09e12c9cc8..29481f99f5 100644 --- a/wallets/provider-metamask/src/namespaces/evm.ts +++ b/wallets/provider-metamask/src/namespaces/evm.ts @@ -1,8 +1,11 @@ import type { EvmActions } from '@rango-dev/wallets-core/namespaces/evm'; -import { NamespaceBuilder } from '@rango-dev/wallets-core'; +import { ActionBuilder, NamespaceBuilder } from '@rango-dev/wallets-core'; import { builders as commonBuilders, + connectAndUpdateStateForMultiNetworks, + intoConnecting, + intoConnectionFinished, standardizeAndThrowError, } from '@rango-dev/wallets-core/namespaces/common'; import { @@ -26,12 +29,25 @@ const [changeAccountSubscriber, changeAccountCleanup] = builders }) .build(); -const connect = builders - .connect() +const connect = new ActionBuilder('connect') .action(actions.connect(evmMetamask)) + /* + * Metamask Wallet's `connect` returns a list where the currently selected account + * is always the first item. We're directly taking this first item as the active account. + * + * ***NOTE***: Please keep it synced with `wallets/core/src/namespaces/solana/builders.ts`. + * + */ + .and((_, connectResult) => ({ + ...connectResult, + accounts: [connectResult.accounts[0]], + })) + .and(connectAndUpdateStateForMultiNetworks) + .before(intoConnecting) .before(changeAccountSubscriber) .or(changeAccountCleanup) .or(standardizeAndThrowError) + .after(intoConnectionFinished) .build(); const canEagerConnect = builders diff --git a/wallets/provider-metamask/src/namespaces/solana.ts b/wallets/provider-metamask/src/namespaces/solana.ts new file mode 100644 index 0000000000..2403109894 --- /dev/null +++ b/wallets/provider-metamask/src/namespaces/solana.ts @@ -0,0 +1,45 @@ +import { ActionBuilder, NamespaceBuilder } from '@rango-dev/wallets-core'; +import { + builders as commonBuilders, + standardizeAndThrowError, +} from '@rango-dev/wallets-core/namespaces/common'; +import { + builders, + type SolanaActions, +} from '@rango-dev/wallets-core/namespaces/solana'; + +import { solanaActions } from '../actions/solana.js'; +import { solanaBuilders } from '../builders/solana.js'; +import { WALLET_ID } from '../constants.js'; +import { solanaMetamask } from '../utils.js'; + +const [changeAccountSubscriber, changeAccountCleanup] = solanaBuilders + .changeAccountSubscriber(solanaMetamask) + .build(); + +const connect = builders + .connect() + .action(solanaActions.connect(solanaMetamask)) + .before(changeAccountSubscriber) + .or(changeAccountCleanup) + .or(standardizeAndThrowError) + .build(); + +const canEagerConnect = new ActionBuilder( + 'canEagerConnect' +) + .action(solanaActions.canEagerConnect(solanaMetamask)) + .build(); + +const disconnect = commonBuilders + .disconnect() + .after(changeAccountCleanup) + .build(); + +const solana = new NamespaceBuilder('Solana', WALLET_ID) + .action(connect) + .action(disconnect) + .action(canEagerConnect) + .build(); + +export { solana }; diff --git a/wallets/provider-metamask/src/provider.ts b/wallets/provider-metamask/src/provider.ts index 0d4286af5f..d97f7ef277 100644 --- a/wallets/provider-metamask/src/provider.ts +++ b/wallets/provider-metamask/src/provider.ts @@ -2,6 +2,7 @@ import { ProviderBuilder } from '@rango-dev/wallets-core'; import { metadata, WALLET_ID } from './constants.js'; import { evm } from './namespaces/evm.js'; +import { solana } from './namespaces/solana.js'; import { metamask as metamask } from './utils.js'; const buildProvider = () => @@ -16,6 +17,7 @@ const buildProvider = () => }) .config('metadata', metadata) .add('evm', evm) + .add('solana', solana) .build(); export { buildProvider }; diff --git a/wallets/provider-metamask/src/signer.ts b/wallets/provider-metamask/src/signer.ts index 093c6c3826..1e1ebdcabc 100644 --- a/wallets/provider-metamask/src/signer.ts +++ b/wallets/provider-metamask/src/signer.ts @@ -1,4 +1,4 @@ -import type { Provider } from './utils.js'; +import type { Provider } from './types.js'; import type { SignerFactory } from 'rango-types'; import { LegacyNetworks as Networks } from '@rango-dev/wallets-core/legacy'; @@ -8,15 +8,22 @@ import { } from '@rango-dev/wallets-shared'; import { DefaultSignerFactory, TransactionType as TxType } from 'rango-types'; +import { MetamaskSolanaSigner } from './signers/solana.js'; + export default async function getSigners( provider: Provider ): Promise { const ethProvider = getNetworkInstance(provider, Networks.ETHEREUM); + const solanaProvider = getNetworkInstance(provider, Networks.SOLANA); + const signers = new DefaultSignerFactory(); const { DefaultEvmSigner } = await dynamicImportWithRefinedError( async () => await import('@rango-dev/signer-evm') ); signers.registerSigner(TxType.EVM, new DefaultEvmSigner(ethProvider)); - + signers.registerSigner( + TxType.SOLANA, + new MetamaskSolanaSigner(solanaProvider) + ); return signers; } diff --git a/wallets/provider-metamask/src/signers/solana.ts b/wallets/provider-metamask/src/signers/solana.ts new file mode 100644 index 0000000000..d79abfb591 --- /dev/null +++ b/wallets/provider-metamask/src/signers/solana.ts @@ -0,0 +1,88 @@ +import type { WalletStandardSolanaInstance } from '../types.js'; + +import { + generalSolanaTransactionExecutor, + type SolanaWeb3Signer, +} from '@rango-dev/signer-solana'; +import base58 from 'bs58'; +import { + type GenericSigner, + SignerError, + SignerErrorCode, + type SolanaTransaction, +} from 'rango-types'; + +async function executeSolanaTransaction( + tx: SolanaTransaction, + solanaProvider: WalletStandardSolanaInstance +): Promise { + const DefaultSolanaSigner: SolanaWeb3Signer = async ( + solanaWeb3Transaction + ) => { + const [currentAccount] = solanaProvider.accounts; + if (!currentAccount.publicKey) { + throw new SignerError( + SignerErrorCode.SIGN_TX_ERROR, + 'Please make sure the required account is connected properly.' + ); + } + + if (tx.from !== currentAccount.address) { + throw new SignerError( + SignerErrorCode.SIGN_TX_ERROR, + `Your connected account doesn't match with the required account. Please ensure that you are connected with the correct account and try again.` + ); + } + + try { + const [signOutput] = await solanaProvider.features[ + 'solana:signTransaction' + ].signTransaction({ + account: currentAccount, + transaction: solanaWeb3Transaction.serialize(), + }); + return signOutput.signedTransaction; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (e: any) { + const REJECTION_CODE = 4001; + if (e && Object.hasOwn(e, 'code') && e.code === REJECTION_CODE) { + throw new SignerError(SignerErrorCode.REJECTED_BY_USER, undefined, e); + } + throw new SignerError(SignerErrorCode.SIGN_TX_ERROR, undefined, e); + } + }; + return await generalSolanaTransactionExecutor(tx, DefaultSolanaSigner); +} + +export class MetamaskSolanaSigner implements GenericSigner { + private _provider: WalletStandardSolanaInstance; + + constructor(provider: WalletStandardSolanaInstance) { + this._provider = provider; + } + + get provider(): WalletStandardSolanaInstance { + return this._provider; + } + + async signMessage(msg: string): Promise { + try { + const encodedMessage = new TextEncoder().encode(msg); + const [account] = this.provider.accounts; + const [signOutput] = await this._provider.features[ + 'solana:signMessage' + ].signMessage({ + message: encodedMessage, + account, + }); + return base58.encode(signOutput.signature); + } catch (error) { + throw new SignerError(SignerErrorCode.SIGN_TX_ERROR, undefined, error); + } + } + + async signAndSendTx(tx: SolanaTransaction): Promise<{ hash: string }> { + const hash = await executeSolanaTransaction(tx, this._provider); + return { hash }; + } +} diff --git a/wallets/provider-metamask/src/types.ts b/wallets/provider-metamask/src/types.ts new file mode 100644 index 0000000000..5e25c29e8b --- /dev/null +++ b/wallets/provider-metamask/src/types.ts @@ -0,0 +1,54 @@ +import type { LegacyNetworks } from '@rango-dev/wallets-core/legacy'; +import type { ProviderAPI as EvmProviderApi } from '@rango-dev/wallets-core/namespaces/evm'; +import type { WalletWithFeatures as StandardWalletWithFeatures } from '@wallet-standard/base'; + +import { + type SolanaSignMessageFeature, + type SolanaSignTransactionFeature, +} from '@solana/wallet-standard-features'; +import { + type StandardConnectFeature, + type StandardEventsFeature, +} from '@wallet-standard/features'; + +export type WalletStandardSolanaInstance = StandardWalletWithFeatures< + StandardConnectFeature & + StandardEventsFeature & + SolanaSignTransactionFeature & + SolanaSignMessageFeature +>; +export type MetamaskEvmProviderApi = EvmProviderApi & { + isMetaMask?: boolean; + isBraveWallet?: boolean; + _events?: boolean; + _state?: boolean; + isApexWallet?: boolean; + isAvalanche?: boolean; + isBitKeep?: boolean; + isBlockWallet?: boolean; + isCoin98?: boolean; + isFordefi?: boolean; + __XDEFI?: boolean; + isMathWallet?: boolean; + isOkxWallet?: boolean; + isOKExWallet?: boolean; + isOneInchIOSWallet?: boolean; + isOneInchAndroidWallet?: boolean; + isOpera?: boolean; + isPortal?: boolean; + isRabby?: boolean; + isDefiant?: boolean; + isTokenPocket?: boolean; + isTokenary?: boolean; + isZeal?: boolean; + isZerion?: boolean; + isSafePal?: boolean; +}; +export type ProviderObject = { + [LegacyNetworks.ETHEREUM]: MetamaskEvmProviderApi; + [LegacyNetworks.SOLANA]: WalletStandardSolanaInstance; +}; +export type Provider = Map< + keyof ProviderObject, + ProviderObject[keyof ProviderObject] +>; diff --git a/wallets/provider-metamask/src/utils.ts b/wallets/provider-metamask/src/utils.ts index 310f05247e..f56d20faa9 100644 --- a/wallets/provider-metamask/src/utils.ts +++ b/wallets/provider-metamask/src/utils.ts @@ -1,13 +1,21 @@ +import type { + MetamaskEvmProviderApi, + Provider, + WalletStandardSolanaInstance, +} from './types.js'; import type { ProviderAPI as EvmProviderApi } from '@rango-dev/wallets-core/namespaces/evm'; import { LegacyNetworks } from '@rango-dev/wallets-core/legacy'; +import { getWallets } from '@wallet-standard/app'; -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export type Provider = Record; +import { + SOLANA_WALLET_STANDARD_MAINNET, + WALLET_STANDARD_NAME, +} from './constants.js'; export function metamask(): Provider | null { const { ethereum } = window; - + const solana = getSolanaWalletInstance(); if (!ethereum || !isEthereumMetamaskProvider(ethereum)) { return null; } @@ -17,10 +25,13 @@ export function metamask(): Provider | null { if (ethereum) { instances.set(LegacyNetworks.ETHEREUM, ethereum); } + if (solana) { + instances.set(LegacyNetworks.SOLANA, solana); + } return instances; } -function isEthereumMetamaskProvider(ethereum: Provider): boolean { +function isEthereumMetamaskProvider(ethereum: MetamaskEvmProviderApi): boolean { if (!ethereum?.isMetaMask) { return false; } @@ -85,9 +96,6 @@ function isEthereumMetamaskProvider(ethereum: Provider): boolean { if (ethereum.isZerion) { return false; } - if (ethereum.isPhantom) { - return false; - } if (ethereum.isSafePal) { return false; } @@ -106,6 +114,16 @@ export function evmMetamask(): EvmProviderApi { return evmInstance as EvmProviderApi; } +export function solanaMetamask(): WalletStandardSolanaInstance { + const instances = metamask(); + const solanaInstance = instances?.get(LegacyNetworks.SOLANA); + if (!solanaInstance) { + throw new Error( + 'Metamask Solana instance is not available. Ensure that Solana support is enabled in your wallet.' + ); + } + return solanaInstance as WalletStandardSolanaInstance; +} export function getInstanceOrThrow(): Provider { const instances = metamask(); @@ -115,3 +133,12 @@ export function getInstanceOrThrow(): Provider { return instances; } +function getSolanaWalletInstance() { + return getWallets() + .get() + .find( + (wallet) => + wallet.name === WALLET_STANDARD_NAME && + wallet.chains.includes(SOLANA_WALLET_STANDARD_MAINNET) + ) as WalletStandardSolanaInstance; +} diff --git a/wallets/readme.md b/wallets/readme.md index c251940275..0343a89956 100644 --- a/wallets/readme.md +++ b/wallets/readme.md @@ -135,7 +135,7 @@ For better user experience, wallet provider tries to connect to a wallet only wh | ----------------------------------------------- | --- | ---- | ------ | ------ | --- | --- | | [CoinBase](provider-coinbase/readme.md) | βœ… | ❌ | βœ… | ❌ | ❌ | ❌ | | [Ledger](provider-ledger/readme.md) | ⚠️ | ❌ | βœ… | ❌ | ❌ | ❌ | -| [MetaMask](provider-metamask/readme.md) | βœ… | ❌ | 🚧 | ❌ | ❌ | ❌ | +| [MetaMask](provider-metamask/readme.md) | βœ… | ❌ | βœ… | ❌ | ❌ | ❌ | | [Phantom](provider-phantom/readme.md) | ⚠️ | ⚠️ | βœ… | ❌ | ❌ | βœ… | | [Rabby](provider-rabby/readme.md) | βœ… | ❌ | ❌ | ❌ | ❌ | ❌ | | [Slush](provider-slush/readme.md) | ❌ | ❌ | ❌ | ❌ | ❌ | βœ… | @@ -147,9 +147,9 @@ For better user experience, wallet provider tries to connect to a wallet only wh | Wallet | Switch Account | Switch Network | Auto Connect | Interface | Cross Browser | | ------------ | -------------- | -------------- | ------------ | ------------------------- | ------------- | -| CoinBase | ⚠️ | βœ… | βœ… | Injected | ❌ | +| CoinBase | ⚠️ | βœ… | βœ… | Injected | ❌ | | Ledger | ❌ | ❌ | ❌ | Transport | βœ… | -| MetaMask | βœ… | βœ… | βœ… | Injected | βœ… | +| MetaMask | ⚠️ | βœ… | βœ… | Injected | βœ… | | Phantom | βœ… | βœ… | ⚠️ | Wallet Standard, Injected | βœ… | | Rabby | βœ… | βœ… | βœ… | Injected | βœ… | | Slush | ❌ | ❌ | βœ… | Wallet Standard | ❌ | diff --git a/yarn.lock b/yarn.lock index eea53f51f6..ee47ca9795 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6771,6 +6771,14 @@ "@solana/rpc-types" "2.0.0" "@solana/transaction-messages" "2.0.0" +"@solana/wallet-standard-features@^1.3.0": + version "1.3.0" + resolved "https://registry.yarnpkg.com/@solana/wallet-standard-features/-/wallet-standard-features-1.3.0.tgz#c489eca9d0c78f97084b4af6ca8ad8c1ca197de5" + integrity sha512-ZhpZtD+4VArf6RPitsVExvgkF+nGghd1rzPjd97GmBximpnt1rsUxMOEyoIEuH3XBxPyNB6Us7ha7RHWQR+abg== + dependencies: + "@wallet-standard/base" "^1.1.0" + "@wallet-standard/features" "^1.1.0" + "@solana/web3.js@1.95.8": version "1.95.8" resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.95.8.tgz#2d49abda23f7a79a3cc499ab6680f7be11786ee1" @@ -19675,7 +19683,16 @@ string-argv@0.3.2: resolved "https://registry.yarnpkg.com/string-argv/-/string-argv-0.3.2.tgz#2b6d0ef24b656274d957d54e0a4bbf6153dc02b6" integrity sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q== -"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -19802,7 +19819,14 @@ stringify-object@^5.0.0: is-obj "^3.0.0" is-regexp "^3.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -21486,7 +21510,7 @@ wordwrap@^1.0.0: resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -21504,6 +21528,15 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.0.1, wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"