Skip to content
Merged
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
9 changes: 9 additions & 0 deletions packages/keyring-api/src/api/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,13 @@ export enum SolAccountType {
DataAccount = 'solana:data-account',
}

/**
* Supported Tron account types.
*/
export enum TrxAccountType {
Eoa = 'tron:eoa',
}

/**
* A generic account type. It can be used to represent any account type that is
* not covered by the other account types. It only applies to non-EVM chains.
Expand All @@ -49,6 +56,7 @@ export type KeyringAccountType =
| `${BtcAccountType.P2wpkh}`
| `${BtcAccountType.P2tr}`
| `${SolAccountType.DataAccount}`
| `${TrxAccountType.Eoa}`
| `${AnyAccountType.Account}`;

/**
Expand All @@ -75,6 +83,7 @@ export const KeyringAccountStruct = object({
`${BtcAccountType.P2wpkh}`,
`${BtcAccountType.P2tr}`,
`${SolAccountType.DataAccount}`,
`${TrxAccountType.Eoa}`,
`${AnyAccountType.Account}`,
]),

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 * from './sol';
export * from './eth';
export * from './trx';
export type * from './contexts';
export * from './rpc';
export * from './events';
10 changes: 10 additions & 0 deletions packages/keyring-api/src/trx/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// istanbul ignore file

/**
* Scopes for TRON account type. See {@link KeyringAccount.scopes}.
*/
export enum TrxScope {
Mainnet = 'tron:728126428',
Nile = 'tron:3448148188',
Shasta = 'tron:2494104990',
}
2 changes: 2 additions & 0 deletions packages/keyring-api/src/trx/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './constants';
export * from './types';
8 changes: 8 additions & 0 deletions packages/keyring-api/src/trx/types.test-d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { Extends } from '@metamask/keyring-utils';
import { expectTrue } from '@metamask/keyring-utils';

import type { TrxEoaAccount } from './types';
import type { KeyringAccount } from '../api';

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

