Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EIP 712 fixes #7095

Merged
merged 11 commits into from
Jun 17, 2024
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 10 additions & 11 deletions packages/web3-eth-abi/src/eip_712.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ along with web3.js. If not, see <http://www.gnu.org/licenses/>.

/**
* The web3.eth.abi functions let you encode and decode parameters to ABI (Application Binary Interface) for function calls to the EVM (Ethereum Virtual Machine).
*
*
* For using Web3 ABI functions, first install Web3 package using `npm i web3` or `yarn add web3`.
* After that, Web3 ABI functions will be available.
* After that, Web3 ABI functions will be available.
* ```ts
* import { Web3 } from 'web3';
*
*
* const web3 = new Web3();
* const encoded = web3.eth.abi.encodeFunctionSignature({
* name: 'myMethod',
Expand All @@ -35,14 +35,14 @@ along with web3.js. If not, see <http://www.gnu.org/licenses/>.
* name: 'myString'
* }]
* });
*
*
* ```
*
*
* For using individual package install `web3-eth-abi` package using `npm i web3-eth-abi` or `yarn add web3-eth-abi` and only import required functions.
* This is more efficient approach for building lightweight applications.
* This is more efficient approach for building lightweight applications.
* ```ts
* import { encodeFunctionSignature } from 'web3-eth-abi';
*
*
* const encoded = encodeFunctionSignature({
* name: 'myMethod',
* type: 'function',
Expand All @@ -54,13 +54,12 @@ along with web3.js. If not, see <http://www.gnu.org/licenses/>.
* name: 'myString'
* }]
* });
*
*
* ```
*
*
* @module ABI
*/


// This code was taken from: https://github.com/Mrtenz/eip-712/tree/master

