Skip to content

Commit 90107b0

Browse files
authored
Merge pull request #361 from ava-labs/dev
Fix/ledger v2 (#360)
2 parents de8cd7a + 4a0e4a9 commit 90107b0

File tree

10 files changed

+146
-107
lines changed

10 files changed

+146
-107
lines changed

package.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
"test": "jest --verbose ./tests"
1818
},
1919
"dependencies": {
20-
"@avalabs/avalanche-wallet-sdk": "0.19.0-alpha.3",
20+
"@avalabs/avalanche-wallet-sdk": "0.19.0-alpha.5",
2121
"@avalabs/hw-app-avalanche": "0.6.6",
2222
"@avalabs/vue_components": "0.10.0",
2323
"@ethereumjs/common": "2.0.0",
@@ -29,10 +29,9 @@
2929
"@fortawesome/vue-fontawesome": "0.1.9",
3030
"@ledgerhq/hw-app-eth": "6.29.7",
3131
"@ledgerhq/hw-transport": "6.27.5",
32-
"@ledgerhq/hw-transport-u2f": "5.22.0",
33-
"@ledgerhq/hw-transport-webhid": "^5.51.1",
34-
"@ledgerhq/hw-transport-webusb": "5.22.0",
35-
"@obsidiansystems/hw-app-avalanche": "0.2.2",
32+
"@ledgerhq/hw-transport-u2f": "5.36.0-deprecated",
33+
"@ledgerhq/hw-transport-webhid": "5.51.1",
34+
"@ledgerhq/hw-transport-webusb": "5.51.1",
3635
"@openzeppelin/contracts": "4.7.3",
3736
"@types/big.js": "4.0.5",
3837
"@types/create-hash": "1.2.2",
@@ -110,6 +109,7 @@
110109
"@babel/plugin-proposal-decorators": "^7.10.3",
111110
"@fortawesome/fontawesome-free": "^5.13.0",
112111
"@types/jest": "^26.0.0",
112+
"@types/ledgerhq__hw-transport-u2f": "^4.21.2",
113113
"@types/randomstring": "^1.1.8",
114114
"@typescript-eslint/eslint-plugin": "4.33.0",
115115
"@typescript-eslint/parser": "4.33.0",

src/components/Ledger/LedgerButton.vue

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,24 +14,19 @@
1414
<script lang="ts">
1515
import 'reflect-metadata'
1616
import { Component, Prop, Vue } from 'vue-property-decorator'
17-
// @ts-ignore
1817
import TransportU2F from '@ledgerhq/hw-transport-u2f'
1918
//@ts-ignore
2019
import TransportWebUSB from '@ledgerhq/hw-transport-webusb'
21-
//@ts-ignore
20+
// @ts-ignore
2221
import TransportWebHID from '@ledgerhq/hw-transport-webhid'
2322
// @ts-ignore
2423
import Eth from '@ledgerhq/hw-app-eth'
25-
// @ts-ignore
26-
import AppAvax from '@obsidiansystems/hw-app-avalanche'
27-
import AvalancheApp from '@avalabs/hw-app-avalanche'
2824
import Transport from '@ledgerhq/hw-transport'
2925
3026
import Spinner from '@/components/misc/Spinner.vue'
3127
import LedgerBlock from '@/components/modals/LedgerBlock.vue'
3228
import { LedgerWallet } from '@/js/wallets/LedgerWallet'
3329
import { AVA_ACCOUNT_PATH, LEDGER_ETH_ACCOUNT_PATH } from '@/js/wallets/MnemonicWallet'
34-
import { ILedgerAppConfig } from '@/store/types'
3530
import { LEDGER_EXCHANGE_TIMEOUT } from '@/store/modules/ledger/types'
3631
import ImageDayNight from '@/components/misc/ImageDayNight.vue'
3732
import { getLedgerProvider } from '@avalabs/avalanche-wallet-sdk'
@@ -94,8 +89,7 @@ export default class LedgerButton extends Vue {
9489
throw new Error('')
9590
}
9691
97-
let title = 'Provide Public Keys'
98-
let messages = [
92+
const messages = [
9993
{
10094
title: 'Derivation Path',
10195
value: AVA_ACCOUNT_PATH,
@@ -107,8 +101,9 @@ export default class LedgerButton extends Vue {
107101
]
108102
109103
this.$store.commit('Ledger/openModal', {
110-
title,
104+
title: 'Getting Public Keys',
111105
messages,
106+
isPrompt: false,
112107
})
113108
114109
let wallet = await LedgerWallet.fromTransport(transport)

src/components/modals/LedgerBlock.vue

Lines changed: 13 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
<template>
2-
<modal ref="modal" title="Check Your Ledger Device" :can_close="false">
2+
<modal ref="modal" title="Accessing Ledger Device" :can_close="false">
33
<div class="ledger_block" v-if="isActive">
4-
<p v-if="!isPrompt" style="margin-bottom: 14px !important; font-size: 18px">
4+
<p v-if="isPrompt" style="font-size: 18px">
55
{{ $t('modal.ledger.desc') }}
66
</p>
7+
<p class="alert" v-if="warning">{{ warning }}</p>
8+
79
<p class="message">{{ title }}</p>
810
<p class="message" v-if="info">{{ info }}</p>
911
<template v-else>
@@ -13,17 +15,6 @@
1315
</div>
1416
</template>
1517
<Spinner class="spinner"></Spinner>
16-
<div v-if="duration >= 0 && !isPrompt">
17-
You have
18-
<span
19-
v-bind:class="{
20-
alert: duration >= 0 && duration < 10,
21-
}"
22-
>
23-
{{ duration }}
24-
</span>
25-
seconds remaining.
26-
</div>
2718
</div>
2819
</modal>
2920
</template>
@@ -43,18 +34,15 @@ import { LEDGER_EXCHANGE_TIMEOUT } from '../../store/modules/ledger/types'
4334
},
4435
})
4536
export default class LedgerBlock extends Vue {
46-
duration: number = LEDGER_EXCHANGE_TIMEOUT / 1000
4737
intervalId: ReturnType<typeof setTimeout> | null = null
4838
4939
open() {
5040
// @ts-ignore
5141
this.$refs.modal.open()
52-
this.startTimer()
5342
}
5443
close() {
5544
// @ts-ignore
5645
this.$refs.modal.close()
57-
this.stopTimer()
5846
}
5947
6048
get title(): string {
@@ -77,6 +65,10 @@ export default class LedgerBlock extends Vue {
7765
return this.$store.state.Ledger.isPrompt
7866
}
7967
68+
get warning() {
69+
return this.$store.state.Ledger.warning
70+
}
71+
8072
@Watch('isActive', { immediate: true })
8173
onActive(val: boolean): void {
8274
if (!this.$refs.modal) return
@@ -86,27 +78,16 @@ export default class LedgerBlock extends Vue {
8678
this.close()
8779
}
8880
}
89-
90-
startTimer() {
91-
this.intervalId = setInterval(() => {
92-
this.duration -= 1
93-
if (this.duration <= 0) {
94-
this.duration = 0
95-
}
96-
}, 1000)
97-
}
98-
99-
stopTimer() {
100-
this.duration = LEDGER_EXCHANGE_TIMEOUT / 1000
101-
clearInterval(this.intervalId!)
102-
}
10381
}
10482
</script>
10583
<style scoped lang="scss">
10684
.ledger_block {
10785
pointer-events: none;
10886
padding: 30px;
10987
text-align: center;
88+
display: flex;
89+
flex-direction: column;
90+
align-items: center;
11091
}
11192
11293
.message .title {
@@ -122,7 +103,9 @@ export default class LedgerBlock extends Vue {
122103
.message {
123104
padding: 12px;
124105
color: var(--primary-color);
106+
width: 100%;
125107
margin: 4px 0 !important;
108+
word-break: break-all;
126109
background-color: var(--bg-wallet);
127110
}
128111

src/components/modals/Modal.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ export default class Modal extends Vue {
101101
102102
.modal_body {
103103
width: max-content;
104-
max-width: 90%;
104+
max-width: min(60vw, 600px);
105105
min-height: 30px;
106106
background-color: var(--bg);
107107
box-shadow: 1px 1px 10px rgba(0, 0, 0, 0.5);

src/js/wallets/LedgerWallet.ts

Lines changed: 48 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ class LedgerWallet extends HdWalletCore implements AvaWalletCore {
287287
getCredentials<UnsignedTx extends AVMUnsignedTx | PlatformUnsignedTx | EVMUnsignedTx>(
288288
unsignedTx: UnsignedTx,
289289
paths: string[],
290-
sigMap: any,
290+
sigMap: Map<string, Buffer>,
291291
chainId: ChainIdType
292292
): Credential[] {
293293
const creds: Credential[] = []
@@ -340,6 +340,7 @@ class LedgerWallet extends HdWalletCore implements AvaWalletCore {
340340
const pathStr = paths[pathIndex]
341341

342342
const sigRaw = sigMap.get(pathStr)
343+
if (!sigRaw) throw new Error('Missing signature.')
343344
const sigBuff = BufferAvax.from(sigRaw)
344345
const sig: Signature = new Signature()
345346
sig.fromBuffer(sigBuff)
@@ -358,6 +359,7 @@ class LedgerWallet extends HdWalletCore implements AvaWalletCore {
358359
const pathStr = paths[pathIndex]
359360

360361
const sigRaw = sigMap.get(pathStr)
362+
if (!sigRaw) throw new Error('Missing signature.')
361363
const sigBuff = BufferAvax.from(sigRaw)
362364
const sig: Signature = new Signature()
363365
sig.fromBuffer(sigBuff)
@@ -376,6 +378,7 @@ class LedgerWallet extends HdWalletCore implements AvaWalletCore {
376378
const pathStr = paths[pathIndex]
377379

378380
const sigRaw = sigMap.get(pathStr)
381+
if (!sigRaw) throw new Error('Missing signature.')
379382
const sigBuff = BufferAvax.from(sigRaw)
380383
const sig: Signature = new Signature()
381384
sig.fromBuffer(sigBuff)
@@ -399,6 +402,8 @@ class LedgerWallet extends HdWalletCore implements AvaWalletCore {
399402
try {
400403
store.commit('Ledger/openModal', {
401404
title: 'Sign Hash',
405+
warning:
406+
'Signing hashes are dangerous, continue at your own risk. Ledger is unable display this transaction because it is too large.',
402407
messages: [],
403408
info: msg.toString('hex').toUpperCase(),
404409
})
@@ -419,7 +424,7 @@ class LedgerWallet extends HdWalletCore implements AvaWalletCore {
419424
const creds: Credential[] = this.getCredentials<UnsignedTx>(
420425
unsignedTx,
421426
paths,
422-
sigMap,
427+
sigMap.signatures,
423428
chainId
424429
)
425430

@@ -472,8 +477,6 @@ class LedgerWallet extends HdWalletCore implements AvaWalletCore {
472477
// Get their paths, for owned ones
473478
const changePaths = this.getAddressPaths(outputAddrs)
474479

475-
console.log('change paths: ', changePaths)
476-
477480
const messages = this.getTransactionMessages<UnsignedTx>(
478481
unsignedTx,
479482
chainId,
@@ -726,12 +729,28 @@ class LedgerWallet extends HdWalletCore implements AvaWalletCore {
726729
const chainId: ChainIdType = 'X'
727730
const { paths } = this.getTransactionPaths<AVMUnsignedTx>(unsignedTx, chainId)
728731

729-
const signedTx = await this.signTransactionParsable<AVMUnsignedTx, AVMTx>(
730-
unsignedTx,
731-
paths,
732-
chainId
732+
// We dont know the number of change paths but can assume worst case and use number of signer paths
733+
const canSign = this.provider.canParseTx(
734+
unsignedTx.toBuffer().length,
735+
paths.length,
736+
paths.length
733737
)
734738

739+
let signedTx
740+
if (canSign) {
741+
signedTx = await this.signTransactionParsable<AVMUnsignedTx, AVMTx>(
742+
unsignedTx,
743+
paths,
744+
chainId
745+
)
746+
} else {
747+
signedTx = await this.signTransactionHash<AVMUnsignedTx, AVMTx>(
748+
unsignedTx,
749+
paths,
750+
chainId
751+
)
752+
}
753+
735754
store.commit('Ledger/closeModal')
736755
return signedTx
737756
}
@@ -741,11 +760,28 @@ class LedgerWallet extends HdWalletCore implements AvaWalletCore {
741760

742761
const { paths } = this.getTransactionPaths<PlatformUnsignedTx>(unsignedTx, chainId)
743762

744-
const signedTx = await this.signTransactionParsable<PlatformUnsignedTx, PlatformTx>(
745-
unsignedTx,
746-
paths,
747-
chainId
763+
// We dont know the number of change paths but can assume worst case and use number of signer paths
764+
const canSign = this.provider.canParseTx(
765+
unsignedTx.toBuffer().length,
766+
paths.length,
767+
paths.length
748768
)
769+
770+
let signedTx
771+
if (canSign) {
772+
signedTx = await this.signTransactionParsable<PlatformUnsignedTx, PlatformTx>(
773+
unsignedTx,
774+
paths,
775+
chainId
776+
)
777+
} else {
778+
signedTx = await this.signTransactionHash<PlatformUnsignedTx, PlatformTx>(
779+
unsignedTx,
780+
paths,
781+
chainId
782+
)
783+
}
784+
749785
store.commit('Ledger/closeModal')
750786
return signedTx
751787
}

src/store/modules/ledger/ledger.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,27 @@
11
import { Module } from 'vuex'
22
import { RootState } from '@/store/types'
3-
import { LedgerState } from '@/store/modules/ledger/types'
3+
import { LedgerModalConfig, LedgerState } from '@/store/modules/ledger/types'
44

55
const ledger_module: Module<LedgerState, RootState> = {
66
namespaced: true,
77
state: {
88
isBlock: false, // if true a modal blocks the window
9-
isPrompt: false,
9+
isPrompt: true, // if true will display a message asking to confirm on ledger
1010
isUpgradeRequired: false,
1111
isWalletLoading: false,
1212
messages: [],
1313
title: 'title',
1414
info: `info'`,
15+
warning: undefined,
1516
},
1617
mutations: {
17-
openModal(state, input) {
18+
openModal(state, input: LedgerModalConfig) {
1819
state.title = input.title
1920
state.info = input.info
2021
state.messages = input.messages
21-
state.isPrompt = input.isPrompt
22+
state.isPrompt = input.isPrompt !== false
2223
state.isBlock = true
24+
state.warning = input.warning
2325
},
2426
closeModal(state) {
2527
state.messages = []

src/store/modules/ledger/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@ export interface ILedgerBlockMessage {
33
value: string
44
}
55

6+
export interface LedgerModalConfig {
7+
title: string
8+
info: string
9+
messages: ILedgerBlockMessage[]
10+
isPrompt?: boolean
11+
warning?: string
12+
}
13+
614
export interface LedgerState {
715
isBlock: boolean
816
isPrompt: boolean
@@ -11,6 +19,7 @@ export interface LedgerState {
1119
messages: ILedgerBlockMessage[]
1220
title: string
1321
info: string
22+
warning: string | undefined
1423
}
1524

1625
export const LEDGER_EXCHANGE_TIMEOUT = 90_000

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"compilerOptions": {
3-
"target": "es5",
3+
"target": "ES6",
44
"module": "es2015",
55
"strict": true,
66
"jsx": "preserve",

vue.config.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ module.exports = {
1111
https: !process.env.USE_HTTP,
1212
port: 5000,
1313
},
14-
// publicPath: '',
1514
configureWebpack: {
1615
optimization: {
1716
splitChunks: {

0 commit comments

Comments
 (0)