describe('types', () => {
describe('TrxAddressStruct', () => {
it.each([
'TRjE1H8dxypKM1NZRdysbs9wo7huR4bdNz',
'TGFCAXqwaBofiLZPWEp5rnin5LKDTKYpct',
])('is valid address: %s', (address) => {
expect(() => TrxAddressStruct.assert(address)).not.toThrow();
});

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

import {
CaipChainIdStruct,
KeyringAccountStruct,
TrxAccountType,
} from '../api';

/**
* TRON addresses are Base58-encoded strings that are exactly 34 characters long
* and start with the letter 'T'.
*/
export const TrxAddressStruct = definePattern(
'TrxAddress',
/^T[1-9A-HJ-NP-Za-km-z]{33}$/iu,
);
Comment thread
zone-live marked this conversation as resolved.

/**
* Supported TRON methods.
*/
export enum TrxMethod {
SignMessageV2 = 'signMessageV2',
VerifyMessageV2 = 'verifyMessageV2',
}

export const TrxEoaAccountStruct = object({
...KeyringAccountStruct.schema,
/**
* Account address.
*/
address: TrxAddressStruct,
/**
* Account type.
*/
type: literal(`${TrxAccountType.Eoa}`),
/**
* Account supported scopes (CAIP-2 chain IDs).
*/
scopes: nonempty(array(CaipChainIdStruct)),
/**
* Account supported methods.
*/
methods: array(enums(Object.values(TrxMethod))),
});

export type TrxEoaAccount = Infer<typeof TrxEoaAccountStruct>;
17 changes: 15 additions & 2 deletions packages/keyring-internal-api/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import {
EthEoaAccountStruct,
EthErc4337AccountStruct,
SolDataAccountStruct,
TrxAccountType,
TrxEoaAccountStruct,
} from '@metamask/keyring-api';
import { exactOptional, object } from '@metamask/keyring-utils';
import type { Infer, Struct } from '@metamask/superstruct';
Expand All @@ -23,7 +25,8 @@ import { boolean, string, number } from '@metamask/superstruct';
export type InternalAccountType =
| EthAccountType
| BtcAccountType
| SolAccountType;
| SolAccountType
| TrxAccountType;

export const InternalAccountMetadataStruct = object({
metadata: object({
Expand Down Expand Up @@ -79,6 +82,11 @@ export const InternalSolDataAccountStruct = object({
...InternalAccountMetadataStruct.schema,
});

export const InternalTrxEoaAccountStruct = object({
...TrxEoaAccountStruct.schema,
...InternalAccountMetadataStruct.schema,
});

export type InternalEthEoaAccount = Infer<typeof InternalEthEoaAccountStruct>;

export type InternalEthErc4337Account = Infer<
Expand All @@ -99,6 +107,8 @@ export type InternalBtcP2trAccount = Infer<typeof InternalBtcP2trAccountStruct>;

export type InternalSolDataAccount = Infer<typeof InternalSolDataAccountStruct>;

export type InternalTrxEoaAccount = Infer<typeof InternalTrxEoaAccountStruct>;

export const InternalAccountStructs: Record<
string,
| Struct<InternalEthEoaAccount>
Expand All @@ -108,6 +118,7 @@ export const InternalAccountStructs: Record<
| Struct<InternalBtcP2wpkhAccount>
| Struct<InternalBtcP2trAccount>
| Struct<InternalSolDataAccount>
| Struct<InternalTrxEoaAccount>
> = {
[`${EthAccountType.Eoa}`]: InternalEthEoaAccountStruct,
[`${EthAccountType.Erc4337}`]: InternalEthErc4337AccountStruct,
Expand All @@ -116,6 +127,7 @@ export const InternalAccountStructs: Record<
[`${BtcAccountType.P2wpkh}`]: InternalBtcP2wpkhAccountStruct,
[`${BtcAccountType.P2tr}`]: InternalBtcP2trAccountStruct,
[`${SolAccountType.DataAccount}`]: InternalSolDataAccountStruct,
[`${TrxAccountType.Eoa}`]: InternalTrxEoaAccountStruct,
};

export type InternalAccountTypes =
Expand All @@ -125,7 +137,8 @@ export type InternalAccountTypes =
| InternalBtcP2shAccount
| InternalBtcP2wpkhAccount
| InternalBtcP2trAccount
| InternalSolDataAccount;
| InternalSolDataAccount
| InternalTrxEoaAccount;

export const InternalAccountStruct = object({
...KeyringAccountStruct.schema,
Expand Down
21 changes: 21 additions & 0 deletions packages/keyring-snap-bridge/src/SnapKeyring.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ import {
KeyringRpcMethod,
CreateAccountRequestStruct,
AnyAccountType,
TrxScope,
TrxMethod,
TrxAccountType,
} from '@metamask/keyring-api';
import type { JsonRpcRequest } from '@metamask/keyring-utils';
import type { HandleSnapRequest } from '@metamask/snaps-controllers';
Expand Down Expand Up @@ -196,6 +199,15 @@ describe('SnapKeyring', () => {
type: SolAccountType.DataAccount,
};

const trxEoaAccount = {
id: '890ee179-5ab5-449d-9c25-34e12c1ada67',
address: 'TJRabPrwbZy45sbavfcjinPJC18kjpRTv8',
options: {},
methods: [...Object.values(TrxMethod)],
scopes: [TrxScope.Mainnet, TrxScope.Nile, TrxScope.Shasta],
type: TrxAccountType.Eoa,
};

// This is an example generic chain, its scope and address are bogus.
const anyGenericAccount: KeyringAccount = {
id: '6131c205-26b5-40a8-9626-4761423bb1d5',
Expand Down Expand Up @@ -241,6 +253,7 @@ describe('SnapKeyring', () => {
btcAccountP2sh,
btcAccountP2tr,
solDataAccount,
trxEoaAccount,
anyGenericAccount,
] as const;

Expand Down Expand Up @@ -1167,6 +1180,7 @@ describe('SnapKeyring', () => {
btcAccountP2sh.address,
btcAccountP2tr.address,
solDataAccount.address,
trxEoaAccount.address,
anyGenericAccount.address,
]);
});
Expand All @@ -1186,6 +1200,7 @@ describe('SnapKeyring', () => {
btcAccountP2sh.address,
btcAccountP2tr.address,
solDataAccount.address,
trxEoaAccount.address,
anyGenericAccount.address,
]);
});
Expand Down Expand Up @@ -1366,6 +1381,7 @@ describe('SnapKeyring', () => {
btcAccountP2sh.address,
btcAccountP2tr.address,
solDataAccount.address,
trxEoaAccount.address,
anyGenericAccount.address,
]);
});
Expand All @@ -1384,6 +1400,7 @@ describe('SnapKeyring', () => {
[btcAccountP2sh.id]: { account: btcAccountP2sh, snapId },
[btcAccountP2tr.id]: { account: btcAccountP2tr, snapId },
[solDataAccount.id]: { account: solDataAccount, snapId },
[trxEoaAccount.id]: { account: trxEoaAccount, snapId },
[anyGenericAccount.id]: { account: anyGenericAccount, snapId },
},
};
Expand Down Expand Up @@ -1437,6 +1454,7 @@ describe('SnapKeyring', () => {
btcAccountP2tr,
btcTestnetAccount,
solDataAccount,
trxEoaAccount,
])('migrates accounts v1: %s', async (expectedAccount: KeyringAccount) => {
// A v1 account has no scopes, so remove it.
const state = {
Expand All @@ -1459,6 +1477,7 @@ describe('SnapKeyring', () => {
btcAccount,
btcTestnetAccount,
solDataAccount,
trxEoaAccount,
anyGenericAccount,
])(
'migrates v2 accounts to v1 accounts is noop: %s',
Expand Down Expand Up @@ -2118,6 +2137,7 @@ describe('SnapKeyring', () => {
accounts[7].address,
accounts[8].address,
accounts[9].address,
accounts[10].address,
]);
});

Expand All @@ -2135,6 +2155,7 @@ describe('SnapKeyring', () => {
accounts[7].address,
accounts[8].address,
accounts[9].address,
accounts[10].address,
]);
expect(console.error).toHaveBeenCalledWith(
"Account '0xc728514df8a7f9271f4b7a4dd2aa6d2d723d3ee3' may not have been removed from snap 'local:snap.mock':",
Expand Down
6 changes: 6 additions & 0 deletions packages/keyring-snap-bridge/src/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
KeyringAccountStruct,
SolAccountType,
SolDataAccountStruct,
TrxAccountType,
TrxEoaAccountStruct,
} from '@metamask/keyring-api';
import { assert, omit, type Infer } from '@metamask/superstruct';

Expand Down Expand Up @@ -66,6 +68,10 @@ export function assertKeyringAccount<
assert(account, EthEoaAccountStruct);
return account;
}
case TrxAccountType.Eoa: {
assert(account, TrxEoaAccountStruct);
return account;
}
case AnyAccountType.Account: {
assert(account, KeyringAccountStruct);
return account;
Expand Down
6 changes: 6 additions & 0 deletions packages/keyring-snap-bridge/src/migrations/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import {
BtcScope,
EthScope,
SolScope,
TrxScope,
TrxAccountType,
} from '@metamask/keyring-api';
import type { CaipChainId, KeyringAccount } from '@metamask/keyring-api';
import { isBtcMainnetAddress } from '@metamask/keyring-utils';
Expand Down Expand Up @@ -69,6 +71,10 @@ export function getScopesForAccountV1(
// Solana account supports multiple chains.
return [SolScope.Mainnet, SolScope.Testnet, SolScope.Devnet];
}
case TrxAccountType.Eoa: {
// TRON account supports multiple chains.
return [TrxScope.Mainnet, TrxScope.Nile, TrxScope.Shasta];
}
default:
// We re-use EOA scopes if we don't know what to do for now.
return [EthScope.Eoa];
Expand Down
Loading