@octaflowlabs/onchain-sdk 1.1.4 → 1.2.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 CHANGED
@@ -1 +1,53 @@
1
- # onchain-sdk
1
+ # onchain-sdk
2
+
3
+ Lightweight TypeScript SDK for EVM onchain utilities. It provides helpers for
4
+ balances, transaction building, broadcasting, gas estimation, and wallet
5
+ derivation.
6
+
7
+ ## Install
8
+
9
+ Install with npm or yarn.
10
+
11
+ ## How to use
12
+
13
+ - Add the package to the project and import the helpers needed.
14
+ - Provide a valid RPC URL (public or private) and the target chain ID.
15
+ - Call the balance, transaction, or signing helpers based on your flow.
16
+ - Handle errors at the call site and decide how often to poll or refresh.
17
+
18
+ ## What this SDK provides
19
+
20
+ Balances
21
+
22
+ - Fetch native token balances for an address.
23
+ - Fetch ERC-20 token balances for an address.
24
+ - Fetch multiple token balances across multiple chains with multicall batching.
25
+
26
+ Transactions
27
+
28
+ - Build unsigned native or ERC-20 transfer transactions.
29
+ - Estimate gas limits and fee data from a provider.
30
+ - Broadcast signed transactions.
31
+ - Check transaction status and receipt confirmation.
32
+
33
+ Wallets and signing
34
+
35
+ - Generate and derive EVM wallets from entropy.
36
+ - Sign messages and transactions.
37
+
38
+ Utilities
39
+
40
+ - Format and parse amounts for display.
41
+ - Normalize addresses and shorten hashes.
42
+ - Transform bigint values for UI use.
43
+
44
+ ABIs and constants
45
+
46
+ - ERC-20 ABI for balance and transfer calls.
47
+ - Multicall3 ABI and address for batched reads.
48
+ - Gas limit defaults per transaction type.
49
+
50
+ ## Design notes
51
+
52
+ - The SDK is stateless and transport-agnostic. It expects a caller-provided RPC URL.
53
+ - Caching, polling, and background jobs should be handled by the consumer app or backend.
@@ -0,0 +1,65 @@
1
+ declare const _default: readonly [{
2
+ readonly inputs: readonly [{
3
+ readonly components: readonly [{
4
+ readonly internalType: "address";
5
+ readonly name: "target";
6
+ readonly type: "address";
7
+ }, {
8
+ readonly internalType: "bytes";
9
+ readonly name: "callData";
10
+ readonly type: "bytes";
11
+ }];
12
+ readonly internalType: "struct Multicall3.Call[]";
13
+ readonly name: "calls";
14
+ readonly type: "tuple[]";
15
+ }];
16
+ readonly name: "aggregate";
17
+ readonly outputs: readonly [{
18
+ readonly internalType: "uint256";
19
+ readonly name: "blockNumber";
20
+ readonly type: "uint256";
21
+ }, {
22
+ readonly internalType: "bytes[]";
23
+ readonly name: "returnData";
24
+ readonly type: "bytes[]";
25
+ }];
26
+ readonly stateMutability: "payable";
27
+ readonly type: "function";
28
+ }, {
29
+ readonly inputs: readonly [{
30
+ readonly components: readonly [{
31
+ readonly internalType: "address";
32
+ readonly name: "target";
33
+ readonly type: "address";
34
+ }, {
35
+ readonly internalType: "bool";
36
+ readonly name: "allowFailure";
37
+ readonly type: "bool";
38
+ }, {
39
+ readonly internalType: "bytes";
40
+ readonly name: "callData";
41
+ readonly type: "bytes";
42
+ }];
43
+ readonly internalType: "struct Multicall3.Call3[]";
44
+ readonly name: "calls";
45
+ readonly type: "tuple[]";
46
+ }];
47
+ readonly name: "aggregate3";
48
+ readonly outputs: readonly [{
49
+ readonly components: readonly [{
50
+ readonly internalType: "bool";
51
+ readonly name: "success";
52
+ readonly type: "bool";
53
+ }, {
54
+ readonly internalType: "bytes";
55
+ readonly name: "returnData";
56
+ readonly type: "bytes";
57
+ }];
58
+ readonly internalType: "struct Multicall3.Result[]";
59
+ readonly name: "returnData";
60
+ readonly type: "tuple[]";
61
+ }];
62
+ readonly stateMutability: "payable";
63
+ readonly type: "function";
64
+ }];
65
+ export default _default;
@@ -0,0 +1,52 @@
1
+ // & Multicall3 ABI - standard contract deployed on most EVM chains
2
+ // & https://www.multicall3.com/
3
+ export default [
4
+ {
5
+ inputs: [
6
+ {
7
+ components: [
8
+ { internalType: 'address', name: 'target', type: 'address' },
9
+ { internalType: 'bytes', name: 'callData', type: 'bytes' },
10
+ ],
11
+ internalType: 'struct Multicall3.Call[]',
12
+ name: 'calls',
13
+ type: 'tuple[]',
14
+ },
15
+ ],
16
+ name: 'aggregate',
17
+ outputs: [
18
+ { internalType: 'uint256', name: 'blockNumber', type: 'uint256' },
19
+ { internalType: 'bytes[]', name: 'returnData', type: 'bytes[]' },
20
+ ],
21
+ stateMutability: 'payable',
22
+ type: 'function',
23
+ },
24
+ {
25
+ inputs: [
26
+ {
27
+ components: [
28
+ { internalType: 'address', name: 'target', type: 'address' },
29
+ { internalType: 'bool', name: 'allowFailure', type: 'bool' },
30
+ { internalType: 'bytes', name: 'callData', type: 'bytes' },
31
+ ],
32
+ internalType: 'struct Multicall3.Call3[]',
33
+ name: 'calls',
34
+ type: 'tuple[]',
35
+ },
36
+ ],
37
+ name: 'aggregate3',
38
+ outputs: [
39
+ {
40
+ components: [
41
+ { internalType: 'bool', name: 'success', type: 'bool' },
42
+ { internalType: 'bytes', name: 'returnData', type: 'bytes' },
43
+ ],
44
+ internalType: 'struct Multicall3.Result[]',
45
+ name: 'returnData',
46
+ type: 'tuple[]',
47
+ },
48
+ ],
49
+ stateMutability: 'payable',
50
+ type: 'function',
51
+ },
52
+ ];
@@ -0,0 +1,3 @@
1
+ import { GetBalanceParams, GetBalanceResult, GetBalancesParams } from '../types/common';
2
+ export declare const getBalance: ({ walletAddress, rpcUrl, tokenAddress, chainId }: GetBalanceParams) => Promise<any> | undefined;
3
+ export declare const getBalances: ({ walletAddress, chains: chainRequests, }: GetBalancesParams) => Promise<GetBalanceResult>;
@@ -0,0 +1,121 @@
1
+ /** npm imports */
2
+ import { Contract, Interface } from 'ethers';
3
+ /** local imports */
4
+ import { getProvider } from './getProvider';
5
+ import ERC20_TOKEN_CONTRACT_ABI from '../ABIs/ERC20_TOKEN_CONTRACT_ABI';
6
+ import MULTICALL3_ABI from '../ABIs/MULTICALL3_ABI';
7
+ import { MULTICALL3_ADDRESS } from '../constants/constants';
8
+ import { handleErrorMessages } from '../utils/handleErrorMessages';
9
+ export const getBalance = ({ walletAddress, rpcUrl, tokenAddress, chainId }) => {
10
+ const provider = getProvider(rpcUrl, chainId);
11
+ if (!provider)
12
+ throw new Error('Failed to create provider with the given RPC URL and chain ID.');
13
+ try {
14
+ if (!tokenAddress)
15
+ return provider.getBalance(walletAddress);
16
+ const tokenContract = new Contract(tokenAddress, ERC20_TOKEN_CONTRACT_ABI, provider);
17
+ return tokenContract.balanceOf(walletAddress);
18
+ }
19
+ catch (error) {
20
+ console.error('Error fetching balance:', error);
21
+ handleErrorMessages({ e: error, message: 'Error fetching balance' });
22
+ }
23
+ };
24
+ const buildChainKey = (rpcUrl, chainId) => `${rpcUrl}|${chainId ?? 'default'}`;
25
+ const buildRequestKey = ({ walletAddress, rpcUrl, chainId, tokenAddress, }) => {
26
+ const walletKey = walletAddress.toLowerCase();
27
+ const tokenKey = tokenAddress ? tokenAddress.toLowerCase() : 'native';
28
+ const chainKey = chainId ?? 'default';
29
+ return `${walletKey}|${rpcUrl}|${chainKey}|${tokenKey}`;
30
+ };
31
+ const fetchTokenBalancesWithMulticall = async (walletAddress, tokenAddresses, rpcUrl, chainId) => {
32
+ const provider = getProvider(rpcUrl, chainId);
33
+ if (!provider)
34
+ return tokenAddresses.map(() => null);
35
+ try {
36
+ const erc20Interface = new Interface(ERC20_TOKEN_CONTRACT_ABI);
37
+ const multicall = new Contract(MULTICALL3_ADDRESS, MULTICALL3_ABI, provider);
38
+ const calls = tokenAddresses.map((tokenAddress) => ({
39
+ target: tokenAddress,
40
+ allowFailure: true,
41
+ callData: erc20Interface.encodeFunctionData('balanceOf', [walletAddress]),
42
+ }));
43
+ const results = await multicall.aggregate3.staticCall(calls);
44
+ return results.map((result, index) => {
45
+ if (!result.success || result.returnData === '0x')
46
+ return null;
47
+ try {
48
+ const decoded = erc20Interface.decodeFunctionResult('balanceOf', result.returnData);
49
+ return decoded[0];
50
+ }
51
+ catch {
52
+ console.error(`Failed to decode balance for token ${tokenAddresses[index]}`);
53
+ return null;
54
+ }
55
+ });
56
+ }
57
+ catch (error) {
58
+ console.error('Multicall failed, falling back to individual calls:', error);
59
+ return Promise.allSettled(tokenAddresses.map((tokenAddress) => getBalance({ walletAddress, rpcUrl, tokenAddress, chainId }))).then((results) => results.map((r) => (r.status === 'fulfilled' && r.value ? r.value : null)));
60
+ }
61
+ };
62
+ export const getBalances = async ({ walletAddress, chains: chainRequests, }) => {
63
+ const chainGroups = new Map();
64
+ chainRequests.forEach(({ rpcUrl, chainId, tokenAddresses, includeNative }) => {
65
+ const chainKey = buildChainKey(rpcUrl, chainId);
66
+ if (!chainGroups.has(chainKey)) {
67
+ chainGroups.set(chainKey, {
68
+ rpcUrl,
69
+ chainId,
70
+ nativeBalanceRequests: [],
71
+ tokenBalanceRequests: [],
72
+ });
73
+ }
74
+ const group = chainGroups.get(chainKey);
75
+ if (includeNative) {
76
+ const nativeRequest = { walletAddress, rpcUrl, chainId };
77
+ const key = buildRequestKey(nativeRequest);
78
+ if (!group.nativeBalanceRequests.some((r) => buildRequestKey(r) === key))
79
+ group.nativeBalanceRequests.push(nativeRequest);
80
+ }
81
+ tokenAddresses.forEach((tokenAddress) => {
82
+ const request = { walletAddress, rpcUrl, chainId, tokenAddress };
83
+ const key = buildRequestKey(request);
84
+ if (!group.tokenBalanceRequests.some((r) => buildRequestKey(r) === key))
85
+ group.tokenBalanceRequests.push(request);
86
+ });
87
+ });
88
+ const chainResults = await Promise.allSettled(Array.from(chainGroups.values()).map(async (group) => {
89
+ const tokenBalances = [];
90
+ const nativeResults = await Promise.allSettled(group.nativeBalanceRequests.map((request) => getBalance(request)));
91
+ nativeResults.forEach((result) => {
92
+ tokenBalances.push({
93
+ tokenAddress: null,
94
+ balance: result.status === 'fulfilled' ? (result.value ?? null) : null,
95
+ error: result.status === 'rejected' ? result.reason : undefined,
96
+ });
97
+ });
98
+ if (group.tokenBalanceRequests.length > 0) {
99
+ const tokenAddresses = group.tokenBalanceRequests.map((r) => r.tokenAddress);
100
+ const balances = await fetchTokenBalancesWithMulticall(walletAddress, tokenAddresses, group.rpcUrl, group.chainId);
101
+ balances.forEach((balance, index) => {
102
+ tokenBalances.push({
103
+ tokenAddress: group.tokenBalanceRequests[index].tokenAddress ?? null,
104
+ balance,
105
+ error: balance === null ? 'Failed to fetch balance' : undefined,
106
+ });
107
+ });
108
+ }
109
+ return {
110
+ chainId: group.chainId,
111
+ balances: tokenBalances,
112
+ };
113
+ }));
114
+ const chainsWithBalances = chainResults
115
+ .filter((result) => result.status === 'fulfilled')
116
+ .map((result) => result.value);
117
+ return {
118
+ walletAddress,
119
+ chains: chainsWithBalances,
120
+ };
121
+ };
@@ -0,0 +1,65 @@
1
+ declare const _default: readonly [{
2
+ readonly inputs: readonly [{
3
+ readonly components: readonly [{
4
+ readonly internalType: "address";
5
+ readonly name: "target";
6
+ readonly type: "address";
7
+ }, {
8
+ readonly internalType: "bytes";
9
+ readonly name: "callData";
10
+ readonly type: "bytes";
11
+ }];
12
+ readonly internalType: "struct Multicall3.Call[]";
13
+ readonly name: "calls";
14
+ readonly type: "tuple[]";
15
+ }];
16
+ readonly name: "aggregate";
17
+ readonly outputs: readonly [{
18
+ readonly internalType: "uint256";
19
+ readonly name: "blockNumber";
20
+ readonly type: "uint256";
21
+ }, {
22
+ readonly internalType: "bytes[]";
23
+ readonly name: "returnData";
24
+ readonly type: "bytes[]";
25
+ }];
26
+ readonly stateMutability: "payable";
27
+ readonly type: "function";
28
+ }, {
29
+ readonly inputs: readonly [{
30
+ readonly components: readonly [{
31
+ readonly internalType: "address";
32
+ readonly name: "target";
33
+ readonly type: "address";
34
+ }, {
35
+ readonly internalType: "bool";
36
+ readonly name: "allowFailure";
37
+ readonly type: "bool";
38
+ }, {
39
+ readonly internalType: "bytes";
40
+ readonly name: "callData";
41
+ readonly type: "bytes";
42
+ }];
43
+ readonly internalType: "struct Multicall3.Call3[]";
44
+ readonly name: "calls";
45
+ readonly type: "tuple[]";
46
+ }];
47
+ readonly name: "aggregate3";
48
+ readonly outputs: readonly [{
49
+ readonly components: readonly [{
50
+ readonly internalType: "bool";
51
+ readonly name: "success";
52
+ readonly type: "bool";
53
+ }, {
54
+ readonly internalType: "bytes";
55
+ readonly name: "returnData";
56
+ readonly type: "bytes";
57
+ }];
58
+ readonly internalType: "struct Multicall3.Result[]";
59
+ readonly name: "returnData";
60
+ readonly type: "tuple[]";
61
+ }];
62
+ readonly stateMutability: "payable";
63
+ readonly type: "function";
64
+ }];
65
+ export default _default;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ // & Multicall3 ABI - standard contract deployed on most EVM chains
4
+ // & https://www.multicall3.com/
5
+ exports.default = [
6
+ {
7
+ inputs: [
8
+ {
9
+ components: [
10
+ { internalType: 'address', name: 'target', type: 'address' },
11
+ { internalType: 'bytes', name: 'callData', type: 'bytes' },
12
+ ],
13
+ internalType: 'struct Multicall3.Call[]',
14
+ name: 'calls',
15
+ type: 'tuple[]',
16
+ },
17
+ ],
18
+ name: 'aggregate',
19
+ outputs: [
20
+ { internalType: 'uint256', name: 'blockNumber', type: 'uint256' },
21
+ { internalType: 'bytes[]', name: 'returnData', type: 'bytes[]' },
22
+ ],
23
+ stateMutability: 'payable',
24
+ type: 'function',
25
+ },
26
+ {
27
+ inputs: [
28
+ {
29
+ components: [
30
+ { internalType: 'address', name: 'target', type: 'address' },
31
+ { internalType: 'bool', name: 'allowFailure', type: 'bool' },
32
+ { internalType: 'bytes', name: 'callData', type: 'bytes' },
33
+ ],
34
+ internalType: 'struct Multicall3.Call3[]',
35
+ name: 'calls',
36
+ type: 'tuple[]',
37
+ },
38
+ ],
39
+ name: 'aggregate3',
40
+ outputs: [
41
+ {
42
+ components: [
43
+ { internalType: 'bool', name: 'success', type: 'bool' },
44
+ { internalType: 'bytes', name: 'returnData', type: 'bytes' },
45
+ ],
46
+ internalType: 'struct Multicall3.Result[]',
47
+ name: 'returnData',
48
+ type: 'tuple[]',
49
+ },
50
+ ],
51
+ stateMutability: 'payable',
52
+ type: 'function',
53
+ },
54
+ ];
@@ -0,0 +1,3 @@
1
+ import { GetBalanceParams, GetBalanceResult, GetBalancesParams } from '../types/common';
2
+ export declare const getBalance: ({ walletAddress, rpcUrl, tokenAddress, chainId }: GetBalanceParams) => Promise<any> | undefined;
3
+ export declare const getBalances: ({ walletAddress, chains: chainRequests, }: GetBalancesParams) => Promise<GetBalanceResult>;
@@ -0,0 +1,129 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getBalances = exports.getBalance = void 0;
7
+ /** npm imports */
8
+ const ethers_1 = require("ethers");
9
+ /** local imports */
10
+ const getProvider_1 = require("./getProvider");
11
+ const ERC20_TOKEN_CONTRACT_ABI_1 = __importDefault(require("../ABIs/ERC20_TOKEN_CONTRACT_ABI"));
12
+ const MULTICALL3_ABI_1 = __importDefault(require("../ABIs/MULTICALL3_ABI"));
13
+ const constants_1 = require("../constants/constants");
14
+ const handleErrorMessages_1 = require("../utils/handleErrorMessages");
15
+ const getBalance = ({ walletAddress, rpcUrl, tokenAddress, chainId }) => {
16
+ const provider = (0, getProvider_1.getProvider)(rpcUrl, chainId);
17
+ if (!provider)
18
+ throw new Error('Failed to create provider with the given RPC URL and chain ID.');
19
+ try {
20
+ if (!tokenAddress)
21
+ return provider.getBalance(walletAddress);
22
+ const tokenContract = new ethers_1.Contract(tokenAddress, ERC20_TOKEN_CONTRACT_ABI_1.default, provider);
23
+ return tokenContract.balanceOf(walletAddress);
24
+ }
25
+ catch (error) {
26
+ console.error('Error fetching balance:', error);
27
+ (0, handleErrorMessages_1.handleErrorMessages)({ e: error, message: 'Error fetching balance' });
28
+ }
29
+ };
30
+ exports.getBalance = getBalance;
31
+ const buildChainKey = (rpcUrl, chainId) => `${rpcUrl}|${chainId ?? 'default'}`;
32
+ const buildRequestKey = ({ walletAddress, rpcUrl, chainId, tokenAddress, }) => {
33
+ const walletKey = walletAddress.toLowerCase();
34
+ const tokenKey = tokenAddress ? tokenAddress.toLowerCase() : 'native';
35
+ const chainKey = chainId ?? 'default';
36
+ return `${walletKey}|${rpcUrl}|${chainKey}|${tokenKey}`;
37
+ };
38
+ const fetchTokenBalancesWithMulticall = async (walletAddress, tokenAddresses, rpcUrl, chainId) => {
39
+ const provider = (0, getProvider_1.getProvider)(rpcUrl, chainId);
40
+ if (!provider)
41
+ return tokenAddresses.map(() => null);
42
+ try {
43
+ const erc20Interface = new ethers_1.Interface(ERC20_TOKEN_CONTRACT_ABI_1.default);
44
+ const multicall = new ethers_1.Contract(constants_1.MULTICALL3_ADDRESS, MULTICALL3_ABI_1.default, provider);
45
+ const calls = tokenAddresses.map((tokenAddress) => ({
46
+ target: tokenAddress,
47
+ allowFailure: true,
48
+ callData: erc20Interface.encodeFunctionData('balanceOf', [walletAddress]),
49
+ }));
50
+ const results = await multicall.aggregate3.staticCall(calls);
51
+ return results.map((result, index) => {
52
+ if (!result.success || result.returnData === '0x')
53
+ return null;
54
+ try {
55
+ const decoded = erc20Interface.decodeFunctionResult('balanceOf', result.returnData);
56
+ return decoded[0];
57
+ }
58
+ catch {
59
+ console.error(`Failed to decode balance for token ${tokenAddresses[index]}`);
60
+ return null;
61
+ }
62
+ });
63
+ }
64
+ catch (error) {
65
+ console.error('Multicall failed, falling back to individual calls:', error);
66
+ return Promise.allSettled(tokenAddresses.map((tokenAddress) => (0, exports.getBalance)({ walletAddress, rpcUrl, tokenAddress, chainId }))).then((results) => results.map((r) => (r.status === 'fulfilled' && r.value ? r.value : null)));
67
+ }
68
+ };
69
+ const getBalances = async ({ walletAddress, chains: chainRequests, }) => {
70
+ const chainGroups = new Map();
71
+ chainRequests.forEach(({ rpcUrl, chainId, tokenAddresses, includeNative }) => {
72
+ const chainKey = buildChainKey(rpcUrl, chainId);
73
+ if (!chainGroups.has(chainKey)) {
74
+ chainGroups.set(chainKey, {
75
+ rpcUrl,
76
+ chainId,
77
+ nativeBalanceRequests: [],
78
+ tokenBalanceRequests: [],
79
+ });
80
+ }
81
+ const group = chainGroups.get(chainKey);
82
+ if (includeNative) {
83
+ const nativeRequest = { walletAddress, rpcUrl, chainId };
84
+ const key = buildRequestKey(nativeRequest);
85
+ if (!group.nativeBalanceRequests.some((r) => buildRequestKey(r) === key))
86
+ group.nativeBalanceRequests.push(nativeRequest);
87
+ }
88
+ tokenAddresses.forEach((tokenAddress) => {
89
+ const request = { walletAddress, rpcUrl, chainId, tokenAddress };
90
+ const key = buildRequestKey(request);
91
+ if (!group.tokenBalanceRequests.some((r) => buildRequestKey(r) === key))
92
+ group.tokenBalanceRequests.push(request);
93
+ });
94
+ });
95
+ const chainResults = await Promise.allSettled(Array.from(chainGroups.values()).map(async (group) => {
96
+ const tokenBalances = [];
97
+ const nativeResults = await Promise.allSettled(group.nativeBalanceRequests.map((request) => (0, exports.getBalance)(request)));
98
+ nativeResults.forEach((result) => {
99
+ tokenBalances.push({
100
+ tokenAddress: null,
101
+ balance: result.status === 'fulfilled' ? (result.value ?? null) : null,
102
+ error: result.status === 'rejected' ? result.reason : undefined,
103
+ });
104
+ });
105
+ if (group.tokenBalanceRequests.length > 0) {
106
+ const tokenAddresses = group.tokenBalanceRequests.map((r) => r.tokenAddress);
107
+ const balances = await fetchTokenBalancesWithMulticall(walletAddress, tokenAddresses, group.rpcUrl, group.chainId);
108
+ balances.forEach((balance, index) => {
109
+ tokenBalances.push({
110
+ tokenAddress: group.tokenBalanceRequests[index].tokenAddress ?? null,
111
+ balance,
112
+ error: balance === null ? 'Failed to fetch balance' : undefined,
113
+ });
114
+ });
115
+ }
116
+ return {
117
+ chainId: group.chainId,
118
+ balances: tokenBalances,
119
+ };
120
+ }));
121
+ const chainsWithBalances = chainResults
122
+ .filter((result) => result.status === 'fulfilled')
123
+ .map((result) => result.value);
124
+ return {
125
+ walletAddress,
126
+ chains: chainsWithBalances,
127
+ };
128
+ };
129
+ exports.getBalances = getBalances;
@@ -3,3 +3,4 @@ export declare const GAS_LIMIT_PER_TX_TYPE: {
3
3
  DEFAULT_TRANSFER_ERC20: bigint;
4
4
  DEFAULT_APPROVAL: bigint;
5
5
  };
