@octaflowlabs/onchain-sdk 1.4.2 → 1.4.4

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
@@ -72,6 +72,7 @@ import {
72
72
  | `formattedAmountForDisplay` | `(amount, decimals, options?) => string` | Locale-aware formatting with group separators, scientific notation for large numbers, and threshold display for tiny amounts. |
73
73
  | `parsedAmount` | `(amount: string, decimals: number) => bigint` | Parse a human-readable amount string into its smallest-unit `bigint`. |
74
74
  | `normalizeAddress` | `(address: string) => string` | Validate and checksum an Ethereum address. |
75
+ | `normalizeEvmAddress` | `(address: string \| null \| undefined) => string \| null` | Validate, trim, and lowercase an EVM address, returning `null` for empty or invalid values. |
75
76
  | `getShortenTransactionHashOrAddress` | `(value, first?, last?) => string` | Shorten a tx hash or address (e.g. `0xAbCd…1234`). |
76
77
  | `getShortenData` | `(data, first?, last?) => string` | Shorten arbitrary hex data. |
77
78
  | `transformBigInt` | `(obj: ContractTransaction) => object` | Convert all `bigint` properties to strings for JSON serialization. |
@@ -103,11 +104,26 @@ Each `NetworkField` entry includes `id`, `name`, `chainId`, `rpcUrl`, optional `
103
104
  |---|---|
104
105
  | `BASIC_TOKENS_BY_CHAIN` | Hardcoded ERC-20 token metadata (USDC, USDT, etc.) grouped by chain ID for Ethereum, BSC, Polygon, and Arbitrum. |
105
106
 
107
+ ### Stablecoin registry
108
+
109
+ | Export | Description |
110
+ |---|---|
111
+ | `STABLECOIN_CONTRACTS_BY_CHAIN_ID` | Allowlisted USDC, USDT, and OSC test stablecoin contracts grouped by chain ID. |
112
+ | `getStablecoinContractsByChainId` | `(chainId) => readonly StablecoinContractData[]` — return allowlisted stablecoin metadata for one chain. |
113
+ | `getStablecoinContractBySymbolAndChainId` | `(symbol, chainId) => StablecoinContractData \| null` — resolve one allowlisted stablecoin contract by symbol and chain ID. |
114
+ | `isAllowedStablecoinContract` | `(chainId, contractAddress) => boolean` — normalize and check whether a contract is an allowlisted stablecoin on a chain. |
115
+
116
+ ### Alchemy webhooks
117
+
118
+ | Export | Description |
119
+ |---|---|
120
+ | `extractPoolInboundStablecoinEvents` | `(payload, poolAddress) => PoolInboundStablecoinEvent[]` — extract inbound token transfers to a pool when the token contract is an allowlisted stablecoin. |
121
+
106
122
  ### Types
107
123
 
108
124
  All interfaces and type aliases are exported for consumer use:
109
125
 
110
- `BroadcastTransactionOptions`, `BuildMaxNativeTransferTxOptions`, `BuildUnsignedTransferTxOptions`, `BuildBaseUnsignedTransferTxParams`, `EstimateGasLimitFromProviderProps`, `GasEstimateResult`, `EstimateTransactionOptions`, `EstimateTransactionResult`, `PrepareTransactionParams`, `PrepareTransactionResult`, `TxStatusOptions`, `TxStatusResponse`, `FormatAmountOptions`, `TransactionRequest`, `GetBalanceParams`, `GetBalancesParams`, `GetBalancesChainRequest`, `GetBalanceResult`, `ChainBalances`, `TokenBalance`, `ChainGroup`, `NetworkField`, `NetworkId`, `NetworkCategory`, `BasicTokenData`, `BasicTokenSymbol`, `ChainTokenDataMap`, `EvmGeneratedWallet`, `EvmDerivedWallet`, `EntropySource`.
126
+ `BroadcastTransactionOptions`, `BuildMaxNativeTransferTxOptions`, `BuildUnsignedTransferTxOptions`, `BuildBaseUnsignedTransferTxParams`, `EstimateGasLimitFromProviderProps`, `GasEstimateResult`, `EstimateTransactionOptions`, `EstimateTransactionResult`, `PrepareTransactionParams`, `PrepareTransactionResult`, `TxStatusOptions`, `TxStatusResponse`, `FormatAmountOptions`, `TransactionRequest`, `GetBalanceParams`, `GetBalancesParams`, `GetBalancesChainRequest`, `GetBalanceResult`, `ChainBalances`, `TokenBalance`, `ChainGroup`, `NetworkField`, `NetworkId`, `NetworkCategory`, `BasicTokenData`, `BasicTokenSymbol`, `ChainTokenDataMap`, `StablecoinContractData`, `StablecoinContractsByChainId`, `StablecoinSymbol`, `EvmGeneratedWallet`, `EvmDerivedWallet`, `EntropySource`.
111
127
 
112
128
  ## Design notes
113
129
 
@@ -0,0 +1,13 @@
1
+ export type StablecoinSymbol = 'USDC' | 'USDT' | 'OSC';
2
+ export interface StablecoinContractData {
3
+ chainId: number;
4
+ symbol: StablecoinSymbol;
5
+ name: string;
6
+ address: string;
7
+ decimals: number;
8
+ }
9
+ export type StablecoinContractsByChainId = Readonly<Record<number, readonly StablecoinContractData[]>>;
10
+ export declare const STABLECOIN_CONTRACTS_BY_CHAIN_ID: StablecoinContractsByChainId;
11
+ export declare const getStablecoinContractsByChainId: (chainId: number | null | undefined) => readonly StablecoinContractData[];
12
+ export declare const getStablecoinContractBySymbolAndChainId: (symbol: StablecoinSymbol, chainId: number | null | undefined) => StablecoinContractData | null;
13
+ export declare const isAllowedStablecoinContract: (chainId: number | null | undefined, contractAddress: string | null | undefined) => boolean;
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isAllowedStablecoinContract = exports.getStablecoinContractBySymbolAndChainId = exports.getStablecoinContractsByChainId = exports.STABLECOIN_CONTRACTS_BY_CHAIN_ID = void 0;
4
+ /** local imports */
5
+ const normalizeAddress_1 = require("../utils/normalizeAddress");
6
+ exports.STABLECOIN_CONTRACTS_BY_CHAIN_ID = {
7
+ 1: [
8
+ {
9
+ chainId: 1,
10
+ symbol: 'USDC',
11
+ name: 'USD Coin',
12
+ address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
13
+ decimals: 6,
14
+ },
15
+ {
16
+ chainId: 1,
17
+ symbol: 'USDT',
18
+ name: 'Tether USD',
19
+ address: '0xdac17f958d2ee523a2206206994597c13d831ec7',
20
+ decimals: 6,
21
+ },
22
+ ],
23
+ 56: [
24
+ {
25
+ chainId: 56,
26
+ symbol: 'USDC',
27
+ name: 'USD Coin',
28
+ address: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d',
29
+ decimals: 18,
30
+ },
31
+ {
32
+ chainId: 56,
33
+ symbol: 'USDT',
34
+ name: 'Tether USD',
35
+ address: '0x55d398326f99059ff775485246999027b3197955',
36
+ decimals: 18,
37
+ },
38
+ ],
39
+ 137: [
40
+ {
41
+ chainId: 137,
42
+ symbol: 'USDC',
43
+ name: 'USD Coin Bridged',
44
+ address: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
45
+ decimals: 6,
46
+ },
47
+ {
48
+ chainId: 137,
49
+ symbol: 'USDC',
50
+ name: 'USD Coin',
51
+ address: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359',
52
+ decimals: 6,
53
+ },
54
+ {
55
+ chainId: 137,
56
+ symbol: 'USDT',
57
+ name: 'Tether USD',
58
+ address: '0xc2132d05d31c914a87c6611c10748aeb04b58e8f',
59
+ decimals: 6,
60
+ },
61
+ ],
62
+ 11155111: [
63
+ {
64
+ chainId: 11155111,
65
+ symbol: 'USDC',
66
+ name: 'Sepolia USDC',
67
+ address: '0x1c7d4b196cb0c7b01d743fbc6116a902379c7238',
68
+ decimals: 6,
69
+ },
70
+ {
71
+ chainId: 11155111,
72
+ symbol: 'OSC',
73
+ name: 'OSC Test Stablecoin',
74
+ address: '0x4357555de1c8ed60218dd42b0b4f404e31b6d744',
75
+ decimals: 6,
76
+ },
77
+ ],
78
+ };
79
+ const getStablecoinContractsByChainId = (chainId) => {
80
+ if (chainId == null)
81
+ return [];
82
+ return exports.STABLECOIN_CONTRACTS_BY_CHAIN_ID[chainId] || [];
83
+ };
84
+ exports.getStablecoinContractsByChainId = getStablecoinContractsByChainId;
85
+ const getStablecoinContractBySymbolAndChainId = (symbol, chainId) => {
86
+ const contracts = (0, exports.getStablecoinContractsByChainId)(chainId);
87
+ return contracts.find((contract) => contract.symbol === symbol) || null;
88
+ };
89
+ exports.getStablecoinContractBySymbolAndChainId = getStablecoinContractBySymbolAndChainId;
90
+ const isAllowedStablecoinContract = (chainId, contractAddress) => {
91
+ if (chainId == null)
92
+ return false;
93
+ const normalizedAddress = (0, normalizeAddress_1.normalizeEvmAddress)(contractAddress);
94
+ if (!normalizedAddress)
95
+ return false;
96
+ const allowedContracts = (0, exports.getStablecoinContractsByChainId)(chainId);
97
+ if (!allowedContracts.length)
98
+ return false;
99
+ return allowedContracts.some((contract) => contract.address === normalizedAddress);
100
+ };
101
+ exports.isAllowedStablecoinContract = isAllowedStablecoinContract;
@@ -5,6 +5,7 @@ export { ERC20_TOKEN_CONTRACT_ABI };
5
5
  export { GAS_LIMIT_PER_TX_TYPE, MULTICALL3_ADDRESS } from './constants/constants';
6
6
  export { NetworkField, NetworkRegistry, NETWORKS_REGISTRY } from './constants/NETWORKS_REGISTRY';
7
7
  export { BASIC_TOKENS_BY_CHAIN, BasicTokenData, BasicTokenSymbol, ChainTokenDataMap, } from './constants/BASIC_TOKENS_REGISTRY';
8
+ export { STABLECOIN_CONTRACTS_BY_CHAIN_ID, getStablecoinContractBySymbolAndChainId, getStablecoinContractsByChainId, isAllowedStablecoinContract, StablecoinContractData, StablecoinContractsByChainId, StablecoinSymbol, } from './constants/STABLECOINS_REGISTRY';
8
9
  /** basic blockchain exports */
9
10
  export { buildBaseUnsignedTransferTx, buildMaxNativeTransferTx, buildUnsignedTransferTx, } from './blockchain/buildUnsignedTransferTx';
10
11
  export { broadcastTransaction } from './blockchain/broadcastTransaction';
@@ -26,8 +27,10 @@ import NATIVE_TOKENS from './utils/tokens';
26
27
  export { NATIVE_TOKENS };
27
28
  export { formattedAmountForDisplay, parsedAmount } from './utils/formatAmount';
28
29
  export { handleErrorMessages, errorMessagesForBroadcast, errorMessagesForGasLimitEstimation, } from './utils/handleErrorMessages';
29
- export { normalizeAddress } from './utils/normalizeAddress';
30
+ export { normalizeAddress, normalizeEvmAddress } from './utils/normalizeAddress';
30
31
  export { toGwei, tryParseGweiToWei, parseGasLimit } from './utils/parseGasFees';
31
32
  export { getFeePresets, calcReserve, estimateSeconds, getBlockTime, BLOCK_TIME_BY_CHAIN, } from './utils/feePresets';
33
+ /** webhook exports */
34
+ export { extractPoolInboundStablecoinEvents } from './webhooks/alchemyAddressActivity';
32
35
  /** types exports */
33
36
  export { BroadcastTransactionOptions, BuildMaxNativeTransferTxOptions, BuildUnsignedTransferTxOptions, EstimateGasLimitFromProviderProps, GasEstimateResult, TxStatusOptions, BuildBaseUnsignedTransferTxParams, TxStatusResponse, EstimateTransactionOptions, EstimateTransactionResult, PrepareTransactionParams, PrepareTransactionResult, FormatAmountOptions, TransactionRequest, GetBalanceParams, GetBalancesParams, GetBalancesChainRequest, GetBalanceResult, ChainBalances, TokenBalance, ChainGroup, FeePreset, FeePresetLabel, FeeEstimateSeconds, ResolvedFees, EIP1559Fees, LegacyFees, FeeModel, FeeHistoryData, FeeDataInput, } from './types/common';
package/dist/cjs/index.js CHANGED
@@ -3,7 +3,7 @@ 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.BLOCK_TIME_BY_CHAIN = exports.getBlockTime = exports.estimateSeconds = exports.calcReserve = exports.getFeePresets = exports.parseGasLimit = exports.tryParseGweiToWei = exports.toGwei = 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.fetchFeeSnapshot = exports.prepareTransaction = exports.estimateTransaction = exports.getBalances = exports.getBalance = exports.txStatus = exports.getProvider = exports.estimateGasLimitFromProvider = exports.broadcastTransaction = exports.buildUnsignedTransferTx = exports.buildMaxNativeTransferTx = exports.buildBaseUnsignedTransferTx = exports.BASIC_TOKENS_BY_CHAIN = exports.NETWORKS_REGISTRY = exports.MULTICALL3_ADDRESS = exports.GAS_LIMIT_PER_TX_TYPE = exports.ERC20_TOKEN_CONTRACT_ABI = void 0;
6
+ exports.extractPoolInboundStablecoinEvents = exports.BLOCK_TIME_BY_CHAIN = exports.getBlockTime = exports.estimateSeconds = exports.calcReserve = exports.getFeePresets = exports.parseGasLimit = exports.tryParseGweiToWei = exports.toGwei = exports.normalizeEvmAddress = 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.fetchFeeSnapshot = exports.prepareTransaction = exports.estimateTransaction = exports.getBalances = exports.getBalance = exports.txStatus = exports.getProvider = exports.estimateGasLimitFromProvider = exports.broadcastTransaction = exports.buildUnsignedTransferTx = exports.buildMaxNativeTransferTx = exports.buildBaseUnsignedTransferTx = exports.isAllowedStablecoinContract = exports.getStablecoinContractsByChainId = exports.getStablecoinContractBySymbolAndChainId = exports.STABLECOIN_CONTRACTS_BY_CHAIN_ID = exports.BASIC_TOKENS_BY_CHAIN = exports.NETWORKS_REGISTRY = 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;
@@ -15,6 +15,11 @@ var NETWORKS_REGISTRY_1 = require("./constants/NETWORKS_REGISTRY");
15
15
  Object.defineProperty(exports, "NETWORKS_REGISTRY", { enumerable: true, get: function () { return NETWORKS_REGISTRY_1.NETWORKS_REGISTRY; } });
16
16
  var BASIC_TOKENS_REGISTRY_1 = require("./constants/BASIC_TOKENS_REGISTRY");
17
17
  Object.defineProperty(exports, "BASIC_TOKENS_BY_CHAIN", { enumerable: true, get: function () { return BASIC_TOKENS_REGISTRY_1.BASIC_TOKENS_BY_CHAIN; } });
18
+ var STABLECOINS_REGISTRY_1 = require("./constants/STABLECOINS_REGISTRY");
19
+ Object.defineProperty(exports, "STABLECOIN_CONTRACTS_BY_CHAIN_ID", { enumerable: true, get: function () { return STABLECOINS_REGISTRY_1.STABLECOIN_CONTRACTS_BY_CHAIN_ID; } });
20
+ Object.defineProperty(exports, "getStablecoinContractBySymbolAndChainId", { enumerable: true, get: function () { return STABLECOINS_REGISTRY_1.getStablecoinContractBySymbolAndChainId; } });
21
+ Object.defineProperty(exports, "getStablecoinContractsByChainId", { enumerable: true, get: function () { return STABLECOINS_REGISTRY_1.getStablecoinContractsByChainId; } });
22
+ Object.defineProperty(exports, "isAllowedStablecoinContract", { enumerable: true, get: function () { return STABLECOINS_REGISTRY_1.isAllowedStablecoinContract; } });
18
23
  /** basic blockchain exports */
19
24
  var buildUnsignedTransferTx_1 = require("./blockchain/buildUnsignedTransferTx");
20
25
  Object.defineProperty(exports, "buildBaseUnsignedTransferTx", { enumerable: true, get: function () { return buildUnsignedTransferTx_1.buildBaseUnsignedTransferTx; } });
@@ -61,6 +66,7 @@ Object.defineProperty(exports, "errorMessagesForBroadcast", { enumerable: true,
61
66
  Object.defineProperty(exports, "errorMessagesForGasLimitEstimation", { enumerable: true, get: function () { return handleErrorMessages_1.errorMessagesForGasLimitEstimation; } });
62
67
  var normalizeAddress_1 = require("./utils/normalizeAddress");
63
68
  Object.defineProperty(exports, "normalizeAddress", { enumerable: true, get: function () { return normalizeAddress_1.normalizeAddress; } });
69
+ Object.defineProperty(exports, "normalizeEvmAddress", { enumerable: true, get: function () { return normalizeAddress_1.normalizeEvmAddress; } });
64
70
  var parseGasFees_1 = require("./utils/parseGasFees");
65
71
  Object.defineProperty(exports, "toGwei", { enumerable: true, get: function () { return parseGasFees_1.toGwei; } });
66
72
  Object.defineProperty(exports, "tryParseGweiToWei", { enumerable: true, get: function () { return parseGasFees_1.tryParseGweiToWei; } });
@@ -71,3 +77,6 @@ Object.defineProperty(exports, "calcReserve", { enumerable: true, get: function
71
77
  Object.defineProperty(exports, "estimateSeconds", { enumerable: true, get: function () { return feePresets_1.estimateSeconds; } });
72
78
  Object.defineProperty(exports, "getBlockTime", { enumerable: true, get: function () { return feePresets_1.getBlockTime; } });
73
79
  Object.defineProperty(exports, "BLOCK_TIME_BY_CHAIN", { enumerable: true, get: function () { return feePresets_1.BLOCK_TIME_BY_CHAIN; } });
80
+ /** webhook exports */
81
+ var alchemyAddressActivity_1 = require("./webhooks/alchemyAddressActivity");
82
+ Object.defineProperty(exports, "extractPoolInboundStablecoinEvents", { enumerable: true, get: function () { return alchemyAddressActivity_1.extractPoolInboundStablecoinEvents; } });
@@ -0,0 +1,7 @@
1
+ export interface TransactionActivityDedupeInput {
2
+ hash?: string | null;
3
+ log?: {
4
+ logIndex?: string | null;
5
+ } | null;
6
+ }
7
+ export declare const getTransactionActivityDedupeKey: (activity: TransactionActivityDedupeInput) => string | null;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getTransactionActivityDedupeKey = void 0;
4
+ const getTransactionActivityDedupeKey = (activity) => {
5
+ const hash = activity.hash?.trim();
6
+ if (!hash)
7
+ return null;
8
+ const logIndex = activity.log?.logIndex?.trim();
9
+ return logIndex ? `${hash}:${logIndex}` : hash;
10
+ };
11
+ exports.getTransactionActivityDedupeKey = getTransactionActivityDedupeKey;
@@ -1 +1,2 @@
1
1
  export declare const normalizeAddress: (address: string) => string;
2
+ export declare const normalizeEvmAddress: (address: string | null | undefined) => string | null;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.normalizeAddress = void 0;
3
+ exports.normalizeEvmAddress = exports.normalizeAddress = void 0;
4
4
  /** npm imports */
5
5
  const ethers_1 = require("ethers");
6
6
  const normalizeAddress = (address) => {
@@ -13,3 +13,14 @@ const normalizeAddress = (address) => {
13
13
  }
14
14
  };
15
15
  exports.normalizeAddress = normalizeAddress;
16
+ const normalizeEvmAddress = (address) => {
17
+ if (!address || typeof address !== 'string')
18
+ return null;
19
+ try {
20
+ return (0, exports.normalizeAddress)(address.trim()).toLowerCase();
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ };
26
+ exports.normalizeEvmAddress = normalizeEvmAddress;
@@ -0,0 +1,39 @@
1
+ export interface AlchemyAddressActivity {
2
+ category?: string;
3
+ asset?: string;
4
+ value?: number | null;
5
+ fromAddress?: string | null;
6
+ toAddress?: string | null;
7
+ hash?: string | null;
8
+ erc721TokenId?: string | number | null;
9
+ rawContract?: {
10
+ address?: string | null;
11
+ } | null;
12
+ log?: {
13
+ removed?: boolean;
14
+ logIndex?: string | null;
15
+ } | null;
16
+ }
17
+ export interface AlchemyAddressActivityPayload {
18
+ id?: string;
19
+ webhookId?: string;
20
+ type?: string;
21
+ event?: {
22
+ network?: string;
23
+ activity?: AlchemyAddressActivity[];
24
+ };
25
+ }
26
+ export interface PoolInboundStablecoinEvent {
27
+ webhookDeliveryId?: string;
28
+ webhookId?: string;
29
+ alchemyNetwork: string;
30
+ chainId: number | null;
31
+ txHash: string;
32
+ fromAddress: string;
33
+ toAddress: string;
34
+ amount: number;
35
+ asset: string;
36
+ tokenContractAddress?: string | null;
37
+ dedupeKey: string;
38
+ }
39
+ export declare function extractPoolInboundStablecoinEvents(payload: AlchemyAddressActivityPayload, poolAddress: string | null | undefined): PoolInboundStablecoinEvent[];
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.extractPoolInboundStablecoinEvents = extractPoolInboundStablecoinEvents;
4
+ /** local imports */
5
+ const STABLECOINS_REGISTRY_1 = require("../constants/STABLECOINS_REGISTRY");
6
+ const activityDedupe_1 = require("../utils/activityDedupe");
7
+ const normalizeAddress_1 = require("../utils/normalizeAddress");
8
+ const CHAIN_ID_TO_ALCHEMY_NETWORK = {
9
+ 1: 'ETH_MAINNET',
10
+ 56: 'BNB_MAINNET',
11
+ 137: 'MATIC_MAINNET',
12
+ 11155111: 'ETH_SEPOLIA',
13
+ };
14
+ const ALCHEMY_NETWORK_TO_CHAIN_ID = Object.fromEntries(Object.entries(CHAIN_ID_TO_ALCHEMY_NETWORK).map(([chainId, network]) => [
15
+ network,
16
+ Number(chainId),
17
+ ]));
18
+ function chainIdFromAlchemyNetwork(network) {
19
+ if (!network)
20
+ return null;
21
+ const chainId = ALCHEMY_NETWORK_TO_CHAIN_ID[network];
22
+ return chainId === undefined ? null : chainId;
23
+ }
24
+ function extractPoolInboundStablecoinEvents(payload, poolAddress) {
25
+ const poolAddressLower = (0, normalizeAddress_1.normalizeEvmAddress)(poolAddress);
26
+ if (!poolAddressLower)
27
+ return [];
28
+ if (payload.type !== 'ADDRESS_ACTIVITY')
29
+ return [];
30
+ const network = payload.event?.network;
31
+ const chainId = chainIdFromAlchemyNetwork(network);
32
+ const activity = payload.event?.activity;
33
+ if (!activity?.length)
34
+ return [];
35
+ const out = [];
36
+ for (const item of activity) {
37
+ if (item.category !== 'token')
38
+ continue;
39
+ if (item.log?.removed === true)
40
+ continue;
41
+ if (item.erc721TokenId != null && item.erc721TokenId !== '')
42
+ continue;
43
+ if (!(0, STABLECOINS_REGISTRY_1.isAllowedStablecoinContract)(chainId, item.rawContract?.address))
44
+ continue;
45
+ const value = item.value;
46
+ if (value === undefined || value === null || typeof value !== 'number' || !(value > 0))
47
+ continue;
48
+ const to = (0, normalizeAddress_1.normalizeEvmAddress)(item.toAddress);
49
+ const from = (0, normalizeAddress_1.normalizeEvmAddress)(item.fromAddress);
50
+ if (!to || !from)
51
+ continue;
52
+ if (to !== poolAddressLower)
53
+ continue;
54
+ const txHash = item.hash?.trim();
55
+ if (!txHash)
56
+ continue;
57
+ const dedupeKey = (0, activityDedupe_1.getTransactionActivityDedupeKey)(item);
58
+ if (!dedupeKey)
59
+ continue;
60
+ out.push({
61
+ webhookDeliveryId: payload.id,
62
+ webhookId: payload.webhookId,
63
+ alchemyNetwork: network || 'UNKNOWN',
64
+ chainId,
65
+ txHash,
66
+ fromAddress: from,
67
+ toAddress: to,
68
+ amount: value,
69
+ asset: item.asset || '',
70
+ tokenContractAddress: item.rawContract?.address,
71
+ dedupeKey,
72
+ });
73
+ }
74
+ return out;
75
+ }
@@ -0,0 +1,13 @@
1
+ export type StablecoinSymbol = 'USDC' | 'USDT' | 'OSC';
2
+ export interface StablecoinContractData {
3
+ chainId: number;
4
+ symbol: StablecoinSymbol;
5
+ name: string;
6
+ address: string;
7
+ decimals: number;
8
+ }
9
+ export type StablecoinContractsByChainId = Readonly<Record<number, readonly StablecoinContractData[]>>;
10
+ export declare const STABLECOIN_CONTRACTS_BY_CHAIN_ID: StablecoinContractsByChainId;
11
+ export declare const getStablecoinContractsByChainId: (chainId: number | null | undefined) => readonly StablecoinContractData[];
12
+ export declare const getStablecoinContractBySymbolAndChainId: (symbol: StablecoinSymbol, chainId: number | null | undefined) => StablecoinContractData | null;
13
+ export declare const isAllowedStablecoinContract: (chainId: number | null | undefined, contractAddress: string | null | undefined) => boolean;
@@ -0,0 +1,95 @@
1
+ /** local imports */
2
+ import { normalizeEvmAddress } from '../utils/normalizeAddress';
3
+ export const STABLECOIN_CONTRACTS_BY_CHAIN_ID = {
4
+ 1: [
5
+ {
6
+ chainId: 1,
7
+ symbol: 'USDC',
8
+ name: 'USD Coin',
9
+ address: '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48',
10
+ decimals: 6,
11
+ },
12
+ {
13
+ chainId: 1,
14
+ symbol: 'USDT',
15
+ name: 'Tether USD',
16
+ address: '0xdac17f958d2ee523a2206206994597c13d831ec7',
17
+ decimals: 6,
18
+ },
19
+ ],
20
+ 56: [
21
+ {
22
+ chainId: 56,
23
+ symbol: 'USDC',
24
+ name: 'USD Coin',
25
+ address: '0x8ac76a51cc950d9822d68b83fe1ad97b32cd580d',
26
+ decimals: 18,
27
+ },
28
+ {
29
+ chainId: 56,
30
+ symbol: 'USDT',
31
+ name: 'Tether USD',
32
+ address: '0x55d398326f99059ff775485246999027b3197955',
33
+ decimals: 18,
34
+ },
35
+ ],
36
+ 137: [
37
+ {
38
+ chainId: 137,
39
+ symbol: 'USDC',
40
+ name: 'USD Coin Bridged',
41
+ address: '0x2791bca1f2de4661ed88a30c99a7a9449aa84174',
42
+ decimals: 6,
43
+ },
44
+ {
45
+ chainId: 137,
46
+ symbol: 'USDC',
47
+ name: 'USD Coin',
48
+ address: '0x3c499c542cef5e3811e1192ce70d8cc03d5c3359',
49
+ decimals: 6,
50
+ },
51
+ {
52
+ chainId: 137,
53
+ symbol: 'USDT',
54
+ name: 'Tether USD',
55
+ address: '0xc2132d05d31c914a87c6611c10748aeb04b58e8f',
56
+ decimals: 6,
57
+ },
58
+ ],
59
+ 11155111: [
60
+ {
61
+ chainId: 11155111,
62
+ symbol: 'USDC',
63
+ name: 'Sepolia USDC',
64
+ address: '0x1c7d4b196cb0c7b01d743fbc6116a902379c7238',
65
+ decimals: 6,
66
+ },
67
+ {
68
+ chainId: 11155111,
69
+ symbol: 'OSC',
70
+ name: 'OSC Test Stablecoin',
71
+ address: '0x4357555de1c8ed60218dd42b0b4f404e31b6d744',
72
+ decimals: 6,
73
+ },
74
+ ],
75
+ };
76
+ export const getStablecoinContractsByChainId = (chainId) => {
77
+ if (chainId == null)
78
+ return [];
79
+ return STABLECOIN_CONTRACTS_BY_CHAIN_ID[chainId] || [];
80
+ };
81
+ export const getStablecoinContractBySymbolAndChainId = (symbol, chainId) => {
82
+ const contracts = getStablecoinContractsByChainId(chainId);
83
+ return contracts.find((contract) => contract.symbol === symbol) || null;
84
+ };
85
+ export const isAllowedStablecoinContract = (chainId, contractAddress) => {
86
+ if (chainId == null)
87
+ return false;
88
+ const normalizedAddress = normalizeEvmAddress(contractAddress);
89
+ if (!normalizedAddress)
90
+ return false;
91
+ const allowedContracts = getStablecoinContractsByChainId(chainId);
92
+ if (!allowedContracts.length)
93
+ return false;
94
+ return allowedContracts.some((contract) => contract.address === normalizedAddress);
95
+ };
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export { ERC20_TOKEN_CONTRACT_ABI };
5
5
  export { GAS_LIMIT_PER_TX_TYPE, MULTICALL3_ADDRESS } from './constants/constants';
6
6
  export { NetworkField, NetworkRegistry, NETWORKS_REGISTRY } from './constants/NETWORKS_REGISTRY';
7
7
  export { BASIC_TOKENS_BY_CHAIN, BasicTokenData, BasicTokenSymbol, ChainTokenDataMap, } from './constants/BASIC_TOKENS_REGISTRY';
8
+ export { STABLECOIN_CONTRACTS_BY_CHAIN_ID, getStablecoinContractBySymbolAndChainId, getStablecoinContractsByChainId, isAllowedStablecoinContract, StablecoinContractData, StablecoinContractsByChainId, StablecoinSymbol, } from './constants/STABLECOINS_REGISTRY';
8
9
  /** basic blockchain exports */
9
10
  export { buildBaseUnsignedTransferTx, buildMaxNativeTransferTx, buildUnsignedTransferTx, } from './blockchain/buildUnsignedTransferTx';
10
11
  export { broadcastTransaction } from './blockchain/broadcastTransaction';
@@ -26,8 +27,10 @@ import NATIVE_TOKENS from './utils/tokens';
26
27
  export { NATIVE_TOKENS };
27
28
  export { formattedAmountForDisplay, parsedAmount } from './utils/formatAmount';
28
29
  export { handleErrorMessages, errorMessagesForBroadcast, errorMessagesForGasLimitEstimation, } from './utils/handleErrorMessages';
29
- export { normalizeAddress } from './utils/normalizeAddress';
30
+ export { normalizeAddress, normalizeEvmAddress } from './utils/normalizeAddress';
30
31
  export { toGwei, tryParseGweiToWei, parseGasLimit } from './utils/parseGasFees';
31
32
  export { getFeePresets, calcReserve, estimateSeconds, getBlockTime, BLOCK_TIME_BY_CHAIN, } from './utils/feePresets';
33
+ /** webhook exports */
34
+ export { extractPoolInboundStablecoinEvents } from './webhooks/alchemyAddressActivity';
32
35
  /** types exports */
33
36
  export { BroadcastTransactionOptions, BuildMaxNativeTransferTxOptions, BuildUnsignedTransferTxOptions, EstimateGasLimitFromProviderProps, GasEstimateResult, TxStatusOptions, BuildBaseUnsignedTransferTxParams, TxStatusResponse, EstimateTransactionOptions, EstimateTransactionResult, PrepareTransactionParams, PrepareTransactionResult, FormatAmountOptions, TransactionRequest, GetBalanceParams, GetBalancesParams, GetBalancesChainRequest, GetBalanceResult, ChainBalances, TokenBalance, ChainGroup, FeePreset, FeePresetLabel, FeeEstimateSeconds, ResolvedFees, EIP1559Fees, LegacyFees, FeeModel, FeeHistoryData, FeeDataInput, } from './types/common';
package/dist/index.js CHANGED
@@ -5,6 +5,7 @@ export { ERC20_TOKEN_CONTRACT_ABI };
5
5
  export { GAS_LIMIT_PER_TX_TYPE, MULTICALL3_ADDRESS } from './constants/constants';
6
6
  export { NETWORKS_REGISTRY } from './constants/NETWORKS_REGISTRY';
7
7
  export { BASIC_TOKENS_BY_CHAIN, } from './constants/BASIC_TOKENS_REGISTRY';
8
+ export { STABLECOIN_CONTRACTS_BY_CHAIN_ID, getStablecoinContractBySymbolAndChainId, getStablecoinContractsByChainId, isAllowedStablecoinContract, } from './constants/STABLECOINS_REGISTRY';
8
9
  /** basic blockchain exports */
9
10
  export { buildBaseUnsignedTransferTx, buildMaxNativeTransferTx, buildUnsignedTransferTx, } from './blockchain/buildUnsignedTransferTx';
10
11
  export { broadcastTransaction } from './blockchain/broadcastTransaction';
@@ -25,6 +26,8 @@ import NATIVE_TOKENS from './utils/tokens';
25
26
  export { NATIVE_TOKENS };
26
27
  export { formattedAmountForDisplay, parsedAmount } from './utils/formatAmount';
27
28
  export { handleErrorMessages, errorMessagesForBroadcast, errorMessagesForGasLimitEstimation, } from './utils/handleErrorMessages';
28
- export { normalizeAddress } from './utils/normalizeAddress';
29
+ export { normalizeAddress, normalizeEvmAddress } from './utils/normalizeAddress';
29
30
  export { toGwei, tryParseGweiToWei, parseGasLimit } from './utils/parseGasFees';
30
31
  export { getFeePresets, calcReserve, estimateSeconds, getBlockTime, BLOCK_TIME_BY_CHAIN, } from './utils/feePresets';
32
+ /** webhook exports */
33
+ export { extractPoolInboundStablecoinEvents } from './webhooks/alchemyAddressActivity';
@@ -0,0 +1,7 @@
1
+ export interface TransactionActivityDedupeInput {
2
+ hash?: string | null;
3
+ log?: {
4
+ logIndex?: string | null;
5
+ } | null;
6
+ }
7
+ export declare const getTransactionActivityDedupeKey: (activity: TransactionActivityDedupeInput) => string | null;
@@ -0,0 +1,7 @@
1
+ export const getTransactionActivityDedupeKey = (activity) => {
2
+ const hash = activity.hash?.trim();
3
+ if (!hash)
4
+ return null;
5
+ const logIndex = activity.log?.logIndex?.trim();
6
+ return logIndex ? `${hash}:${logIndex}` : hash;
7
+ };
@@ -1 +1,2 @@
1
1
  export declare const normalizeAddress: (address: string) => string;
2
+ export declare const normalizeEvmAddress: (address: string | null | undefined) => string | null;
@@ -9,3 +9,13 @@ export const normalizeAddress = (address) => {
9
9
  throw new Error('Error normalizing address: ' + (e instanceof Error ? e.message : String(e)));
10
10
  }
11
11
  };
12
+ export const normalizeEvmAddress = (address) => {
13
+ if (!address || typeof address !== 'string')
14
+ return null;
15
+ try {
16
+ return normalizeAddress(address.trim()).toLowerCase();
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ };
@@ -0,0 +1,39 @@
1
+ export interface AlchemyAddressActivity {
2
+ category?: string;
3
+ asset?: string;
4
+ value?: number | null;
5
+ fromAddress?: string | null;
6
+ toAddress?: string | null;
7
+ hash?: string | null;
8
+ erc721TokenId?: string | number | null;
9
+ rawContract?: {
10
+ address?: string | null;
11
+ } | null;
12
+ log?: {
13
+ removed?: boolean;
14
+ logIndex?: string | null;
15
+ } | null;
16
+ }
17
+ export interface AlchemyAddressActivityPayload {
18
+ id?: string;
19
+ webhookId?: string;
20
+ type?: string;
21
+ event?: {
22
+ network?: string;
23
+ activity?: AlchemyAddressActivity[];
24
+ };
25
+ }
26
+ export interface PoolInboundStablecoinEvent {
27
+ webhookDeliveryId?: string;
28
+ webhookId?: string;
29
+ alchemyNetwork: string;
30
+ chainId: number | null;
31
+ txHash: string;
32
+ fromAddress: string;
33
+ toAddress: string;
34
+ amount: number;
35
+ asset: string;
36
+ tokenContractAddress?: string | null;
37
+ dedupeKey: string;
38
+ }
39
+ export declare function extractPoolInboundStablecoinEvents(payload: AlchemyAddressActivityPayload, poolAddress: string | null | undefined): PoolInboundStablecoinEvent[];
@@ -0,0 +1,72 @@
1
+ /** local imports */
2
+ import { isAllowedStablecoinContract } from '../constants/STABLECOINS_REGISTRY';
3
+ import { getTransactionActivityDedupeKey } from '../utils/activityDedupe';
4
+ import { normalizeEvmAddress } from '../utils/normalizeAddress';
5
+ const CHAIN_ID_TO_ALCHEMY_NETWORK = {
6
+ 1: 'ETH_MAINNET',
7
+ 56: 'BNB_MAINNET',
8
+ 137: 'MATIC_MAINNET',
9
+ 11155111: 'ETH_SEPOLIA',
10
+ };
11
+ const ALCHEMY_NETWORK_TO_CHAIN_ID = Object.fromEntries(Object.entries(CHAIN_ID_TO_ALCHEMY_NETWORK).map(([chainId, network]) => [
12
+ network,
13
+ Number(chainId),
14
+ ]));
15
+ function chainIdFromAlchemyNetwork(network) {
16
+ if (!network)
17
+ return null;
18
+ const chainId = ALCHEMY_NETWORK_TO_CHAIN_ID[network];
19
+ return chainId === undefined ? null : chainId;
20
+ }
21
+ export function extractPoolInboundStablecoinEvents(payload, poolAddress) {
22
+ const poolAddressLower = normalizeEvmAddress(poolAddress);
23
+ if (!poolAddressLower)
24
+ return [];
25
+ if (payload.type !== 'ADDRESS_ACTIVITY')
26
+ return [];
27
+ const network = payload.event?.network;
28
+ const chainId = chainIdFromAlchemyNetwork(network);
29
+ const activity = payload.event?.activity;
30
+ if (!activity?.length)
31
+ return [];
32
+ const out = [];
33
+ for (const item of activity) {
34
+ if (item.category !== 'token')
35
+ continue;
36
+ if (item.log?.removed === true)
37
+ continue;
38
+ if (item.erc721TokenId != null && item.erc721TokenId !== '')
39
+ continue;
40
+ if (!isAllowedStablecoinContract(chainId, item.rawContract?.address))
41
+ continue;
42
+ const value = item.value;
43
+ if (value === undefined || value === null || typeof value !== 'number' || !(value > 0))
44
+ continue;
45
+ const to = normalizeEvmAddress(item.toAddress);
46
+ const from = normalizeEvmAddress(item.fromAddress);
47
+ if (!to || !from)
48
+ continue;
49
+ if (to !== poolAddressLower)
50
+ continue;
51
+ const txHash = item.hash?.trim();
52
+ if (!txHash)
53
+ continue;
54
+ const dedupeKey = getTransactionActivityDedupeKey(item);
55
+ if (!dedupeKey)
56
+ continue;
57
+ out.push({
58
+ webhookDeliveryId: payload.id,
59
+ webhookId: payload.webhookId,
60
+ alchemyNetwork: network || 'UNKNOWN',
61
+ chainId,
62
+ txHash,
63
+ fromAddress: from,
64
+ toAddress: to,
65
+ amount: value,
66
+ asset: item.asset || '',
67
+ tokenContractAddress: item.rawContract?.address,
68
+ dedupeKey,
69
+ });
70
+ }
71
+ return out;
72
+ }
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@octaflowlabs/onchain-sdk",
3
- "version": "1.4.2",
3
+ "version": "1.4.4",
4
4
  "description": "onchain methods for web3",
5
- "repository": {"type": "git", "url": "git+https://github.com/octaflowlabs/onchain-sdk.git"},
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://github.com/octaflowlabs/onchain-sdk.git"
8
+ },
6
9
  "license": "MIT",
7
10
  "main": "dist/cjs/index.js",
8
11
  "module": "dist/index.js",