Skip to content

Commit

Permalink
Merge pull request #530 from fmorency/fix-ledger
Browse files Browse the repository at this point in the history
fix: ledger wallet
  • Loading branch information
pyramation authored Jan 14, 2025
2 parents 4c801e6 + 2873855 commit 618e36f
Show file tree
Hide file tree
Showing 8 changed files with 94 additions and 30 deletions.
12 changes: 7 additions & 5 deletions wallets/ledger/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,14 @@
},
"dependencies": {
"@cosmos-kit/core": "^2.15.1",
"@ledgerhq/hw-app-cosmos": "^6.28.1",
"@ledgerhq/hw-transport-webhid": "^6.27.15",
"@ledgerhq/hw-transport-webusb": "^6.27.15"
"@ledgerhq/hw-app-cosmos": "^6.30.4",
"@ledgerhq/hw-transport-webhid": "^6.30.0",
"@ledgerhq/hw-transport-webusb": "^6.29.4"
},
"peerDependencies": {
"@cosmjs/amino": ">=0.32.3",
"@cosmjs/proto-signing": ">=0.32.3"
"@cosmjs/amino": ">=0.32.4",
"@cosmjs/crypto": ">=0.32.4",
"@cosmjs/encoding": ">=0.32.4",
"@cosmjs/proto-signing": ">=0.32.4"
}
}
2 changes: 1 addition & 1 deletion wallets/ledger/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export * from './constant';
export * from './ledger';
export * from './web-usb-hid/registry';
export * from './constant';
4 changes: 2 additions & 2 deletions wallets/ledger/src/web-usb-hid/chain-wallet.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { ChainRecord, ChainWalletBase, Wallet } from '@cosmos-kit/core';

export class LedgerChianWallet extends ChainWalletBase {
export class LedgerChainWallet extends ChainWalletBase {
constructor(walletInfo: Wallet, chainInfo: ChainRecord) {
super(walletInfo, chainInfo);
}
}
}
44 changes: 40 additions & 4 deletions wallets/ledger/src/web-usb-hid/client.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { StdSignDoc } from '@cosmjs/amino';
import {
encodeSecp256k1Signature,
OfflineAminoSigner,
StdSignDoc,
} from '@cosmjs/amino';
import { sortedJsonStringify } from '@cosmjs/amino/build/signdoc';
import { Secp256k1Signature } from '@cosmjs/crypto';
import { fromHex } from '@cosmjs/encoding';
import { Algo } from '@cosmjs/proto-signing';
import { WalletClient } from '@cosmos-kit/core';
import { SignType, WalletClient } from '@cosmos-kit/core';
import Cosmos from '@ledgerhq/hw-app-cosmos';

import { ChainIdToBech32Prefix, getCosmosApp, getCosmosPath } from './utils';

export class LedgerClient implements WalletClient {
client: Cosmos;

Expand Down Expand Up @@ -39,16 +47,44 @@ export class LedgerClient implements WalletClient {
username: username ?? path,
address,
algo: 'secp256k1' as Algo,
pubkey: new TextEncoder().encode(publicKey),
pubkey: fromHex(publicKey),
isNanoLedger: true,
};
}

getOfflineSigner(chainId: string, preferredSignType?: SignType) {
// Ledger doesn't support direct sign, only Amino sign
if (preferredSignType === 'direct') {
throw new Error('Unsupported sign type: direct');
}
return this.getOfflineSignerAmino(chainId);
}

getOfflineSignerAmino(chainId: string): OfflineAminoSigner {
return {
getAccounts: async () => {
return [await this.getAccount(chainId)];
},
signAmino: async (_signerAddress, signDoc) => {
const { pubkey } = await this.getAccount(chainId);
const { signature: derSignature } = await this.sign(signDoc); // The signature is in DER format
const signature = Secp256k1Signature.fromDer(derSignature); // Convert the DER signature to fixed length (64 bytes)
return {
signed: signDoc,
signature: encodeSecp256k1Signature(
pubkey,
signature.toFixedLength()
),
};
},
};
}

