Skip to content

Commit

Permalink
Loosen input requirements for byte arrays; they can now implement jus…
Browse files Browse the repository at this point in the history
…t the read interface (#64)

Though unintuitive, this PR actually makes the input type of Wallet Standard methods _less_ restrictive. They will now take not only `Uint8Arrays`, but things shaped like `Uint8Arrays` that behave more read-only like.

The motivation here is to make Wallet Standard more compatible with libraries like `@solana/web3.js` that deal in `ReadonlyUint8Array`.
  • Loading branch information
steveluscher authored Jun 7, 2024
2 parents c97e19e + 1a83351 commit 4e9c419
Show file tree
Hide file tree
Showing 12 changed files with 50 additions and 25 deletions.
9 changes: 9 additions & 0 deletions .changeset/rich-spoons-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
'@wallet-standard/experimental-features': patch
'@wallet-standard/errors': patch
'@wallet-standard/wallet': patch
'@wallet-standard/base': patch
'@wallet-standard/core': patch
---

Wherever a bytearray is required as input, you can now pass read-only instances of `Uint8Array` – namely ones without mutative methods like `fill` and `reverse`. This makes it so that Wallet Standard methods are _less_ strict about these inputs, and can accept a wider variety of them
6 changes: 6 additions & 0 deletions packages/core/base/src/bytes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
type TypedArrayMutableProperties = 'copyWithin' | 'fill' | 'reverse' | 'set' | 'sort';
export interface ReadonlyUint8Array extends Omit<Uint8Array, TypedArrayMutableProperties> {
readonly [n: number]: number;
}

export {};
1 change: 1 addition & 0 deletions packages/core/base/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './bytes.js';
export * from './identifier.js';
export * from './wallet.js';
export * from './window.js';
3 changes: 2 additions & 1 deletion packages/core/base/src/wallet.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ReadonlyUint8Array } from './bytes.js';
import type { IdentifierArray, IdentifierRecord, IdentifierString } from './identifier.js';

/**
Expand Down Expand Up @@ -132,7 +133,7 @@ export interface WalletAccount {
readonly address: string;

/** Public key of the account, corresponding with a secret key to use. */
readonly publicKey: Uint8Array;
readonly publicKey: ReadonlyUint8Array;