6
+ export declare const MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
@@ -1,8 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.GAS_LIMIT_PER_TX_TYPE = void 0;
3
+ exports.MULTICALL3_ADDRESS = exports.GAS_LIMIT_PER_TX_TYPE = void 0;
4
4
  exports.GAS_LIMIT_PER_TX_TYPE = {
5
5
  DEFAULT_TRANSFER_NATIVE: 21000n,
6
6
  DEFAULT_TRANSFER_ERC20: 65000n,
7
7
  DEFAULT_APPROVAL: 100000n,
8
8
  };
9
+ // & Multicall3 contract address (same on most chains)
10
+ // & https://www.multicall3.com/deployments
11
+ exports.MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';
@@ -2,13 +2,14 @@
2
2
  import ERC20_TOKEN_CONTRACT_ABI from './ABIs/ERC20_TOKEN_CONTRACT_ABI';
3
3
  export { ERC20_TOKEN_CONTRACT_ABI };
4
4
  /** constants exports */
5
- export { GAS_LIMIT_PER_TX_TYPE } from './constants/constants';
5
+ export { GAS_LIMIT_PER_TX_TYPE, MULTICALL3_ADDRESS } from './constants/constants';
6
6
  /** basic blockchain exports */
7
7
  export { buildMaxNativeTransferTx, buildUnsignedTransferTx, } from './blockchain/buildUnsignedTransferTx';
