diff --git a/package-lock.json b/package-lock.json index 0e2cd22b..313d4d1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,20 @@ { "name": "gridplus-sdk", - "version": "2.7.0", + "version": "2.7.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "gridplus-sdk", - "version": "2.7.0", + "version": "2.7.1", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@ethereumjs/common": "4.3.0", + "@ethereumjs/common": "4.4.0", "@ethereumjs/rlp": "^5.0.2", - "@ethereumjs/tx": "5.3.0", + "@ethereumjs/tx": "5.4.0", "@ethersproject/abi": "^5.7.0", - "@metamask/eth-sig-util": "^7.0.3", + "@metamask/eth-sig-util": "^8.0.0", "@types/uuid": "^10.0.0", "aes-js": "^3.1.2", "bech32": "^2.0.0", @@ -27,7 +27,7 @@ "elliptic": "6.5.6", "hash.js": "^1.1.7", "js-sha3": "^0.9.3", - "secp256k1": "5.0.0", + "secp256k1": "5.0.1", "uuid": "^10.0.0" }, "devDependencies": { @@ -67,7 +67,7 @@ "random-words": "^1.1.1", "readline-sync": "^1.4.9", "seedrandom": "^3.0.5", - "typescript": "^4.7.0", + "typescript": "^5.6.3", "vite": "^3.0.2", "vitest": "^0.15.2" } @@ -350,11 +350,11 @@ } }, "node_modules/@ethereumjs/common": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-4.3.0.tgz", - "integrity": "sha512-shBNJ0ewcPNTUfZduHiczPmqkfJDn0Dh/9BR5fq7xUFTuIq7Fu1Vx00XDwQVIrpVL70oycZocOhBM6nDO+4FEQ==", + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/common/-/common-4.4.0.tgz", + "integrity": "sha512-Fy5hMqF6GsE6DpYTyqdDIJPJgUtDn4dL120zKw+Pswuo+iLyBsEYuSyzMw6NVzD2vDzcBG9fE4+qX4X2bPc97w==", "dependencies": { - "@ethereumjs/util": "^9.0.3" + "@ethereumjs/util": "^9.1.0" } }, "node_modules/@ethereumjs/rlp": { @@ -369,26 +369,26 @@ } }, "node_modules/@ethereumjs/tx": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-5.3.0.tgz", - "integrity": "sha512-uv++XYuIfuqYbvymL3/o14hHuC6zX0nRQ1nI2FHsbkkorLZ2ChEIDqVeeVk7Xc9/jQNU/22sk9qZZkRlsveXxw==", + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/tx/-/tx-5.4.0.tgz", + "integrity": "sha512-SCHnK7m/AouZ7nyoR0MEXw1OO/tQojSbp88t8oxhwes5iZkZCtfFdUrJaiIb72qIpH2FVw6s1k1uP7LXuH7PsA==", "dependencies": { - "@ethereumjs/common": "^4.3.0", + "@ethereumjs/common": "^4.4.0", "@ethereumjs/rlp": "^5.0.2", - "@ethereumjs/util": "^9.0.3", - "ethereum-cryptography": "^2.1.3" + "@ethereumjs/util": "^9.1.0", + "ethereum-cryptography": "^2.2.1" }, "engines": { "node": ">=18" } }, "node_modules/@ethereumjs/util": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-9.0.3.tgz", - "integrity": "sha512-PmwzWDflky+7jlZIFqiGsBPap12tk9zK5SVH9YW2OEnDN7OEhCjUOMzbOqwuClrbkSIkM2ERivd7sXZ48Rh/vg==", + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-9.1.0.tgz", + "integrity": "sha512-XBEKsYqLGXLah9PNJbgdkigthkG7TAGvlD/sH12beMXEyHDyigfcbdvHhmLyDWgDyOJn4QwiQUaF7yeuhnjdog==", "dependencies": { "@ethereumjs/rlp": "^5.0.2", - "ethereum-cryptography": "^2.1.3" + "ethereum-cryptography": "^2.2.1" }, "engines": { "node": ">=18" @@ -1313,9 +1313,9 @@ } }, "node_modules/@metamask/eth-sig-util": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-7.0.3.tgz", - "integrity": "sha512-PAtGnOkYvh90k2lEZldq/FK7GTLF6WxE+2bV85PoA3pqlJnmJCAY62tuvxHSwnVngSKlc4mcNvjnUg2eYO6JGg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-8.0.0.tgz", + "integrity": "sha512-IwE6aoxUL39IhmsAgE4nk+OZbNo+ThFZRNsUjE1pjdEa4MFpWzm1Rue4zJ5DMy1oUyZBi/aiCLMhdMnjl2bh2Q==", "dependencies": { "@ethereumjs/util": "^8.1.0", "@metamask/abi-utils": "^2.0.4", @@ -1325,7 +1325,7 @@ "tweetnacl": "^1.0.3" }, "engines": { - "node": "^16.20 || ^18.16 || >=20" + "node": "^18.18 || ^20.14 || >=22" } }, "node_modules/@metamask/eth-sig-util/node_modules/@ethereumjs/rlp": { @@ -6238,6 +6238,19 @@ "ansi-styles": "^3.2.0" } }, + "node_modules/prettier-eslint/node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/prettier-linter-helpers": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz", @@ -6650,17 +6663,36 @@ "dev": true }, "node_modules/secp256k1": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.0.tgz", - "integrity": "sha512-TKWX8xvoGHrxVdqbYeZM9w+izTF4b9z3NhSaDkdn81btvuh+ivbIMGT/zQvDtTFWhRlThpoz6LEYTr7n8A5GcA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-5.0.1.tgz", + "integrity": "sha512-lDFs9AAIaWP9UCdtWrotXWWF9t8PWgQDcxqgAnpM9rMqxb3Oaq2J0thzPVSxBwdJgyQtkU/sYtFtbM1RSt/iYA==", "hasInstallScript": true, "dependencies": { - "elliptic": "^6.5.4", + "elliptic": "^6.5.7", "node-addon-api": "^5.0.0", "node-gyp-build": "^4.2.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" + } + }, + "node_modules/secp256k1/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/secp256k1/node_modules/elliptic": { + "version": "6.5.7", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.7.tgz", + "integrity": "sha512-ESVCtTwiA+XhY3wyh24QqRGBoP3rEdDUl3EDUUo9tft074fi19IrdpH7hLCMMP3CIj7jb3W96rn8lt/BqIlt5Q==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" } }, "node_modules/seedrandom": { @@ -7064,16 +7096,16 @@ "dev": true }, "node_modules/typescript": { - "version": "4.7.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz", - "integrity": "sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==", + "version": "5.6.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", + "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/undici-types": { diff --git a/package.json b/package.json index 4221bcbe..2f21c2e7 100644 --- a/package.json +++ b/package.json @@ -49,11 +49,11 @@ "js-sha3": "^0.9.3" }, "dependencies": { - "@ethereumjs/common": "4.3.0", + "@ethereumjs/common": "4.4.0", "@ethereumjs/rlp": "^5.0.2", - "@ethereumjs/tx": "5.3.0", + "@ethereumjs/tx": "5.4.0", "@ethersproject/abi": "^5.7.0", - "@metamask/eth-sig-util": "^7.0.3", + "@metamask/eth-sig-util": "^8.0.0", "@types/uuid": "^10.0.0", "aes-js": "^3.1.2", "bech32": "^2.0.0", @@ -66,7 +66,7 @@ "elliptic": "6.5.6", "hash.js": "^1.1.7", "js-sha3": "^0.9.3", - "secp256k1": "5.0.0", + "secp256k1": "5.0.1", "uuid": "^10.0.0" }, "devDependencies": { @@ -106,7 +106,7 @@ "random-words": "^1.1.1", "readline-sync": "^1.4.9", "seedrandom": "^3.0.5", - "typescript": "^4.7.0", + "typescript": "^5.6.3", "vite": "^3.0.2", "vitest": "^0.15.2" }, diff --git a/src/__test__/e2e/api.test.ts b/src/__test__/e2e/api.test.ts index 31480925..8c615776 100644 --- a/src/__test__/e2e/api.test.ts +++ b/src/__test__/e2e/api.test.ts @@ -1,7 +1,5 @@ /* eslint-disable quotes */ import { getClient } from './../../api/utilities'; -import { Chain, Common, Hardfork } from '@ethereumjs/common'; -import { TransactionFactory } from '@ethereumjs/tx'; import { question } from 'readline-sync'; import { RLP } from '@ethereumjs/rlp'; import { @@ -98,25 +96,19 @@ describe('API', () => { describe('transactions', () => { const txData = { type: 1, - maxFeePerGas: 1200000000, - maxPriorityFeePerGas: 1200000000, + chainId: 1, + maxFeePerGas: '1200000000', + maxPriorityFeePerGas: '1200000000', nonce: 0, - gasLimit: 50000, - to: '0xe242e54155b1abc71fc118065270cecaaf8b7768', - value: 1000000000000, - data: '0x17e914679b7e160613be4f8c2d3203d236286d74eb9192f6d6f71b9118a42bb033ccd8e8', - gasPrice: 1200000000, - }; + gasLimit: '50000', + to: '0x7a250d5630b4cf539739df2c5dacb4c659f2488d', + value: '1000000000000', + data: '0x38ed17390000000000000000000000000000000000000000000c1c173c5b782a5b154ab900000000000000000000000000000000000000000000000f380d77022fe8c32600000000000000000000000000000000000000000000000000000000000000a00000000000000000000000007ae7684581f0298241c3d6a6567a48d56b42b15c00000000000000000000000000000000000000000000000000000000622f8d27000000000000000000000000000000000000000000000000000000000000000300000000000000000000000095ad61b0a150d79219dcf64e1e6cc01f0b64c4ce000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc200000000000000000000000050522c769e01eb06c02bd299066509d8f97a69ae', + gasPrice: '1200000000', + } as const; test('generic', async () => { - const common = new Common({ - chain: Chain.Mainnet, - hardfork: Hardfork.London, - }); - const tx = TransactionFactory.fromTxData(txData, { common }); - const payload = tx.getMessageToSign(false); - - await sign(payload); + await sign(txData); }); test('legacy', async () => { diff --git a/src/api/signing.ts b/src/api/signing.ts index 333a0342..e30f4dbb 100644 --- a/src/api/signing.ts +++ b/src/api/signing.ts @@ -1,3 +1,4 @@ +import { ethers } from 'ethers'; import { Constants } from '..'; import { BTC_LEGACY_DERIVATION, @@ -7,30 +8,33 @@ import { DEFAULT_ETH_DERIVATION, SOLANA_DERIVATION, } from '../constants'; +import { fetchDecoder } from '../functions/fetchDecoder'; import { - SignRequestParams, - SignData, - EIP712MessagePayload, BitcoinSignPayload, - Currency, + EIP712MessagePayload, + SignData, + SigningPayload, + SignRequestParams, + TransactionRequest, } from '../types'; import { isEIP712Payload, queue } from './utilities'; export const sign = async ( - payload: Uint8Array | Buffer | Buffer[], + transaction: TransactionRequest, overrides?: SignRequestParams, ): Promise => { - const tx: SignRequestParams = { - data: { - signerPath: DEFAULT_ETH_DERIVATION, - curveType: Constants.SIGNING.CURVES.SECP256K1, - hashType: Constants.SIGNING.HASHES.KECCAK256, - encodingType: Constants.SIGNING.ENCODINGS.EVM, - payload, - }, - ...overrides, + const serializedTx = ethers.utils.serializeTransaction(transaction); + + const payload: SigningPayload = { + signerPath: DEFAULT_ETH_DERIVATION, + curveType: Constants.SIGNING.CURVES.SECP256K1, + hashType: Constants.SIGNING.HASHES.KECCAK256, + encodingType: Constants.SIGNING.ENCODINGS.EVM, + payload: serializedTx, + decoder: await fetchDecoder(transaction), }; - return queue((client) => client.sign(tx)); + + return queue((client) => client.sign({ data: payload, ...overrides })); }; export const signMessage = async ( @@ -45,9 +49,9 @@ export const signMessage = async ( protocol: 'signPersonal', payload, ...overrides, - }, - currency: CURRENCIES.ETH_MSG as Currency, - } as SignRequestParams & { data: {protocol: string}}; + } as SigningPayload, + currency: CURRENCIES.ETH_MSG, + }; if (isEIP712Payload(payload)) { tx.data.protocol = 'eip712'; @@ -64,7 +68,7 @@ export const signBtcLegacyTx = async ( signerPath: BTC_LEGACY_DERIVATION, ...payload, }, - currency: 'BTC' as Currency, + currency: CURRENCIES.BTC, }; return queue((client) => client.sign(tx)); }; @@ -77,7 +81,7 @@ export const signBtcSegwitTx = async ( signerPath: BTC_SEGWIT_DERIVATION, ...payload, }, - currency: 'BTC'as Currency, + currency: CURRENCIES.BTC, }; return queue((client) => client.sign(tx)); }; @@ -90,7 +94,7 @@ export const signBtcWrappedSegwitTx = async ( signerPath: BTC_WRAPPED_SEGWIT_DERIVATION, ...payload, }, - currency: 'BTC'as Currency, + currency: CURRENCIES.BTC, }; return queue((client) => client.sign(tx)); }; diff --git a/src/functions/fetchDecoder.ts b/src/functions/fetchDecoder.ts new file mode 100644 index 00000000..af1afb5d --- /dev/null +++ b/src/functions/fetchDecoder.ts @@ -0,0 +1,37 @@ +import { validateConnectedClient } from '../shared/validators'; + +import { getClient } from '../api'; +import { fetchCalldataDecoder } from '../util'; +import { TransactionRequest } from '../types'; + +/** + * `fetchDecoder` fetches the ABI for a given contract address and chain ID. + * @category Lattice + * @returns An object containing the ABI and encoded definition of the contract. + */ +export async function fetchDecoder({ + data, + to, + chainId, +}: TransactionRequest): Promise { + try { + const client = await getClient(); + validateConnectedClient(client); + + const fwVersion = client.getFwVersion(); + const supportsDecoderRecursion = + fwVersion.major > 0 || fwVersion.minor >= 16; + + const { def } = await fetchCalldataDecoder( + data, + to, + chainId, + supportsDecoderRecursion, + ); + + return def; + } catch (error) { + console.warn('Failed to fetch ABI:', error); + return undefined; + } +} diff --git a/src/types/sign.ts b/src/types/sign.ts index 2bf0ced7..7ac75374 100644 --- a/src/types/sign.ts +++ b/src/types/sign.ts @@ -4,6 +4,26 @@ import { FirmwareConstants } from './firmware'; export type ETH_MESSAGE_PROTOCOLS = 'eip712' | 'signPersonal'; +export const TRANSACTION_TYPE = { + LEGACY: 0, + EIP2930: 1, + EIP1559: 2, +}; + +export type TransactionRequest = { + to: string; + value: string; + data: string; + chainId: number; + nonce: number; + gasLimit: string; + maxFeePerGas: string; + maxPriorityFeePerGas: string; + from?: string; + accessList?: Array<{ address: string; storageKeys: string[] }>; + type?: (typeof TRANSACTION_TYPE)[keyof typeof TRANSACTION_TYPE]; +}; + export interface SigningPayload { signerPath: SigningPath; payload: Uint8Array | Buffer | Buffer[] | string | EIP712MessagePayload; @@ -11,6 +31,7 @@ export interface SigningPayload { hashType: number; encodingType?: number; protocol?: ETH_MESSAGE_PROTOCOLS; + decoder?: Buffer; } export interface SignRequestParams { @@ -84,7 +105,6 @@ export type BitcoinSignPayload = { export interface DecodeSignResponseParams { data: Buffer; - /** The original request data */ request: SignRequest; isGeneric: boolean; currency?: Currency; diff --git a/src/util.ts b/src/util.ts index c6fcab8a..ccf2da68 100644 --- a/src/util.ts +++ b/src/util.ts @@ -446,7 +446,8 @@ async function fetchSupportedChainData( throw new Error('Server response was malformed'); } }) - .catch(() => { + .catch((error) => { + console.log(error); throw new Error('Fetching data from external network failed'); }); } diff --git a/tsconfig.json b/tsconfig.json index 999e31a8..8a497815 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -61,8 +61,14 @@ /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ "target": "es5" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, - "typeRoots": ["node_modules/@types", "src/**/types"], - "types": ["node", "jest", "vitest", "vitest/globals"] + "typeRoots": [ + "node_modules/@types", + "src/**/types" + ], + "types": [ + "node", + "jest" + ] }, "exclude": [ "node_modules", @@ -71,5 +77,8 @@ "**/*.test.ts", "forge" ], - "include": ["./src", "forge"] -} + "include": [ + "./src", + "forge" + ] +} \ No newline at end of file