Skip to content

Commit 7066f29

Browse files
Feature/populate txn (#962)
* Populate transactions with missing fields * Create helper method and finish ledger module * Add error handling * Version bumps + cleanup * Updated Trezor provider to handle new transaction data setting flow * Version bumps * Update react version Co-authored-by: Adam Carpenter <adamcarpenter86@gmail.com>
1 parent 8389414 commit 7066f29

File tree

15 files changed

+252
-113
lines changed

15 files changed

+252
-113
lines changed

packages/common/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@web3-onboard/common",
3-
"version": "2.0.9",
3+
"version": "2.1.0-alpha.1",
44
"scripts": {
55
"build": "rollup -c",
66
"dev": "rollup -c -w",

packages/common/src/hdwallets.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import type Common from '@ethereumjs/common'
2+
import type { BigNumber } from 'ethers'
23
import type { CustomNetwork } from './types'
4+
import type { TransactionRequest } from '@ethersproject/providers'
35

46
/**
57
* Creates the common instance used for signing
@@ -35,3 +37,43 @@ export const getCommon = async ({
3537
}
3638
return common
3739
}
40+
41+
type StringifiedTransactionRequest = Omit<
42+
TransactionRequest,
43+
| 'nonce'
44+
| 'gasLimit'
45+
| 'gasPrice'
46+
| 'value'
47+
| 'maxPriorityFeePerGas'
48+
| 'maxFeePerGas'
49+
> & {
50+
nonce: string
51+
gasLimit: string
52+
gasPrice?: string
53+
value: string
54+
maxPriorityFeePerGas?: string
55+
maxFeePerGas?: string
56+
}
57+
58+
/**
59+
* Takes in TransactionRequest and converts all BigNumber values to strings
60+
* @param transaction
61+
* @returns a transaction where all BigNumber properties are now strings
62+
*/
63+
export const bigNumberFieldsToStrings = (
64+
transaction: TransactionRequest
65+
): StringifiedTransactionRequest =>
66+
Object.keys(transaction).reduce(
67+
(transaction, txnProperty) => ({
68+
...transaction,
69+
...((transaction[txnProperty as keyof TransactionRequest] as BigNumber)
70+
.toHexString
71+
? {
72+
[txnProperty]: (
73+
transaction[txnProperty as keyof TransactionRequest] as BigNumber
74+
).toHexString()
75+
}
76+
: {})
77+
}),
78+
transaction
79+
) as StringifiedTransactionRequest

packages/common/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export { createEIP1193Provider } from './eip-1193'
6262
export { default as accountSelect } from './account-select'
6363
export { entryModal } from './entry-modal'
6464
export { SofiaProLight, SofiaProRegular, SofiaProSemiBold } from './fonts'
65-
export { getCommon } from './hdwallets'
65+
export { getCommon, bigNumberFieldsToStrings } from './hdwallets'
6666

6767
export type {
6868
RequestPatch,

packages/demo/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "demo",
3-
"version": "2.0.1",
3+
"version": "2.0.2",
44
"devDependencies": {
55
"assert": "^2.0.0",
66
"buffer": "^6.0.3",

packages/demo/src/App.svelte

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -172,11 +172,16 @@
172172
// Subscribe to wallet updates
173173
const wallets$ = onboard.state.select('wallets').pipe(share())
174174
175-
const signTransactionMessage = provider => {
176-
provider.request({
177-
method: 'eth_signTransaction',
178-
params: [JSON.parse(transactionObject)]
175+
const signTransactionMessage = async provider => {
176+
const ethersProvider = new ethers.providers.Web3Provider(provider, 'any')
177+
178+
const signer = ethersProvider.getSigner()
179+
180+
const signature = await signer.signTransaction({
181+
to: '',
182+
value: 1000000000000000
179183
})
184+
console.log(signature)
180185
}
181186
182187
const signMessage = async (provider, address) => {

packages/keepkey/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@web3-onboard/keepkey",
3-
"version": "2.0.3",
3+
"version": "2.1.0-alpha.1",
44
"description": "KeepKey module for web3-onboard",
55
"module": "dist/index.js",
66
"browser": "dist/index.js",
@@ -27,7 +27,7 @@
2727
"@ethersproject/providers": "^5.5.0",
2828
"@shapeshiftoss/hdwallet-core": "^1.15.2",
2929
"@shapeshiftoss/hdwallet-keepkey-webusb": "^1.15.2",
30-
"@web3-onboard/common": "^2.0.7",
30+
"@web3-onboard/common": "^2.1.0-alpha.1",
3131
"ethereumjs-util": "^7.1.3"
3232
}
3333
}

packages/keepkey/src/index.ts

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,12 @@ function keepkey(): WalletInit {
6464
accountSelect,
6565
createEIP1193Provider,
6666
ProviderRpcError,
67-
entryModal
67+
entryModal,
68+
bigNumberFieldsToStrings
6869
} = await import('@web3-onboard/common')
6970

71+
const { utils } = await import('ethers')
72+
7073
const { StaticJsonRpcProvider } = await import(
7174
'@ethersproject/providers'
7275
)
@@ -196,7 +199,7 @@ function keepkey(): WalletInit {
196199

197200
return accounts
198201
}
199-
202+
let ethersProvider: StaticJsonRpcProvider
200203
const scanAccounts = async ({
201204
derivationPath,
202205
chainId,
@@ -205,7 +208,7 @@ function keepkey(): WalletInit {
205208
if (!keepKeyWallet)
206209
throw new Error('Device must be connected before scanning accounts')
207210
currentChain = chains.find(({ id }) => id === chainId) || currentChain
208-
const provider = new StaticJsonRpcProvider(currentChain.rpcUrl)
211+
ethersProvider = new StaticJsonRpcProvider(currentChain.rpcUrl)
209212

210213
// Checks to see if this is a custom derivation path
211214
// If it is then just return the single account
@@ -214,15 +217,23 @@ function keepkey(): WalletInit {
214217
) {
215218
try {
216219
const accountIdx = getAccountIdx(derivationPath)
217-
const account = await getAccount({ accountIdx, provider, asset })
220+
const account = await getAccount({
221+
accountIdx,
222+
provider: ethersProvider,
223+
asset
224+
})
218225

219226
return [account]
220227
} catch (error) {
221228
throw new Error('Invalid derivation path')
222229
}
223230
}
224231

225-
return getAllAccounts({ derivationPath, asset, provider })
232+
return getAllAccounts({
233+
derivationPath,
234+
asset,
235+
provider: ethersProvider
236+
})
226237
}
227238

228239
const getAccounts = async () => {
@@ -364,27 +375,44 @@ function keepkey(): WalletInit {
364375
'No account selected. Must call eth_requestAccounts first.'
365376
)
366377

378+
// Per the code above if accounts is empty or undefined then this line of code won't execute
379+
// ∴ account must be defined here which is why it is cast without the 'undefined' type
367380
const account =
368381
!transactionObject || !transactionObject.hasOwnProperty('from')
369382
? accounts[0]
370-
: accounts.find(
371-
account => account.address === transactionObject.from
372-
)
383+
: (accounts.find(
384+
account =>
385+
account.address.toLocaleLowerCase() ===
386+
transactionObject.from.toLocaleLowerCase()
387+
) as Account)
373388

374-
const { derivationPath } = account || accounts[0]
389+
const { derivationPath, address } = account
375390
const addressNList = bip32ToAddressNList(derivationPath)
376391

392+
const signer = ethersProvider.getSigner(address)
393+
394+
transactionObject.gasLimit =
395+
transactionObject.gas || transactionObject.gasLimit
396+
397+
// 'gas' is an invalid property for the TransactionRequest type
398+
delete transactionObject.gas
399+
400+
transactionObject.gasLimit = undefined
401+
402+
let populatedTransaction = await signer.populateTransaction(
403+
transactionObject
404+
)
405+
377406
const {
378-
nonce,
379-
gasPrice,
380-
gas,
381-
gasLimit,
382407
to,
383408
value,
384-
data,
409+
nonce,
410+
gasLimit,
411+
gasPrice,
385412
maxFeePerGas,
386-
maxPriorityFeePerGas
387-
} = transactionObject
413+
maxPriorityFeePerGas,
414+
data
415+
} = bigNumberFieldsToStrings(populatedTransaction)
388416

389417
const gasData = gasPrice
390418
? {
@@ -394,14 +422,15 @@ function keepkey(): WalletInit {
394422
maxFeePerGas,
395423
maxPriorityFeePerGas
396424
}
425+
397426
const txn = {
398427
addressNList,
399-
nonce: nonce || '0x0',
400-
gasLimit: gasLimit || gas || '0x5208',
401-
to,
402-
value: value || '0x0',
403-
data: data || '',
404428
chainId: parseInt(currentChain.id),
429+
to: to || '',
430+
value: value || '',
431+
nonce: utils.hexValue(nonce),
432+
gasLimit: gasLimit || '0x0',
433+
data: data?.toString() || '',
405434
...gasData
406435
}
407436

packages/keystone/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@web3-onboard/keystone",
3-
"version": "2.0.3",
3+
"version": "2.1.0-alpha.1",
44
"description": "Keystone module for web3-onboard",
55
"module": "dist/index.js",
66
"typings": "dist/index.d.ts",
@@ -20,7 +20,7 @@
2020
"dependencies": {
2121
"@ethereumjs/tx": "^3.4.0",
2222
"@ethersproject/providers": "^5.5.0",
23-
"@keystonehq/eth-keyring": "^0.11.2-alpha.2",
24-
"@web3-onboard/common": "^2.0.7"
23+
"@keystonehq/eth-keyring": "^0.14.0-alpha.10.3",
24+
"@web3-onboard/common": "^2.1.0-alpha.1"
2525
}
2626
}

packages/keystone/src/index.ts

Lines changed: 37 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import type {
66
WalletInit
77
} from '@web3-onboard/common'
88

9-
import type { providers } from 'ethers'
9+
import type { StaticJsonRpcProvider } from '@ethersproject/providers'
1010

1111
const DEFAULT_BASE_PATH = "m/44'/60'/0'/0"
1212

@@ -25,7 +25,7 @@ const assets = [
2525

2626
const getAccount = async (
2727
keyring: any,
28-
provider: providers.StaticJsonRpcProvider,
28+
provider: StaticJsonRpcProvider,
2929
index: number
3030
): Promise<Account> => {
3131
const address = (await keyring.addAccounts())[index]
@@ -42,7 +42,7 @@ const getAccount = async (
4242

4343
const generateAccounts = async (
4444
keyring: any,
45-
provider: providers.StaticJsonRpcProvider
45+
provider: StaticJsonRpcProvider
4646
): Promise<Account[]> => {
4747
const accounts = []
4848
let zeroBalanceAccounts = 0,
@@ -83,6 +83,10 @@ function keystone({
8383
'@keystonehq/eth-keyring'
8484
)
8585

86+
// @ts-ignore super weird esm issue where the default export is an object with a property default on it
87+
// if that is the case then we just grab the default value
88+
AirGappedKeyring = AirGappedKeyring?.default || AirGappedKeyring
89+
8690
const { TransactionFactory: Transaction } = await import(
8791
'@ethereumjs/tx'
8892
)
@@ -92,25 +96,26 @@ function keystone({
9296
createEIP1193Provider,
9397
ProviderRpcError,
9498
ProviderRpcErrorCode,
95-
getCommon
99+
getCommon,
100+
bigNumberFieldsToStrings
96101
} = await import('@web3-onboard/common')
97102

98103
const keyring = AirGappedKeyring.getEmptyKeyring()
99104
await keyring.readKeyring()
100105

101106
const eventEmitter = new EventEmitter()
102107

108+
let ethersProvider: StaticJsonRpcProvider
109+
103110
let currentChain: Chain = chains[0]
104111
const scanAccounts = async ({
105-
derivationPath,
106-
chainId,
107-
asset
112+
chainId
108113
}: ScanAccountsOptions): Promise<Account[]> => {
109114
currentChain =
110115
chains.find(({ id }: Chain) => id === chainId) || currentChain
111116

112-
const provider = new StaticJsonRpcProvider(currentChain.rpcUrl)
113-
return generateAccounts(keyring, provider)
117+
ethersProvider = new StaticJsonRpcProvider(currentChain.rpcUrl)
118+
return generateAccounts(keyring, ethersProvider)
114119
}
115120

116121
const getAccounts = async () => {
@@ -216,15 +221,31 @@ function keystone({
216221
transactionObject.gasLimit =
217222
transactionObject.gas || transactionObject.gasLimit
218223

219-
const transaction = Transaction.fromTxData(
220-
{
221-
...transactionObject
222-
},
223-
{ common, freeze: false }
224+
// 'gas' is an invalid property for the TransactionRequest type
225+
delete transactionObject.gas
226+
227+
const signer = ethersProvider.getSigner(from)
228+
229+
let populatedTransaction = bigNumberFieldsToStrings(
230+
await signer.populateTransaction(transactionObject)
224231
)
225232

226-
// @ts-ignore
227-
const signedTx = await keyring.signTransaction(from, transaction)
233+
const transaction = Transaction.fromTxData(populatedTransaction, {
234+
common,
235+
freeze: false
236+
})
237+
238+
let signedTx
239+
try {
240+
// @ts-ignore
241+
signedTx = await keyring.signTransaction(from, transaction)
242+
} catch (error: any) {
243+
if (error.message && error.message.message) {
244+
throw new Error(error.message.message)
245+
} else {
246+
throw new Error(error)
247+
}
248+
}
228249

229250
return `0x${signedTx.serialize().toString('hex')}`
230251
},

packages/ledger/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@web3-onboard/ledger",
3-
"version": "2.0.4",
3+
"version": "2.1.0-alpha.1",
44
"description": "Ledger module for web3-onboard",
55
"module": "dist/index.js",
66
"browser": "dist/index.js",
@@ -27,7 +27,7 @@
2727
"@ledgerhq/hw-transport-u2f": "^5.36.0-deprecated",
2828
"@ledgerhq/hw-transport-webusb": "^6.19.0",
2929
"@metamask/eth-sig-util": "^4.0.0",
30-
"@web3-onboard/common": "^2.0.7",
30+
"@web3-onboard/common": "^2.1.0-alpha.1",
3131
"buffer": "^6.0.3",
3232
"ethereumjs-util": "^7.1.3"
3333
}

0 commit comments

Comments
 (0)