diff --git a/.yarnrc.yml b/.yarnrc.yml index a6812f301e..147eac7afd 100644 --- a/.yarnrc.yml +++ b/.yarnrc.yml @@ -1,3 +1,6 @@ +approvedGitRepositories: + - "**" + compressionLevel: mixed enableGlobalCache: false @@ -12,18 +15,14 @@ logFilters: nodeLinker: node-modules -plugins: - - path: .yarn/plugins/@yarnpkg/plugin-allow-scripts.cjs - spec: "https://raw.githubusercontent.com/LavaMoat/LavaMoat/main/packages/yarn-plugin-allow-scripts/bundles/@yarnpkg/plugin-allow-scripts.js" - -# Configure the NPM minimal age gate to 3 days, meaning packages must be at -# least 3 days old to be installed. -npmMinimalAgeGate: 4320 # 3 days (in minutes) +npmMinimalAgeGate: 4320 -# Override the minimal age gate, allowing certain packages to be installed -# regardless of their publish age. npmPreapprovedPackages: - "@metamask/*" - "@metamask-previews/*" - "@lavamoat/*" - "@ts-bridge/*" + +plugins: + - path: .yarn/plugins/@yarnpkg/plugin-allow-scripts.cjs + spec: "https://raw.githubusercontent.com/LavaMoat/LavaMoat/main/packages/yarn-plugin-allow-scripts/bundles/@yarnpkg/plugin-allow-scripts.js" diff --git a/package.json b/package.json index 630047a945..bea9c59885 100644 --- a/package.json +++ b/package.json @@ -105,7 +105,7 @@ "engines": { "node": "^18.18 || >=20" }, - "packageManager": "yarn@4.10.3", + "packageManager": "yarn@4.14.0", "lavamoat": { "allowScripts": { "@lavamoat/preinstall-always-fail": false, diff --git a/packages/multichain-account-service/src/MultichainAccountService.test.ts b/packages/multichain-account-service/src/MultichainAccountService.test.ts index d8b3456d11..1d41e9bc90 100644 --- a/packages/multichain-account-service/src/MultichainAccountService.test.ts +++ b/packages/multichain-account-service/src/MultichainAccountService.test.ts @@ -10,6 +10,7 @@ import { EthAccountType, SolAccountType, TrxAccountType, + XlmAccountType, } from '@metamask/keyring-api'; import type { KeyringObject } from '@metamask/keyring-controller'; import type { EthKeyring } from '@metamask/keyring-internal-api'; @@ -37,6 +38,10 @@ import { TRX_ACCOUNT_PROVIDER_NAME, TrxAccountProvider, } from './providers/TrxAccountProvider'; +import { + XLM_ACCOUNT_PROVIDER_NAME, + XlmAccountProvider, +} from './providers/XlmAccountProvider'; import { SnapPlatformWatcher } from './snaps/SnapPlatformWatcher'; import type { RootMessenger, MockAccountProvider } from './tests'; import { @@ -91,6 +96,12 @@ jest.mock('./providers/TrxAccountProvider', () => { TrxAccountProvider: jest.fn(), }; }); +jest.mock('./providers/XlmAccountProvider', () => { + return { + ...jest.requireActual('./providers/XlmAccountProvider'), + XlmAccountProvider: jest.fn(), + }; +}); type Mocks = { // eslint-disable-next-line @typescript-eslint/naming-convention @@ -124,6 +135,8 @@ type Mocks = { BtcAccountProvider: MockAccountProvider; // eslint-disable-next-line @typescript-eslint/naming-convention TrxAccountProvider: MockAccountProvider; + // eslint-disable-next-line @typescript-eslint/naming-convention + XlmAccountProvider: MockAccountProvider; }; type Spies = { @@ -172,6 +185,11 @@ function mockAccountProvider( mocks.isAccountCompatible?.mockImplementation( (account: KeyringAccount) => account.type === TrxAccountType.Eoa, ); + } else if (providerClass === (XlmAccountProvider as unknown)) { + mocks.getName.mockReturnValue(XLM_ACCOUNT_PROVIDER_NAME); + mocks.isAccountCompatible?.mockImplementation( + (account: KeyringAccount) => account.type === XlmAccountType.Account, + ); } } @@ -218,6 +236,7 @@ async function setup({ SolAccountProvider: makeMockAccountProvider(), BtcAccountProvider: makeMockAccountProvider(), TrxAccountProvider: makeMockAccountProvider(), + XlmAccountProvider: makeMockAccountProvider(), }; const spies: Spies = { @@ -292,6 +311,7 @@ async function setup({ SolAccountProvider.NAME = SOL_ACCOUNT_PROVIDER_NAME; BtcAccountProvider.NAME = BTC_ACCOUNT_PROVIDER_NAME; TrxAccountProvider.NAME = TRX_ACCOUNT_PROVIDER_NAME; + XlmAccountProvider.NAME = XLM_ACCOUNT_PROVIDER_NAME; mockAccountProvider( EvmAccountProvider, @@ -321,6 +341,13 @@ async function setup({ 3, TrxAccountType.Eoa, ); + mockAccountProvider( + XlmAccountProvider, + mocks.XlmAccountProvider, + accounts, + 4, + XlmAccountType.Account, + ); } const messenger = getMultichainAccountServiceMessenger(rootMessenger); diff --git a/packages/multichain-account-service/src/MultichainAccountService.ts b/packages/multichain-account-service/src/MultichainAccountService.ts index 283e747c46..032477bc21 100644 --- a/packages/multichain-account-service/src/MultichainAccountService.ts +++ b/packages/multichain-account-service/src/MultichainAccountService.ts @@ -25,10 +25,13 @@ import { EVM_ACCOUNT_PROVIDER_NAME, BtcAccountProviderConfig, TrxAccountProviderConfig, + XlmAccountProviderConfig, BTC_ACCOUNT_PROVIDER_NAME, TRX_ACCOUNT_PROVIDER_NAME, + XLM_ACCOUNT_PROVIDER_NAME, BtcAccountProvider, TrxAccountProvider, + XlmAccountProvider, } from './providers'; import { AccountProviderWrapper, @@ -59,6 +62,7 @@ export type MultichainAccountServiceOptions = { [SOL_ACCOUNT_PROVIDER_NAME]?: SolAccountProviderConfig; [BTC_ACCOUNT_PROVIDER_NAME]?: BtcAccountProviderConfig; [TRX_ACCOUNT_PROVIDER_NAME]?: TrxAccountProviderConfig; + [XLM_ACCOUNT_PROVIDER_NAME]?: XlmAccountProviderConfig; }; config?: MultichainAccountServiceConfig; /** @@ -206,6 +210,14 @@ export class MultichainAccountService { trace, ), ), + new AccountProviderWrapper( + this.#messenger, + new XlmAccountProvider( + this.#messenger, + providerConfigs?.[XLM_ACCOUNT_PROVIDER_NAME], + trace, + ), + ), // Custom account providers that can be provided by the MetaMask client. ...providers, ]; diff --git a/packages/multichain-account-service/src/MultichainAccountWallet.test.ts b/packages/multichain-account-service/src/MultichainAccountWallet.test.ts index 705ff8a9cd..57d3c09788 100644 --- a/packages/multichain-account-service/src/MultichainAccountWallet.test.ts +++ b/packages/multichain-account-service/src/MultichainAccountWallet.test.ts @@ -300,7 +300,7 @@ describe('MultichainAccountWallet', () => { ).rejects.toThrow('Unable to create accounts'); expect(captureExceptionSpy).toHaveBeenCalledWith( new Error( - 'Unable to create some accounts with provider "Mocked Provider 0"', + 'Unable to create accounts with provider "Mocked Provider 0" (group indices 1–1)', ), ); expect(captureExceptionSpy.mock.lastCall[0]).toHaveProperty( @@ -609,7 +609,7 @@ describe('MultichainAccountWallet', () => { ).rejects.toThrow(`Bad range, to (${badIndex}) must be >= 0`); }); - it('captures an error with batch mode message when EVM provider fails', async () => { + it('captures an error with group index range message when EVM provider fails', async () => { const { wallet, providers, messenger } = setup({ accounts: [[]], }); @@ -626,7 +626,7 @@ describe('MultichainAccountWallet', () => { expect(captureExceptionSpy).toHaveBeenCalledWith( new Error( - 'Unable to create some accounts (batch) with provider "Mocked Provider 0"', + 'Unable to create accounts with provider "Mocked Provider 0" (group indices 0–2)', ), ); expect(captureExceptionSpy.mock.lastCall[0]).toHaveProperty( diff --git a/packages/multichain-account-service/src/MultichainAccountWallet.ts b/packages/multichain-account-service/src/MultichainAccountWallet.ts index d38114eefe..0aca148d89 100644 --- a/packages/multichain-account-service/src/MultichainAccountWallet.ts +++ b/packages/multichain-account-service/src/MultichainAccountWallet.ts @@ -237,8 +237,6 @@ export class MultichainAccountWallet< from: number, to: number, ): Promise[]> { - const isBatching = to > from; - try { return await provider.createAccounts({ type: AccountCreationType.Bip44DeriveIndexRange, @@ -251,12 +249,12 @@ export class MultichainAccountWallet< } catch (error) { reportError( this.#messenger, - `Unable to create ${isBatching ? 'some accounts (batch)' : 'some accounts'} with provider "${provider.getName()}"`, + `Unable to create accounts with provider "${provider.getName()}" (group indices ${from}–${to})`, error, { range: { from, to }, provider: provider.getName(), - isBatching, + spansMultipleGroupIndices: to > from, }, ); throw error; diff --git a/packages/multichain-account-service/src/index.ts b/packages/multichain-account-service/src/index.ts index 802cbfbc97..440af75b7d 100644 --- a/packages/multichain-account-service/src/index.ts +++ b/packages/multichain-account-service/src/index.ts @@ -35,6 +35,8 @@ export { BtcAccountProvider, TRX_ACCOUNT_PROVIDER_NAME, TrxAccountProvider, + XLM_ACCOUNT_PROVIDER_NAME, + XlmAccountProvider, } from './providers'; export { MultichainAccountWallet } from './MultichainAccountWallet'; export { MultichainAccountGroup } from './MultichainAccountGroup'; diff --git a/packages/multichain-account-service/src/providers/SnapAccountProvider.test.ts b/packages/multichain-account-service/src/providers/SnapAccountProvider.test.ts index af33672f58..0ed4bd50ab 100644 --- a/packages/multichain-account-service/src/providers/SnapAccountProvider.test.ts +++ b/packages/multichain-account-service/src/providers/SnapAccountProvider.test.ts @@ -13,8 +13,8 @@ import type { CreateAccountOptions, DeleteAccountRequest, GetAccountRequest, - KeyringCapabilities, } from '@metamask/keyring-api'; +import type { KeyringCapabilities } from '@metamask/keyring-api/v2'; import type { EntropySourceId, KeyringAccount } from '@metamask/keyring-api'; import type { InternalAccount } from '@metamask/keyring-internal-api'; import type { JsonRpcRequest, SnapId } from '@metamask/snaps-sdk'; diff --git a/packages/multichain-account-service/src/providers/XlmAccountProvider.test.ts b/packages/multichain-account-service/src/providers/XlmAccountProvider.test.ts new file mode 100644 index 0000000000..0c2d2942ef --- /dev/null +++ b/packages/multichain-account-service/src/providers/XlmAccountProvider.test.ts @@ -0,0 +1,286 @@ +import { isBip44Account } from '@metamask/account-api'; +import type { SnapKeyring } from '@metamask/eth-snap-keyring'; +import { AccountCreationType } from '@metamask/keyring-api'; +import type { KeyringMetadata } from '@metamask/keyring-controller'; +import type { + EthKeyring, + InternalAccount, +} from '@metamask/keyring-internal-api'; +import { SnapControllerState } from '@metamask/snaps-controllers'; +import type { Json } from '@metamask/utils'; +import deepmerge from 'deepmerge'; + +import { + getMultichainAccountServiceMessenger, + getRootMessenger, + MOCK_HD_KEYRING_2, + MOCK_XLM_ACCOUNT_1, + MockAccountBuilder, + toGroupIndexRangeArray, +} from '../tests'; +import type { RootMessenger, DeepPartial } from '../tests'; +import { AccountProviderWrapper } from './AccountProviderWrapper'; +import type { SnapAccountProviderConfig } from './SnapAccountProvider'; +import { + XLM_ACCOUNT_PROVIDER_DEFAULT_CONFIG, + XLM_ACCOUNT_PROVIDER_NAME, + XlmAccountProvider, +} from './XlmAccountProvider'; + +function asConfig( + partial: DeepPartial, +): SnapAccountProviderConfig { + return deepmerge( + XLM_ACCOUNT_PROVIDER_DEFAULT_CONFIG, + partial, + ) as SnapAccountProviderConfig; +} + +class MockStellarKeyring { + readonly type = 'MockStellarKeyring'; + + readonly metadata: KeyringMetadata = { + id: 'mock-stellar-keyring-id', + name: '', + }; + + readonly accounts: InternalAccount[]; + + constructor(accounts: InternalAccount[]) { + this.accounts = accounts; + } + + createAccount: SnapKeyring['createAccount'] = jest + .fn() + .mockImplementation((_, options: Record) => { + const { index } = options; + if (typeof index === 'number') { + const found = this.accounts.find( + (account) => + isBip44Account(account) && + account.options.entropy.groupIndex === index, + ); + + if (found) { + return found; + } + } + + const account = MockAccountBuilder.from(MOCK_XLM_ACCOUNT_1) + .withUuid() + .withAddressSuffix(`${this.accounts.length}`) + .withGroupIndex(typeof index === 'number' ? index : this.accounts.length) + .get(); + this.accounts.push(account); + + return account; + }); + + createAccounts: SnapKeyring['createAccounts'] = jest + .fn() + .mockImplementation((_, options) => { + const groupIndices = + options.type === 'bip44:derive-index' + ? [options.groupIndex] + : toGroupIndexRangeArray(options.range); + + return groupIndices.map((groupIndex) => { + const found = this.accounts.find( + (account) => + isBip44Account(account) && + account.options.entropy.groupIndex === groupIndex, + ); + + if (found) { + return found; + } + + const account = MockAccountBuilder.from(MOCK_XLM_ACCOUNT_1) + .withUuid() + .withAddressSuffix(`${groupIndex}`) + .withGroupIndex(groupIndex) + .get(); + this.accounts.push(account); + return account; + }); + }); +} + +class MockXlmAccountProvider extends XlmAccountProvider { + override async ensureCanUseSnapPlatform(): Promise { + // Override to avoid waiting during tests. + } +} + +function setup({ + messenger = getRootMessenger(), + accounts = [], + config, +}: { + messenger?: RootMessenger; + accounts?: InternalAccount[]; + config?: SnapAccountProviderConfig; +} = {}): { + provider: AccountProviderWrapper; + messenger: RootMessenger; + keyring: MockStellarKeyring; + mocks: { + handleRequest: jest.Mock; + keyring: { + createAccount: jest.Mock; + createAccounts: jest.Mock; + }; + trace: jest.Mock; + }; +} { + const keyring = new MockStellarKeyring(accounts); + + messenger.registerActionHandler( + 'AccountsController:getAccounts', + () => accounts, + ); + + messenger.registerActionHandler( + 'SnapController:getState', + () => ({ isReady: true }) as SnapControllerState, + ); + + messenger.registerActionHandler( + 'AccountsController:listMultichainAccounts', + () => accounts, + ); + + const mockGetAccount = jest.fn().mockImplementation((id) => { + return keyring.accounts.find((account) => account.id === id); + }); + messenger.registerActionHandler( + 'AccountsController:getAccount', + mockGetAccount, + ); + + const mockHandleRequest = jest + .fn() + .mockImplementation((address: string) => + keyring.accounts.find((account) => account.address === address), + ); + + const mockTrace = jest.fn().mockImplementation(async (_request, fn) => { + return await fn(); + }); + + messenger.registerActionHandler( + 'SnapController:handleRequest', + mockHandleRequest, + ); + + messenger.registerActionHandler( + 'KeyringController:withKeyring', + async (_, operation) => + operation({ + keyring: keyring as unknown as EthKeyring, + metadata: keyring.metadata, + }), + ); + + const multichainMessenger = getMultichainAccountServiceMessenger(messenger); + const xlmProvider = new MockXlmAccountProvider( + multichainMessenger, + config, + mockTrace, + ); + const accountIds = accounts.map((account) => account.id); + xlmProvider.init(accountIds); + const provider = new AccountProviderWrapper(multichainMessenger, xlmProvider); + + return { + provider, + messenger, + keyring, + mocks: { + handleRequest: mockHandleRequest, + keyring: { + createAccount: keyring.createAccount as jest.Mock, + createAccounts: keyring.createAccounts as jest.Mock, + }, + trace: mockTrace, + }, + }; +} + +describe('XlmAccountProvider', () => { + it('getName returns Stellar', () => { + const { provider } = setup({ accounts: [] }); + expect(provider.getName()).toBe(XLM_ACCOUNT_PROVIDER_NAME); + }); + + describe('v1', () => { + it('uses createAccount when batching is disabled', async () => { + const accounts = [MOCK_XLM_ACCOUNT_1]; + const { provider, mocks } = setup({ + accounts, + config: asConfig({ createAccounts: { batched: false } }), + }); + + await provider.createAccounts({ + type: AccountCreationType.Bip44DeriveIndex, + entropySource: MOCK_HD_KEYRING_2.metadata.id, + groupIndex: accounts.length, + }); + + expect(mocks.keyring.createAccount).toHaveBeenCalled(); + expect(mocks.keyring.createAccounts).not.toHaveBeenCalled(); + }); + }); + + describe('v2 - batched', () => { + it('creates one account via createAccounts', async () => { + const accounts = [MOCK_XLM_ACCOUNT_1]; + const { provider, mocks } = setup({ + accounts, + config: asConfig({ createAccounts: { batched: true } }), + }); + + const newGroupIndex = accounts.length; + const newAccounts = await provider.createAccounts({ + type: AccountCreationType.Bip44DeriveIndex, + entropySource: MOCK_HD_KEYRING_2.metadata.id, + groupIndex: newGroupIndex, + }); + + expect(newAccounts).toHaveLength(1); + expect(mocks.keyring.createAccounts).toHaveBeenCalledWith( + XlmAccountProvider.XLM_SNAP_ID, + { + type: AccountCreationType.Bip44DeriveIndex, + entropySource: MOCK_HD_KEYRING_2.metadata.id, + groupIndex: newGroupIndex, + }, + ); + expect(mocks.keyring.createAccount).not.toHaveBeenCalled(); + }); + + it('creates multiple accounts using Bip44DeriveIndexRange', async () => { + const accounts = [MOCK_XLM_ACCOUNT_1]; + const { provider, mocks } = setup({ + accounts, + config: asConfig({ createAccounts: { batched: true } }), + }); + + const from = 1; + const newAccounts = await provider.createAccounts({ + type: AccountCreationType.Bip44DeriveIndexRange, + entropySource: MOCK_HD_KEYRING_2.metadata.id, + range: { from, to: 3 }, + }); + + expect(newAccounts).toHaveLength(3); + expect(mocks.keyring.createAccounts).toHaveBeenCalledTimes(1); + expect(mocks.keyring.createAccount).not.toHaveBeenCalled(); + + for (const [index, account] of newAccounts.entries()) { + expect(isBip44Account(account)).toBe(true); + expect(account.options.entropy.groupIndex).toBe(from + index); + } + }); + }); +}); diff --git a/packages/multichain-account-service/src/providers/XlmAccountProvider.ts b/packages/multichain-account-service/src/providers/XlmAccountProvider.ts new file mode 100644 index 0000000000..55e109d5a3 --- /dev/null +++ b/packages/multichain-account-service/src/providers/XlmAccountProvider.ts @@ -0,0 +1,145 @@ +import type { Bip44Account } from '@metamask/account-api'; +import type { TraceCallback } from '@metamask/controller-utils'; +import type { + EntropySourceId, + KeyringAccount, +} from '@metamask/keyring-api'; +import type { KeyringCapabilities } from '@metamask/keyring-api/v2'; +import { + AccountCreationType, + XlmAccountType, + XlmScope, +} from '@metamask/keyring-api'; +import { KeyringTypes } from '@metamask/keyring-controller'; +import type { InternalAccount } from '@metamask/keyring-internal-api'; +import type { SnapId } from '@metamask/snaps-sdk'; + +import { SnapAccountProvider } from './SnapAccountProvider'; +import type { + RestrictedSnapKeyring, + SnapAccountProviderConfig, +} from './SnapAccountProvider'; +import { withRetry, withTimeout } from './utils'; +import { traceFallback } from '../analytics'; +import { TraceName } from '../analytics/traces'; +import type { MultichainAccountServiceMessenger } from '../types'; + +export type XlmAccountProviderConfig = SnapAccountProviderConfig; + +export const XLM_ACCOUNT_PROVIDER_NAME = 'Stellar'; + +export const XLM_ACCOUNT_PROVIDER_DEFAULT_CONFIG: XlmAccountProviderConfig = { + maxConcurrency: 3, + discovery: { + enabled: true, + timeoutMs: 2000, + maxAttempts: 3, + backOffMs: 1000, + }, + createAccounts: { + batched: true, + timeoutMs: 10000, + }, + resyncAccounts: { + autoRemoveExtraSnapAccounts: true, + }, +}; + +export class XlmAccountProvider extends SnapAccountProvider { + static NAME = XLM_ACCOUNT_PROVIDER_NAME; + + static XLM_SNAP_ID = 'npm:@metamask/stellar-wallet-snap' as SnapId; + + readonly capabilities: KeyringCapabilities = { + scopes: [XlmScope.Pubnet, XlmScope.Testnet], + bip44: { + deriveIndex: true, + deriveIndexRange: true, + }, + }; + + constructor( + messenger: MultichainAccountServiceMessenger, + config: XlmAccountProviderConfig = XLM_ACCOUNT_PROVIDER_DEFAULT_CONFIG, + trace: TraceCallback = traceFallback, + ) { + super(XlmAccountProvider.XLM_SNAP_ID, messenger, config, trace); + } + + getName(): string { + return XlmAccountProvider.NAME; + } + + isAccountCompatible(account: Bip44Account): boolean { + return ( + account.type === XlmAccountType.Account && + account.metadata.keyring.type === (KeyringTypes.snap as string) + ); + } + + protected override createAccountV1( + keyring: RestrictedSnapKeyring, + { + entropySource, + groupIndex, + }: { entropySource: EntropySourceId; groupIndex: number }, + ): Promise { + return keyring.createAccount({ + entropySource, + index: groupIndex, + addressType: XlmAccountType.Account, + scope: XlmScope.Pubnet, + }); + } + + async discoverAccounts({ + entropySource, + groupIndex, + }: { + entropySource: EntropySourceId; + groupIndex: number; + }): Promise[]> { + return this.withSnap(async ({ client, keyring }) => { + return await super.trace( + { + name: TraceName.SnapDiscoverAccounts, + data: { + provider: this.getName(), + }, + }, + async () => { + if (!this.config.discovery.enabled) { + return []; + } + + const discoveredAccounts = await withRetry( + () => + withTimeout( + () => + client.discoverAccounts( + [XlmScope.Pubnet], + entropySource, + groupIndex, + ), + this.config.discovery.timeoutMs, + ), + { + maxAttempts: this.config.discovery.maxAttempts, + backOffMs: this.config.discovery.backOffMs, + }, + ); + + if (!discoveredAccounts.length) { + return []; + } + + return await this.createBip44Accounts(keyring, { + type: AccountCreationType.Bip44DeriveIndex, + entropySource, + groupIndex, + }); + }, + ); + }); + } +} diff --git a/packages/multichain-account-service/src/providers/index.ts b/packages/multichain-account-service/src/providers/index.ts index 0504f99fea..51f20af248 100644 --- a/packages/multichain-account-service/src/providers/index.ts +++ b/packages/multichain-account-service/src/providers/index.ts @@ -10,3 +10,4 @@ export * from './SolAccountProvider'; export * from './EvmAccountProvider'; export * from './BtcAccountProvider'; export * from './TrxAccountProvider'; +export * from './XlmAccountProvider'; diff --git a/packages/multichain-account-service/src/tests/accounts.ts b/packages/multichain-account-service/src/tests/accounts.ts index 4479a3000e..a403d8d37e 100644 --- a/packages/multichain-account-service/src/tests/accounts.ts +++ b/packages/multichain-account-service/src/tests/accounts.ts @@ -19,6 +19,9 @@ import { TrxAccountType, TrxMethod, TrxScope, + XlmAccountType, + XlmMethod, + XlmScope, } from '@metamask/keyring-api'; import { KeyringTypes } from '@metamask/keyring-controller'; import type { InternalAccount } from '@metamask/keyring-internal-api'; @@ -149,6 +152,31 @@ export const MOCK_SOL_ACCOUNT_1: Bip44Account = { }, }; +const XLM_METHODS = Object.values(XlmMethod); + +export const MOCK_XLM_ACCOUNT_1: Bip44Account = { + id: 'mock-snap-id-1', + address: `G${'A'.repeat(55)}`, + options: { + entropy: { + type: KeyringAccountEntropyTypeOption.Mnemonic, + id: MOCK_HD_KEYRING_2.metadata.id, + groupIndex: 0, + derivationPath: '', + }, + }, + methods: XLM_METHODS, + type: XlmAccountType.Account, + scopes: [XlmScope.Pubnet, XlmScope.Testnet], + metadata: { + name: 'Stellar Account 1', + keyring: { type: KeyringTypes.snap }, + snap: MOCK_SNAP_1, + importTime: 0, + lastSelected: 0, + }, +}; + export const MOCK_TRX_ACCOUNT_1: Bip44Account = { id: 'mock-snap-id-1', address: 'aabbccdd', @@ -185,6 +213,12 @@ export const MOCK_TRX_DISCOVERED_ACCOUNT_1: DiscoveredAccount = { derivationPath: `m/44'/195'/0'/0'`, }; +export const MOCK_XLM_DISCOVERED_ACCOUNT_1: DiscoveredAccount = { + type: 'bip44', + scopes: [XlmScope.Pubnet], + derivationPath: `m/44'/148'/0'`, +}; + export const MOCK_BTC_P2TR_DISCOVERED_ACCOUNT_1: DiscoveredAccount = { type: 'bip44', scopes: [BtcScope.Mainnet], diff --git a/packages/multichain-account-service/src/tests/providers.ts b/packages/multichain-account-service/src/tests/providers.ts index 2c92fca4ba..e88c66256d 100644 --- a/packages/multichain-account-service/src/tests/providers.ts +++ b/packages/multichain-account-service/src/tests/providers.ts @@ -1,5 +1,11 @@ import type { Bip44Account } from '@metamask/account-api'; -import { BtcScope, EthScope, SolScope, TrxScope } from '@metamask/keyring-api'; +import { + BtcScope, + EthScope, + SolScope, + TrxScope, + XlmScope, +} from '@metamask/keyring-api'; import type { KeyringAccount } from '@metamask/keyring-api'; import type { KeyringCapabilities } from '@metamask/keyring-api/v2'; @@ -37,6 +43,7 @@ export function makeMockAccountProvider( SolScope.Testnet, BtcScope.Testnet, TrxScope.Shasta, + XlmScope.Testnet, EthScope.Eoa, ], bip44: { deriveIndex: true }, diff --git a/yarn.lock b/yarn.lock index b5a994e4db..bdf01f1c92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,7 +2,7 @@ # Manual changes might be lost - proceed with caution! __metadata: - version: 8 + version: 9 cacheKey: 10 "@adraffy/ens-normalize@npm:1.10.1": @@ -2560,7 +2560,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/accounts-controller@npm:^37.2.0, @metamask/accounts-controller@workspace:packages/accounts-controller": +"@metamask/accounts-controller@npm:^37.1.1, @metamask/accounts-controller@npm:^37.2.0, @metamask/accounts-controller@workspace:packages/accounts-controller": version: 0.0.0-use.local resolution: "@metamask/accounts-controller@workspace:packages/accounts-controller" dependencies: @@ -3815,6 +3815,29 @@ __metadata: languageName: node linkType: hard +"@metamask/eth-snap-keyring@npm:^19.0.0": + version: 19.0.0 + resolution: "@metamask/eth-snap-keyring@npm:19.0.0" + dependencies: + "@ethereumjs/tx": "npm:^5.4.0" + "@metamask/eth-sig-util": "npm:^8.2.0" + "@metamask/keyring-api": "npm:^21.4.0" + "@metamask/keyring-internal-api": "npm:^10.0.0" + "@metamask/keyring-internal-snap-client": "npm:^9.0.0" + "@metamask/keyring-snap-sdk": "npm:^7.2.0" + "@metamask/keyring-utils": "npm:^3.2.0" + "@metamask/messenger": "npm:^0.3.0" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^11.1.0" + "@types/uuid": "npm:^9.0.8" + async-mutex: "npm:^0.5.0" + uuid: "npm:^9.0.1" + peerDependencies: + "@metamask/keyring-api": ^21.4.0 + checksum: 10/6e307295cb15ab44aba4ff89fb1886ad8c0ea6636748e5d84c87250fbaff9d5a3c316e63b614d7b472e4027aad0a7912109b36981e978e978bf31deea7980726 + languageName: node + linkType: hard + "@metamask/eth-snap-keyring@npm:^21.0.1": version: 21.0.1 resolution: "@metamask/eth-snap-keyring@npm:21.0.1" @@ -4160,6 +4183,35 @@ __metadata: languageName: node linkType: hard +"@metamask/keyring-api@npm:^21.4.0, @metamask/keyring-api@npm:^21.6.0": + version: 21.6.0 + resolution: "@metamask/keyring-api@npm:21.6.0" + dependencies: + "@ethereumjs/tx": "npm:^5.4.0" + "@metamask/eth-sig-util": "npm:^8.2.0" + "@metamask/keyring-utils": "npm:^3.2.0" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^11.1.0" + "@types/uuid": "npm:^9.0.8" + async-mutex: "npm:^0.5.0" + bitcoin-address-validation: "npm:^2.2.3" + uuid: "npm:^9.0.1" + checksum: 10/ecd482ec83fbdb16da5f0c548db29931edd4718c57550547aed9f3532c8e60ec39a6894571c96a819e5f205e53ec149e6b52710194ac0610960aa51834f15dd8 + languageName: node + linkType: hard + +"@metamask/keyring-api@npm:^22.0.0": + version: 22.0.0 + resolution: "@metamask/keyring-api@npm:22.0.0" + dependencies: + "@metamask/keyring-utils": "npm:^3.2.0" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^11.1.0" + bitcoin-address-validation: "npm:^2.2.3" + checksum: 10/ff1e9537c7219fb906b61d6755de28890239ec44f634732bd8571801c662e8b801671e98961a1c5047e079cef314353297499a52f7091a2b5391325d575ec4f9 + languageName: node + linkType: hard + "@metamask/keyring-api@npm:^23.0.1": version: 23.0.1 resolution: "@metamask/keyring-api@npm:23.0.1" @@ -4172,7 +4224,7 @@ __metadata: languageName: node linkType: hard -"@metamask/keyring-controller@npm:^25.2.0, @metamask/keyring-controller@workspace:packages/keyring-controller": +"@metamask/keyring-controller@npm:^25.1.1, @metamask/keyring-controller@npm:^25.2.0, @metamask/keyring-controller@workspace:packages/keyring-controller": version: 0.0.0-use.local resolution: "@metamask/keyring-controller@workspace:packages/keyring-controller" dependencies: @@ -4212,7 +4264,7 @@ __metadata: languageName: unknown linkType: soft -"@metamask/keyring-internal-api@npm:^10.1.1": +"@metamask/keyring-internal-api@npm:^10.0.0, @metamask/keyring-internal-api@npm:^10.0.1, @metamask/keyring-internal-api@npm:^10.1.1": version: 10.1.1 resolution: "@metamask/keyring-internal-api@npm:10.1.1" dependencies: @@ -4236,6 +4288,19 @@ __metadata: languageName: node linkType: hard +"@metamask/keyring-internal-snap-client@npm:^9.0.0": + version: 9.0.1 + resolution: "@metamask/keyring-internal-snap-client@npm:9.0.1" + dependencies: + "@metamask/keyring-api": "npm:^22.0.0" + "@metamask/keyring-internal-api": "npm:^10.0.1" + "@metamask/keyring-snap-client": "npm:^8.2.1" + "@metamask/keyring-utils": "npm:^3.2.0" + "@metamask/messenger": "npm:^0.3.0" + checksum: 10/fb683a7826856612d2d89a8f6eb83bd14a20e27c18dddb5c50699d0fefce7475ff9e3077b65296820cf0911ee49d4daf3014932d1b1d7bfd330612229e02e6f4 + languageName: node + linkType: hard + "@metamask/keyring-sdk@npm:^2.0.1": version: 2.0.1 resolution: "@metamask/keyring-sdk@npm:2.0.1" @@ -4254,6 +4319,22 @@ __metadata: languageName: node linkType: hard +"@metamask/keyring-snap-client@npm:^8.2.0, @metamask/keyring-snap-client@npm:^8.2.1": + version: 8.2.1 + resolution: "@metamask/keyring-snap-client@npm:8.2.1" + dependencies: + "@metamask/keyring-api": "npm:^22.0.0" + "@metamask/keyring-utils": "npm:^3.2.0" + "@metamask/superstruct": "npm:^3.1.0" + "@types/uuid": "npm:^9.0.8" + uuid: "npm:^9.0.1" + webextension-polyfill: "npm:^0.12.0" + peerDependencies: + "@metamask/providers": ^19.0.0 + checksum: 10/f69dcb56d3089dfd1bd902ad409c017288e31d3631a2024f42f6b6ee1b4f6e663ad4d7bdfd5d3bcf6c9827190d3bad6983c89f7c821959d305f6ba8f4aae0d7c + languageName: node + linkType: hard + "@metamask/keyring-snap-client@npm:^9.0.1": version: 9.0.1 resolution: "@metamask/keyring-snap-client@npm:9.0.1" @@ -4270,6 +4351,22 @@ __metadata: languageName: node linkType: hard +"@metamask/keyring-snap-sdk@npm:^7.2.0": + version: 7.2.1 + resolution: "@metamask/keyring-snap-sdk@npm:7.2.1" + dependencies: + "@metamask/keyring-utils": "npm:^3.2.0" + "@metamask/snaps-sdk": "npm:^10.4.0" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^11.1.0" + webextension-polyfill: "npm:^0.12.0" + peerDependencies: + "@metamask/keyring-api": ^21.5.0 + "@metamask/providers": ^19.0.0 + checksum: 10/e314e56d3d82e0306564a3f6c2a3e207b36e51a43b9589780331a2fa67cc9e6a65d6008e3c81c3ffbea29c93950f6a8c9329694fcdd0136786d957baa7cd48df + languageName: node + linkType: hard + "@metamask/keyring-snap-sdk@npm:^9.0.1": version: 9.0.1 resolution: "@metamask/keyring-snap-sdk@npm:9.0.1" @@ -4375,7 +4472,14 @@ __metadata: languageName: unknown linkType: soft -"@metamask/messenger@npm:^1.1.0, @metamask/messenger@npm:^1.1.1, @metamask/messenger@workspace:packages/messenger": +"@metamask/messenger@npm:^0.3.0": + version: 0.3.0 + resolution: "@metamask/messenger@npm:0.3.0" + checksum: 10/84e9f4193646d749c7260a4958b13974b3c8738cc2e414116279ed31734e1edba687ff56ddbfdb75033bce30aaa9eeb7c391bccb87a66dbc99a902882271f673 + languageName: node + linkType: hard + +"@metamask/messenger@npm:^1.0.0, @metamask/messenger@npm:^1.1.0, @metamask/messenger@npm:^1.1.1, @metamask/messenger@workspace:packages/messenger": version: 0.0.0-use.local resolution: "@metamask/messenger@workspace:packages/messenger" dependencies: @@ -4461,7 +4565,37 @@ __metadata: languageName: unknown linkType: soft -"@metamask/multichain-account-service@npm:^8.0.1, @metamask/multichain-account-service@workspace:packages/multichain-account-service": +"@metamask/multichain-account-service@npm:^8.0.1": + version: 8.0.1 + resolution: "@metamask/multichain-account-service@npm:8.0.1" + dependencies: + "@ethereumjs/util": "npm:^9.1.0" + "@metamask/accounts-controller": "npm:^37.1.1" + "@metamask/base-controller": "npm:^9.0.1" + "@metamask/eth-snap-keyring": "npm:^19.0.0" + "@metamask/key-tree": "npm:^10.1.1" + "@metamask/keyring-api": "npm:^21.6.0" + "@metamask/keyring-controller": "npm:^25.1.1" + "@metamask/keyring-internal-api": "npm:^10.0.0" + "@metamask/keyring-snap-client": "npm:^8.2.0" + "@metamask/keyring-utils": "npm:^3.1.0" + "@metamask/messenger": "npm:^1.0.0" + "@metamask/snaps-controllers": "npm:^19.0.0" + "@metamask/snaps-sdk": "npm:^11.0.0" + "@metamask/snaps-utils": "npm:^12.1.2" + "@metamask/superstruct": "npm:^3.1.0" + "@metamask/utils": "npm:^11.9.0" + async-mutex: "npm:^0.5.0" + lodash: "npm:^4.17.21" + peerDependencies: + "@metamask/account-api": ^1.0.0 + "@metamask/providers": ^22.0.0 + webextension-polyfill: ^0.10.0 || ^0.11.0 || ^0.12.0 + checksum: 10/7ac3c38db8afd47593cd7ed4cc95de99195ccd2b2903281382be83990b2a47fa2f03de77e325c1ea3c7fd96367e3eac20039994d95154d478bebacd61215140a + languageName: node + linkType: hard + +"@metamask/multichain-account-service@workspace:packages/multichain-account-service": version: 0.0.0-use.local resolution: "@metamask/multichain-account-service@workspace:packages/multichain-account-service" dependencies: @@ -5377,6 +5511,20 @@ __metadata: languageName: node linkType: hard +"@metamask/snaps-sdk@npm:^10.4.0": + version: 10.4.0 + resolution: "@metamask/snaps-sdk@npm:10.4.0" + dependencies: + "@metamask/key-tree": "npm:^10.1.1" + "@metamask/providers": "npm:^22.1.1" + "@metamask/rpc-errors": "npm:^7.0.3" + "@metamask/superstruct": "npm:^3.2.1" + "@metamask/utils": "npm:^11.9.0" + luxon: "npm:^3.5.0" + checksum: 10/215a73f41f5043ca9767241da83308bb25b3faae96521ed71a63a9d29078461bcf278a01799e014bb20b49e34d96a05ea1822648c9bcf81e92f22e8c9b6c0f94 + languageName: node + linkType: hard + "@metamask/snaps-sdk@npm:^11.0.0, @metamask/snaps-sdk@npm:^11.1.0": version: 11.1.0 resolution: "@metamask/snaps-sdk@npm:11.1.0"