/**
* Chains supported by the account.
Expand Down
6 changes: 3 additions & 3 deletions packages/core/wallet/src/util.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { WalletAccount } from '@wallet-standard/base';
import type { ReadonlyUint8Array, WalletAccount } from '@wallet-standard/base';

/**
* Base implementation of a {@link "@wallet-standard/base".WalletAccount} to be used or extended by a
Expand Down Expand Up @@ -102,7 +102,7 @@ export function arraysEqual<T>(a: Indexed<T>, b: Indexed<T>): boolean {
*
* @group Util
*/
export function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {
export function bytesEqual(a: ReadonlyUint8Array, b: ReadonlyUint8Array): boolean {
return arraysEqual(a, b);
}

Expand All @@ -116,7 +116,7 @@ export function bytesEqual(a: Uint8Array, b: Uint8Array): boolean {
*
* @group Util
*/
export function concatBytes(first: Uint8Array, ...others: Uint8Array[]): Uint8Array {
export function concatBytes(first: ReadonlyUint8Array, ...others: ReadonlyUint8Array[]): Uint8Array {
const length = others.reduce((length, bytes) => length + bytes.length, first.length);
const bytes = new Uint8Array(length);

Expand Down
7 changes: 4 additions & 3 deletions packages/example/extension/src/background/wallet.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Keypair as SolKeypair } from '@solana/web3.js';
import type { ReadonlyUint8Array } from '@wallet-standard/core';
import * as bip39 from 'bip39';
import { derivePath } from 'ed25519-hd-key';
import { utils as ethUtils, Wallet as EthWallet } from 'ethers';
Expand All @@ -11,15 +12,15 @@ const BIP44_COIN_TYPE_SOL = 501;
export type Mnemonic = string;

export interface Keypair {
publicKey: Uint8Array;
privateKey: Uint8Array;
publicKey: ReadonlyUint8Array;
privateKey: ReadonlyUint8Array;
}

export type Network = 'ethereum' | 'solana';

export interface Account {
network: Network;
publicKey: Uint8Array;
publicKey: ReadonlyUint8Array;
}

/**
Expand Down
4 changes: 3 additions & 1 deletion packages/example/extension/src/messages/serialization.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
function serializeMessage(message: Uint8Array): string {
import type { ReadonlyUint8Array } from '@wallet-standard/core';

function serializeMessage(message: ReadonlyUint8Array): string {
return Buffer.from(message).toString('base64');
}

Expand Down
17 changes: 11 additions & 6 deletions packages/example/wallets/src/solanaWallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {
} from '@solana/wallet-standard';
import { getEndpointForChain, SOLANA_CHAINS } from '@solana/wallet-standard';
import { Keypair, PublicKey, Transaction } from '@solana/web3.js';
import type { ConnectFeature, ConnectMethod, EventsFeature, Wallet } from '@wallet-standard/core';
import type { ConnectFeature, ConnectMethod, EventsFeature, ReadonlyUint8Array, Wallet } from '@wallet-standard/core';
import type {
DecryptFeature,
DecryptMethod,
Expand All @@ -34,9 +34,9 @@ import { sendAndConfirmTransaction } from './solana.js';

// A reference to an underlying Ledger device that has already been connected and account initialized
interface SolanaLedgerApp {
publicKey: Uint8Array;
publicKey: ReadonlyUint8Array;

signTransaction(transaction: Uint8Array): Promise<Uint8Array>;
signTransaction(transaction: ReadonlyUint8Array): Promise<Uint8Array>;
}

export class SolanaWallet extends AbstractWallet implements Wallet {
Expand Down Expand Up @@ -123,7 +123,7 @@ export class SolanaWallet extends AbstractWallet implements Wallet {
],
}),
new LedgerWalletAccount({
address: bs58.encode(ledger.publicKey),
address: bs58.encode(ledger.publicKey as Uint8Array),
publicKey: ledger.publicKey,
chains: SOLANA_CHAINS,
features: ['solana:signAndSendTransaction', 'solana:signTransaction'],
Expand Down Expand Up @@ -243,7 +243,7 @@ export class SolanaWallet extends AbstractWallet implements Wallet {
if (!keypair) throw new Error('invalid account');

const nonce = randomBytes(box.nonceLength);
const ciphertext = box(cleartext, nonce, publicKey, keypair.secretKey);
const ciphertext = box(cleartext as Uint8Array, nonce, publicKey as Uint8Array, keypair.secretKey);
outputs.push({ ciphertext, nonce });
}

Expand All @@ -261,7 +261,12 @@ export class SolanaWallet extends AbstractWallet implements Wallet {
const keypair = this.#keys[account.address]?.keypair;
if (!keypair) throw new Error('invalid account');

const cleartext = box.open(ciphertext, nonce, publicKey, keypair.secretKey);
const cleartext = box.open(
ciphertext as Uint8Array,
nonce as Uint8Array,
publicKey as Uint8Array,
keypair.secretKey
);
if (!cleartext) throw new Error('message authentication failed');
outputs.push({ cleartext });
}
Expand Down
8 changes: 4 additions & 4 deletions packages/experimental/features/src/decrypt.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { WalletAccount } from '@wallet-standard/base';
import type { ReadonlyUint8Array, WalletAccount } from '@wallet-standard/base';

/** TODO: docs */
export type DecryptFeature = {
Expand Down Expand Up @@ -36,13 +36,13 @@ export interface DecryptInput {
cipher: string;

/** Public key to derive a shared key to decrypt the data using. */
publicKey: Uint8Array;
publicKey: ReadonlyUint8Array;

/** Ciphertext to decrypt. */
ciphertext: Uint8Array;
ciphertext: ReadonlyUint8Array;

/** Nonce to use for decryption. */
nonce: Uint8Array;
nonce: ReadonlyUint8Array;

/** Multiple of padding bytes to use for decryption, defaulting to 0. */
padding?: 0 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048;
Expand Down
6 changes: 3 additions & 3 deletions packages/experimental/features/src/encrypt.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { WalletAccount } from '@wallet-standard/base';
import type { ReadonlyUint8Array, WalletAccount } from '@wallet-standard/base';

/** TODO: docs */
export type EncryptFeature = {
Expand Down Expand Up @@ -37,10 +37,10 @@ export interface EncryptInput {
cipher: string;

/** Public key to derive a shared key to encrypt the data using. */
publicKey: Uint8Array;
publicKey: ReadonlyUint8Array;

/** Cleartext to decrypt. */
cleartext: Uint8Array;
cleartext: ReadonlyUint8Array;

/** Multiple of padding bytes to use for encryption, defaulting to 0. */
padding?: 0 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024 | 2048;
Expand Down
4 changes: 2 additions & 2 deletions packages/experimental/features/src/signMessage.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { WalletAccount } from '@wallet-standard/base';
import type { ReadonlyUint8Array, WalletAccount } from '@wallet-standard/base';

/** TODO: docs */
export type SignMessageFeature = {
Expand All @@ -24,7 +24,7 @@ export interface SignMessageInput {
account: WalletAccount;

/** Message to sign, as raw bytes. */
message: Uint8Array;
message: ReadonlyUint8Array;
}

/** Output of signing a message. */
Expand Down
4 changes: 2 additions & 2 deletions packages/experimental/features/src/signTransaction.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { IdentifierString, WalletAccount } from '@wallet-standard/base';
import type { IdentifierString, ReadonlyUint8Array, WalletAccount } from '@wallet-standard/base';

/** TODO: docs */
export type SignTransactionFeature = {
Expand All @@ -24,7 +24,7 @@ export interface SignTransactionInput {
account: WalletAccount;

/** Serialized transactions, as raw bytes. */
transaction: Uint8Array;
transaction: ReadonlyUint8Array;

/** Chain to use. */
chain?: IdentifierString;
Expand Down

0 comments on commit 4e9c419

Please sign in to comment.