8
8
  export { broadcastTransaction } from './blockchain/broadcastTransaction';
9
9
  export { estimateGasLimitFromProvider } from './blockchain/estimateGasLimitFromProvider';
10
10
  export { getProvider } from './blockchain/getProvider';
11
11
  export { txStatus } from './blockchain/txStatus';
12
+ export { getBalance, getBalances } from './blockchain/getBalances';
12
13
  /** services exports */
13
14
  export { EvmWalletService, EvmGeneratedWallet, EvmDerivedWallet, } from './services/evm-wallet-core/evmWalletService';
14
15
  export { EntropySource } from './services/evm-wallet-core/entropy';
@@ -24,4 +25,4 @@ export { normalizeAddress } from './utils/normalizeAddress';
24
25
  /** rpc exports */
25
26
  export { validateRpcUrl, ensurePublicHost, testJsonRpc } from './rpc/index';
26
27
  /** types exports */
27
- export { BroadcastTransactionOptions, BuildMaxNativeTransferTxOptions, BuildMaxNativeTransferTxResponse, BuildUnsignedTransferTxOptions, EstimateGasLimitFromProviderProps, GasEstimateResult, TxStatusOptions, TxStatusResponse, UnsignedTransferTxResponse, FormatAmountOptions, TransactionRequest, } from './types/common';
28
+ export { BroadcastTransactionOptions, BuildMaxNativeTransferTxOptions, BuildMaxNativeTransferTxResponse, BuildUnsignedTransferTxOptions, EstimateGasLimitFromProviderProps, GasEstimateResult, TxStatusOptions, TxStatusResponse, UnsignedTransferTxResponse, FormatAmountOptions, TransactionRequest, GetBalanceParams, GetBalancesParams, GetBalancesChainRequest, GetBalanceResult, ChainBalances, TokenBalance, ChainGroup, } from './types/common';
package/dist/cjs/index.js CHANGED
@@ -3,13 +3,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.testJsonRpc = exports.ensurePublicHost = exports.validateRpcUrl = exports.normalizeAddress = exports.errorMessagesForGasLimitEstimation = exports.errorMessagesForBroadcast = exports.handleErrorMessages = exports.parsedAmount = exports.formattedAmountForDisplay = exports.NATIVE_TOKENS = exports.transformBigInt = exports.getShortenData = exports.getShortenTransactionHashOrAddress = exports.signTransaction = exports.signMessage = exports.createWallet = exports.EvmWalletService = exports.txStatus = exports.getProvider = exports.estimateGasLimitFromProvider = exports.broadcastTransaction = exports.buildUnsignedTransferTx = exports.buildMaxNativeTransferTx = exports.GAS_LIMIT_PER_TX_TYPE = exports.ERC20_TOKEN_CONTRACT_ABI = void 0;
6
+ exports.testJsonRpc = exports.ensurePublicHost = exports.validateRpcUrl = exports.normalizeAddress = exports.errorMessagesForGasLimitEstimation = exports.errorMessagesForBroadcast = exports.handleErrorMessages = exports.parsedAmount = exports.formattedAmountForDisplay = exports.NATIVE_TOKENS = exports.transformBigInt = exports.getShortenData = exports.getShortenTransactionHashOrAddress = exports.signTransaction = exports.signMessage = exports.createWallet = exports.EvmWalletService = exports.getBalances = exports.getBalance = exports.txStatus = exports.getProvider = exports.estimateGasLimitFromProvider = exports.broadcastTransaction = exports.buildUnsignedTransferTx = exports.buildMaxNativeTransferTx = exports.MULTICALL3_ADDRESS = exports.GAS_LIMIT_PER_TX_TYPE = exports.ERC20_TOKEN_CONTRACT_ABI = void 0;
7
7
  /** ABIs exports */
