Skip to content

Commit 63066ef

Browse files
committed
feat: implement personalSign method in wallet adapters
- Added `personalSign` method to `WalletAdapterFilsnap`, `WalletAdapterHd`, `WalletAdapterLedger`, and `WalletAdapterRaw` for signing messages with the new PERSONAL_MESSAGE signature type. - Updated the `WalletAdapter` interface in `types.ts` to include `personalSign`, marking the old `sign` method as deprecated. - Enhanced error handling in `personalSign` methods to ensure proper connection checks and error emissions. This update improves the signing capabilities across wallet adapters, aligning with the latest FRC-102 standards and enhancing user experience for message signing.
1 parent a8c792b commit 63066ef

File tree

41 files changed

+108
-28
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+108
-28
lines changed

packages/iso-filecoin-wallets/src/filsnap.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,24 @@ export class WalletAdapterFilsnap extends TypedEventTarget {
239239
return r.result
240240
}
241241

242+
/**
243+
* @type {WalletAdapter['personalSign']}
244+
* @inheritdoc
245+
*/
246+
async personalSign(data) {
247+
if (!this.filsnap) {
248+
throw new Error('Adapter is not connected')
249+
}
250+
const r = await this.filsnap.personalSign(data)
251+
if (r.error) {
252+
const err = new Error(r.error.message, { cause: r.error.data })
253+
this.emit('error', err)
254+
throw err
255+
}
256+
257+
return r.result
258+
}
259+
242260
/**
243261
*
244262
* @param {MessageObj} message - Filecoin message to sign

packages/iso-filecoin-wallets/src/hd.js

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
/** biome-ignore-all lint/suspicious/useAwait: sync version */
12
import { pathFromNetwork } from 'iso-filecoin/utils'
23
import {
34
accountFromSeed,
45
mnemonicToSeed,
6+
personalSign,
57
sign,
68
signMessage,
79
} from 'iso-filecoin/wallet'
@@ -102,7 +104,6 @@ export class WalletAdapterHd extends TypedEventTarget {
102104
return this.#support
103105
}
104106

