multi-chain-balance-diff 0.1.0
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 +332 -0
- package/package.json +52 -0
- package/schema/mcbd-output.schema.json +297 -0
- package/src/adapters/baseAdapter.js +121 -0
- package/src/adapters/evmAdapter.js +126 -0
- package/src/adapters/index.js +70 -0
- package/src/adapters/solanaAdapter.js +179 -0
- package/src/config/networks.js +234 -0
- package/src/index.js +1031 -0
- package/src/services/balanceService.js +156 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Balance fetching service for native coins and ERC-20 tokens.
|
|
3
|
+
*
|
|
4
|
+
* Provides functions to:
|
|
5
|
+
* - Fetch current native balance
|
|
6
|
+
* - Fetch native balance at a specific block
|
|
7
|
+
* - Compute balance diff over N blocks
|
|
8
|
+
* - Fetch ERC-20 token balances
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const { ethers } = require('ethers');
|
|
12
|
+
|
|
13
|
+
// Minimal ERC-20 ABI for balanceOf
|
|
14
|
+
const ERC20_ABI = [
|
|
15
|
+
'function balanceOf(address owner) view returns (uint256)',
|
|
16
|
+
'function decimals() view returns (uint8)',
|
|
17
|
+
'function symbol() view returns (string)',
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Create an ethers provider for the given RPC URL.
|
|
22
|
+
* @param {string} rpcUrl - JSON-RPC endpoint URL
|
|
23
|
+
* @returns {ethers.JsonRpcProvider}
|
|
24
|
+
*/
|
|
25
|
+
function createProvider(rpcUrl) {
|
|
26
|
+
return new ethers.JsonRpcProvider(rpcUrl);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Fetch native coin balance at a specific block.
|
|
31
|
+
* @param {ethers.JsonRpcProvider} provider - Ethers provider
|
|
32
|
+
* @param {string} address - Wallet address
|
|
33
|
+
* @param {number|string} blockTag - Block number or 'latest'
|
|
34
|
+
* @returns {Promise<bigint>} Balance in wei
|
|
35
|
+
*/
|
|
36
|
+
async function getNativeBalance(provider, address, blockTag = 'latest') {
|
|
37
|
+
return provider.getBalance(address, blockTag);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Fetch current block number.
|
|
42
|
+
* @param {ethers.JsonRpcProvider} provider - Ethers provider
|
|
43
|
+
* @returns {Promise<number>} Current block number
|
|
44
|
+
*/
|
|
45
|
+
async function getCurrentBlock(provider) {
|
|
46
|
+
return provider.getBlockNumber();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Compute native balance diff over the last N blocks.
|
|
51
|
+
* @param {ethers.JsonRpcProvider} provider - Ethers provider
|
|
52
|
+
* @param {string} address - Wallet address
|
|
53
|
+
* @param {number} blocksBack - Number of blocks to look back
|
|
54
|
+
* @returns {Promise<{current: bigint, previous: bigint, diff: bigint, currentBlock: number, previousBlock: number}>}
|
|
55
|
+
*/
|
|
56
|
+
async function getNativeBalanceDiff(provider, address, blocksBack) {
|
|
57
|
+
const currentBlock = await getCurrentBlock(provider);
|
|
58
|
+
const previousBlock = Math.max(0, currentBlock - blocksBack);
|
|
59
|
+
|
|
60
|
+
const [currentBalance, previousBalance] = await Promise.all([
|
|
61
|
+
getNativeBalance(provider, address, currentBlock),
|
|
62
|
+
getNativeBalance(provider, address, previousBlock),
|
|
63
|
+
]);
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
current: currentBalance,
|
|
67
|
+
previous: previousBalance,
|
|
68
|
+
diff: currentBalance - previousBalance,
|
|
69
|
+
currentBlock,
|
|
70
|
+
previousBlock,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Fetch ERC-20 token balance for a single token.
|
|
76
|
+
* @param {ethers.JsonRpcProvider} provider - Ethers provider
|
|
77
|
+
* @param {string} walletAddress - Wallet address
|
|
78
|
+
* @param {object} tokenConfig - Token configuration {symbol, address, decimals}
|
|
79
|
+
* @returns {Promise<{symbol: string, balance: string, rawBalance: bigint}|null>}
|
|
80
|
+
*/
|
|
81
|
+
async function getTokenBalance(provider, walletAddress, tokenConfig) {
|
|
82
|
+
try {
|
|
83
|
+
const contract = new ethers.Contract(tokenConfig.address, ERC20_ABI, provider);
|
|
84
|
+
const rawBalance = await contract.balanceOf(walletAddress);
|
|
85
|
+
const balance = ethers.formatUnits(rawBalance, tokenConfig.decimals);
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
symbol: tokenConfig.symbol,
|
|
89
|
+
balance,
|
|
90
|
+
rawBalance,
|
|
91
|
+
};
|
|
92
|
+
} catch (error) {
|
|
93
|
+
// Token contract might not exist or be accessible; return null
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Fetch balances for multiple ERC-20 tokens.
|
|
100
|
+
* @param {ethers.JsonRpcProvider} provider - Ethers provider
|
|
101
|
+
* @param {string} walletAddress - Wallet address
|
|
102
|
+
* @param {object[]} tokens - Array of token configurations
|
|
103
|
+
* @returns {Promise<object[]>} Array of token balances (non-zero only)
|
|
104
|
+
*/
|
|
105
|
+
async function getTokenBalances(provider, walletAddress, tokens) {
|
|
106
|
+
const balancePromises = tokens.map(token =>
|
|
107
|
+
getTokenBalance(provider, walletAddress, token)
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const results = await Promise.all(balancePromises);
|
|
111
|
+
|
|
112
|
+
// Filter out failed fetches and zero balances
|
|
113
|
+
return results.filter(result =>
|
|
114
|
+
result !== null && result.rawBalance > 0n
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Format a wei value to a human-readable string with the given symbol.
|
|
120
|
+
* @param {bigint} weiValue - Value in wei
|
|
121
|
+
* @param {string} symbol - Currency symbol
|
|
122
|
+
* @param {number} decimals - Number of decimals (default 18 for ETH)
|
|
123
|
+
* @returns {string} Formatted string like "1.234 ETH"
|
|
124
|
+
*/
|
|
125
|
+
function formatBalance(weiValue, symbol, decimals = 18) {
|
|
126
|
+
const formatted = ethers.formatUnits(weiValue, decimals);
|
|
127
|
+
// Trim to reasonable precision (6 decimals)
|
|
128
|
+
const trimmed = parseFloat(formatted).toFixed(6).replace(/\.?0+$/, '');
|
|
129
|
+
return `${trimmed} ${symbol}`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Format a balance diff with +/- prefix.
|
|
134
|
+
* @param {bigint} diffWei - Diff value in wei
|
|
135
|
+
* @param {string} symbol - Currency symbol
|
|
136
|
+
* @param {number} decimals - Number of decimals
|
|
137
|
+
* @returns {string} Formatted diff like "+0.012 ETH" or "-0.005 ETH"
|
|
138
|
+
*/
|
|
139
|
+
function formatBalanceDiff(diffWei, symbol, decimals = 18) {
|
|
140
|
+
const formatted = ethers.formatUnits(diffWei, decimals);
|
|
141
|
+
const value = parseFloat(formatted);
|
|
142
|
+
const prefix = value >= 0 ? '+' : '';
|
|
143
|
+
const trimmed = value.toFixed(6).replace(/\.?0+$/, '');
|
|
144
|
+
return `${prefix}${trimmed} ${symbol}`;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
module.exports = {
|
|
148
|
+
createProvider,
|
|
149
|
+
getNativeBalance,
|
|
150
|
+
getCurrentBlock,
|
|
151
|
+
getNativeBalanceDiff,
|
|
152
|
+
getTokenBalance,
|
|
153
|
+
getTokenBalances,
|
|
154
|
+
formatBalance,
|
|
155
|
+
formatBalanceDiff,
|
|
156
|
+
};
|