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 +107 -49
- package/bin/uniswap-v2-loader +41 -7
- package/calp-dark.svg +3 -0
- package/calp-light.svg +3 -0
- package/default_cache_filename.js +2 -2
- package/index.d.ts +0 -5
- package/index.js +2 -7
- package/package.json +5 -5
- package/CHANGELOG.md +0 -25
- package/test.js +0 -67
package/README.md
CHANGED
|
@@ -1,65 +1,123 @@
|
|
|
1
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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
|
```
|
package/bin/uniswap-v2-loader
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
const { load
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
package/calp-light.svg
ADDED
|
@@ -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
|
|
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
|
|
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
|
+
"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
|
-
})
|