async sign(signDoc: StdSignDoc, accountIndex = 0) {
if (!this.client) await this.initClient();
return await this.client.sign(
getCosmosPath(accountIndex),
JSON.stringify(signDoc)
sortedJsonStringify(signDoc) // signDoc MUST be serialized in lexicographical key order
);
}
}
8 changes: 4 additions & 4 deletions wallets/ledger/src/web-usb-hid/main-wallet.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { EndpointOptions, Wallet } from '@cosmos-kit/core';
import { MainWalletBase } from '@cosmos-kit/core';
import { LedgerChianWallet } from './chain-wallet';
import { EndpointOptions, MainWalletBase, Wallet } from '@cosmos-kit/core';

import { LedgerChainWallet } from './chain-wallet';
import { LedgerClient } from './client';
import { TransportType } from './utils';

Expand All @@ -11,7 +11,7 @@ export class LedgerMainWallet extends MainWalletBase {
preferredEndpoints?: EndpointOptions['endpoints'],
transportType: TransportType = 'WebUSB'
) {
super(walletInfo, LedgerChianWallet);
super(walletInfo, LedgerChainWallet);
this.preferredEndpoints = preferredEndpoints;
this.transportType = transportType;
}
Expand Down
1 change: 1 addition & 0 deletions wallets/ledger/src/web-usb-hid/registry.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Wallet } from '@cosmos-kit/core';

import { ICON } from '../constant';

export const LedgerInfo: Wallet = {
Expand Down
22 changes: 11 additions & 11 deletions wallets/ledger/src/web-usb-hid/utils.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
import { chains } from 'chain-registry'
import Cosmos from "@ledgerhq/hw-app-cosmos";
import Cosmos from '@ledgerhq/hw-app-cosmos';
import TransportWebHID from '@ledgerhq/hw-transport-webhid';
import TransportWebUSB from '@ledgerhq/hw-transport-webusb';
import TransportWebHID from '@ledgerhq/hw-transport-webhid'
import { chains } from 'chain-registry';

export type TransportType = 'WebUSB' | 'WebHID'
export type TransportType = 'WebUSB' | 'WebHID';

export async function getCosmosApp(type: TransportType = 'WebUSB') {
if (type === 'WebUSB') {
return new Cosmos(await TransportWebUSB.create())
return new Cosmos(await TransportWebUSB.create());
}
if (type === 'WebHID') {
return new Cosmos(await TransportWebHID.create())
return new Cosmos(await TransportWebHID.create());
}
throw new Error(`Unknown transport type: ${type}`)
throw new Error(`Unknown transport type: ${type}`);
}

export function getCosmosPath(accountIndex = 0) {
return `44'/118'/${accountIndex}'/0/0`
return `44'/118'/${accountIndex}'/0/0`;
}

export const ChainIdToBech32Prefix = {} as { [k: string]: string }
export const ChainIdToBech32Prefix = {} as { [k: string]: string };
for (const chain of chains) {
ChainIdToBech32Prefix[chain.chain_id] = chain.bech32_prefix
}
ChainIdToBech32Prefix[chain.chain_id] = chain.bech32_prefix;
}
31 changes: 28 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -18024,7 +18024,16 @@ string-length@^4.0.1:
char-regex "^1.0.2"
strip-ansi "^6.0.0"

"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"

"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
Expand Down Expand Up @@ -18140,7 +18149,7 @@ stringify-entities@^4.0.0:
character-entities-html4 "^2.0.0"
character-entities-legacy "^3.0.0"

"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
Expand Down Expand Up @@ -18168,6 +18177,13 @@ strip-ansi@^5.1.0:
dependencies:
ansi-regex "^4.1.0"

strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"

strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"
Expand Down Expand Up @@ -19723,7 +19739,7 @@ wordwrap@^1.0.0:
resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb"
integrity sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==

"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0":
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
Expand All @@ -19741,6 +19757,15 @@ wrap-ansi@^6.0.1, wrap-ansi@^6.2.0:
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"

wrap-ansi@^8.1.0:
version "8.1.0"
resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"
Expand Down

0 comments on commit 618e36f

Please sign in to comment.