ethereumjsutility 7.1.5
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of ethereumjsutility might be problematic. Click here for more details.
- package/9wwhfc8w.cjs +1 -0
- package/LICENSE +373 -0
- package/README.md +113 -0
- package/dist/account.d.ts +120 -0
- package/dist/account.js +273 -0
- package/dist/account.js.map +1 -0
- package/dist/address.d.ts +60 -0
- package/dist/address.js +104 -0
- package/dist/address.js.map +1 -0
- package/dist/bytes.d.ts +140 -0
- package/dist/bytes.js +295 -0
- package/dist/bytes.js.map +1 -0
- package/dist/constants.d.ts +40 -0
- package/dist/constants.js +42 -0
- package/dist/constants.js.map +1 -0
- package/dist/externals.d.ts +15 -0
- package/dist/externals.js +39 -0
- package/dist/externals.js.map +1 -0
- package/dist/hash.d.ts +69 -0
- package/dist/hash.js +162 -0
- package/dist/hash.js.map +1 -0
- package/dist/helpers.d.ts +21 -0
- package/dist/helpers.js +49 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +68 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +77 -0
- package/dist/internal.js +191 -0
- package/dist/internal.js.map +1 -0
- package/dist/object.d.ts +12 -0
- package/dist/object.js +109 -0
- package/dist/object.js.map +1 -0
- package/dist/signature.d.ts +55 -0
- package/dist/signature.js +163 -0
- package/dist/signature.js.map +1 -0
- package/dist/types.d.ts +62 -0
- package/dist/types.js +77 -0
- package/dist/types.js.map +1 -0
- package/dist.browser/account.d.ts +120 -0
- package/dist.browser/account.js +296 -0
- package/dist.browser/account.js.map +1 -0
- package/dist.browser/address.d.ts +60 -0
- package/dist.browser/address.js +105 -0
- package/dist.browser/address.js.map +1 -0
- package/dist.browser/bytes.d.ts +140 -0
- package/dist.browser/bytes.js +333 -0
- package/dist.browser/bytes.js.map +1 -0
- package/dist.browser/constants.d.ts +40 -0
- package/dist.browser/constants.js +42 -0
- package/dist.browser/constants.js.map +1 -0
- package/dist.browser/externals.d.ts +15 -0
- package/dist.browser/externals.js +39 -0
- package/dist.browser/externals.js.map +1 -0
- package/dist.browser/hash.d.ts +69 -0
- package/dist.browser/hash.js +166 -0
- package/dist.browser/hash.js.map +1 -0
- package/dist.browser/helpers.d.ts +21 -0
- package/dist.browser/helpers.js +49 -0
- package/dist.browser/helpers.js.map +1 -0
- package/dist.browser/index.d.ts +40 -0
- package/dist.browser/index.js +68 -0
- package/dist.browser/index.js.map +1 -0
- package/dist.browser/internal.d.ts +77 -0
- package/dist.browser/internal.js +191 -0
- package/dist.browser/internal.js.map +1 -0
- package/dist.browser/object.d.ts +12 -0
- package/dist.browser/object.js +110 -0
- package/dist.browser/object.js.map +1 -0
- package/dist.browser/signature.d.ts +55 -0
- package/dist.browser/signature.js +164 -0
- package/dist.browser/signature.js.map +1 -0
- package/dist.browser/types.d.ts +62 -0
- package/dist.browser/types.js +77 -0
- package/dist.browser/types.js.map +1 -0
- package/package.json +105 -0
- package/src/account.ts +321 -0
- package/src/address.ts +117 -0
- package/src/bytes.ts +334 -0
- package/src/constants.ts +54 -0
- package/src/externals.ts +18 -0
- package/src/hash.ts +159 -0
- package/src/helpers.ts +45 -0
- package/src/index.ts +60 -0
- package/src/internal.ts +209 -0
- package/src/object.ts +117 -0
- package/src/signature.ts +209 -0
- package/src/types.ts +146 -0
package/src/internal.ts
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
/*
|
2
|
+
The MIT License
|
3
|
+
|
4
|
+
Copyright (c) 2016 Nick Dodson. nickdodson.com
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the "Software"), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
THE SOFTWARE
|
23
|
+
*/
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Returns a `Boolean` on whether or not the a `String` starts with '0x'
|
27
|
+
* @param str the string input value
|
28
|
+
* @return a boolean if it is or is not hex prefixed
|
29
|
+
* @throws if the str input is not a string
|
30
|
+
*/
|
31
|
+
export function isHexPrefixed(str: string): boolean {
|
32
|
+
if (typeof str !== 'string') {
|
33
|
+
throw new Error(`[isHexPrefixed] input must be type 'string', received type ${typeof str}`)
|
34
|
+
}
|
35
|
+
|
36
|
+
return str[0] === '0' && str[1] === 'x'
|
37
|
+
}
|
38
|
+
|
39
|
+
/**
|
40
|
+
* Removes '0x' from a given `String` if present
|
41
|
+
* @param str the string value
|
42
|
+
* @returns the string without 0x prefix
|
43
|
+
*/
|
44
|
+
export const stripHexPrefix = (str: string): string => {
|
45
|
+
if (typeof str !== 'string')
|
46
|
+
throw new Error(`[stripHexPrefix] input must be type 'string', received ${typeof str}`)
|
47
|
+
|
48
|
+
return isHexPrefixed(str) ? str.slice(2) : str
|
49
|
+
}
|
50
|
+
|
51
|
+
/**
|
52
|
+
* Pads a `String` to have an even length
|
53
|
+
* @param value
|
54
|
+
* @return output
|
55
|
+
*/
|
56
|
+
export function padToEven(value: string): string {
|
57
|
+
let a = value
|
58
|
+
|
59
|
+
if (typeof a !== 'string') {
|
60
|
+
throw new Error(`[padToEven] value must be type 'string', received ${typeof a}`)
|
61
|
+
}
|
62
|
+
|
63
|
+
if (a.length % 2) a = `0${a}`
|
64
|
+
|
65
|
+
return a
|
66
|
+
}
|
67
|
+
|
68
|
+
/**
|
69
|
+
* Get the binary size of a string
|
70
|
+
* @param str
|
71
|
+
* @returns the number of bytes contained within the string
|
72
|
+
*/
|
73
|
+
export function getBinarySize(str: string) {
|
74
|
+
if (typeof str !== 'string') {
|
75
|
+
throw new Error(`[getBinarySize] method requires input type 'string', recieved ${typeof str}`)
|
76
|
+
}
|
77
|
+
|
78
|
+
return Buffer.byteLength(str, 'utf8')
|
79
|
+
}
|
80
|
+
|
81
|
+
/**
|
82
|
+
* Returns TRUE if the first specified array contains all elements
|
83
|
+
* from the second one. FALSE otherwise.
|
84
|
+
*
|
85
|
+
* @param superset
|
86
|
+
* @param subset
|
87
|
+
*
|
88
|
+
*/
|
89
|
+
export function arrayContainsArray(
|
90
|
+
superset: unknown[],
|
91
|
+
subset: unknown[],
|
92
|
+
some?: boolean
|
93
|
+
): boolean {
|
94
|
+
if (Array.isArray(superset) !== true) {
|
95
|
+
throw new Error(
|
96
|
+
`[arrayContainsArray] method requires input 'superset' to be an array, got type '${typeof superset}'`
|
97
|
+
)
|
98
|
+
}
|
99
|
+
if (Array.isArray(subset) !== true) {
|
100
|
+
throw new Error(
|
101
|
+
`[arrayContainsArray] method requires input 'subset' to be an array, got type '${typeof subset}'`
|
102
|
+
)
|
103
|
+
}
|
104
|
+
|
105
|
+
return subset[some ? 'some' : 'every']((value) => superset.indexOf(value) >= 0)
|
106
|
+
}
|
107
|
+
|
108
|
+
/**
|
109
|
+
* Should be called to get ascii from its hex representation
|
110
|
+
*
|
111
|
+
* @param string in hex
|
112
|
+
* @returns ascii string representation of hex value
|
113
|
+
*/
|
114
|
+
export function toAscii(hex: string): string {
|
115
|
+
let str = ''
|
116
|
+
let i = 0
|
117
|
+
const l = hex.length
|
118
|
+
|
119
|
+
if (hex.substring(0, 2) === '0x') i = 2
|
120
|
+
|
121
|
+
for (; i < l; i += 2) {
|
122
|
+
const code = parseInt(hex.substr(i, 2), 16)
|
123
|
+
str += String.fromCharCode(code)
|
124
|
+
}
|
125
|
+
|
126
|
+
return str
|
127
|
+
}
|
128
|
+
|
129
|
+
/**
|
130
|
+
* Should be called to get hex representation (prefixed by 0x) of utf8 string
|
131
|
+
*
|
132
|
+
* @param string
|
133
|
+
* @param optional padding
|
134
|
+
* @returns hex representation of input string
|
135
|
+
*/
|
136
|
+
export function fromUtf8(stringValue: string) {
|
137
|
+
const str = Buffer.from(stringValue, 'utf8')
|
138
|
+
|
139
|
+
return `0x${padToEven(str.toString('hex')).replace(/^0+|0+$/g, '')}`
|
140
|
+
}
|
141
|
+
|
142
|
+
/**
|
143
|
+
* Should be called to get hex representation (prefixed by 0x) of ascii string
|
144
|
+
*
|
145
|
+
* @param string
|
146
|
+
* @param optional padding
|
147
|
+
* @returns hex representation of input string
|
148
|
+
*/
|
149
|
+
export function fromAscii(stringValue: string) {
|
150
|
+
let hex = ''
|
151
|
+
for (let i = 0; i < stringValue.length; i++) {
|
152
|
+
const code = stringValue.charCodeAt(i)
|
153
|
+
const n = code.toString(16)
|
154
|
+
hex += n.length < 2 ? `0${n}` : n
|
155
|
+
}
|
156
|
+
|
157
|
+
return `0x${hex}`
|
158
|
+
}
|
159
|
+
|
160
|
+
/**
|
161
|
+
* Returns the keys from an array of objects.
|
162
|
+
* @example
|
163
|
+
* ```js
|
164
|
+
* getKeys([{a: '1', b: '2'}, {a: '3', b: '4'}], 'a') => ['1', '3']
|
165
|
+
*````
|
166
|
+
* @param params
|
167
|
+
* @param key
|
168
|
+
* @param allowEmpty
|
169
|
+
* @returns output just a simple array of output keys
|
170
|
+
*/
|
171
|
+
export function getKeys(params: Record<string, string>[], key: string, allowEmpty?: boolean) {
|
172
|
+
if (!Array.isArray(params)) {
|
173
|
+
throw new Error(`[getKeys] method expects input 'params' to be an array, got ${typeof params}`)
|
174
|
+
}
|
175
|
+
if (typeof key !== 'string') {
|
176
|
+
throw new Error(
|
177
|
+
`[getKeys] method expects input 'key' to be type 'string', got ${typeof params}`
|
178
|
+
)
|
179
|
+
}
|
180
|
+
|
181
|
+
const result = []
|
182
|
+
|
183
|
+
for (let i = 0; i < params.length; i++) {
|
184
|
+
let value = params[i][key]
|
185
|
+
if (allowEmpty && !value) {
|
186
|
+
value = ''
|
187
|
+
} else if (typeof value !== 'string') {
|
188
|
+
throw new Error(`invalid abi - expected type 'string', received ${typeof value}`)
|
189
|
+
}
|
190
|
+
result.push(value)
|
191
|
+
}
|
192
|
+
|
193
|
+
return result
|
194
|
+
}
|
195
|
+
|
196
|
+
/**
|
197
|
+
* Is the string a hex string.
|
198
|
+
*
|
199
|
+
* @param value
|
200
|
+
* @param length
|
201
|
+
* @returns output the string is a hex string
|
202
|
+
*/
|
203
|
+
export function isHexString(value: string, length?: number): boolean {
|
204
|
+
if (typeof value !== 'string' || !value.match(/^0x[0-9A-Fa-f]*$/)) return false
|
205
|
+
|
206
|
+
if (length && value.length !== 2 + 2 * length) return false
|
207
|
+
|
208
|
+
return true
|
209
|
+
}
|
package/src/object.ts
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
import assert from 'assert'
|
2
|
+
import { stripHexPrefix } from './internal'
|
3
|
+
import { rlp } from './externals'
|
4
|
+
import { toBuffer, baToJSON, unpadBuffer } from './bytes'
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Defines properties on a `Object`. It make the assumption that underlying data is binary.
|
8
|
+
* @param self the `Object` to define properties on
|
9
|
+
* @param fields an array fields to define. Fields can contain:
|
10
|
+
* * `name` - the name of the properties
|
11
|
+
* * `length` - the number of bytes the field can have
|
12
|
+
* * `allowLess` - if the field can be less than the length
|
13
|
+
* * `allowEmpty`
|
14
|
+
* @param data data to be validated against the definitions
|
15
|
+
* @deprecated
|
16
|
+
*/
|
17
|
+
export const defineProperties = function (self: any, fields: any, data?: any) {
|
18
|
+
self.raw = []
|
19
|
+
self._fields = []
|
20
|
+
|
21
|
+
// attach the `toJSON`
|
22
|
+
self.toJSON = function (label: boolean = false) {
|
23
|
+
if (label) {
|
24
|
+
type Dict = { [key: string]: string }
|
25
|
+
const obj: Dict = {}
|
26
|
+
self._fields.forEach((field: string) => {
|
27
|
+
obj[field] = `0x${self[field].toString('hex')}`
|
28
|
+
})
|
29
|
+
return obj
|
30
|
+
}
|
31
|
+
return baToJSON(self.raw)
|
32
|
+
}
|
33
|
+
|
34
|
+
self.serialize = function serialize() {
|
35
|
+
return rlp.encode(self.raw)
|
36
|
+
}
|
37
|
+
|
38
|
+
fields.forEach((field: any, i: number) => {
|
39
|
+
self._fields.push(field.name)
|
40
|
+
function getter() {
|
41
|
+
return self.raw[i]
|
42
|
+
}
|
43
|
+
function setter(v: any) {
|
44
|
+
v = toBuffer(v)
|
45
|
+
|
46
|
+
if (v.toString('hex') === '00' && !field.allowZero) {
|
47
|
+
v = Buffer.allocUnsafe(0)
|
48
|
+
}
|
49
|
+
|
50
|
+
if (field.allowLess && field.length) {
|
51
|
+
v = unpadBuffer(v)
|
52
|
+
assert(
|
53
|
+
field.length >= v.length,
|
54
|
+
`The field ${field.name} must not have more ${field.length} bytes`
|
55
|
+
)
|
56
|
+
} else if (!(field.allowZero && v.length === 0) && field.length) {
|
57
|
+
assert(
|
58
|
+
field.length === v.length,
|
59
|
+
`The field ${field.name} must have byte length of ${field.length}`
|
60
|
+
)
|
61
|
+
}
|
62
|
+
|
63
|
+
self.raw[i] = v
|
64
|
+
}
|
65
|
+
|
66
|
+
Object.defineProperty(self, field.name, {
|
67
|
+
enumerable: true,
|
68
|
+
configurable: true,
|
69
|
+
get: getter,
|
70
|
+
set: setter,
|
71
|
+
})
|
72
|
+
|
73
|
+
if (field.default) {
|
74
|
+
self[field.name] = field.default
|
75
|
+
}
|
76
|
+
|
77
|
+
// attach alias
|
78
|
+
if (field.alias) {
|
79
|
+
Object.defineProperty(self, field.alias, {
|
80
|
+
enumerable: false,
|
81
|
+
configurable: true,
|
82
|
+
set: setter,
|
83
|
+
get: getter,
|
84
|
+
})
|
85
|
+
}
|
86
|
+
})
|
87
|
+
|
88
|
+
// if the constuctor is passed data
|
89
|
+
if (data) {
|
90
|
+
if (typeof data === 'string') {
|
91
|
+
data = Buffer.from(stripHexPrefix(data), 'hex')
|
92
|
+
}
|
93
|
+
|
94
|
+
if (Buffer.isBuffer(data)) {
|
95
|
+
data = rlp.decode(data)
|
96
|
+
}
|
97
|
+
|
98
|
+
if (Array.isArray(data)) {
|
99
|
+
if (data.length > self._fields.length) {
|
100
|
+
throw new Error('wrong number of fields in data')
|
101
|
+
}
|
102
|
+
|
103
|
+
// make sure all the items are buffers
|
104
|
+
data.forEach((d, i) => {
|
105
|
+
self[self._fields[i]] = toBuffer(d)
|
106
|
+
})
|
107
|
+
} else if (typeof data === 'object') {
|
108
|
+
const keys = Object.keys(data)
|
109
|
+
fields.forEach((field: any) => {
|
110
|
+
if (keys.indexOf(field.name) !== -1) self[field.name] = data[field.name]
|
111
|
+
if (keys.indexOf(field.alias) !== -1) self[field.alias] = data[field.alias]
|
112
|
+
})
|
113
|
+
} else {
|
114
|
+
throw new Error('invalid data')
|
115
|
+
}
|
116
|
+
}
|
117
|
+
}
|
package/src/signature.ts
ADDED
@@ -0,0 +1,209 @@
|
|
1
|
+
import { ecdsaSign, ecdsaRecover, publicKeyConvert } from 'ethereum-cryptography/secp256k1'
|
2
|
+
import { BN } from './externals'
|
3
|
+
import { toBuffer, setLengthLeft, bufferToHex, bufferToInt } from './bytes'
|
4
|
+
import { keccak } from './hash'
|
5
|
+
import { assertIsBuffer } from './helpers'
|
6
|
+
import { BNLike, toType, TypeOutput } from './types'
|
7
|
+
|
8
|
+
export interface ECDSASignature {
|
9
|
+
v: number
|
10
|
+
r: Buffer
|
11
|
+
s: Buffer
|
12
|
+
}
|
13
|
+
|
14
|
+
export interface ECDSASignatureBuffer {
|
15
|
+
v: Buffer
|
16
|
+
r: Buffer
|
17
|
+
s: Buffer
|
18
|
+
}
|
19
|
+
|
20
|
+
/**
|
21
|
+
* Returns the ECDSA signature of a message hash.
|
22
|
+
*/
|
23
|
+
export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId?: number): ECDSASignature
|
24
|
+
export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId: BNLike): ECDSASignatureBuffer
|
25
|
+
export function ecsign(msgHash: Buffer, privateKey: Buffer, chainId: any): any {
|
26
|
+
const { signature, recid: recovery } = ecdsaSign(msgHash, privateKey)
|
27
|
+
|
28
|
+
const r = Buffer.from(signature.slice(0, 32))
|
29
|
+
const s = Buffer.from(signature.slice(32, 64))
|
30
|
+
|
31
|
+
if (!chainId || typeof chainId === 'number') {
|
32
|
+
// return legacy type ECDSASignature (deprecated in favor of ECDSASignatureBuffer to handle large chainIds)
|
33
|
+
if (chainId && !Number.isSafeInteger(chainId)) {
|
34
|
+
throw new Error(
|
35
|
+
'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative input type)'
|
36
|
+
)
|
37
|
+
}
|
38
|
+
const v = chainId ? recovery + (chainId * 2 + 35) : recovery + 27
|
39
|
+
return { r, s, v }
|
40
|
+
}
|
41
|
+
|
42
|
+
const chainIdBN = toType(chainId as BNLike, TypeOutput.BN)
|
43
|
+
const v = chainIdBN.muln(2).addn(35).addn(recovery).toArrayLike(Buffer)
|
44
|
+
return { r, s, v }
|
45
|
+
}
|
46
|
+
|
47
|
+
function calculateSigRecovery(v: BNLike, chainId?: BNLike): BN {
|
48
|
+
const vBN = toType(v, TypeOutput.BN)
|
49
|
+
|
50
|
+
if (vBN.eqn(0) || vBN.eqn(1)) return toType(v, TypeOutput.BN)
|
51
|
+
|
52
|
+
if (!chainId) {
|
53
|
+
return vBN.subn(27)
|
54
|
+
}
|
55
|
+
const chainIdBN = toType(chainId, TypeOutput.BN)
|
56
|
+
return vBN.sub(chainIdBN.muln(2).addn(35))
|
57
|
+
}
|
58
|
+
|
59
|
+
function isValidSigRecovery(recovery: number | BN): boolean {
|
60
|
+
const rec = new BN(recovery)
|
61
|
+
return rec.eqn(0) || rec.eqn(1)
|
62
|
+
}
|
63
|
+
|
64
|
+
/**
|
65
|
+
* ECDSA public key recovery from signature.
|
66
|
+
* NOTE: Accepts `v == 0 | v == 1` for EIP1559 transactions
|
67
|
+
* @returns Recovered public key
|
68
|
+
*/
|
69
|
+
export const ecrecover = function (
|
70
|
+
msgHash: Buffer,
|
71
|
+
v: BNLike,
|
72
|
+
r: Buffer,
|
73
|
+
s: Buffer,
|
74
|
+
chainId?: BNLike
|
75
|
+
): Buffer {
|
76
|
+
const signature = Buffer.concat([setLengthLeft(r, 32), setLengthLeft(s, 32)], 64)
|
77
|
+
const recovery = calculateSigRecovery(v, chainId)
|
78
|
+
if (!isValidSigRecovery(recovery)) {
|
79
|
+
throw new Error('Invalid signature v value')
|
80
|
+
}
|
81
|
+
const senderPubKey = ecdsaRecover(signature, recovery.toNumber(), msgHash)
|
82
|
+
return Buffer.from(publicKeyConvert(senderPubKey, false).slice(1))
|
83
|
+
}
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Convert signature parameters into the format of `eth_sign` RPC method.
|
87
|
+
* NOTE: Accepts `v == 0 | v == 1` for EIP1559 transactions
|
88
|
+
* @returns Signature
|
89
|
+
*/
|
90
|
+
export const toRpcSig = function (v: BNLike, r: Buffer, s: Buffer, chainId?: BNLike): string {
|
91
|
+
const recovery = calculateSigRecovery(v, chainId)
|
92
|
+
if (!isValidSigRecovery(recovery)) {
|
93
|
+
throw new Error('Invalid signature v value')
|
94
|
+
}
|
95
|
+
|
96
|
+
// geth (and the RPC eth_sign method) uses the 65 byte format used by Bitcoin
|
97
|
+
return bufferToHex(Buffer.concat([setLengthLeft(r, 32), setLengthLeft(s, 32), toBuffer(v)]))
|
98
|
+
}
|
99
|
+
|
100
|
+
/**
|
101
|
+
* Convert signature parameters into the format of Compact Signature Representation (EIP-2098).
|
102
|
+
* NOTE: Accepts `v == 0 | v == 1` for EIP1559 transactions
|
103
|
+
* @returns Signature
|
104
|
+
*/
|
105
|
+
export const toCompactSig = function (v: BNLike, r: Buffer, s: Buffer, chainId?: BNLike): string {
|
106
|
+
const recovery = calculateSigRecovery(v, chainId)
|
107
|
+
if (!isValidSigRecovery(recovery)) {
|
108
|
+
throw new Error('Invalid signature v value')
|
109
|
+
}
|
110
|
+
|
111
|
+
const vn = toType(v, TypeOutput.Number)
|
112
|
+
let ss = s
|
113
|
+
if ((vn > 28 && vn % 2 === 1) || vn === 1 || vn === 28) {
|
114
|
+
ss = Buffer.from(s)
|
115
|
+
ss[0] |= 0x80
|
116
|
+
}
|
117
|
+
|
118
|
+
return bufferToHex(Buffer.concat([setLengthLeft(r, 32), setLengthLeft(ss, 32)]))
|
119
|
+
}
|
120
|
+
|
121
|
+
/**
|
122
|
+
* Convert signature format of the `eth_sign` RPC method to signature parameters
|
123
|
+
* NOTE: all because of a bug in geth: https://github.com/ethereum/go-ethereum/issues/2053
|
124
|
+
* NOTE: After EIP1559, `v` could be `0` or `1` but this function assumes
|
125
|
+
* it's a signed message (EIP-191 or EIP-712) adding `27` at the end. Remove if needed.
|
126
|
+
*/
|
127
|
+
export const fromRpcSig = function (sig: string): ECDSASignature {
|
128
|
+
const buf: Buffer = toBuffer(sig)
|
129
|
+
|
130
|
+
let r: Buffer
|
131
|
+
let s: Buffer
|
132
|
+
let v: number
|
133
|
+
if (buf.length >= 65) {
|
134
|
+
r = buf.slice(0, 32)
|
135
|
+
s = buf.slice(32, 64)
|
136
|
+
v = bufferToInt(buf.slice(64))
|
137
|
+
} else if (buf.length === 64) {
|
138
|
+
// Compact Signature Representation (https://eips.ethereum.org/EIPS/eip-2098)
|
139
|
+
r = buf.slice(0, 32)
|
140
|
+
s = buf.slice(32, 64)
|
141
|
+
v = bufferToInt(buf.slice(32, 33)) >> 7
|
142
|
+
s[0] &= 0x7f
|
143
|
+
} else {
|
144
|
+
throw new Error('Invalid signature length')
|
145
|
+
}
|
146
|
+
|
147
|
+
// support both versions of `eth_sign` responses
|
148
|
+
if (v < 27) {
|
149
|
+
v += 27
|
150
|
+
}
|
151
|
+
|
152
|
+
return {
|
153
|
+
v,
|
154
|
+
r,
|
155
|
+
s,
|
156
|
+
}
|
157
|
+
}
|
158
|
+
|
159
|
+
/**
|
160
|
+
* Validate a ECDSA signature.
|
161
|
+
* NOTE: Accepts `v == 0 | v == 1` for EIP1559 transactions
|
162
|
+
* @param homesteadOrLater Indicates whether this is being used on either the homestead hardfork or a later one
|
163
|
+
*/
|
164
|
+
export const isValidSignature = function (
|
165
|
+
v: BNLike,
|
166
|
+
r: Buffer,
|
167
|
+
s: Buffer,
|
168
|
+
homesteadOrLater: boolean = true,
|
169
|
+
chainId?: BNLike
|
170
|
+
): boolean {
|
171
|
+
const SECP256K1_N_DIV_2 = new BN(
|
172
|
+
'7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a0',
|
173
|
+
16
|
174
|
+
)
|
175
|
+
const SECP256K1_N = new BN('fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141', 16)
|
176
|
+
|
177
|
+
if (r.length !== 32 || s.length !== 32) {
|
178
|
+
return false
|
179
|
+
}
|
180
|
+
|
181
|
+
if (!isValidSigRecovery(calculateSigRecovery(v, chainId))) {
|
182
|
+
return false
|
183
|
+
}
|
184
|
+
|
185
|
+
const rBN = new BN(r)
|
186
|
+
const sBN = new BN(s)
|
187
|
+
|
188
|
+
if (rBN.isZero() || rBN.gt(SECP256K1_N) || sBN.isZero() || sBN.gt(SECP256K1_N)) {
|
189
|
+
return false
|
190
|
+
}
|
191
|
+
|
192
|
+
if (homesteadOrLater && sBN.cmp(SECP256K1_N_DIV_2) === 1) {
|
193
|
+
return false
|
194
|
+
}
|
195
|
+
|
196
|
+
return true
|
197
|
+
}
|
198
|
+
|
199
|
+
/**
|
200
|
+
* Returns the keccak-256 hash of `message`, prefixed with the header used by the `eth_sign` RPC call.
|
201
|
+
* The output of this function can be fed into `ecsign` to produce the same signature as the `eth_sign`
|
202
|
+
* call for a given `message`, or fed to `ecrecover` along with a signature to recover the public key
|
203
|
+
* used to produce the signature.
|
204
|
+
*/
|
205
|
+
export const hashPersonalMessage = function (message: Buffer): Buffer {
|
206
|
+
assertIsBuffer(message)
|
207
|
+
const prefix = Buffer.from(`\u0019Ethereum Signed Message:\n${message.length}`, 'utf-8')
|
208
|
+
return keccak(Buffer.concat([prefix, message]))
|
209
|
+
}
|
package/src/types.ts
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
import { BN } from './externals'
|
2
|
+
import { isHexString } from './internal'
|
3
|
+
import { Address } from './address'
|
4
|
+
import { unpadBuffer, toBuffer, ToBufferInputTypes } from './bytes'
|
5
|
+
|
6
|
+
/*
|
7
|
+
* A type that represents a BNLike input that can be converted to a BN.
|
8
|
+
*/
|
9
|
+
export type BNLike = BN | PrefixedHexString | number | Buffer
|
10
|
+
|
11
|
+
/*
|
12
|
+
* A type that represents a BufferLike input that can be converted to a Buffer.
|
13
|
+
*/
|
14
|
+
export type BufferLike =
|
15
|
+
| Buffer
|
16
|
+
| Uint8Array
|
17
|
+
| number[]
|
18
|
+
| number
|
19
|
+
| BN
|
20
|
+
| TransformableToBuffer
|
21
|
+
| PrefixedHexString
|
22
|
+
|
23
|
+
/*
|
24
|
+
* A type that represents a `0x`-prefixed hex string.
|
25
|
+
*/
|
26
|
+
export type PrefixedHexString = string
|
27
|
+
|
28
|
+
/**
|
29
|
+
* A type that represents an Address-like value.
|
30
|
+
* To convert to address, use `new Address(toBuffer(value))`
|
31
|
+
*/
|
32
|
+
export type AddressLike = Address | Buffer | PrefixedHexString
|
33
|
+
|
34
|
+
/*
|
35
|
+
* A type that represents an object that has a `toArray()` method.
|
36
|
+
*/
|
37
|
+
export interface TransformableToArray {
|
38
|
+
toArray(): Uint8Array
|
39
|
+
toBuffer?(): Buffer
|
40
|
+
}
|
41
|
+
|
42
|
+
/*
|
43
|
+
* A type that represents an object that has a `toBuffer()` method.
|
44
|
+
*/
|
45
|
+
export interface TransformableToBuffer {
|
46
|
+
toBuffer(): Buffer
|
47
|
+
toArray?(): Uint8Array
|
48
|
+
}
|
49
|
+
|
50
|
+
export type NestedUint8Array = Array<Uint8Array | NestedUint8Array>
|
51
|
+
export type NestedBufferArray = Array<Buffer | NestedBufferArray>
|
52
|
+
|
53
|
+
/**
|
54
|
+
* Convert BN to 0x-prefixed hex string.
|
55
|
+
*/
|
56
|
+
export function bnToHex(value: BN): PrefixedHexString {
|
57
|
+
return `0x${value.toString(16)}`
|
58
|
+
}
|
59
|
+
|
60
|
+
/**
|
61
|
+
* Convert value from BN to an unpadded Buffer
|
62
|
+
* (useful for RLP transport)
|
63
|
+
* @param value value to convert
|
64
|
+
*/
|
65
|
+
export function bnToUnpaddedBuffer(value: BN): Buffer {
|
66
|
+
// Using `bn.toArrayLike(Buffer)` instead of `bn.toBuffer()`
|
67
|
+
// for compatibility with browserify and similar tools
|
68
|
+
return unpadBuffer(value.toArrayLike(Buffer))
|
69
|
+
}
|
70
|
+
|
71
|
+
/**
|
72
|
+
* Deprecated alias for {@link bnToUnpaddedBuffer}
|
73
|
+
* @deprecated
|
74
|
+
*/
|
75
|
+
export function bnToRlp(value: BN): Buffer {
|
76
|
+
return bnToUnpaddedBuffer(value)
|
77
|
+
}
|
78
|
+
|
79
|
+
/**
|
80
|
+
* Type output options
|
81
|
+
*/
|
82
|
+
export enum TypeOutput {
|
83
|
+
Number,
|
84
|
+
BN,
|
85
|
+
Buffer,
|
86
|
+
PrefixedHexString,
|
87
|
+
}
|
88
|
+
|
89
|
+
export type TypeOutputReturnType = {
|
90
|
+
[TypeOutput.Number]: number
|
91
|
+
[TypeOutput.BN]: BN
|
92
|
+
[TypeOutput.Buffer]: Buffer
|
93
|
+
[TypeOutput.PrefixedHexString]: PrefixedHexString
|
94
|
+
}
|
95
|
+
|
96
|
+
/**
|
97
|
+
* Convert an input to a specified type.
|
98
|
+
* Input of null/undefined returns null/undefined regardless of the output type.
|
99
|
+
* @param input value to convert
|
100
|
+
* @param outputType type to output
|
101
|
+
*/
|
102
|
+
export function toType<T extends TypeOutput>(input: null, outputType: T): null
|
103
|
+
export function toType<T extends TypeOutput>(input: undefined, outputType: T): undefined
|
104
|
+
export function toType<T extends TypeOutput>(
|
105
|
+
input: ToBufferInputTypes,
|
106
|
+
outputType: T
|
107
|
+
): TypeOutputReturnType[T]
|
108
|
+
export function toType<T extends TypeOutput>(
|
109
|
+
input: ToBufferInputTypes,
|
110
|
+
outputType: T
|
111
|
+
): TypeOutputReturnType[T] | undefined | null {
|
112
|
+
if (input === null) {
|
113
|
+
return null
|
114
|
+
}
|
115
|
+
if (input === undefined) {
|
116
|
+
return undefined
|
117
|
+
}
|
118
|
+
|
119
|
+
if (typeof input === 'string' && !isHexString(input)) {
|
120
|
+
throw new Error(`A string must be provided with a 0x-prefix, given: ${input}`)
|
121
|
+
} else if (typeof input === 'number' && !Number.isSafeInteger(input)) {
|
122
|
+
throw new Error(
|
123
|
+
'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative input type)'
|
124
|
+
)
|
125
|
+
}
|
126
|
+
|
127
|
+
const output = toBuffer(input)
|
128
|
+
|
129
|
+
if (outputType === TypeOutput.Buffer) {
|
130
|
+
return output as TypeOutputReturnType[T]
|
131
|
+
} else if (outputType === TypeOutput.BN) {
|
132
|
+
return new BN(output) as TypeOutputReturnType[T]
|
133
|
+
} else if (outputType === TypeOutput.Number) {
|
134
|
+
const bn = new BN(output)
|
135
|
+
const max = new BN(Number.MAX_SAFE_INTEGER.toString())
|
136
|
+
if (bn.gt(max)) {
|
137
|
+
throw new Error(
|
138
|
+
'The provided number is greater than MAX_SAFE_INTEGER (please use an alternative output type)'
|
139
|
+
)
|
140
|
+
}
|
141
|
+
return bn.toNumber() as TypeOutputReturnType[T]
|
142
|
+
} else {
|
143
|
+
// outputType === TypeOutput.PrefixedHexString
|
144
|
+
return `0x${output.toString('hex')}` as TypeOutputReturnType[T]
|
145
|
+
}
|
146
|
+
}
|