uniswap-v2-loader 3.0.0 → 4.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,65 +1,123 @@
1
- # uniswap-v2-loader
1
+ # <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="./calp-dark.svg">
3
+ <img alt="calp.pro icon" src="./calp-light.svg" height="32" style="vertical-align: middle;">
4
+ </picture> uniswap-v2-loader
5
+ <br>
6
+ <br>
2
7
 
3
- [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
4
- High-speed Uniswap v2 pair loader using viem multicall and parallel CPU processing.
8
+ **Fast DeFi AMM pools loader.** Optimized for **Multi-core CPUs** with **viem** multicall and smart **disk-cache**.
5
9
 
6
- ## Configuration
7
- The package uses Alchemy. Set your key as an environment variable (a default key is used if none is provided):
8
- `export KEY=your_alchemy_key`
10
+ ### Popular Uniswap V2 based protocols
11
+ | Protocol | Factory Address |
12
+ | :--- | :--- |
13
+ | **Uniswap V2** | `0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f` |
14
+ | **SushiSwap** | `0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac` |
15
+ | **PancakeSwap** | `0x1097053Fd2ea711dad45caCcc45EfF7548fCB362` |
16
+ | **ShibaSwap** | `0x115934131916C8b277DD010Ee02de363c09d037c` |
17
+ | **DefiSwap** | `0x9DEB29c9a4c7A88a3C0257393b7f3335338D9A9D` |
18
+ | **EtherVista** | `0x9a27cb5ae0B2cEe0bb71f9A85C0D60f3920757B4` |
19
+ | **Balancer V2** | `0xBA12222222228d8Ba445958a75a0704d566BF2C8` |
9
20
 
10
- ## CLI
11
- ```
21
+
22
+ ### CLI
23
+ ```bash
12
24
  npm i -g uniswap-v2-loader
13
- uniswap-v2-loader
25
+ uniswap-v2-loader --help
26
+ Options:
27
+ --key
28
+ --factory
29
+ --filename
30
+ --multicall_size
31
+ --from
32
+ --to
33
+ --workers
34
+ --update_timeout
14
35
  ```
15
36
 
16
-
17
37
  ## API Reference
38
+
18
39
  ### `load(params)`
19
- - **Description**: Fetches token pairs from a Uniswap V2-compatible factory (e.g., Uniswap, PancakeSwap, SushiSwap). It utilizes multicall from `viem` and splits the loading process between multiple CPUs for high-speed execution.
20
- - **Arguments**:
21
- - `params`: (Object)
22
- - `from`: (number) Start loading from this index (default 0).
23
- - `to`: (number) Load up to this index.
24
- - `filename`: (string) Path to cache CSV file.
25
- - `multicall_size`: (number) Items per multicall (default 50).
26
- - `factory`: (string) Factory address.
27
- - `key`: (string) Alchemy/Infura API key.
28
- - `workers`: (number) Number of worker threads (default max CPUs - 1).
29
- - **Returns**: `Promise<Array<Object>>` (Array of Pair Objects)
40
+ High-performance parallel fetcher for liquidity pairs. Efficiently synchronizes state with local disk cache and executes multi-threaded RPC requests.
41
+
42
+ **Parameters**
43
+ | Name | Type | Description | Default |
44
+ | :--- | :--- | :--- | :--- |
45
+ | `from` | `number` | Start loading from this pair index. | `0` |
46
+ | `to` | `number` | End index (exclusive). Required for range loading. | `undefined` |
47
+ | `filename` | `string` | Local CSV cache path. Supports OS-standard locations. | *Auto-detected* |
48
+ | `factory` | `string` | Smart contract factory address. | `Uniswap V2` |
49
+ | `key` | `string` | Alchemy/RPC API Key (priority over ENV). | `process.env.KEY` |
50
+ | `multicall_size` | `number` | RPC batch size per multicall request. | `50` |
51
+ | `workers` | `number` | Number of parallel worker threads. | `CPU - 1` |
52
+ | `progress` | `function` | Progress callback: `(current, total) => {}`. | `undefined` |
53
+
54
+ **Returns**: `Promise<Pair[]>`
55
+
56
+ **Example Response**
57
+ ```json
58
+ [
59
+ {
60
+ "id": 0,
61
+ "pair": "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc",
62
+ "token0": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eb48",
63
+ "token1": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
64
+ }
65
+ ]
66
+ ```
67
+
68
+ ---
69
+
70
+ ### Smart Cross-Platform Caching
71
+ The loader automatically identifies the optimal persistent storage path for your operating system to ensure zero-configuration caching:
72
+ - **Linux:** `$XDG_CACHE_HOME` or `~/.cache/`
73
+ - **macOS:** `~/Library/Caches/`
74
+ - **Windows:** `%LOCALAPPDATA%` or `AppData/Local/`
75
+
76
+ Cache files are named following the pattern `${package_name}_{factory_address}.csv`.
77
+
78
+ ---
30
79
 
31
80
  ### `onupdate(callback, params)`
32
- - **Description**: Subscribes to new pairs appearing at the factory contract. It initially calls the callback with cached pairs and then polls for updates.
33
- - **Arguments**:
34
- - `callback`: (function) Called with an array of Pair Objects.
35
- - `params`: (Object) Same as `load()` plus:
36
- - `update_timeout`: (number) Polling interval in ms (default 5000).
37
- - **Returns**: `Function` An unsubscribe function to stop polling.
38
-
39
- ### `clear_cache()`
40
- - **Description**: Clears the default cache file.
41
-
42
- ### Pair Object
43
- The pair object contains the following fields:
44
- - `id`: (number) The pair index.
45
- - `pair`: (string) The pair contract address.
46
- - `token0`: (string) The address of the first token in the pair.
47
- - `token1`: (string) The address of the second token in the pair.
48
-
49
- ## Usage
81
+ Continuous synchronization engine. Performs initial load and subsequently polls for new pairs.
82
+
83
+ **Parameters**
84
+ | Name | Type | Description | Default |
85
+ | :--- | :--- | :--- | :--- |
86
+ | `callback` | `function` | Invoked with updated `Pair[]` array. | **Required** |
87
+ | `params` | `object` | All options from `load()` plus `update_timeout`. | - |
88
+ | `update_timeout` | `number` | Polling interval in milliseconds. | `5000` |
89
+
90
+ **Returns**: `function` (Stop/Unsubscribe function)
91
+
92
+ ---
93
+
94
+ ### Schema: `Pair`
95
+ Standardized liquidity pool object.
96
+
97
+ | Property | Type | Description |
98
+ | :--- | :--- | :--- |
99
+ | `id` | `number` | Global index of the pair in the factory. |
100
+ | `pair` | `string` | Ethereum address of the liquidity pool. |
101
+ | `token0` | `string` | Address of the first asset. |
102
+ | `token1` | `string` | Address of the second asset. |
103
+
104
+ ---
105
+
106
+ ## Usage Example
50
107
  ```javascript
51
108
  const { load, onupdate } = require('uniswap-v2-loader')
109
+ const rl = require('readline')
52
110
 
53
- // Load initial set
54
- load({to: 10}).then(pairs =>
55
- pairs.forEach(({id, pair}) => console.log(`ID: ${id} | Pair: ${pair}`))
56
- )
57
111
 
58
- // Subscribe to new pairs (24/7 monitoring)
59
- const unsubscribe = onupdate(pairs => {
60
- console.log(`Pools count: ${pairs.length}`)
112
+ load({
113
+ factory: '0xC0AEe478e3658e2610c5F7A4A2E1777cE9e4f2Ac', // SushiSwap
114
+ to: 1000,
115
+ progress: (c, t) => {
116
+ rl.cursorTo(process.stdout, 0)
117
+ rl.clearLine(process.stdout, 0)
118
+ process.stdout.write(`Loaded: ${c} / ${t} (${(c/t*100).toFixed(2)}%)`)
119
+ }
120
+ }).then(pairs => {
121
+ console.log(`\nSuccessfully loaded ${pairs.length} SushiSwap pairs`)
61
122
  })
62
-
63
- // Stop monitoring if needed
64
- // unsubscribe()
65
123
  ```
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- const { load, count, clear_cache } = require('../index')
2
+ const { load } = require('../index')
3
3
  const rl = require('readline')
4
4
 
5
5
  const progress = (c, t) => {
@@ -11,9 +11,43 @@ const progress = (c, t) => {
11
11
  process.stdout.write(`Loaded: ${cur} / ${t} (${pct}%)`)
12
12
  }
13
13
 
14
- if (process.argv[2] == '-c' || process.argv[2] == '--count')
15
- console.log(count())
16
- else if (process.argv[2] == '--clear')
17
- clear_cache()
18
- else
19
- load({progress})
14
+ const options = [
15
+ 'key',
16
+ 'factory',
17
+ 'filename',
18
+ 'multicall_size',
19
+ 'from',
20
+ 'to',
21
+ 'workers',
22
+ 'update_timeout'
23
+ ]
24
+
25
+ const params = { progress }
26
+ var i = 2
27
+ while (arg = process.argv[i]) {
28
+ if (arg == '-h' || arg == '--help') {
29
+ console.log('Options:\n', options.map(option => `\t--${option}`).join('\n'))
30
+ process.exit(0)
31
+ }
32
+ if (!arg.startsWith('--')) {
33
+ console.log(`Option should start with "--", not: "${arg}".`)
34
+ process.exit(22)
35
+ }
36
+ var [option, value] = arg.slice(2).split('=')
37
+ if (!options.includes(option)) {
38
+ console.log(`Unsupported option: "${option}`)
39
+ process.exit(22)
40
+ }
41
+ if (!value) {
42
+ i++
43
+ value = process.argv[i]
44
+ if (!value || value.startsWith('--')) {
45
+ console.log(`Missed value for an option "${option}".`)
46
+ process.exit(22)
47
+ }
48
+ }
49
+ params[option] = value
50
+ i++
51
+ }
52
+
53
+ load(params)
package/calp-dark.svg ADDED
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <polygon points="16,2 28,9 28,23 16,30 4,23 4,9" fill="none" stroke="#fff" stroke-width="3" transform="rotate(24 16 16)"/>
3
+ </svg>
package/calp-light.svg ADDED
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32">
2
+ <polygon points="16,2 28,9 28,23 16,30 4,23 4,9" fill="none" stroke="#000" stroke-width="3" transform="rotate(24 16 16)"/>
3
+ </svg>
@@ -4,7 +4,7 @@ const os = require('os')
4
4
  const home = os.homedir()
5
5
  const pkg = require('./package.json')
6
6
 
7
- module.exports = path.join(
7
+ module.exports = (factory) => path.join(
8
8
  ...(process.platform === 'win32'
9
9
  ? (env.LOCALAPPDATA || env.APPDATA)
10
10
  ? [env.LOCALAPPDATA || env.APPDATA]
@@ -15,5 +15,5 @@ module.exports = path.join(
15
15
  ? [env.XDG_CACHE_HOME]
16
16
  : [home, '.cache']
17
17
  ),
18
- pkg.name + '_pairs.csv'
18
+ `${pkg.name}_${factory.toLowerCase()}.csv`
19
19
  )
package/index.d.ts CHANGED
@@ -45,11 +45,6 @@ export interface load_params {
45
45
  */
46
46
  export function load(params?: load_params): Promise<pair[]>
47
47
 
48
- /**
49
- * Clears the default cache file.
50
- */
51
- export function clear_cache(): void
52
-
53
48
  /**
54
49
  * Subscribes to new pairs being added to the factory.
55
50
  * @param callback Called whenever new pairs are loaded.
package/index.js CHANGED
@@ -13,7 +13,7 @@ const load = (params = {}) => {
13
13
  var {
14
14
  key = debug_key,
15
15
  factory = uniswap_v2_factory,
16
- filename = default_cache_filename,
16
+ filename,
17
17
  multicall_size = 50,
18
18
  from = 0,
19
19
  to,
@@ -21,6 +21,7 @@ const load = (params = {}) => {
21
21
  workers = max_workers,
22
22
  pairs,
23
23
  } = params
24
+ filename ??= default_cache_filename(factory)
24
25
  const client = createPublicClient({
25
26
  chain: mainnet,
26
27
  transport: http('https://eth-mainnet.g.alchemy.com/v2/' + key)
@@ -106,12 +107,6 @@ const load = (params = {}) => {
106
107
  })
107
108
  }
108
109
 
109
-
110
- module.exports.clear_cache = () => {
111
- if (fs.existsSync(default_cache_filename))
112
- fs.unlinkSync(default_cache_filename)
113
- }
114
-
115
110
  module.exports.load = (params = {}) =>
116
111
  load(params)
117
112
 
package/package.json CHANGED
@@ -1,13 +1,16 @@
1
1
  {
2
2
  "name": "uniswap-v2-loader",
3
- "version": "3.0.0",
3
+ "version": "4.0.1",
4
4
  "description": "Uniswap v2 protocol loader",
5
5
  "keywords": [
6
6
  "uniswap-v2",
7
7
  "protocol",
8
8
  "loader",
9
9
  "crypto",
10
- "ethereum"
10
+ "ethereum",
11
+ "defi",
12
+ "sushiswap",
13
+ "pancakeswap"
11
14
  ],
12
15
  "homepage": "https://github.com/calp-pro/uniswap-v2-loader#readme",
13
16
  "bugs": {
@@ -30,8 +33,5 @@
30
33
  },
31
34
  "dependencies": {
32
35
  "viem": "^2.46.2"
33
- },
34
- "devDependencies": {
35
- "typescript": "^5.9.3"
36
36
  }
37
37
  }
package/CHANGELOG.md DELETED
@@ -1,25 +0,0 @@
1
- ## [3.0.0] - 2026-02-27
2
- - Added TypeScript type definitions (`index.d.ts`).
3
- - Added JSDoc documentation for a better developer experience.
4
- - Generalized protocol support for any Uniswap V2-compatible factory.
5
- - Removed `count` method from the API for a cleaner, promise-based interface.
6
-
7
- ## [2.0.0] - 2026-02-26
8
- - API Cleanup: `load` replaces `all` (now strictly returning `Promise<pair[]>`).
9
- - API Cleanup: `multicall_size` replaces `chunk_size`.
10
- - single core option ("multicore" - false)
11
- - prune async/await sintax sugar
12
- - worker delivery message at expected output pair format
13
- - fix: cross OS combine path for worker
14
- - fix: case where filename is not exist
15
-
16
- ## [1.4.0] - 2026-02-24
17
- - spawn -> cluster
18
- - test order pool by factory id at CSV
19
- - clear cache CSV file
20
-
21
- ## [1.3.0] - 2026-02-23
22
- - Add CLI version with `-c` or `--count` flag to counting loaded pairs from cache
23
-
24
- ## [1.2.0] - 2026-02-22
25
- - Added `onupdate` function for subscribing to new pairs.
package/test.js DELETED
@@ -1,67 +0,0 @@
1
- const fs = require('fs')
2
- const { describe, before, it } = require('node:test')
3
- const assert = require('node:assert/strict')
4
- const {clear_cache, load, onupdate} = require('./index')
5
-
6
- describe('Uniswap V2', () => {
7
- before(() => clear_cache())
8
-
9
- it('Exist USDC/USDP pair', () =>
10
- load({to: 2})
11
- .then(pairs => {
12
- assert.equal(pairs.length, 2)
13
- const i = pairs.findIndex(({id}) => id == 1)
14
- assert.ok(i != -1)
15
- if (i != -1) {
16
- const {pair, token0, token1} = pairs[i]
17
- assert.equal(pair, '0x3139Ffc91B99aa94DA8A2dc13f1fC36F9BDc98eE')
18
- assert.equal(token0, '0x8E870D67F660D95d5be530380D0eC0bd388289E1')
19
- assert.equal(token1, '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48')
20
- }
21
- })
22
- )
23
-
24
-
25
- it('Re-load first two pairs to custom CSV file', () => {
26
- // If user specify a filename then
27
- // a cache data will be taken from
28
- // the filename provided. If file is empty then
29
- // data will be uploaded again from network.
30
- const filename = Date.now() + '.csv'
31
- return load({to: 2, filename})
32
- .then(() => {
33
- const lines = fs.readFileSync(filename).toString().trim().split('\n')
34
- assert.equal(lines.length, 2)
35
- })
36
- .finally(() =>
37
- fs.unlinkSync(filename)
38
- )
39
- })
40
-
41
- it('onupdate should call provided callback with 2 pairs for a current moment (from cache)', () => {
42
- return new Promise(y => {
43
- const unsubscribe = onupdate(pairs => {
44
- assert.equal(pairs.length, 2)
45
- unsubscribe()
46
- y()
47
- }, {to: 2})
48
- })
49
- })
50
-
51
- it('Multi-core test 2 workers load 2 pools using multicall', () =>
52
- // There are already 2 pools loaded from previous test
53
- // 6 - 2 = 4. Rest 4 will be loaded by 2 workers. Each load 2.
54
- // Multicall size is 2.
55
- load({to: 6, multicall_size: 2, workers: 2 })
56
- .then(pairs => {
57
- assert.equal(pairs.length, 6)
58
- })
59
- )
60
-
61
- it('Each line at CSV cache file should be orderd by pair id (factory id)', () => {
62
- const lines = fs.readFileSync(require('./default_cache_filename'), 'utf8').trim().split('\n')
63
- for (var i = 0; i < lines.length; i++)
64
- assert.equal(i, +lines[i].split(',').shift())
65
- })
66
-
67
- })