Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 8 additions & 9 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
approvedGitRepositories:
- "**"

compressionLevel: mixed

enableGlobalCache: false
Expand All @@ -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"
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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 = {
Expand Down Expand Up @@ -172,6 +185,11 @@ function mockAccountProvider<Provider extends Bip44AccountProvider>(
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,
);
}
}

Expand Down Expand Up @@ -218,6 +236,7 @@ async function setup({
SolAccountProvider: makeMockAccountProvider(),
BtcAccountProvider: makeMockAccountProvider(),
TrxAccountProvider: makeMockAccountProvider(),
XlmAccountProvider: makeMockAccountProvider(),
};

const spies: Spies = {
Expand Down Expand Up @@ -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>(
EvmAccountProvider,
Expand Down Expand Up @@ -321,6 +341,13 @@ async function setup({
3,
TrxAccountType.Eoa,
);
mockAccountProvider<XlmAccountProvider>(
XlmAccountProvider,
mocks.XlmAccountProvider,
accounts,
4,
XlmAccountType.Account,
);
}

const messenger = getMultichainAccountServiceMessenger(rootMessenger);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
/**
Expand Down Expand Up @@ -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,
];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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: [[]],
});
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,8 +237,6 @@ export class MultichainAccountWallet<
from: number,
to: number,
): Promise<Bip44Account<Account>[]> {
const isBatching = to > from;

try {
return await provider.createAccounts({
type: AccountCreationType.Bip44DeriveIndexRange,
Expand All @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions packages/multichain-account-service/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down
Loading