-
-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #542 from Adamant-im/feat/update-web3-eth
Upgrade `web3-eth` from v1 to v4
- Loading branch information
Showing
8 changed files
with
639 additions
and
1,507 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
// @vitest-environment node | ||
// Some crypto libs throw errors when using `jsdom` environment | ||
|
||
import { describe, it, expect } from 'vitest' | ||
import Web3Eth from 'web3-eth' | ||
|
||
import { toEther, toWei, getAccountFromPassphrase } from '@/lib/eth-utils' | ||
|
||
describe('eth-utils', () => { | ||
describe('toEther', () => { | ||
it('should convert Wei amount to Ether from a number', () => { | ||
expect(toEther(1)).toBe('0.000000000000000001') | ||
}) | ||
|
||
it('should convert Wei amount to Ether from a numeric string', () => { | ||
expect(toEther('1')).toBe('0.000000000000000001') | ||
}) | ||
}) | ||
|
||
describe('toWei', () => { | ||
it('should convert Ether value into Wei from a number', () => { | ||
expect(toWei(1)).toBe('1000000000000000000') | ||
}) | ||
|
||
it('should convert Ether value into Wei from a numeric string', () => { | ||
expect(toWei('1')).toBe('1000000000000000000') | ||
}) | ||
|
||
it('should convert Gwei value into Wei', () => { | ||
expect(toWei(1, 'gwei')).toBe('1000000000') | ||
}) | ||
}) | ||
|
||
describe('getAccountFromPassphrase', () => { | ||
const passphrase = 'joy mouse injury soft decade bid rough about alarm wreck season sting' | ||
const api = new Web3Eth('https://clown.adamant.im') | ||
|
||
it('should generate account from passphrase with "web3Account"', () => { | ||
expect(getAccountFromPassphrase(passphrase, api)).toMatchObject({ | ||
web3Account: { | ||
address: '0x045d7e948087D9C6D88D58e41587A610400869B6', | ||
privateKey: '0x344854fa2184c252bdcc09daf8fe7fbcc960aed8f4da68de793f9fbc50b5a686' | ||
}, | ||
address: '0x045d7e948087D9C6D88D58e41587A610400869B6', | ||
privateKey: '0x344854fa2184c252bdcc09daf8fe7fbcc960aed8f4da68de793f9fbc50b5a686' | ||
}) | ||
}) | ||
|
||
it('should generate account from passphrase without "web3Account"', () => { | ||
expect(getAccountFromPassphrase(passphrase)).toEqual({ | ||
privateKey: '0x344854fa2184c252bdcc09daf8fe7fbcc960aed8f4da68de793f9fbc50b5a686' | ||
}) | ||
}) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,136 @@ | ||
/** | ||
* The code is based on https://github.com/Consensys/abi-decoder | ||
*/ | ||
import type { Components, JsonEventInterface, JsonFunctionInterface } from 'web3-types' | ||
import { sha3 } from 'web3-utils' | ||
import { decodeParameters } from 'web3-eth-abi' | ||
import BigNumber from 'bignumber.js' | ||
|
||
type Method = { | ||
name: string | ||
params: MethodsParams[] | ||
} | ||
|
||
type MethodsParams = { | ||
name: string | ||
type: string | ||
value: string | string[] | ||
} | ||
|
||
function componentType(input: Components): string { | ||
if (input.type === 'tuple') { | ||
const tupleTypes = input.components!.map(componentType) | ||
|
||
return '(' + tupleTypes.join(',') + ')' | ||
} | ||
|
||
return input.type | ||
} | ||
|
||
/** | ||
* Returns `true` if the input type is one of: | ||
* uint, uint8, uint16, uint32, uint64, uint128, uint256 | ||
*/ | ||
function isUint(input: Components) { | ||
return input.type.startsWith('uint') | ||
} | ||
|
||
/** | ||
* Returns `true` if the input type is one of: | ||
* int, int8, int16, int32, int64, int128, int256 | ||
*/ | ||
function isInt(input: Components) { | ||
return input.type.startsWith('int') | ||
} | ||
|
||
/** | ||
* Returns `true` if the input is an ETH address | ||
*/ | ||
function isAddress(input: Components) { | ||
return input.type === 'address' | ||
} | ||
|
||
export class AbiDecoder { | ||
readonly schema: Array<JsonFunctionInterface | JsonEventInterface> | ||
readonly methods: Record<string, JsonFunctionInterface | JsonEventInterface> | ||
|
||
constructor(schema: Array<JsonFunctionInterface | JsonEventInterface>) { | ||
this.schema = schema | ||
this.methods = this.parseMethods() | ||
} | ||
|
||
decodeMethod(data: string) { | ||
const methodId = data.slice(2, 10) | ||
const abiItem = this.methods[methodId] | ||
if (abiItem) { | ||
const decodedParams = decodeParameters(abiItem.inputs, data.slice(10)) | ||
|
||
const retData: Method = { | ||
name: abiItem.name, | ||
params: [] | ||
} | ||
|
||
for (let i = 0; i < decodedParams.__length__; i++) { | ||
const param = decodedParams[i] as string | string[] | ||
let parsedParam = param | ||
|
||
const input = abiItem.inputs[i] | ||
|
||
if (isInt(input) || isUint(input)) { | ||
const isArray = Array.isArray(param) | ||
|
||
if (isArray) { | ||
parsedParam = param.map((number) => new BigNumber(number).toString()) | ||
} else { | ||
parsedParam = new BigNumber(param).toString() | ||
} | ||
} | ||
|
||
// Addresses returned by web3 are randomly cased, so we need to standardize and lowercase all | ||
if (isAddress(input)) { | ||
const isArray = Array.isArray(param) | ||
|
||
if (isArray) { | ||
parsedParam = param.map((address) => address.toLowerCase()) | ||
} else { | ||
parsedParam = param.toLowerCase() | ||
} | ||
} | ||
|
||
retData.params.push({ | ||
name: input.name, | ||
value: parsedParam, | ||
type: input.type | ||
}) | ||
} | ||
|
||
return retData | ||
} | ||
} | ||
|
||
methodName(data: string): string | null { | ||
const methodId = data.slice(2, 10) | ||
const method = this.methods[methodId] | ||
|
||
return method ? method.name : null | ||
} | ||
|
||
private parseMethods(): Record<string, JsonFunctionInterface | JsonEventInterface> { | ||
const methods: Record<string, JsonFunctionInterface | JsonEventInterface> = {} | ||
|
||
for (const abi of this.schema) { | ||
if (!abi.name) { | ||
continue | ||
} | ||
|
||
const inputTypes = abi.inputs.map(componentType) | ||
const signature = sha3(abi.name + '(' + inputTypes.join(',') + ')') as string | ||
|
||
const methodId = abi.type === 'event' ? signature.slice(2) : signature.slice(2, 10) // event | function | ||
|
||
methods[methodId] = abi | ||
} | ||
|
||
return methods | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.