8
8
  const ERC20_TOKEN_CONTRACT_ABI_1 = __importDefault(require("./ABIs/ERC20_TOKEN_CONTRACT_ABI"));
9
9
  exports.ERC20_TOKEN_CONTRACT_ABI = ERC20_TOKEN_CONTRACT_ABI_1.default;
10
10
  /** constants exports */
11
11
  var constants_1 = require("./constants/constants");
12
12
  Object.defineProperty(exports, "GAS_LIMIT_PER_TX_TYPE", { enumerable: true, get: function () { return constants_1.GAS_LIMIT_PER_TX_TYPE; } });
13
+ Object.defineProperty(exports, "MULTICALL3_ADDRESS", { enumerable: true, get: function () { return constants_1.MULTICALL3_ADDRESS; } });
13
14
  /** basic blockchain exports */
14
15
  var buildUnsignedTransferTx_1 = require("./blockchain/buildUnsignedTransferTx");
15
16
  Object.defineProperty(exports, "buildMaxNativeTransferTx", { enumerable: true, get: function () { return buildUnsignedTransferTx_1.buildMaxNativeTransferTx; } });
@@ -22,6 +23,9 @@ var getProvider_1 = require("./blockchain/getProvider");
22
23
  Object.defineProperty(exports, "getProvider", { enumerable: true, get: function () { return getProvider_1.getProvider; } });
