Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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',
'TPAe77oEGDLXuNjJhTyYeo5vMqLYdE3GN8U',
])('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 declare 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>;
8 changes: 7 additions & 1 deletion 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 Expand Up @@ -95,5 +101,5 @@ export function transformAccount(
: accountToTransform;

// We still assert that the converted account is valid according to their account's type.
return assertKeyringAccount(account);
return assertKeyringAccount(account as { type: KeyringAccountType });
Comment thread
zone-live marked this conversation as resolved.
Outdated
}
10 changes: 8 additions & 2 deletions packages/keyring-snap-bridge/src/migrations/v1.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
BtcScope,
EthScope,
SolScope,
TrxScope,
TrxAccountType,
} from '@metamask/keyring-api';
import type { CaipChainId, KeyringAccount } from '@metamask/keyring-api';
import type { CaipChainId, KeyringAccount, KeyringAccountType } from '@metamask/keyring-api';

Check failure on line 11 in packages/keyring-snap-bridge/src/migrations/v1.ts

View workflow job for this annotation

GitHub Actions / Build, lint, and test / Lint (22.x)

Replace `·CaipChainId,·KeyringAccount,·KeyringAccountType·` with `⏎··CaipChainId,⏎··KeyringAccount,⏎··KeyringAccountType,⏎`
import { isBtcMainnetAddress } from '@metamask/keyring-utils';
import { is } from '@metamask/superstruct';

Expand Down Expand Up @@ -69,6 +71,10 @@
// 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 Expand Up @@ -102,7 +108,7 @@

// For all other non-EVM and ERC4337 Snap accounts, the `scopes` is required, and
// each `*AccountStruct` should assert that automatically.
return assertKeyringAccount(accountV1);
return assertKeyringAccount(accountV1 as { type: KeyringAccountType });
Comment thread
zone-live marked this conversation as resolved.
Outdated
}

/**
Expand Down
Loading