Skip to content

[Issue-4365] Extension - Update ledger-substrate-js library #4372

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 12 commits into
base: subwallet-dev
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@
"@subwallet/react-ui": "5.1.2-b79",
"@subwallet/ui-keyring": "^0.1.12",
"@types/bn.js": "^5.1.6",
"@zondax/ledger-substrate": "1.0.1",
"@zondax/ledger-substrate": "1.1.2",
"babel-core": "^7.0.0-bridge.0",
"babel-jest": "^29.3.1",
"browserify-sign": "^4.2.2",
Expand Down
10 changes: 9 additions & 1 deletion packages/extension-base/src/background/KoniTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -262,13 +262,13 @@
externalUrl?: string;
rarity?: string;
description?: string;
properties?: Record<any, any> | null;

Check warning on line 265 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type

Check warning on line 265 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type
}

interface NftItemExtraInfo {
type?: _AssetType.ERC721 | _AssetType.PSP34 | RMRK_VER; // for sending
rmrk_ver?: RMRK_VER;
onChainOption?: any; // for sending PSP-34 tokens, should be done better

Check warning on line 271 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type
assetHubType?: AssetHubNftType // for sending assetHub nft. There're 2 types nft
}

Expand Down Expand Up @@ -599,7 +599,7 @@
[ExtrinsicType.STAKING_COMPOUNDING]: RequestTuringStakeCompound,
[ExtrinsicType.STAKING_CANCEL_COMPOUNDING]: RequestTuringCancelStakeCompound,
[ExtrinsicType.STAKING_CANCEL_UNSTAKE]: RequestStakeCancelWithdrawal,
[ExtrinsicType.STAKING_POOL_WITHDRAW]: any,

Check warning on line 602 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type

// Yield
[ExtrinsicType.JOIN_YIELD_POOL]: RequestYieldStepSubmit,
Expand Down Expand Up @@ -630,9 +630,9 @@
[ExtrinsicType.CLAIM_BRIDGE]: RequestClaimBridge

[ExtrinsicType.EVM_EXECUTE]: TransactionConfig,
[ExtrinsicType.CROWDLOAN]: any,

Check warning on line 633 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type
[ExtrinsicType.SWAP]: SwapTxData
[ExtrinsicType.UNKNOWN]: any

Check warning on line 635 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type
}