23
24
  var txStatus_1 = require("./blockchain/txStatus");
24
25
  Object.defineProperty(exports, "txStatus", { enumerable: true, get: function () { return txStatus_1.txStatus; } });
26
+ var getBalances_1 = require("./blockchain/getBalances");
27
+ Object.defineProperty(exports, "getBalance", { enumerable: true, get: function () { return getBalances_1.getBalance; } });
28
+ Object.defineProperty(exports, "getBalances", { enumerable: true, get: function () { return getBalances_1.getBalances; } });
25
29
  /** services exports */
26
30
  var evmWalletService_1 = require("./services/evm-wallet-core/evmWalletService");
27
31
  Object.defineProperty(exports, "EvmWalletService", { enumerable: true, get: function () { return evmWalletService_1.EvmWalletService; } });
@@ -72,3 +72,38 @@ export interface FormatAmountOptions {
72
72
  maxDisplayDigits?: number;
73
73
  }
74
74
  export type { TransactionRequest };
75
+ export interface GetBalanceParams {
76
+ walletAddress: string;
77
+ rpcUrl: string;
78
+ tokenAddress?: string;
79
+ chainId?: number;
80
+ }
81
+ export interface GetBalancesChainRequest {
82
+ rpcUrl: string;
83
+ chainId?: number;
84
+ tokenAddresses: string[];
85
+ includeNative?: boolean;
86
+ }
87
+ export interface GetBalancesParams {
88
+ walletAddress: string;
89
+ chains: GetBalancesChainRequest[];
90
+ }
91
+ export interface TokenBalance {
92
+ tokenAddress: string | null;
93
+ balance: bigint | null;
94
+ error?: unknown;
95
+ }
96
+ export interface ChainBalances {
97
+ chainId?: number;
98
+ balances: TokenBalance[];
99
+ }
100
+ export interface GetBalanceResult {
101
+ walletAddress: string;
102
+ chains: ChainBalances[];
103
+ }
104
+ export interface ChainGroup {
105
+ rpcUrl: string;
106
+ chainId?: number;
107
+ nativeBalanceRequests: GetBalanceParams[];
108
+ tokenBalanceRequests: GetBalanceParams[];
109
+ }
@@ -6,9 +6,8 @@ const ethers_1 = require("ethers");
6
6
  const formattedAmountForDisplay = (amount, decimals, options = {}) => {
7
7
  const { decimalsToShow = 0, useGroupSeparator = true, locale = 'en-US', minimumFractionDigits, minDisplayDecimals = 9, maxDisplayDigits = 16, } = options;
8
8
  const formattedAmount = (0, ethers_1.formatUnits)(amount, decimals);
9
- if (formattedAmount.startsWith('-')) {
9
+ if (formattedAmount.startsWith('-'))
10
10
  throw new Error('Negative balances are not supported');
11
- }
12
11
  const [wholePart = '0', fractionPart = ''] = formattedAmount.split('.');
13
12
  if (wholePart.length >= maxDisplayDigits) {
14
13
  const mantissaWhole = wholePart[0] ?? '0';
@@ -3,3 +3,4 @@ export declare const GAS_LIMIT_PER_TX_TYPE: {
3
3
  DEFAULT_TRANSFER_ERC20: bigint;
4
4
  DEFAULT_APPROVAL: bigint;
5
5
  };
6
+ export declare const MULTICALL3_ADDRESS = "0xcA11bde05977b3631167028862bE2a173976CA11";
@@ -3,3 +3,6 @@ export const GAS_LIMIT_PER_TX_TYPE = {
3
3
  DEFAULT_TRANSFER_ERC20: 65000n,
4
4
  DEFAULT_APPROVAL: 100000n,
5
5
  };
6
+ // & Multicall3 contract address (same on most chains)
7
+ // & https://www.multicall3.com/deployments
8
+ export const MULTICALL3_ADDRESS = '0xcA11bde05977b3631167028862bE2a173976CA11';
package/dist/index.d.ts CHANGED
@@ -2,13 +2,14 @@
2
2
  import ERC20_TOKEN_CONTRACT_ABI from './ABIs/ERC20_TOKEN_CONTRACT_ABI';
3
3
  export { ERC20_TOKEN_CONTRACT_ABI };
4
4
  /** constants exports */
5
- export { GAS_LIMIT_PER_TX_TYPE } from './constants/constants';
5
+ export { GAS_LIMIT_PER_TX_TYPE, MULTICALL3_ADDRESS } from './constants/constants';
6
6
  /** basic blockchain exports */
7
7
  export { buildMaxNativeTransferTx, buildUnsignedTransferTx, } from './blockchain/buildUnsignedTransferTx';
8
8
  export { broadcastTransaction } from './blockchain/broadcastTransaction';
9
9
  export { estimateGasLimitFromProvider } from './blockchain/estimateGasLimitFromProvider';
10
10
  export { getProvider } from './blockchain/getProvider';
11
11
  export { txStatus } from './blockchain/txStatus';
12
+ export { getBalance, getBalances } from './blockchain/getBalances';
12
13
  /** services exports */
13
14
  export { EvmWalletService, EvmGeneratedWallet, EvmDerivedWallet, } from './services/evm-wallet-core/evmWalletService';
14
15
  export { EntropySource } from './services/evm-wallet-core/entropy';
@@ -24,4 +25,4 @@ export { normalizeAddress } from './utils/normalizeAddress';
24
25
  /** rpc exports */
25
26
  export { validateRpcUrl, ensurePublicHost, testJsonRpc } from './rpc/index';
26
27
  /** types exports */
27
- export { BroadcastTransactionOptions, BuildMaxNativeTransferTxOptions, BuildMaxNativeTransferTxResponse, BuildUnsignedTransferTxOptions, EstimateGasLimitFromProviderProps, GasEstimateResult, TxStatusOptions, TxStatusResponse, UnsignedTransferTxResponse, FormatAmountOptions, TransactionRequest, } from './types/common';
28
+ export { BroadcastTransactionOptions, BuildMaxNativeTransferTxOptions, BuildMaxNativeTransferTxResponse, BuildUnsignedTransferTxOptions, EstimateGasLimitFromProviderProps, GasEstimateResult, TxStatusOptions, TxStatusResponse, UnsignedTransferTxResponse, FormatAmountOptions, TransactionRequest, GetBalanceParams, GetBalancesParams, GetBalancesChainRequest, GetBalanceResult, ChainBalances, TokenBalance, ChainGroup, } from './types/common';
package/dist/index.js CHANGED
@@ -2,13 +2,14 @@
2
2
  import ERC20_TOKEN_CONTRACT_ABI from './ABIs/ERC20_TOKEN_CONTRACT_ABI';
3
3
  export { ERC20_TOKEN_CONTRACT_ABI };
4
4
  /** constants exports */
5
- export { GAS_LIMIT_PER_TX_TYPE } from './constants/constants';
5
+ export { GAS_LIMIT_PER_TX_TYPE, MULTICALL3_ADDRESS } from './constants/constants';
6
6
  /** basic blockchain exports */
7
7
  export { buildMaxNativeTransferTx, buildUnsignedTransferTx, } from './blockchain/buildUnsignedTransferTx';
8
8
  export { broadcastTransaction } from './blockchain/broadcastTransaction';
9
9
  export { estimateGasLimitFromProvider } from './blockchain/estimateGasLimitFromProvider';
10
10
  export { getProvider } from './blockchain/getProvider';
11
11
  export { txStatus } from './blockchain/txStatus';
12
+ export { getBalance, getBalances } from './blockchain/getBalances';
12
13
  /** services exports */
13
14
  export { EvmWalletService, } from './services/evm-wallet-core/evmWalletService';
14
15
  export { createWallet, signMessage, signTransaction } from './services/evm-wallet-core/signer';
@@ -72,3 +72,38 @@ export interface FormatAmountOptions {
72
72
  maxDisplayDigits?: number;
73
73
  }
74
74
  export type { TransactionRequest };
75
+ export interface GetBalanceParams {
76
+ walletAddress: string;
77
+ rpcUrl: string;
78
+ tokenAddress?: string;
79
+ chainId?: number;
80
+ }
81
+ export interface GetBalancesChainRequest {
82
+ rpcUrl: string;
83
+ chainId?: number;
84
+ tokenAddresses: string[];
85
+ includeNative?: boolean;
86
+ }
87
+ export interface GetBalancesParams {
88
+ walletAddress: string;
89
+ chains: GetBalancesChainRequest[];
90
+ }
91
+ export interface TokenBalance {
92
+ tokenAddress: string | null;
93
+ balance: bigint | null;
94
+ error?: unknown;
95
+ }
96
+ export interface ChainBalances {
97
+ chainId?: number;
98
+ balances: TokenBalance[];
99
+ }
100
+ export interface GetBalanceResult {
101
+ walletAddress: string;
102
+ chains: ChainBalances[];
103
+ }
104
+ export interface ChainGroup {
105
+ rpcUrl: string;
106
+ chainId?: number;
107
+ nativeBalanceRequests: GetBalanceParams[];
108
+ tokenBalanceRequests: GetBalanceParams[];
109
+ }
@@ -3,9 +3,8 @@ import { formatUnits, parseUnits } from 'ethers';
3
3
  export const formattedAmountForDisplay = (amount, decimals, options = {}) => {
4
4
  const { decimalsToShow = 0, useGroupSeparator = true, locale = 'en-US', minimumFractionDigits, minDisplayDecimals = 9, maxDisplayDigits = 16, } = options;
5
5
  const formattedAmount = formatUnits(amount, decimals);
6
- if (formattedAmount.startsWith('-')) {
6
+ if (formattedAmount.startsWith('-'))
7
7
  throw new Error('Negative balances are not supported');
8
- }
9
8
  const [wholePart = '0', fractionPart = ''] = formattedAmount.split('.');
10
9
  if (wholePart.length >= maxDisplayDigits) {
11
10
  const mantissaWhole = wholePart[0] ?? '0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@octaflowlabs/onchain-sdk",
3
- "version": "1.1.4",
3
+ "version": "1.2.0",
4
4
  "description": "onchain methods for web3",
5
5
  "repository": "https://github.com/crisramb665/onchain-sdk.git",
6
6
  "license": "MIT",