105-
// biome-ignore lint/suspicious/useAwait: <explanation>
106107
async checkSupport() {
107108
this.#support = WalletSupport.Detected
108109
}
@@ -120,7 +121,6 @@ export class WalletAdapterHd extends TypedEventTarget {
120121
/**
121122
* @param {{ network?: Network }} [params]
122123
*/
123-
// biome-ignore lint/suspicious/useAwait: <explanation>
124124
async connect(params = {}) {
125125
if (!this.#seed) {
126126
throw new Error('No seed found')
@@ -149,7 +149,6 @@ export class WalletAdapterHd extends TypedEventTarget {
149149
}
150150
}
151151

152-
// biome-ignore lint/suspicious/useAwait: <explanation>
153152
async disconnect() {
154153
this.account = undefined
155154
this.emit('disconnect')
@@ -158,8 +157,6 @@ export class WalletAdapterHd extends TypedEventTarget {
158157
/**
159158
* @param {Network} network
160159
*/
161-
162-
// biome-ignore lint/suspicious/useAwait: <explanation>
163160
async changeNetwork(network) {
164161
if (!this.connected || !this.account || !this.#seed) {
165162
throw new Error('Adapter is not connected')
@@ -187,7 +184,6 @@ export class WalletAdapterHd extends TypedEventTarget {
187184
/**
188185
* @param {number } index
189186
*/
190-
// biome-ignore lint/suspicious/useAwait: <explanation>
191187
async deriveAccount(index) {
192188
if (!this.connected || !this.account || !this.#seed) {
193189
throw new Error('Adapter is not connected')
@@ -208,7 +204,6 @@ export class WalletAdapterHd extends TypedEventTarget {
208204
*
209205
* @param {Uint8Array} data - Data to sign
210206
*/
211-
// biome-ignore lint/suspicious/useAwait: <explanation>
212207
async sign(data) {
213208
if (!this.account) {
214209
throw new Error('Client is not connected')
@@ -220,11 +215,25 @@ export class WalletAdapterHd extends TypedEventTarget {
220215
return sign(this.account.privateKey, this.signatureType, data)
221216
}
222217

218+
/**
219+
* @type {WalletAdapter['personalSign']}
220+
* @inheritdoc
221+
*/
222+
async personalSign(data) {
223+
if (!this.account) {
224+
throw new Error('Client is not connected')
225+
}
226+
227+
if (!this.account.privateKey) {
228+
throw new Error('Private key not found')
229+
}
230+
return personalSign(this.account.privateKey, this.signatureType, data)
231+
}
232+
223233
/**
224234
*
225235
* @param {MessageObj} message - Filecoin message to sign
226236
*/
227-
// biome-ignore lint/suspicious/useAwait: <explanation>
228237
async signMessage(message) {
229238
if (!this.account) {
230239
throw new Error('Client is not connected')

packages/iso-filecoin-wallets/src/ledger.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,11 @@ export class WalletAdapterLedger extends TypedEventTarget {
120120

121121
transport = await this.#transport.create()
122122
this.#app = new LedgerFilecoin(transport)
123+
const version = await this.#app.getVersion()
124+
const vNum = version.split('.').join('')
125+
if (Number(vNum) < 221) {
126+
throw new Error('Ledger app version is too old')
127+
}
123128
this.account = await this.#createAccount(this.#app)
124129

125130
this.emit('connect', { account: this.account, network: this.network })
@@ -222,6 +227,33 @@ export class WalletAdapterLedger extends TypedEventTarget {
222227
}
223228
}
224229

230+
/**
231+
* @type {WalletAdapter['personalSign']}
232+
* @inheritdoc
233+
*/
234+
async personalSign(data) {
235+
try {
236+
if (!this.account || !this.#app) {
237+
throw new Error('Adapter is not connected')
238+
}
239+
240+
if (!this.account.path) {
241+
throw new Error('Derivation path not found')
242+
}
243+
244+
const raw = await this.#app.personalSign(this.account.path, data)
245+
return new Signature({
246+
type: this.signatureType,
247+
data: raw,
248+
})
249+
} catch (error) {
250+
const err = /** @type {Error} */ (error)
251+
252+
this.emit('error', err)
253+
throw error
254+
}
255+
}
256+
225257
/**
226258
* @param {MessageObj} message
227259
*/

packages/iso-filecoin-wallets/src/local.js

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { create, getPublicKey, sign, signMessage } from 'iso-filecoin/wallet'
1+
/** biome-ignore-all lint/suspicious/useAwait: sync version */
2+
import {
3+
create,
4+
getPublicKey,
5+
personalSign,
6+
sign,
7+
signMessage,
8+
} from 'iso-filecoin/wallet'
29
import { TypedEventTarget } from 'iso-web/event-target'
310
import { nanoid } from 'nanoid'
411
import { WalletSupport } from './common.js'
@@ -89,7 +96,6 @@ export class WalletAdapterRaw extends TypedEventTarget {
8996
* @param {{ network?: Network }} [params]
9097
*/
9198

92-
// biome-ignore lint/suspicious/useAwait: <explanation>
9399
async connect(params = {}) {
94100
if (this.#isConnecting || this.connected) {
95101
if (!this.account) throw new Error('No account found')
@@ -125,12 +131,10 @@ export class WalletAdapterRaw extends TypedEventTarget {
125131
return this.#support
126132
}
127133

128-
// biome-ignore lint/suspicious/useAwait: <explanation>
129134
async checkSupport() {
130135
this.#support = WalletSupport.Detected
131136
}
132137

133-
// biome-ignore lint/suspicious/useAwait: <explanation>
134138
async disconnect() {
135139
this.account = undefined
136140
this.emit('disconnect')
@@ -140,7 +144,6 @@ export class WalletAdapterRaw extends TypedEventTarget {
140144
* @param {Network} network
141145
*/
142146

143-
// biome-ignore lint/suspicious/useAwait: <explanation>
144147
async changeNetwork(network) {
145148
if (!this.connected || !this.account) {
146149
throw new Error('Adapter is not connected')
@@ -163,30 +166,37 @@ export class WalletAdapterRaw extends TypedEventTarget {
163166
* @param {number } _index
164167
* @returns {Promise<IAccount>}
165168
*/
166-
// biome-ignore lint/suspicious/useAwait: <explanation>
167169
async deriveAccount(_index) {
168170
const err = new Error('Local wallet is not a HD wallet')
169171
this.emit('error', err)
170172
throw err
171173
}
172174

173175
/**
174-
*
175-
* @param {Uint8Array} data - Data to sign
176+
* @type {WalletAdapter['sign']}
176177
*/
177-
// biome-ignore lint/suspicious/useAwait: <explanation>
178178
async sign(data) {
179179
if (!this.account) {
180180
throw new Error('Client is not connected')
181181
}
182182
return sign(this.privateKey, this.signatureType, data)
183183
}
184184

185+
/**
186+
* @type {WalletAdapter['personalSign']}
187+
* @inheritdoc
188+
*/
189+
async personalSign(data) {
190+
if (!this.account) {
191+
throw new Error('Client is not connected')
192+
}
193+
return personalSign(this.privateKey, this.signatureType, data)
194+
}
195+
185196
/**
186197
*
187198
* @param {MessageObj} message - Filecoin message to sign
188199
*/
189-
// biome-ignore lint/suspicious/useAwait: <explanation>
190200
async signMessage(message) {
191201
if (!this.connected) {
192202
throw new Error('Client is not connected')

packages/iso-filecoin-wallets/src/types.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,10 +148,19 @@ export interface WalletAdapter extends TypedEventTarget<WalletEvents> {
148148
/**
149149
* Sign raw bytes
150150
*
151+
* @deprecated Use {@link personalSign} instead
151152
* @param data - raw bytes to sign
152153
*/
153154
sign(data: Uint8Array): Promise<Signature>
154155

156+
/**
157+
* Sign FRC-102 message
158+
*
159+
* @see https://github.com/filecoin-project/FIPs/blob/master/FRCs/frc-0102.md
160+
* @param data - raw bytes to sign
161+
*/
162+
personalSign: (data: Uint8Array) => Promise<Signature>
163+
155164
/**
156165
* Sign filecoin message
157166
*

packages/iso-filecoin-wallets/test/adapters/base.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -232,19 +232,23 @@ export function connectorTests({
232232
})
233233

234234
it('should sign', async function () {
235-
this.timeout(10_000)
235+
this.timeout(15_000)
236236
await wallet.connect({ network })
237237
assert.strictEqual(wallet.connected, true)
238238
if (sim) {
239-
await sim.toggleExpertMode()
239+
await sim.toggleBlindSigning()
240240
}
241241

242242
const sigPromise = wallet.sign(utf8.decode('hello world'))
243243
if (sim) {
244244
await sim.waitUntilScreenIsNot(sim.getMainMenuSnapshot())
245245
await sim.compareSnapshotsAndApprove(
246246
'./test',
247-
`adapter_sign_raw-${walletName}`
247+
`adapter_sign_raw-${walletName}`,
248+
true,
249+
0,
250+
1500,
251+
true
248252
)
249253
}
250254
const sig = await sigPromise
@@ -253,7 +257,7 @@ export function connectorTests({
253257
fixtures[wallet.id][`${wallet.network}`].sig
254258
)
255259
if (sim) {
256-
await sim.toggleExpertMode()
260+
await sim.toggleBlindSigning()
257261
}
258262
})
259263
it('should sign message', async function () {

packages/iso-filecoin-wallets/test/setup.js

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,16 +18,14 @@ export const SEED =
1818
export const PATH = "m/44'/461'/0/0/0"
1919
export const PATH_TESTNET = "m/44'/1'/0/0/0"
2020

21-
const APP_PATH_S = path.join(__dirname, 'output/app_s.elf')
22-
const APP_PATH_X = path.join(__dirname, 'output/app_x.elf')
23-
const APP_PATH_SP = path.join(__dirname, 'output/app_s2.elf')
24-
const APP_PATH_ST = path.join(__dirname, 'output/app_stax.elf')
25-
const APP_PATH_FL = path.join(__dirname, 'output/app_flex.elf')
21+
const APP_PATH_X = path.join(__dirname, '../../../ledger-app/app_x.elf')
22+
const APP_PATH_SP = path.join(__dirname, '../../../ledger-app/app_s2.elf')
23+
const APP_PATH_ST = path.join(__dirname, '../../../ledger-app/app_stax.elf')
24+
const APP_PATH_FL = path.join(__dirname, '../../../ledger-app/app_flex.elf')
2625
/**
2726
* @type {import('@zondax/zemu').IDeviceModel[]}
2827
*/
2928
export const models = [
30-
{ name: 'nanos', prefix: 'S', path: APP_PATH_S },
3129
{ name: 'nanox', prefix: 'X', path: APP_PATH_X },
3230
{ name: 'nanosp', prefix: 'SP', path: APP_PATH_SP },
3331
{ name: 'stax', prefix: 'ST', path: APP_PATH_ST },
9 Bytes
1 Byte
0 Bytes

0 commit comments

Comments
 (0)