export enum ExtrinsicStatus {
Expand Down Expand Up @@ -731,7 +731,7 @@
// : T extends ExtrinsicType.MINT_VDOT
// ? Pick<SubmitBifrostLiquidStaking, 'rewardTokenSlug' | 'estimatedAmountReceived'>
// : undefined;
export interface TransactionHistoryItem<ET extends ExtrinsicType = ExtrinsicType.TRANSFER_BALANCE> {

Check warning on line 734 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

'ET' is defined but never used
origin?: 'app' | 'migration' | 'subsquid' | 'subscan', // 'app' or history source
callhash?: string,
signature?: string,
Expand All @@ -756,7 +756,7 @@
tip?: AmountData,
fee?: AmountData,
explorerUrl?: string,
additionalInfo?: any,

Check warning on line 759 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type
startBlock?: number,
nonce?: number,
addressPrefix?: number,
Expand Down Expand Up @@ -850,6 +850,7 @@
isEthereum: boolean;
isGeneric: boolean;
isLedgerRecovery?: boolean;
isSubstrateECDSA?: boolean;
}

export interface RequestAccountCreateHardwareV2 extends CreateHardwareAccountItem {
Expand Down Expand Up @@ -989,12 +990,12 @@
recipientAddress: string,

nftItemName?: string, // Use for confirmation view only
params: Record<string, any>,

Check warning on line 993 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type
nftItem: NftItem
}

export interface EvmNftTransaction extends ValidateTransactionResponse {
tx: Record<string, any> | null;

Check warning on line 998 in packages/extension-base/src/background/KoniTypes.ts

View workflow job for this annotation

GitHub Actions / Build Development Preview

Unexpected any. Specify a different type
}

export interface ValidateNetworkResponse {
Expand Down Expand Up @@ -1433,10 +1434,17 @@
isRecovery?: boolean;
/** Slip44 in the derivation path */
slip44: number;
/** Signature substrate scheme */
scheme?: LEDGER_SCHEME;
}

export const enum LEDGER_SCHEME {
ED25519 = 0,
ECDSA = 2
}

export interface MigrationLedgerNetwork extends Omit<LedgerNetwork, 'isGeneric' | 'isEthereum' | 'isDevMode' | 'icon' > {
ss58_addr_type: number
ss58_addr_type: number;
}

/// Qr Sign
Expand Down
8 changes: 5 additions & 3 deletions packages/extension-base/src/background/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,9 @@ export type AccountAuthType = 'substrate' | 'evm' | 'ton' | 'cardano';
export interface RequestAuthorizeTab {
origin: string;
accountAuthTypes?: AccountAuthType[];
allowedAccounts?: string[]
reConfirm?: boolean
allowedAccounts?: string[];
reConfirm?: boolean;
isSubstrateConnector?: boolean;
}

export interface RequestAuthorizeApprove {
Expand Down Expand Up @@ -245,7 +246,8 @@ export interface RequestAccountBatchExport {

export interface RequestAccountList {
anyType?: boolean;
accountAuthType?: AccountAuthType
accountAuthType?: AccountAuthType;
isSubstrateConnector?: boolean;
}

export interface RequestAccountSubscribe {
Expand Down
20 changes: 19 additions & 1 deletion packages/extension-base/src/core/logic-validation/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -327,7 +327,7 @@ export async function validationEvmDataTransactionMiddleware (koni: KoniState, u
const errors: Error[] = payload.errors || [];
let estimateGas = '';
const transactionParams = payload.payloadAfterValidated as EvmSendTransactionParams;
const { address: fromAddress, networkKey } = payload;
const { address: fromAddress, networkKey, pair: pair_ } = payload;
const evmApi = koni.getEvmApi(networkKey || '');
const web3 = evmApi?.api;

Expand Down Expand Up @@ -371,6 +371,16 @@ export async function validationEvmDataTransactionMiddleware (koni: KoniState, u
handleError('the sender address must be the ethereum address type');
}

const pair = pair_ || keyring.getPair(fromAddress);

if (!pair) {
handleError('Not found address to sign');
}

if (pair_?.meta.isSubstrateECDSA) {
handleError('Substrate account can not send this transaction');
}

if (transaction.to && !isEthereumAddress(transaction.to)) {
handleError('invalid recipient address');
}
Expand Down Expand Up @@ -535,6 +545,14 @@ export async function validationEvmSignMessageMiddleware (koni: KoniState, url:

const pair = pair_ || keyring.getPair(address);

if (!pair) {
handleError('Not found address to sign');
}

if (pair_?.meta.isSubstrateECDSA) {
handleError('Substrate account can not sign this message');
}

if (method) {
if (['eth_sign', 'personal_sign', 'eth_signTypedData', 'eth_signTypedData_v1', 'eth_signTypedData_v3', 'eth_signTypedData_v4'].indexOf(method) < 0) {
handleError('Unsupported action');
Expand Down
3 changes: 2 additions & 1 deletion packages/extension-base/src/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
import { _ChainInfo } from '@subwallet/chain-list/types';
import { AccountJson } from '@subwallet/extension-base/types';

export type LedgerMustCheckType = 'polkadot' | 'migration' | 'unnecessary';
export type LedgerMustCheckType = 'polkadot' | 'migration' | 'polkadot_ecdsa' | 'unnecessary';

export enum ValidationCondition {
IS_NOT_NULL = 'IS_NOT_NULL',
IS_ADDRESS = 'IS_ADDRESS',
IS_VALID_ADDRESS_FOR_ECOSYSTEM = 'IS_VALID_ADDRESS_FOR_ECOSYSTEM',
IS_VALID_SUBSTRATE_ADDRESS_FORMAT = 'IS_VALID_SUBSTRATE_ADDRESS_FORMAT',
IS_VALID_EVM_ADDRESS_FORMAT = 'IS_VALID_EVM_ADDRESS_FORMAT',
IS_VALID_TON_ADDRESS_FORMAT = 'IS_VALID_TON_ADDRESS_FORMAT',
IS_VALID_CARDANO_ADDRESS_FORMAT = 'IS_VALID_CARDANO_ADDRESS_FORMAT',
IS_NOT_DUPLICATE_ADDRESS = 'IS_NOT_DUPLICATE_ADDRESS',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1405,7 +1405,7 @@ export default class KoniExtension {
}

private async makeTransfer (inputData: RequestSubmitTransfer): Promise<SWTransactionResponse> {
const { chain, feeCustom, feeOption, from, to, tokenPayFeeSlug, tokenSlug, transferAll, transferBounceable, value } = inputData;
const { chain, feeCustom, feeOption, from, isSubstrateTransaction, to, tokenPayFeeSlug, tokenSlug, transferAll, transferBounceable, value } = inputData;
const transferTokenInfo = this.#koniState.chainService.getAssetBySlug(tokenSlug);
const errors = validateTransferRequest(transferTokenInfo, from, to, value, transferAll);
const warnings: TransactionWarning[] = [];
Expand All @@ -1427,7 +1427,7 @@ export default class KoniExtension {
const transferTokenAvailable = await this.getAddressTransferableBalance({ address: from, networkKey: chain, token: tokenSlug, extrinsicType });

try {
if (isEthereumAddress(from) && isEthereumAddress(to) && _isTokenTransferredByEvm(transferTokenInfo)) {
if (isEthereumAddress(from) && isEthereumAddress(to) && _isTokenTransferredByEvm(transferTokenInfo) && !isSubstrateTransaction) {
chainType = ChainType.EVM;
const txVal: string = transferAll ? transferTokenAvailable.value : (value || '0');
const evmApi = this.#koniState.getEvmApi(chain);
Expand Down
13 changes: 9 additions & 4 deletions packages/extension-base/src/koni/background/handlers/Tabs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ interface AccountSub {
url: string;
}

function transformAccountsV2 (accounts: SubjectInfo, anyType = false, authInfo?: AuthUrlInfo, accountAuthTypes?: AccountAuthType[]): InjectedAccount[] {
function transformAccountsV2 (accounts: SubjectInfo, anyType = false, authInfo?: AuthUrlInfo, accountAuthTypes?: AccountAuthType[], isSubstrateConnector?: boolean): InjectedAccount[] {
const accountSelected = authInfo
? (
authInfo.isAllowed
Expand Down Expand Up @@ -80,6 +80,11 @@ function transformAccountsV2 (accounts: SubjectInfo, anyType = false, authInfo?:
return false;
}

// If the dApp has not connected to the Substrate type yet, we do not return Substrate ECDSA accounts.
if (type === 'ethereum' && json.meta.isSubstrateECDSA && !isSubstrateConnector) {
return false;
}

return true;
} else {
return true;
Expand Down Expand Up @@ -316,7 +321,7 @@ export default class KoniTabs {
return authList[shortenUrl];
}

private async accountsListV2 (url: string, { accountAuthType, anyType }: RequestAccountList): Promise<InjectedAccount[]> {
private async accountsListV2 (url: string, { accountAuthType, anyType, isSubstrateConnector }: RequestAccountList): Promise<InjectedAccount[]> {
const authInfo = await this.getAuthInfo(url);

const accountAuthTypes: AccountAuthType[] = [];
Expand All @@ -341,7 +346,7 @@ export default class KoniTabs {
}
}

return transformAccountsV2(this.#koniState.keyringService.context.pairs, anyType, authInfo, accountAuthTypes);
return transformAccountsV2(this.#koniState.keyringService.context.pairs, anyType, authInfo, accountAuthTypes, isSubstrateConnector);
}

// TODO: Update logic
Expand Down Expand Up @@ -369,7 +374,7 @@ export default class KoniTabs {

const accounts = this.#koniState.keyringService.context.pairs;

return cb(transformAccountsV2(accounts, false, authInfo, accountAuthTypes));
return cb(transformAccountsV2(accounts, false, authInfo, accountAuthTypes, true));
})
.catch(console.error);
}),
Expand Down
2 changes: 1 addition & 1 deletion packages/extension-base/src/page/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export function sendMessage<TMessageType extends MessageTypes> (message: TMessag
export async function enable (origin: string, opt?: AuthRequestOption): Promise<Injected> {
const accountAuthTypes: AccountAuthType[] = opt?.accountAuthType === 'both' ? ['substrate', 'evm'] : [opt?.accountAuthType || 'substrate'];

await sendMessage('pub(authorize.tabV2)', { origin, accountAuthTypes });
await sendMessage('pub(authorize.tabV2)', { origin, accountAuthTypes, isSubstrateConnector: true });

return new Injected(sendMessage);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/extension-base/src/page/substrate/Accounts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default class Accounts implements InjectedAccounts {
}

public get (anyType?: boolean): Promise<InjectedAccount[]> {
return sendRequest('pub(accounts.listV2)', { anyType });
return sendRequest('pub(accounts.listV2)', { anyType, isSubstrateConnector: true });
}

public subscribe (cb: (accounts: InjectedAccount[]) => unknown): Unsubcall {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ export function _isChainEvmCompatible (chainInfo: _ChainInfo) {
return !!chainInfo.evmInfo;
}

export function _isSubstrateEvmCompatibleChain (chainInfo: _ChainInfo) {
return !!chainInfo.evmInfo && !!chainInfo.substrateInfo;
}

export function _isChainBitcoinCompatible (chainInfo: _ChainInfo) {
return !!chainInfo.bitcoinInfo;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

import { RequestAccountCreateHardwareMultiple, RequestAccountCreateHardwareV2 } from '@subwallet/extension-base/background/KoniTypes';
import { ALL_ACCOUNT_KEY } from '@subwallet/extension-base/constants';
import { KeyringPair, KeyringPair$Meta } from '@subwallet/keyring/types';
import { KeypairType, KeyringPair, KeyringPair$Meta } from '@subwallet/keyring/types';
import { keyring } from '@subwallet/ui-keyring';
import { t } from 'i18next';

Expand Down Expand Up @@ -86,7 +86,7 @@ export class AccountLedgerHandler extends AccountBaseHandler {
const pairs: KeyringPair[] = [];

for (const account of accounts) {
const { accountIndex, address, addressOffset, genesisHash, hardwareType, isEthereum, isGeneric, isLedgerRecovery, name, originGenesisHash } = account;
const { accountIndex, address, addressOffset, genesisHash, hardwareType, isEthereum, isGeneric, isLedgerRecovery, isSubstrateECDSA, name, originGenesisHash } = account;

const baseMeta: KeyringPair$Meta = {
name,
Expand All @@ -96,10 +96,16 @@ export class AccountLedgerHandler extends AccountBaseHandler {
genesisHash,
originGenesisHash,
isGeneric,
isLedgerRecovery
isLedgerRecovery,
isSubstrateECDSA
};

const type = isEthereum ? 'ethereum' : 'sr25519';
let type: KeypairType = 'sr25519';

if (isEthereum || isSubstrateECDSA) {
type = 'ethereum';
}

const pair = keyring.keyring.createFromAddress(
address,
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ export default class AuthRequestHandler {
value.currentNetworkMap = {};
}

if (value.accountAuthTypes?.includes('substrate')) {
value.isSubstrateConnector = true;
}

acc[key] = { ...value };

return acc;
Expand Down Expand Up @@ -97,6 +101,22 @@ export default class AuthRequestHandler {
return addressList.reduce((addressList, v) => ({ ...addressList, [v]: value }), {});
}

private getEcdsaAddressList (): Set<string> {
const addressList = Object.keys(this.keyringService.context.pairs);
const pairs = this.keyringService.context.pairs;
const ecdsaAddressList = new Set<string>();

addressList.forEach((address) => {
const pair = pairs[address];

if (pair && pair.json.meta.isSubstrateECDSA) {
ecdsaAddressList.add(address);
}
});

return ecdsaAddressList;
}

public get numAuthRequestsV2 (): number {
return Object.keys(this.#authRequestsV2).length;
}
Expand Down Expand Up @@ -193,6 +213,7 @@ export default class AuthRequestHandler {

private authCompleteV2 = (id: string, url: string, resolve: (result: boolean) => void, reject: (error: Error) => void): Resolver<ResultResolver> => {
const isAllowedMap = this.getAddressList();
const ecdsaAddressList = this.getEcdsaAddressList();

const complete = (result: boolean | Error, cb: () => void, accounts?: string[]) => {
const isAllowed = result === true;
Expand All @@ -212,15 +233,15 @@ export default class AuthRequestHandler {
});
}

const { accountAuthTypes, idStr, request: { allowedAccounts, origin }, url } = this.#authRequestsV2[id];
const { accountAuthTypes, idStr, request: { allowedAccounts, isSubstrateConnector, origin }, url } = this.#authRequestsV2[id];

// Note: accountAuthTypes represents the accountAuthType of this request
// allowedAccounts is a list of connected accounts that exist for this origin during this request.

if (accountAuthTypes.length !== ALL_ACCOUNT_AUTH_TYPES.length) {
const backupAllowed = (allowedAccounts || [])
.filter((a) => {
if (isEthereumAddress(a) && !accountAuthTypes.includes('evm')) {
if (isEthereumAddress(a) && (isSubstrateConnector || !ecdsaAddressList.has(a)) && !accountAuthTypes.includes('evm')) {
return true;
}

Expand Down Expand Up @@ -284,7 +305,8 @@ export default class AuthRequestHandler {
origin,
url,
accountAuthTypes: [...new Set<AccountAuthType>([...accountAuthTypes, ...(existed?.accountAuthTypes || [])])],
currentNetworkMap: existed ? { ...defaultNetworkMap, ...existed.currentNetworkMap } : defaultNetworkMap
currentNetworkMap: existed ? { ...defaultNetworkMap, ...existed.currentNetworkMap } : defaultNetworkMap,
isSubstrateConnector: isSubstrateConnector || existed?.isSubstrateConnector
};

this.setAuthorize(authorizeList, () => {
Expand Down Expand Up @@ -315,6 +337,7 @@ export default class AuthRequestHandler {
const idStr = stripUrl(url);
const isAllowedDappConnectBothType = !!DAPP_CONNECT_BOTH_TYPE_ACCOUNT_URL.find((url_) => url.includes(url_));
let accountAuthTypes = [...new Set<AccountAuthType>(isAllowedDappConnectBothType ? ['evm', 'substrate'] : (request.accountAuthTypes || ['substrate']))];
const isSubstrateConnector = !!request.isSubstrateConnector;

if (!authList) {
authList = {};
Expand Down Expand Up @@ -385,10 +408,11 @@ export default class AuthRequestHandler {
.filter((item) => (item !== ''));

let allowedListByRequestType = [...request.allowedAccounts];
const ecdsaAddressList = this.getEcdsaAddressList();

allowedListByRequestType = accountAuthTypes.reduce<string[]>((list, accountAuthType) => {
if (accountAuthType === 'evm') {
list.push(...allowedListByRequestType.filter((a) => isEthereumAddress(a)));
list.push(...allowedListByRequestType.filter((a) => isEthereumAddress(a) && (isSubstrateConnector || !ecdsaAddressList.has(a))));
} else if (accountAuthType === 'substrate') {
list.push(...allowedListByRequestType.filter((a) => isSubstrateAddress(a)));
} else if (accountAuthType === 'ton') {
Expand Down Expand Up @@ -421,7 +445,8 @@ export default class AuthRequestHandler {
origin,
url,
accountAuthTypes: ALL_ACCOUNT_AUTH_TYPES,
currentNetworkMap: {}
currentNetworkMap: {},
isSubstrateConnector
};

this.setAuthorize(authList);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ export interface AuthUrlInfo {
isAllowedMap: Record<string, boolean>;
currentNetworkMap: Partial<Record<AccountAuthType, string>>;
currentAccount?: string;
isSubstrateConnector?: boolean;
}

export interface AuthUrlInfoNeedMigration extends Omit<AuthUrlInfo, 'accountAuthTypes'> {
Expand Down
3 changes: 3 additions & 0 deletions packages/extension-base/src/types/account/info/keyring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ export interface AccountLedgerData {
availableGenesisHashes?: string[];
/** Is Ledger recovery chain */
isLedgerRecovery?: boolean;
/** Is Ledger substrate ECDSA scheme signature */
isSubstrateECDSA?: boolean;
}

/**
Expand Down Expand Up @@ -113,6 +115,7 @@ export enum AccountSignMode {
QR = 'qr',
LEGACY_LEDGER = 'legacy-ledger',
GENERIC_LEDGER = 'generic-ledger',
ECDSA_SUBSTRATE_LEDGER = 'ecdsa-substrate-ledger',
READ_ONLY = 'readonly',
ALL_ACCOUNT = 'all',
INJECTED = 'injected',
Expand Down
1 change: 1 addition & 0 deletions packages/extension-base/src/types/balance/transfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@ export interface RequestSubmitTransfer extends BaseRequestSign, TransactionFee {
transferAll: boolean;
value: string;
transferBounceable?: boolean;
isSubstrateTransaction?: boolean;
}
Loading
Loading