Skip to content

Commit

Permalink
feat: adds Solana account type to keyring API (#82)
Browse files Browse the repository at this point in the history
We are adding the `SolAccountType` to support Solana account creation.

---------

Co-authored-by: Charly Chevalier <[email protected]>
  • Loading branch information
zone-live and ccharly authored Nov 5, 2024
1 parent 8abbd21 commit c60ad8b
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 5 deletions.
11 changes: 10 additions & 1 deletion packages/keyring-api/src/api/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,21 @@ export enum BtcAccountType {
P2wpkh = 'bip122:p2wpkh',
}

/**
* Supported Solana account types.
*/
export enum SolAccountType {
DataAccount = 'solana:data-account',
}

/**
* Supported account types.
*/
export type KeyringAccountType =
| `${EthAccountType.Eoa}`
| `${EthAccountType.Erc4337}`
| `${BtcAccountType.P2wpkh}`;
| `${BtcAccountType.P2wpkh}`
| `${SolAccountType.DataAccount}`;

/**
* A struct which represents a Keyring account object. It is abstract enough to
Expand All @@ -48,6 +56,7 @@ export const KeyringAccountStruct = object({
`${EthAccountType.Eoa}`,
`${EthAccountType.Erc4337}`,
`${BtcAccountType.P2wpkh}`,
`${SolAccountType.DataAccount}`,
]),

/**
Expand Down
3 changes: 2 additions & 1 deletion packages/keyring-api/src/eth/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { isEvmAccountType } from './utils';
import { BtcAccountType, EthAccountType } from '../api';
import { BtcAccountType, EthAccountType, SolAccountType } from '../api';

describe('isEvmAccountType', () => {
it.each([
[EthAccountType.Eoa, true],
[EthAccountType.Erc4337, true],
[BtcAccountType.P2wpkh, false],
[SolAccountType.DataAccount, false],
[{}, false],
[null, false],
['bitcoin', false],
Expand Down
1 change: 1 addition & 0 deletions packages/keyring-api/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export * from './api';
export * from './btc';
export type * from './contexts';
export * from './eth';
export * from './sol';
export * from './events';
export * from './internal';
export * from './KeyringClient';
Expand Down
25 changes: 22 additions & 3 deletions packages/keyring-api/src/internal/types.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,21 @@
import type { Infer, Struct } from '@metamask/superstruct';
import { boolean, string, number } from '@metamask/superstruct';

import { BtcAccountType, EthAccountType, KeyringAccountStruct } from '../api';
import {
BtcAccountType,
EthAccountType,
KeyringAccountStruct,
SolAccountType,
} from '../api';
import { BtcP2wpkhAccountStruct } from '../btc/types';
import { EthEoaAccountStruct, EthErc4337AccountStruct } from '../eth/types';
import { SolDataAccountStruct } from '../sol/types';
import { exactOptional, object } from '../superstruct';

export type InternalAccountType = EthAccountType | BtcAccountType;
export type InternalAccountType =
| EthAccountType
| BtcAccountType
| SolAccountType;

export const InternalAccountMetadataStruct = object({
metadata: object({
Expand Down Expand Up @@ -42,6 +51,11 @@ export const InternalBtcP2wpkhAccountStruct = object({
...InternalAccountMetadataStruct.schema,
});

export const InternalSolDataAccountStruct = object({
...SolDataAccountStruct.schema,
...InternalAccountMetadataStruct.schema,
});

export type InternalEthEoaAccount = Infer<typeof InternalEthEoaAccountStruct>;

export type InternalEthErc4337Account = Infer<
Expand All @@ -52,21 +66,26 @@ export type InternalBtcP2wpkhAccount = Infer<
typeof InternalBtcP2wpkhAccountStruct
>;

export type InternalSolDataAccount = Infer<typeof InternalSolDataAccountStruct>;

export const InternalAccountStructs: Record<
string,
| Struct<InternalEthEoaAccount>
| Struct<InternalEthErc4337Account>
| Struct<InternalBtcP2wpkhAccount>
| Struct<InternalSolDataAccount>
> = {
[`${EthAccountType.Eoa}`]: InternalEthEoaAccountStruct,
[`${EthAccountType.Erc4337}`]: InternalEthErc4337AccountStruct,
[`${BtcAccountType.P2wpkh}`]: InternalBtcP2wpkhAccountStruct,
[`${SolAccountType.DataAccount}`]: InternalSolDataAccountStruct,
};

export type InternalAccountTypes =
| InternalEthEoaAccount
| InternalEthErc4337Account
| InternalBtcP2wpkhAccount;
| InternalBtcP2wpkhAccount
| InternalSolDataAccount;

export const InternalAccountStruct = object({
...KeyringAccountStruct.schema,
Expand Down
1 change: 1 addition & 0 deletions packages/keyring-api/src/sol/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './types';
7 changes: 7 additions & 0 deletions packages/keyring-api/src/sol/types.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import type { SolDataAccount } from './types';
import type { KeyringAccount } from '../api';
import type { Extends } from '../utils';
import { expectTrue } from '../utils';

// `SolDataAccount` extends `KeyringAccount`
expectTrue<Extends<SolDataAccount, KeyringAccount>>();
28 changes: 28 additions & 0 deletions packages/keyring-api/src/sol/types.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { SolAddressStruct } from './types';

describe('types', () => {
describe('SolAddressStruct', () => {
it.each([
'7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV',
'A5R99q8qeyUhgwYAdp5h8pAD1iteVjCzpCv7G6JRZLaQ',
])('is valid address: %s', (address) => {
expect(() => SolAddressStruct.assert(address)).not.toThrow();
});

it.each([
// Invalid lengths, too long (45 chars)
'7EcDhSYGxXyscszYEp35KHN8vvw3svAuLKTzXwCFLtV11',
// Too short (31 chars)
'7EcDhSYGxXyscszYEp35KHN8vvw',
// Empty or invalid input
'',
// Eth style address
'0x1234',
'not-an-address',
])('rejects invalid address: %s', (address) => {
expect(() => SolAddressStruct.assert(address)).toThrow(
`Expected a value of type \`SolAddress\`, but received: \`"${address}"\``,
);
});
});
});
44 changes: 44 additions & 0 deletions packages/keyring-api/src/sol/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import type { Infer } from '@metamask/superstruct';
import { array, enums, literal } from '@metamask/superstruct';

import { KeyringAccountStruct, SolAccountType } from '../api';
import { object, definePattern } from '../superstruct';

/**
* Solana addresses are represented in the format of a 256-bit ed25519 public key and
* are encoded using base58.
* They are usually 32 to 44 characters long.
*/
export const SolAddressStruct = definePattern(
'SolAddress',
/^[1-9A-HJ-NP-Za-km-z]{32,44}$/iu,
);

/**
* Supported Solana methods.
*/
export enum SolMethod {
// General transaction methods
SendAndConfirmTransaction = 'sendAndConfirmTransaction',
}

export const SolDataAccountStruct = object({
...KeyringAccountStruct.schema,

/**
* Account address.
*/
address: SolAddressStruct,

/**
* Account type.
*/
type: literal(`${SolAccountType.DataAccount}`),

/**
* Account supported methods.
*/
methods: array(enums([`${SolMethod.SendAndConfirmTransaction}`])),
});

export type SolDataAccount = Infer<typeof SolDataAccountStruct>;

0 comments on commit c60ad8b

Please sign in to comment.