Skip to content
Closed
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
6 changes: 3 additions & 3 deletions packages/bitcoin-wallet-snap/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

- Bump `@metamask/keyring-api` from `^23.7.0` to `^24.1.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- Bump `@metamask/keyring-snap-sdk` from `^9.2.1` to `^10.0.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- Bump `@metamask/snaps-sdk` from `^11.2.0` to `^12.0.1` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- **BREAKING** Bump `@metamask/keyring-api` from `^23.7.0` to `^24.1.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- **BREAKING** Bump `@metamask/keyring-snap-sdk` from `^9.2.1` to `^10.0.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- **BREAKING** Bump `@metamask/snaps-sdk` from `^11.2.0` to `^12.0.1` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- Reduce `keyring_createAccounts` entropy RPCs from one per account to one per distinct parent path by fetching the account-level parent node once and deriving hardened children locally ([#221](https://github.com/MetaMask/internal-snaps/pull/221))
- The private parent node is held transiently in memory during the batch — the same trust boundary as the previous per-account implementation — and children are neutered before descriptor construction.
- The creation concurrency throttle is removed: with derivation local, the remaining per-account work is synchronous WASM wallet construction.
Expand Down
2 changes: 1 addition & 1 deletion packages/snap-networks-utils/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- **BREAKING** Replace the logger utilities with a configurable `Logger` class that requires a log level and supports level filtering, per-instance prefixes, and method decorators. ([#136](https://github.com/MetaMask/internal-snaps/pull/136))
- Bump `@metamask/utils` from `^11.9.0` to `^11.11.9` ([#161](https://github.com/MetaMask/internal-snaps/pull/161))
- Bump `@metamask/snaps-sdk` from `^11.2.0` to `^12.0.1` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- **BREAKING** Bump `@metamask/snaps-sdk` from `^11.2.0` to `^12.0.1` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))

## [1.0.0]

Expand Down
6 changes: 3 additions & 3 deletions packages/solana-wallet-snap/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Extract Snap-owned assets domain logic into `SnapAssetsAdapter`; `AssetsService` is a thin facade that delegates metadata, market data, fetch, persist, and account asset reads through the adapter (no Core routing yet). ([#121](https://github.com/MetaMask/internal-snaps/pull/121))
- Align `AssetsService` read API with `snap-networks-utils` / AssetsController shapes by adding `getAccountAssetByID`, `getAccountAssetsByIDs`, `getAccountAssetsByScope`, and `getAccountAssets`, and routing Keyring and Send through them (still Snap-owned storage). ([#120](https://github.com/MetaMask/internal-snaps/pull/120))
- Bump `@metamask/utils` from `^11.9.0` to `^11.11.9` ([#161](https://github.com/MetaMask/internal-snaps/pull/161))
- Bump `@metamask/keyring-api` from `^23.7.0` to `^24.1.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- Bump `@metamask/keyring-snap-sdk` from `^9.2.1` to `^10.0.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- Bump `@metamask/snaps-sdk` from `^11.2.0` to `^12.0.1` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- **BREAKING** Bump `@metamask/keyring-api` from `^23.7.0` to `^24.1.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- **BREAKING** Bump `@metamask/keyring-snap-sdk` from `^9.2.1` to `^10.0.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- **BREAKING** Bump `@metamask/snaps-sdk` from `^11.2.0` to `^12.0.1` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))

### Fixed

Expand Down
6 changes: 3 additions & 3 deletions packages/stellar-wallet-snap/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed

- `createValidatedSendTransaction` now throws `InvalidAssetForCreateAccountException` instead of `AccountNotActivatedException` when sending a non-native asset to an unfunded destination ([#185](https://github.com/MetaMask/internal-snaps/pull/185))
- Bump `@metamask/keyring-api` from `^23.7.0` to `^24.1.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- Bump `@metamask/keyring-snap-sdk` from `^9.2.1` to `^10.0.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- Bump `@metamask/snaps-sdk` from `^11.2.0` to `^12.0.1` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- **BREAKING** Bump `@metamask/keyring-api` from `^23.7.0` to `^24.1.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- **BREAKING** Bump `@metamask/keyring-snap-sdk` from `^9.2.1` to `^10.0.0` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))
- **BREAKING** Bump `@metamask/snaps-sdk` from `^11.2.0` to `^12.0.1` ([#214](https://github.com/MetaMask/internal-snaps/pull/214))

## [0.1.0]

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Account management and SEP-43 signing entry points via `onKeyringRequest` → `K
| `AccountService` | `services/account` | Persist / derive / select accounts (snap state) |
| `OnChainAccountService` | `services/on-chain-account` | Snap-state snapshots for balances/assets; live activation for discovery |
| `TransactionService` | `services/transaction` | Local pending keyring txs for `listAccountTransactions` |
| `WalletService` | `services/wallet` | HD derive signing material; used by `exportAccount` (never persisted) |
| `SyncAccountsHandler` | `handlers/cronjob` | Scheduled after selection changes to refresh on-chain snapshots |

## Request / response
Expand Down Expand Up @@ -45,6 +46,7 @@ Requests are origin-checked, then dispatched to the methods below.
| `listAccountTransactions` | Paginated keyring transactions for the account | **Snap state** (pending / local txs via `TransactionService` — **not** Horizon history) |
| `discoverAccounts` | Derive BIP-44 address for index; return it only if activated on any requested scope | Derive locally; activation check is **live on-chain** (`NetworkService.getAccount`) |
| `resolveAccountAddress` | Given an address, return CAIP-10 if this snap owns it; else `null` (MetaMask may fall back) | **Snap state** (keyring account lookup by address) |
| `exportAccount` | Export the Stellar secret seed (`S…` strkey / base32). Only `encoding: "base32"` is supported | **Derived** via `WalletService` (never persisted) |
| `filterAccountChains` | Not implemented | Throws `MethodNotSupportedError` |
| `updateAccount` | Not implemented | Throws `MethodNotSupportedError` |
| `submitRequest` | [signTransaction.md](./signTransaction.md) · [signMessage.md](./signMessage.md) · [signAuthEntry.md](./signAuthEntry.md) | |
20 changes: 20 additions & 0 deletions packages/stellar-wallet-snap/src/api/address.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { assert, StructError } from '@metamask/superstruct';
import {
StellarAddressOrContractStruct,
StellarAddressStruct,
StellarSecretKeyStruct,
} from './address';

describe('StellarAddressStruct', () => {
Expand All @@ -21,6 +22,25 @@ describe('StellarAddressStruct', () => {
});
});

describe('StellarSecretKeyStruct', () => {
it('accepts a valid Stellar secret seed', () => {
expect(() =>
assert(
'SAKICEVQLYWGSOJS4WW7HZJWAHZVEEBS527LHK5V4MLJALYKICQCJXMW',
StellarSecretKeyStruct,
),
).not.toThrow();
});

it.each([
'invalid-secret',
'GA7UCNSASSOPQYTRGJ2NC7TDBSXHMWK6JHS7AO6X2ZQAIQSTB5ELNFSO',
'',
])('rejects an invalid Stellar secret seed: "%s"', (secret) => {
expect(() => assert(secret, StellarSecretKeyStruct)).toThrow(StructError);
});
});

describe('StellarAddressOrContractStruct', () => {
it('accepts a valid Stellar address', () => {
expect(() =>
Expand Down
27 changes: 25 additions & 2 deletions packages/stellar-wallet-snap/src/api/address.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { refine, string, nonempty } from '@metamask/superstruct';
import { refine, string, nonempty, sensitive } from '@metamask/superstruct';
import type { Infer } from '@metamask/superstruct';
import { StrKey } from '@stellar/stellar-sdk';

Expand All @@ -7,7 +7,7 @@ import { StrKey } from '@stellar/stellar-sdk';
* We only support non-muxed addresses.
*/
export const StellarAddressStruct = refine(
nonempty(string()),
nonempty(sensitive(string())),
'stellar_address',
(value: string) => {
try {
Expand All @@ -21,6 +21,29 @@ export const StellarAddressStruct = refine(
},
);

/**
* Validation struct for a Stellar secret seed (`S…` strkey / base32).
*/
export const StellarSecretKeyStruct = refine(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you use the sensitive struct that we have here?

nonempty(string()),
'stellar_secret_key',
(value: string) => {
try {
if (!StrKey.isValidEd25519SecretSeed(value)) {
return 'Invalid Stellar secret key';
}
return true;
} catch {
return 'Invalid Stellar secret key';
}
},
);

/**
* Type for a Stellar secret seed.
*/
export type StellarSecretKey = Infer<typeof StellarSecretKeyStruct>;

export const StellarAddressOrContractStruct = refine(
nonempty(string()),
'stellar_contract_or_address',
Expand Down
8 changes: 8 additions & 0 deletions packages/stellar-wallet-snap/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@ import snapManifest from '../snap.manifest.json';
export const SUPPORTED_SCOPES =
snapManifest.initialPermissions['endowment:keyring'].capabilities.scopes;

/**
* Private-key export encoding supported by this snap.
*
* Always `base32` (Stellar `S…` strkey). Not read from the snap manifest:
* this snap does not support any other encoding.
*/
export const PRIVATE_KEY_EXPORT_ENCODING = 'base32' as const;

/**
* The base reserve for the Stellar network.
*
Expand Down
1 change: 1 addition & 0 deletions packages/stellar-wallet-snap/src/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ const keyringHandler = new KeyringHandler({
accountService,
onChainAccountService,
transactionService,
walletService,
handlers: keyringMethodHandlers,
});

Expand Down
61 changes: 60 additions & 1 deletion packages/stellar-wallet-snap/src/handlers/keyring/api.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { assert, StructError } from '@metamask/superstruct';
import { assert, create, StructError } from '@metamask/superstruct';

import { KnownCaip2ChainId } from '../../api';
import type { StellarKeyringAccount } from '../../services/account';
import { generateMockStellarKeyringAccounts } from '../../services/account/__mocks__/account.fixtures';
import {
CreateAccountOptionsStruct,
ExportAccountRequestStruct,
ResolveAccountAddressRequestStruct,
ListAccountTransactionsRequestStruct,
MultichainMethod,
Expand Down Expand Up @@ -586,3 +587,61 @@ describe('ListAccountTransactionsRequestStruct', () => {
);
});
});

describe('ExportAccountRequestStruct', () => {
it.each([
{
request: { accountId: account.id },
expected: {
accountId: account.id,
options: { type: 'private-key' as const, encoding: 'base32' as const },
},
},
{
request: {
accountId: account.id,
options: { type: 'private-key' as const },
},
expected: {
accountId: account.id,
options: { type: 'private-key' as const, encoding: 'base32' as const },
},
},
{
request: {
accountId: account.id,
options: { type: 'private-key' as const, encoding: 'base32' as const },
},
expected: {
accountId: account.id,
options: { type: 'private-key' as const, encoding: 'base32' as const },
},
},
])('accepts a valid exportAccount request', ({ request, expected }) => {
expect(create(request, ExportAccountRequestStruct)).toStrictEqual(expected);
});

it.each([
{ accountId: 'not-a-uuid' },
{
accountId: account.id,
options: { type: 'mnemonic', encoding: 'base32' },
},
{
accountId: account.id,
options: { type: 'private-key', encoding: 'utf-8' },
},
{
accountId: account.id,
options: { type: 'private-key', encoding: 'hexadecimal' },
},
{
accountId: account.id,
options: { type: 'private-key', encoding: 'base58' },
},
])('rejects an invalid exportAccount request', (request) => {
expect(() => assert(request, ExportAccountRequestStruct)).toThrow(
StructError,
);
});
});
30 changes: 30 additions & 0 deletions packages/stellar-wallet-snap/src/handlers/keyring/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import {
nullable,
enums,
refine,
defaulted,
} from '@metamask/superstruct';
import type { Infer } from '@metamask/superstruct';
import { base64 } from '@metamask/utils';
Expand All @@ -29,6 +30,7 @@ import { KnownCaip2ChainId, KnownCaip2ChainIdStruct } from '../../api/network';
import { Utf8StringStruct } from '../../api/string';
import { UuidStruct } from '../../api/uuid';
import { HashIdPreimageXdrStruct, XdrStruct } from '../../api/xdr';
import { PRIVATE_KEY_EXPORT_ENCODING } from '../../constants';
import { networkToCaip2ChainId } from '../../services/network/utils';

/** JSON-RPC methods supported by this snap's multichain keyring. */
Expand Down Expand Up @@ -309,6 +311,29 @@ export const SignAuthEntryResponseStruct = union([
*/
export const GetAccountRequestStruct = UuidStruct;

/**
* Validation struct for the exportAccount request.
*
* Only {@link PRIVATE_KEY_EXPORT_ENCODING} (`base32`) is accepted. Missing
* `options` or `encoding` default to that encoding.
*/
export const ExportAccountRequestStruct = object({
accountId: UuidStruct,
options: defaulted(
object({
type: literal('private-key'),
encoding: defaulted(
enums([PRIVATE_KEY_EXPORT_ENCODING]),
PRIVATE_KEY_EXPORT_ENCODING,
),
}),
{
type: 'private-key' as const,
encoding: PRIVATE_KEY_EXPORT_ENCODING,
},
),
});

/**
* Validation struct for the deleteAccount request.
*/
Expand Down Expand Up @@ -352,6 +377,11 @@ export type ResolveAccountAddressJsonRpcRequest = Infer<
*/
export type GetAccountRequest = Infer<typeof GetAccountRequestStruct>;

/**
* Type for the exportAccount request.
*/
export type ExportAccountRequest = Infer<typeof ExportAccountRequestStruct>;

/**
* Type for the deleteAccount request.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@ import { StellarSnapException } from '../../utils/errors';

export class KeyringException extends StellarSnapException {}

/**
* Thrown when private-key export fails. Messages must not include the secret.
*/
export class ExportAccountException extends KeyringException {}

/**
* SEP-43 error codes.
*
Expand Down
Loading
Loading