Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

micro-eth-signer

Package Overview
Dependencies
Maintainers
1
Versions
41
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

micro-eth-signer - npm Package Compare versions

Comparing version 0.8.1 to 0.9.0

_type_test.d.ts

59

package.json
{
"name": "micro-eth-signer",
"version": "0.8.1",
"version": "0.9.0",
"description": "Minimal library for Ethereum transactions, addresses and smart contracts",
"files": [
"lib",
"src"
],
"main": "lib/index.js",
"module": "lib/esm/index.js",
"types": "lib/index.d.ts",
"files": ["abi", "esm", "net", "src", "*.js", "*.d.ts", "*.js.map", "*.d.ts.map"],
"main": "index.js",
"module": "esm/index.js",
"types": "index.d.ts",
"dependencies": {
"@noble/curves": "~1.4.0",
"@noble/hashes": "~1.4.0",
"micro-packed": "~0.5.3"
"micro-packed": "~0.6.2"
},

@@ -22,3 +19,5 @@ "devDependencies": {

"prettier": "3.1.1",
"typescript": "5.3.2"
"snappyjs": "0.7.0",
"typescript": "5.3.2",
"yaml": "2.4.1"
},

@@ -33,25 +32,35 @@ "author": "Paul Miller (https://paulmillr.com)",

".": {
"types": "./lib/index.d.ts",
"import": "./lib/esm/index.js",
"default": "./lib/index.js"
"types": "./index.d.ts",
"import": "./esm/index.js",
"default": "./index.js"
},
"./abi": {
"types": "./lib/abi/index.d.ts",
"import": "./lib/esm/abi/index.js",
"default": "./lib/abi/index.js"
"types": "./abi/index.d.ts",
"import": "./esm/abi/index.js",
"default": "./abi/index.js"
},
"./net": {
"types": "./lib/net/index.d.ts",
"import": "./lib/esm/net/index.js",
"default": "./lib/net/index.js"
"types": "./net/index.d.ts",
"import": "./esm/net/index.js",
"default": "./net/index.js"
},
"./rlp": {
"types": "./net/rlp.d.ts",
"import": "./esm/net/rlp.js",
"default": "./net/rlp.js"
},
"./ssz": {
"types": "./net/ssz.d.ts",
"import": "./esm/net/ssz.js",
"default": "./net/ssz.js"
},
"./tx": {
"types": "./lib/tx.d.ts",
"import": "./lib/esm/tx.js",
"default": "./lib/tx.js"
"types": "./tx.d.ts",
"import": "./esm/tx.js",
"default": "./tx.js"
},
"./utils": {
"types": "./lib/utils.d.ts",
"import": "./lib/esm/utils.js",
"default": "./lib/utils.js"
"types": "./utils.d.ts",
"import": "./esm/utils.js",
"default": "./utils.js"
}

@@ -58,0 +67,0 @@ },

@@ -5,11 +5,10 @@ # micro-eth-signer