import { Eip712TypedData } from 'web3-types';
Expand Down Expand Up @@ -231,7 +230,7 @@ const encodeData = (
): string => {
const [types, values] = typedData.types[type].reduce<[string[], unknown[]]>(
([_types, _values], field) => {
if (isNullish(data[field.name]) || isNullish(data[field.name])) {
if (isNullish(data[field.name]) || isNullish(field.type)) {
throw new AbiError(`Cannot encode data: missing data for '${field.name}'`, {
data,
field,
Expand Down
87 changes: 45 additions & 42 deletions packages/web3-eth-accounts/src/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,29 @@

/**
* The web3 accounts package contains functions to generate Ethereum accounts and sign transactions & data.
*
*
* For using accounts functions, first install Web3 package using `npm i web3` or `yarn add web3` based on your package manager usage.
* After that, Accounts functions will be available as mentioned in following snippet.
* After that, Accounts functions will be available as mentioned in following snippet.
* ```ts
* import {Web3} from 'web3';
*
*
* const web3 = new Web3();
* const account = web3.eth.accounts.create();
* const result = web3.eth.accounts.hashMessage("Test Message");
*
*
* ```
*
*
* For using individual package install `web3-eth-accounts` package using `npm i web3-eth-accounts` or `yarn add web3-eth-accounts` and only import required functions.
* This is more efficient approach for building lightweight applications.
* This is more efficient approach for building lightweight applications.
* ```ts
* import {create,hashMessage} from 'web3-eth-accounts';
*
*
* const account = create();
* const result = hashMessage("Test Message");
*
*
* ```
* @module Accounts
*
*
*/

import {
Expand Down Expand Up @@ -98,24 +98,23 @@
SignResult,
} from './types.js';


/**
* Get the private key Uint8Array after the validation.
* Note: This function is not exported through main web3 package, so for using it directly import from accounts package.
* Note: This function is not exported through main web3 package, so for using it directly import from accounts package.
* @param data - Private key
* @param ignoreLength - Optional, ignore length check during validation
* @param ignoreLength - Optional, ignore length check during validation
* @returns The Uint8Array private key
*
* ```ts
* parseAndValidatePrivateKey("0x08c673022000ece7964ea4db2d9369c50442b2869cbd8fc21baaca59e18f642c")
*
*
* > Uint8Array(32) [
* 186, 26, 143, 168, 235, 179, 90, 75,
* 101, 63, 84, 221, 152, 150, 30, 203,
* 8, 113, 94, 226, 53, 213, 216, 5,
* 194, 159, 17, 53, 219, 97, 121, 248
* ]
*
*
* ```
*/
export const parseAndValidatePrivateKey = (data: Bytes, ignoreLength?: boolean): Uint8Array => {
Expand All @@ -127,7 +126,7 @@
}

try {
privateKeyUint8Array = isUint8Array(data) ? (data ) : bytesToUint8Array(data);
privateKeyUint8Array = isUint8Array(data) ? data : bytesToUint8Array(data);
} catch {
throw new InvalidPrivateKeyError();
}
Expand All @@ -149,19 +148,22 @@
*
* ```ts
* web3.eth.accounts.hashMessage("Hello world")
*
*
* > "0x8144a6fa26be252b86456491fbcd43c1de7e022241845ffea1c3df066f7cfede"
*
*
* web3.eth.accounts.hashMessage(web3.utils.utf8ToHex("Hello world")) // Will be hex decoded in hashMessage
*
*
* > "0x8144a6fa26be252b86456491fbcd43c1de7e022241845ffea1c3df066f7cfede"
* ```
*/
export const hashMessage = (message: string): string => {
export const hashMessage = (message: string, noPreamble?: boolean): string => {
const messageHex = isHexStrict(message) ? message : utf8ToHex(message);

const messageBytes = hexToBytes(messageHex);

if (noPreamble) {
return sha3Raw(messageBytes);

Check warning on line 165 in packages/web3-eth-accounts/src/account.ts

View check run for this annotation

Codecov / codecov/patch

packages/web3-eth-accounts/src/account.ts#L165

Added line #L165 was not covered by tests
}
const preamble = hexToBytes(
fromUtf8(`\x19Ethereum Signed Message:\n${messageBytes.byteLength}`),
);
Expand Down Expand Up @@ -193,10 +195,10 @@
* }
* ```
*/
export const sign = (data: string, privateKey: Bytes): SignResult => {
export const sign = (data: string, privateKey: Bytes, noPreamble = false): SignResult => {
const privateKeyUint8Array = parseAndValidatePrivateKey(privateKey);

const hash = hashMessage(data);
const hash = hashMessage(data, noPreamble);

const signature = secp256k1.sign(hash.substring(2), privateKeyUint8Array);
const signatureBytes = signature.toCompactRawBytes();
Expand Down Expand Up @@ -228,7 +230,7 @@
* Signing a legacy transaction
* ```ts
* import {signTransaction, Transaction} from 'web3-eth-accounts';
*
*
* signTransaction(new Transaction({
* to: '0x118C2E5F57FD62C2B5b46a5ae9216F4FF4011a07',
* value: '0x186A0',
Expand All @@ -238,7 +240,7 @@
* chainId: 1,
* nonce: 0 }),
* '0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318')
*
*
* > {
* messageHash: '0x28b7b75f7ba48d588a902c1ff4d5d13cc0ca9ac0aaa39562368146923fb853bf',
* v: '0x25',
Expand All @@ -247,11 +249,11 @@
* rawTransaction: '0xf869808609184e72a0008352081294118c2e5f57fd62c2b5b46a5ae9216f4ff4011a07830186a08025a00601b0017b0e20dd0eeda4b895fbc1a9e8968990953482214f880bae593e71b5a0690d984493560552e3ebdcc19a65b9c301ea9ddc82d3ab8cfde60485fd5722ce',
* transactionHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'
* ```
*
*
* Signing an eip 1559 transaction
* ```ts
* import {signTransaction, Transaction} from 'web3-eth-accounts';
*
*
* signTransaction(new Transaction({
* to: '0xF0109fC8DF283027b6285cc889F5aA624EaC1F55',
* maxPriorityFeePerGas: '0x3B9ACA00',
Expand All @@ -271,11 +273,11 @@
* transactionHash: '0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470'
* }
* ```
*
*
* Signing an eip 2930 transaction
* ```ts
* import {signTransaction, Transaction} from 'web3-eth-accounts';
*
*
* signTransaction(new Transaction ({
* chainId: 1,
* nonce: 0,
Expand All @@ -294,7 +296,7 @@
* },
* ],
* }),"0x4c0883a69102937d6231471b5dbb6204fe5129617082792ae468d01a3f362318")
*
*
* > {
* messageHash: '0xc55ea24bdb4c379550a7c9a6818ac39ca33e75bc78ddb862bd82c31cc1c7a073',
* v: '0x26',
Expand Down Expand Up @@ -366,11 +368,11 @@
* @param s - S value in signature
* @param prefixed - (default: false) If the last parameter is true, the given message will NOT automatically be prefixed with `"\\x19Ethereum Signed Message:\\n" + message.length + message`, and assumed to be already prefixed.
* @returns The Ethereum address used to sign this data
*
*
* ```ts
* const data = 'Some data';
* const sigObj = web3.eth.accounts.sign(data, '0xbe6383dad004f233317e46ddb46ad31b16064d14447a95cc1d8c8d4bc61c3728')
*
*
* > {
* message: 'Some data',
* messageHash: '0x1da44b586eb0729ff70a73c326926f6ed5a25f5b056e7f47fbc6e58d86871655',
Expand All @@ -379,10 +381,10 @@
* s: '0x53e41351267b20d4a89ebfe9c8f03c04de9b345add4a52f15bd026b63c8fb150',
* signature: '0xa8037a6116c176a25e6fc224947fde9e79a2deaa0dd8b67b366fbdfdbffc01f953e41351267b20d4a89ebfe9c8f03c04de9b345add4a52f15bd026b63c8fb1501b'
* }
*
*
* // now recover
* web3.eth.accounts.recover(data, sigObj.v, sigObj.r, sigObj.s)
*
*
* > 0xEB014f8c8B418Db6b45774c326A0E64C78914dC0
* ```
*/
Expand All @@ -392,20 +394,21 @@
prefixedOrR?: boolean | string,
s?: string,
prefixed?: boolean,
noPreamble?: boolean,
): Address => {
if (typeof data === 'object') {
const signatureStr = `${data.r}${data.s.slice(2)}${data.v.slice(2)}`;
return recover(data.messageHash, signatureStr, prefixedOrR);
return recover(data.messageHash, signatureStr, prefixedOrR, s, prefixed, noPreamble);

Check warning on line 401 in packages/web3-eth-accounts/src/account.ts

View check run for this annotation

Codecov / codecov/patch

packages/web3-eth-accounts/src/account.ts#L401

Added line #L401 was not covered by tests
}
if (typeof signatureOrV === 'string' && typeof prefixedOrR === 'string' && !isNullish(s)) {
const signatureStr = `${prefixedOrR}${s.slice(2)}${signatureOrV.slice(2)}`;
return recover(data, signatureStr, prefixed);
return recover(data, signatureStr, prefixed, s, prefixed, noPreamble);
}

if (isNullish(signatureOrV)) throw new InvalidSignatureError('signature string undefined');

const V_INDEX = 130; // r = first 32 bytes, s = second 32 bytes, v = last byte of signature
const hashedMessage = prefixedOrR ? data : hashMessage(data);
const hashedMessage = prefixedOrR ? data : hashMessage(data, noPreamble);

let v = parseInt(signatureOrV.substring(V_INDEX), 16); // 0x + r + s + v
if (v > 26) {
Expand All @@ -422,7 +425,7 @@
const address = toChecksumAddress(`0x${publicHash.slice(-40)}`);

return address;
};;
};

/**
* Get the ethereum Address from a private key
Expand All @@ -433,7 +436,7 @@
* @example
* ```ts
* web3.eth.accounts.privateKeyToAddress("0xbe6383dad004f233317e46ddb46ad31b16064d14447a95cc1d8c8d4bc61c3728")
*
*
* > "0xEB014f8c8B418Db6b45774c326A0E64C78914dC0"
* ```
*/
Expand Down Expand Up @@ -462,7 +465,7 @@
* @example
* ```ts
* web3.eth.accounts.privateKeyToPublicKey("0x1e046a882bb38236b646c9f135cf90ad90a140810f439875f2a6dd8e50fa261f", true)
*
*
* > "0x42beb65f179720abaa3ec9a70a539629cbbc5ec65bb57e7fc78977796837e537662dd17042e6449dc843c281067a4d6d8d1a1775a13c41901670d5de7ee6503a" // uncompressed public key
* ```
*/
Expand All @@ -485,7 +488,7 @@
*
* Encrypt using scrypt options:
* ```ts
*
*
* web3.eth.accounts.encrypt(
* '0x67f476289210e3bef3c1c75e4de993ff0a00663df00def84e73aa7411eac18a6',
* '123',
Expand Down Expand Up @@ -659,7 +662,7 @@
*
* ```ts
* web3.eth.accounts.privateKeyToAccount("0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709");
*
*
* > {
* address: '0xb8CE9ab6943e0eCED004cDe8e3bBed6568B2Fa01',
* privateKey: '0x348ce564d427a3311b6536bbcff9390d69395b06ed6c486954e971d960fe8709',
Expand Down Expand Up @@ -740,8 +743,8 @@
* mac: 'efbf6d3409f37c0084a79d5fdf9a6f5d97d11447517ef1ea8374f51e581b7efd'
* }
* }, '123').then(console.log);
*
*
*
*
* > {
* address: '0xcdA9A91875fc35c8Ac1320E098e584495d66e47c',
* privateKey: '67f476289210e3bef3c1c75e4de993ff0a00663df00def84e73aa7411eac18a6',
Expand Down
Loading
Loading