- 🔓 Secure: 3 deps, audited [noble](https://paulmillr.com/noble/) cryptography
- 🔓 Secure: 3 deps, audited [noble](https://paulmillr.com/noble/) cryptography, no network code
- 🔻 Tree-shaking-friendly: use only what's necessary, other code won't be included
- 🌍 No network code: simplified auditing and offline usage
- 🔍 Unique tests: 150MB of test vectors from EIPs, ethers and viem
- ✍️ Create and sign transactions, generate and checksum addresses
- 📖 Human-readable hints for transactions and events
- 🔍 Reliable: 150MB of test vectors from EIPs, ethers and viem
- ✍️ Create, sign and decode transactions using human-readable hints
- 🌍 Fetch balances and history from an archive node
- 🆎 Call smart contracts: Chainlink and Uniswap APIs are included
- 🦺 Typescript-friendly ABI and RLP decoding
- 🪶 3000 lines for everything
- 🦺 Typescript-friendly ABI, RLP and SSZ decoding
- 🪶 1200 lines for core functionality

@@ -29,8 +28,6 @@ Check out article [ZSTs, ABIs, stolen keys and broken legs](https://github.com/paulmillr/micro-eth-signer/discussions/20) about caveats of secure ABI parsing found during development of the library.

- [Create and sign transactions](#create-and-sign-transactions)
- [Create and checksum addresses](#create-and-checksum-addresses)
- [Generate random keys and addresses](#generate-random-keys-and-addresses)
- [Human-readable hints](#human-readable-hints)
- [Decoding transactions](#decoding-transactions)
- [Decoding events](#decoding-events)
- [Transactions: create, sign](#create-and-sign-transactions)
- [Addresses: create, checksum](#create-and-checksum-addresses)
- [Generate random wallet](#generate-random-keys-and-addresses)
- [Fetch balances and history from an archive node](#fetch-balances-and-history-from-an-archive-node)
- [Call smart contracts](#call-smart-contracts)

@@ -40,3 +37,7 @@ - [Fetch Chainlink oracle prices](#fetch-chainlink-oracle-prices)

- [ABI type inference](#abi-type-inference)
- [RLP parsing](#rlp-parsing)
- Parsing
- [Human-readable transaction hints](#human-readable-transaction-hints)
- [Human-readable event hints](#human-readable-event-hints)
- [RLP parsing](#rlp-parsing)
- [SSZ parsing](#ssz-parsing)
- [Sign and verify messages](#sign-and-verify-messages)

@@ -47,3 +48,3 @@ - [Security](#security)

### Create and sign transactions
### Transactions: create, sign

@@ -68,3 +69,3 @@ ```ts

### Create and checksum addresses
### Addresses: create, checksum

@@ -79,4 +80,4 @@ ```ts

checksummedAddress, // 0x0089d53F703f7E0843953D48133f74cE247184c2
addr.verifyChecksum(checksummedAddress), // true
addr.verifyChecksum(nonChecksummedAddress), // also true
addr.isValid(checksummedAddress), // true
addr.isValid(nonChecksummedAddress), // also true
addr.fromPrivateKey(priv),

@@ -87,3 +88,3 @@ addr.fromPublicKey(pub)

### Generate random keys and addresses
### Generate random wallet

@@ -98,91 +99,39 @@ ```ts

### Human-readable hints
### Fetch balances and history from an archive node
#### Decoding transactions
The transaction sent ERC-20 USDT token between addresses. The library produces a following hint:
> Transfer 22588 USDT to 0xdac17f958d2ee523a2206206994597c13d831ec7
```ts
import { decodeTx } from 'micro-eth-signer/abi';
import { ArchiveNodeProvider, FetchProvider } from 'micro-eth-signer/net';
const RPC_URL = 'http://localhost:8545'; // URL to any RPC
const rpc = new FetchProvider(globalThis.fetch, RPC_URL); // use built-in fetch()
const prov = new ArchiveNodeProvider(rpc);
const addr = '0xd8da6bf26964af9d7eed9e03e53415d37aa96045';
const tx =
'0xf8a901851d1a94a20082c12a94dac17f958d2ee523a2206206994597c13d831ec780b844a9059cbb000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000054259870025a066fcb560b50e577f6dc8c8b2e3019f760da78b4c04021382ba490c572a303a42a0078f5af8ac7e11caba9b7dc7a64f7bdc3b4ce1a6ab0a1246771d7cc3524a7200';
// Decode tx information
deepStrictEqual(decodeTx(tx), {
name: 'transfer',
signature: 'transfer(address,uint256)',
value: {
to: '0xdac17f958d2ee523a2206206994597c13d831ec7',
value: 22588000000n,
},
hint: 'Transfer 22588 USDT to 0xdac17f958d2ee523a2206206994597c13d831ec7',
});
```
const block = await prov.blockInfo(await prov.height());
console.log('current block', block.number, block.timestamp, block.baseFeePerGas);
console.log('info for addr', addr, await prov.unspent(addr));
Or if you have already decoded tx:
// ENS example:
// import { ENS } from 'micro-eth-signer/net'; const ens = new ENS(rpc), addr = await ens.nameToAddress('vitalik.eth');
```ts
import { decodeData } from 'micro-eth-signer/abi';
const to = '0x7a250d5630b4cf539739df2c5dacb4c659f2488d';
const data =
'7ff36ab5000000000000000000000000000000000000000000000000ab54a98ceb1f0ad30000000000000000000000000000000000000000000000000000000000000080000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045000000000000000000000000000000000000000000000000000000006fd9c6ea0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000106d3c66d22d2dd0446df23d7f5960752994d600';
const value = 100000000000000000n;
deepStrictEqual(decodeData(to, data, value, { customContracts }), {
name: 'swapExactETHForTokens',
signature: 'swapExactETHForTokens(uint256,address[],address,uint256)',
value: {
amountOutMin: 12345678901234567891n,
path: [
'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
'0x106d3c66d22d2dd0446df23d7f5960752994d600',
],
to: '0xd8da6bf26964af9d7eed9e03e53415d37aa96045',
deadline: 1876543210n,
},
});
// With custom tokens/contracts
const customContracts = {
'0x106d3c66d22d2dd0446df23d7f5960752994d600': { abi: 'ERC20', symbol: 'LABRA', decimals: 9 },
};
deepStrictEqual(decodeData(to, data, value, { customContracts }), {
name: 'swapExactETHForTokens',
signature: 'swapExactETHForTokens(uint256,address[],address,uint256)',
value: {
amountOutMin: 12345678901234567891n,
path: [
'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
'0x106d3c66d22d2dd0446df23d7f5960752994d600',
],
to: '0xd8da6bf26964af9d7eed9e03e53415d37aa96045',
deadline: 1876543210n,
},
hint: 'Swap 0.1 ETH for at least 12345678901.234567891 LABRA. Expires at Tue, 19 Jun 2029 06:00:10 GMT',
});
// Other methods of ArchiveNodeProvider:
// blockInfo(block: number): Promise<BlockInfo>; // {baseFeePerGas, hash, timestamp...}
// height(): Promise<number>;
// internalTransactions(address: string, opts?: TraceOpts): Promise<any[]>;
// ethLogsSingle(topics: Topics, opts: LogOpts): Promise<Log[]>;
// ethLogs(topics: Topics, opts?: LogOpts): Promise<Log[]>;
// tokenTransfers(address: string, opts?: LogOpts): Promise<[Log[], Log[]]>;
// wethTransfers(address: string, opts?: LogOpts): Promise<[Log[]]>;
// txInfo(txHash: string, opts?: TxInfoOpts): Promise<{
// type: "legacy" | "eip2930" | "eip1559" | "eip4844"; info: any; receipt: any; raw: string | undefined;
// }>;
// tokenInfo(address: string): Promise<TokenInfo | undefined>;
// transfers(address: string, opts?: TraceOpts & LogOpts): Promise<TxTransfers[]>;
// allowances(address: string, opts?: LogOpts): Promise<TxAllowances>;
// tokenBalances(address: string, tokens: string[]): Promise<Record<string, bigint>>;
```
#### Decoding events
Basic data can be fetched from any node.
Decoding the event produces the following hint:
Historical balances, transactions and others can only be fetched from an archive node, such as Erigon or Reth.
> Allow 0xe592427a0aece92de3edee1f18e0157c05861564 spending up to 1000 BAT from 0xd8da6bf26964af9d7eed9e03e53415d37aa96045
```ts
import { decodeEvent } from 'micro-eth-signer/abi';
const to = '0x0d8775f648430679a709e98d2b0cb6250d2887ef';
const topics = [
'0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925',
'0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045',
'0x000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564',
];
const data = '0x00000000000000000000000000000000000000000000003635c9adc5dea00000';
const einfo = decodeEvent(to, topics, data);
console.log(einfo);
```
### Call smart contracts

@@ -289,11 +238,109 @@

### RLP parsing
### Parsers
#### Human-readable transaction hints
The transaction sent ERC-20 USDT token between addresses. The library produces a following hint:
> Transfer 22588 USDT to 0xdac17f958d2ee523a2206206994597c13d831ec7
```ts
import { decodeTx } from 'micro-eth-signer/abi';
const tx =
'0xf8a901851d1a94a20082c12a94dac17f958d2ee523a2206206994597c13d831ec780b844a9059cbb000000000000000000000000dac17f958d2ee523a2206206994597c13d831ec7000000000000000000000000000000000000000000000000000000054259870025a066fcb560b50e577f6dc8c8b2e3019f760da78b4c04021382ba490c572a303a42a0078f5af8ac7e11caba9b7dc7a64f7bdc3b4ce1a6ab0a1246771d7cc3524a7200';
// Decode tx information
deepStrictEqual(decodeTx(tx), {
name: 'transfer',
signature: 'transfer(address,uint256)',
value: {
to: '0xdac17f958d2ee523a2206206994597c13d831ec7',
value: 22588000000n,
},
hint: 'Transfer 22588 USDT to 0xdac17f958d2ee523a2206206994597c13d831ec7',
});
```
Or if you have already decoded tx:
```ts
import { decodeData } from 'micro-eth-signer/abi';
const to = '0x7a250d5630b4cf539739df2c5dacb4c659f2488d';
const data =
'7ff36ab5000000000000000000000000000000000000000000000000ab54a98ceb1f0ad30000000000000000000000000000000000000000000000000000000000000080000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045000000000000000000000000000000000000000000000000000000006fd9c6ea0000000000000000000000000000000000000000000000000000000000000002000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000106d3c66d22d2dd0446df23d7f5960752994d600';
const value = 100000000000000000n;
deepStrictEqual(decodeData(to, data, value, { customContracts }), {
name: 'swapExactETHForTokens',
signature: 'swapExactETHForTokens(uint256,address[],address,uint256)',
value: {
amountOutMin: 12345678901234567891n,
path: [
'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
'0x106d3c66d22d2dd0446df23d7f5960752994d600',
],
to: '0xd8da6bf26964af9d7eed9e03e53415d37aa96045',
deadline: 1876543210n,
},
});
// With custom tokens/contracts
const customContracts = {
'0x106d3c66d22d2dd0446df23d7f5960752994d600': { abi: 'ERC20', symbol: 'LABRA', decimals: 9 },
};
deepStrictEqual(decodeData(to, data, value, { customContracts }), {
name: 'swapExactETHForTokens',
signature: 'swapExactETHForTokens(uint256,address[],address,uint256)',
value: {
amountOutMin: 12345678901234567891n,
path: [
'0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
'0x106d3c66d22d2dd0446df23d7f5960752994d600',
],
to: '0xd8da6bf26964af9d7eed9e03e53415d37aa96045',
deadline: 1876543210n,
},
hint: 'Swap 0.1 ETH for at least 12345678901.234567891 LABRA. Expires at Tue, 19 Jun 2029 06:00:10 GMT',
});
```
#### Human-readable event hints
Decoding the event produces the following hint:
> Allow 0xe592427a0aece92de3edee1f18e0157c05861564 spending up to 1000 BAT from 0xd8da6bf26964af9d7eed9e03e53415d37aa96045
```ts
import { decodeEvent } from 'micro-eth-signer/abi';
const to = '0x0d8775f648430679a709e98d2b0cb6250d2887ef';
const topics = [
'0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925',
'0x000000000000000000000000d8da6bf26964af9d7eed9e03e53415d37aa96045',
'0x000000000000000000000000e592427a0aece92de3edee1f18e0157c05861564',
];
const data = '0x00000000000000000000000000000000000000000000003635c9adc5dea00000';
const einfo = decodeEvent(to, topics, data);
console.log(einfo);
```
#### RLP parsing
We implement RLP in just 100 lines of code, powered by [packed](https://github.com/paulmillr/micro-packed):
```ts
import { RLP } from 'micro-eth-signer/tx';
import { RLP } from 'micro-eth-signer/rlp';
RLP.decode(RLP.encode('dog'));
```
#### SSZ parsing
Simple serialize (SSZ) is the serialization method used on the Beacon Chain.
We implement RLP in just 900 lines of code, powered by [packed](https://github.com/paulmillr/micro-packed):
```ts
import * as ssz from 'micro-eth-signer/ssz';
```
### Sign and verify messages

@@ -300,0 +347,0 @@

import { keccak_256 } from '@noble/hashes/sha3';
import { concatBytes } from '@noble/hashes/utils';
import { bytesToHex, concatBytes, hexToBytes } from '@noble/hashes/utils';
import * as P from 'micro-packed';
import { Web3CallArgs, Web3Provider, add0x, strip0x, omit, zip } from '../utils.js';
import { bytesToHex, hexToBytes } from '@noble/hashes/utils';

@@ -18,15 +17,13 @@ /*

}
// Save pointers values next to array. ETH only stuff.
// By some reason, ptr value inside arrays is placed right after ptr.
// This should work by default without 'wrappedArray'.
// TODO: Debug more later.
function wrappedArray<T>(len: P.Length, inner: P.CoderType<T>): P.CoderType<T[]> {
// Main difference between regular array: length stored outside and offsets calculated without length
function ethArray<T>(inner: P.CoderType<T>): P.CoderType<T[]> {
return P.wrap({
size: typeof len === 'number' && inner.size ? len * inner.size : undefined,
size: undefined,
encodeStream: (w: P.Writer, value: T[]) => {
w.length(len, value.length);
U256BE_LEN.encodeStream(w, value.length);
w.bytes(P.array(value.length, inner).encode(value));
},
decodeStream: (r: P.Reader): T[] =>
P.array(r.length(len), inner).decodeStream(r.offsetReader(r.pos)),
P.array(U256BE_LEN.decodeStream(r), inner).decodeStream(r.offsetReader(r.pos)),
});

@@ -43,15 +40,15 @@ }

const inner = P.bigint(32, false, signed);
return P.wrap({
size: inner.size,
encodeStream: (w: P.Writer, value: bigint) => {
const _value = BigInt(value);
P.checkBounds(w, _value, _bits, !!signed);
inner.encodeStream(w, BigInt(_value));
},
decodeStream: (r: P.Reader): bigint => {
const value = inner.decodeStream(r);
P.checkBounds(r, value, _bits, !!signed);
return P.validate(
P.wrap({
size: inner.size,
encodeStream: (w: P.Writer, value: bigint) => inner.encodeStream(w, value),
decodeStream: (r: P.Reader): bigint => inner.decodeStream(r),
}),
(value) => {
// TODO: validate useful for narrowing types, need to add support in types?
if (typeof value === 'number') value = BigInt(value);
P.utils.checkBounds(value, _bits, !!signed);
return value;
},
});
}
);
};

@@ -175,3 +172,3 @@

// Dynamic array
return P.pointer(PTR, wrappedArray(U256BE_LEN, inner)) as any;
return P.pointer(PTR, ethArray(inner)) as any;
}

@@ -205,3 +202,3 @@ }

return P.pointer(PTR, P.padRight(32, P.bytes(U256BE_LEN), P.ZeroPad)) as any;
if (c.type === 'address') return EPad(P.hex(20, false, true)) as any;
if (c.type === 'address') return EPad(P.hex(20, { isLE: false, with0x: true })) as any;
if (c.type === 'bool') return EPad(P.bool) as any;

@@ -208,0 +205,0 @@ if ((m = /^(u?)int([0-9]+)?$/.exec(c.type)))

@@ -9,5 +9,2 @@ /*! micro-eth-signer - MIT License (c) 2021 Paul Miller (paulmillr.com) */

RE: /^(0[xX])?([0-9a-fA-F]{40})?$/,
// Not much support, only RSK for now
EIP1991_CHAINS: [30n, 31n],
parse(address: string) {

@@ -25,11 +22,2 @@ astr(address);

isValid(address: string) {
try {
const a = addr.parse(address);
return a && a.hasPrefix;
} catch (error) {
return false;
}
},
/**

@@ -41,9 +29,5 @@ * Address checksum is calculated by hashing with keccak_256.

*/
addChecksum(nonChecksummedAddress: string, chainId = 1n): string {
if (typeof chainId !== 'bigint')
throw new Error(`address.addChecksum wrong chainId=${chainId}`);
const hasEIP1191 = addr.EIP1991_CHAINS.includes(chainId);
addChecksum(nonChecksummedAddress: string): string {
const low = addr.parse(nonChecksummedAddress).data.toLowerCase();
const hashInput = hasEIP1191 ? `${chainId}0x${low}` : low;
const hash = bytesToHex(keccak_256(hashInput));
const hash = bytesToHex(keccak_256(low));
let checksummed = '';

@@ -89,9 +73,16 @@ for (let i = 0; i < low.length; i++) {

*/
verifyChecksum(checksummedAddress: string, chainId = 1n): boolean {
const { data: address } = addr.parse(checksummedAddress);
isValid(checksummedAddress: string): boolean {
let parsed: { hasPrefix: boolean; data: string };
try {
parsed = addr.parse(checksummedAddress);
} catch (error) {
return false;
}
const { data: address, hasPrefix } = parsed;
if (!hasPrefix) return false;
const low = address.toLowerCase();
const upp = address.toUpperCase();
if (address === low || address === upp) return true;
return addr.addChecksum(low, chainId) === checksummedAddress;
return addr.addChecksum(low) === checksummedAddress;
},
};
import Chainlink from './chainlink.js';
import ENS from './ens.js';
import FetchProvider from './provider.js';
import { ArchiveNodeProvider, calcTransfersDiff } from './archive.js';
import UniswapV2 from './uniswap-v2.js';
import UniswapV3 from './uniswap-v3.js';
import { Web3Provider, Web3CallArgs, hexToNumber } from '../utils.js';
// There are many low level APIs inside which are not exported yet.
export { Chainlink, ENS, UniswapV2, UniswapV3 };
export const FetchProvider = (
fetch: (url: string, opt?: Record<string, any>) => Promise<{ json: () => Promise<any> }>,
url: string,
headers: Record<string, string> = {}
): Web3Provider => {
const jsonrpc = async (method: string, ...params: any[]) => {
const res = await fetch(url, {
method: 'POST',
headers: { 'Content-Type': 'application/json', ...headers },
body: JSON.stringify({ jsonrpc: '2.0', id: 0, method, params }),
});
const json = await res.json();
if (json && json.error)
throw new Error(`FetchProvider(${json.error.code}): ${json.error.message}`);
return json.result;
};
return {
ethCall: (args: Web3CallArgs, tag = 'latest') =>
jsonrpc('eth_call', args, tag) as Promise<string>,
estimateGas: async (args: Web3CallArgs, tag = 'latest') =>
hexToNumber(await jsonrpc('eth_estimateGas', args, tag)),
};
export {
ArchiveNodeProvider,
calcTransfersDiff,
Chainlink,
ENS,
FetchProvider,
UniswapV2,
UniswapV3,
};

@@ -1,2 +0,2 @@

import { Web3Provider, ethHex, ethDecimal, createDecimal } from '../utils.js';
import { Web3Provider, ethHex, ethDecimal, isBytes, createDecimal } from '../utils.js';
import { addr } from '../index.js';

@@ -49,3 +49,3 @@ import { tokenFromSymbol } from '../abi/index.js';

if (Array.isArray(o)) return o.map((i) => traverse(i));
if (o instanceof Uint8Array) return o; // TODO: replace with isBytes
if (isBytes(o)) return o;
if (isPromise(o)) return { awaitDeep: promises.push(o) };

@@ -69,3 +69,3 @@ if (typeof o === 'object') {

if (Array.isArray(o)) return o.map((i) => trBack(i));
if (o instanceof Uint8Array) return o; // TODO: replace with isBytes
if (isBytes(o)) return o;
if (typeof o === 'object') {

@@ -72,0 +72,0 @@ if (typeof o === 'object' && o.awaitDeep) return values[o.awaitDeep - 1];

@@ -74,3 +74,2 @@ import { concatBytes } from '@noble/hashes/utils';

for (let fee2 in Fee) {
// TODO: replace with ethHex
let path = [wA, packFee(fee1), c.contract, packFee(fee2), wB].map((i) => ethHex.decode(i));

@@ -77,0 +76,0 @@ if (exactOutput) path = path.reverse();

@@ -1,108 +0,8 @@

import { numberToVarBytesBE } from '@noble/curves/abstract/utils';
import * as P from 'micro-packed';
import { addr } from './address.js';
import { amounts, ethHex } from './utils.js';
import { RLP } from './rlp.js';
import { isBytes, amounts, ethHex } from './utils.js';
// Transaction parsers
// Spec-compliant RLP in 100 lines of code.
export type RLPInput = string | number | Uint8Array | bigint | RLPInput[] | null;
// length: first 3 bit !== 111 ? 6 bit length : 3bit lenlen
const RLPLength = P.wrap({
encodeStream(w: P.Writer, value: number) {
if (value < 56) return w.bits(value, 6);
w.bits(0b111, 3);
const length = P.U32BE.encode(value);
let pos = 0;
for (; pos < length.length; pos++) if (length[pos] !== 0) break;
w.bits(4 - pos - 1, 3);
w.bytes(length.slice(pos));
},
decodeStream(r: P.Reader): number {
const start = r.bits(3);
if (start !== 0b111) return (start << 3) | r.bits(3);
const len = r.bytes(r.bits(3) + 1);
for (let i = 0; i < len.length; i++) {
if (len[i]) break;
throw new Error('Wrong length encoding with leading zeros');
}
const res = P.int(len.length).decode(len);
if (res <= 55) throw new Error('RLPLength: less than 55, but used multi-byte flag');
return res;
},
});
// Recursive struct definition
export type InternalRLP =
| { TAG: 'byte'; data: number }
| {
TAG: 'complex';
data: { TAG: 'string'; data: Uint8Array } | { TAG: 'list'; data: InternalRLP[] };
};
const rlpInner = P.tag(P.map(P.bits(1), { byte: 0, complex: 1 }), {
byte: P.bits(7),
complex: P.tag(P.map(P.bits(1), { string: 0, list: 1 }), {
string: P.bytes(RLPLength),
list: P.prefix(
RLPLength,
P.array(
null,
P.lazy((): P.CoderType<InternalRLP> => rlpInner)
)
),
}),
});
const phex = P.hex(null);
const pstr = P.string(null);
const empty = Uint8Array.from([]);
/**
* RLP parser.
* Real type of rlp is `Item = Uint8Array | Item[]`.
* Strings/number encoded to Uint8Array, but not decoded back: type information is lost.
*/
export const RLP = P.apply(rlpInner, {
encode(from: InternalRLP): RLPInput {
if (from.TAG === 'byte') return new Uint8Array([from.data]);
if (from.TAG !== 'complex') throw new Error('RLP.encode: unexpected type');
const complex = from.data;
if (complex.TAG === 'string') {
if (complex.data.length === 1 && complex.data[0] < 128)
throw new Error('RLP.encode: wrong string length encoding, should use single byte mode');
return complex.data;
}
if (complex.TAG === 'list') return complex.data.map((i) => this.encode(i));
throw new Error('RLP.encode: unknown TAG');
},
decode(data: RLPInput): InternalRLP {
if (data == null) return this.decode(empty);
switch (typeof data) {
case 'object':
if (P.isBytes(data)) {
if (data.length === 1) {
const head = data[0];
if (head < 128) return { TAG: 'byte', data: head };
}
return { TAG: 'complex', data: { TAG: 'string', data: data } };
}
if (Array.isArray(data))
return { TAG: 'complex', data: { TAG: 'list', data: data.map((i) => this.decode(i)) } };
throw new Error('RLP.encode: unknown type');
case 'number':
if (data < 0) throw new Error('RLP.encode: invalid integer as argument, must be unsigned');
if (data === 0) return this.decode(empty);
return this.decode(numberToVarBytesBE(data));
case 'bigint':
if (data < 0n) throw new Error('RLP.encode: invalid integer as argument, must be unsigned');
return this.decode(numberToVarBytesBE(data));
case 'string':
return this.decode(data.startsWith('0x') ? phex.encode(data) : pstr.encode(data));
default:
throw new Error('RLP.encode: unknown type');
}
},
});
export type AnyCoder = Record<string, P.Coder<any, any>>;

@@ -200,3 +100,3 @@ export type AnyCoderStream = Record<string, P.CoderType<any>>;

function ensureBlob(hash: Uint8Array): Uint8Array {
if (!P.isBytes(hash) || hash.length !== 32)
if (!isBytes(hash) || hash.length !== 32)
throw new Error('blobVersionedHashes must contain 32-byte Uint8Array-s');

@@ -216,3 +116,3 @@ return hash;

// - allows to keep legacy logic here, instead of copying to Transaction
const legacySig = {
export const legacySig = {
encode: (data: VRS) => {

@@ -433,6 +333,6 @@ const { v, r, s } = data;

encode: (data) => {
data.data.to = addr.addChecksum(data.data.to, data.data.chainId);
data.data.to = addr.addChecksum(data.data.to);
if (data.type !== 'legacy' && data.data.accessList) {
for (const item of data.data.accessList) {
item[0] = addr.addChecksum(item[0], data.data.chainId);
item[0] = addr.addChecksum(item[0]);
}

@@ -508,5 +408,4 @@ }

},
to(address: string, { data }: ValidationOpts) {
const chainId = typeof data.chainId === 'bigint' ? data.chainId : undefined;
if (!addr.verifyChecksum(address, chainId)) throw new Error('address checksum does not match');
to(address: string) {
if (!addr.isValid(address)) throw new Error('address checksum does not match');
},

@@ -529,8 +428,6 @@ value(num: bigint) {

},
accessList(list: [string, string[]][], { data }: ValidationOpts) {
accessList(list: [string, string[]][]) {
// NOTE: we cannot handle this validation in coder, since it requires chainId to calculate correct checksum
const chainId = typeof data.chainId === 'bigint' ? data.chainId : undefined;
for (const [address, _] of list) {
if (!addr.verifyChecksum(address, chainId))
throw new Error('address checksum does not match');
if (!addr.isValid(address)) throw new Error('address checksum does not match');
}

@@ -537,0 +434,0 @@ },

@@ -1,4 +0,6 @@

import { hexToBytes as _hexToBytes, bytesToHex } from '@noble/hashes/utils';
import { isBytes as _isBytes, hexToBytes as _hexToBytes, bytesToHex } from '@noble/hashes/utils';
import { Coder, coders } from 'micro-packed';
export const isBytes = _isBytes;
// There is no network code in the library.

@@ -20,2 +22,3 @@ // The types are used to check external network provider interfaces.

estimateGas: (args: Web3CallArgs) => Promise<bigint>;
call: (method: string, ...args: any[]) => Promise<any>;
};

@@ -22,0 +25,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc