@mento-protocol/mento-sdk 3.1.0-beta.1 → 3.1.0-beta.2

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.
@@ -9,42 +9,62 @@ export async function fetchFPMMPoolDetails(publicClient, chainId, pool) {
9
9
  try {
10
10
  // Known liquidity strategy addresses for this chain
11
11
  const knownStrategies = getKnownLiquidityStrategies(chainId);
12
- // Fetch core data
13
- const [reservesResult, decimals0, decimals1, lpFee, protocolFee, rebalanceIncentive, rebalanceThresholdAbove, rebalanceThresholdBelow, ...strategyResults] = await Promise.all([
14
- publicClient.readContract({ address, abi: FPMM_ABI, functionName: 'getReserves' }),
15
- publicClient.readContract({ address, abi: FPMM_ABI, functionName: 'decimals0' }),
16
- publicClient.readContract({ address, abi: FPMM_ABI, functionName: 'decimals1' }),
17
- publicClient.readContract({ address, abi: FPMM_ABI, functionName: 'lpFee' }),
18
- publicClient.readContract({ address, abi: FPMM_ABI, functionName: 'protocolFee' }),
19
- publicClient.readContract({ address, abi: FPMM_ABI, functionName: 'rebalanceIncentive' }),
20
- publicClient.readContract({ address, abi: FPMM_ABI, functionName: 'rebalanceThresholdAbove' }),
21
- publicClient.readContract({ address, abi: FPMM_ABI, functionName: 'rebalanceThresholdBelow' }),
22
- ...knownStrategies.map((strategyAddr) => publicClient.readContract({
12
+ // Build all contract reads for a single multicall
13
+ const coreContracts = [
14
+ { address, abi: FPMM_ABI, functionName: 'getReserves' },
15
+ { address, abi: FPMM_ABI, functionName: 'decimals0' },
16
+ { address, abi: FPMM_ABI, functionName: 'decimals1' },
17
+ { address, abi: FPMM_ABI, functionName: 'lpFee' },
18
+ { address, abi: FPMM_ABI, functionName: 'protocolFee' },
19
+ { address, abi: FPMM_ABI, functionName: 'rebalanceIncentive' },
20
+ { address, abi: FPMM_ABI, functionName: 'rebalanceThresholdAbove' },
21
+ { address, abi: FPMM_ABI, functionName: 'rebalanceThresholdBelow' },
22
+ ...knownStrategies.map((strategyAddr) => ({
23
23
  address,
24
24
  abi: FPMM_ABI,
25
25
  functionName: 'liquidityStrategy',
26
26
  args: [strategyAddr],
27
27
  })),
28
- ]);
29
- const [reserve0, reserve1, blockTimestampLast] = reservesResult;
30
- const lpFeeBps = lpFee;
31
- const protocolFeeBps = protocolFee;
32
- const rebalanceIncentiveBps = rebalanceIncentive;
33
- const thresholdAboveBps = rebalanceThresholdAbove;
34
- const thresholdBelowBps = rebalanceThresholdBelow;
35
- // Find the active liquidity strategy (first match wins)
36
- const activeIndex = strategyResults.findIndex((result) => result === true);
28
+ // Include getRebalancingState in the same multicall (allowFailure handles FXMarketClosed)
29
+ { address, abi: FPMM_ABI, functionName: 'getRebalancingState' },
30
+ ];
31
+ const results = await publicClient.multicall({ contracts: coreContracts });
32
+ // Parse core results (first 8 are fixed)
33
+ const reservesRes = results[0];
34
+ const decimals0Res = results[1];
35
+ const decimals1Res = results[2];
36
+ const lpFeeRes = results[3];
37
+ const protocolFeeRes = results[4];
38
+ const rebalanceIncentiveRes = results[5];
39
+ const thresholdAboveRes = results[6];
40
+ const thresholdBelowRes = results[7];
41
+ // Check core results
42
+ if (reservesRes.status === 'failure' ||
43
+ decimals0Res.status === 'failure' ||
44
+ decimals1Res.status === 'failure' ||
45
+ lpFeeRes.status === 'failure' ||
46
+ protocolFeeRes.status === 'failure' ||
47
+ rebalanceIncentiveRes.status === 'failure' ||
48
+ thresholdAboveRes.status === 'failure' ||
49
+ thresholdBelowRes.status === 'failure') {
50
+ throw new Error('One or more core pool reads failed');
51
+ }
52
+ const [reserve0, reserve1, blockTimestampLast] = reservesRes.result;
53
+ const lpFeeBps = lpFeeRes.result;
54
+ const protocolFeeBps = protocolFeeRes.result;
55
+ const rebalanceIncentiveBps = rebalanceIncentiveRes.result;
56
+ const thresholdAboveBps = thresholdAboveRes.result;
57
+ const thresholdBelowBps = thresholdBelowRes.result;
58
+ // Parse strategy results (indices 8 .. 8+N-1)
59
+ const strategyResults = results.slice(8, 8 + knownStrategies.length);
60
+ const activeIndex = strategyResults.findIndex((r) => r.status === 'success' && r.result === true);
37
61
  const liquidityStrategy = activeIndex >= 0 ? knownStrategies[activeIndex] : null;
38
- // Fetch pricing separately — graceful degradation when FX market is closed
62
+ // Parse getRebalancingState (last result) — graceful degradation when FX market is closed
63
+ const rebalancingRes = results[8 + knownStrategies.length];
39
64
  let pricing = null;
40
65
  let inBand = null;
41
- try {
42
- const rebalancingStateResult = await publicClient.readContract({
43
- address,
44
- abi: FPMM_ABI,
45
- functionName: 'getRebalancingState',
46
- });
47
- const [oraclePriceNum, oraclePriceDen, reservePriceNum, reservePriceDen, reservePriceAboveOraclePrice, rebalanceThreshold, priceDifference,] = rebalancingStateResult;
66
+ if (rebalancingRes.status === 'success') {
67
+ const [oraclePriceNum, oraclePriceDen, reservePriceNum, reservePriceDen, reservePriceAboveOraclePrice, rebalanceThreshold, priceDifference,] = rebalancingRes.result;
48
68
  pricing = {
49
69
  oraclePriceNum,
50
70
  oraclePriceDen,
@@ -58,14 +78,12 @@ export async function fetchFPMMPoolDetails(publicClient, chainId, pool) {
58
78
  };
59
79
  inBand = priceDifference < BigInt(rebalanceThreshold);
60
80
  }
61
- catch {
62
- // getRebalancingState() failed (likely FXMarketClosed) — pricing stays null
63
- }
81
+ // If rebalancingRes.status === 'failure' (likely FXMarketClosed) — pricing stays null
64
82
  return {
65
83
  ...pool,
66
84
  poolType: 'FPMM',
67
- scalingFactor0: decimals0,
68
- scalingFactor1: decimals1,
85
+ scalingFactor0: decimals0Res.result,
86
+ scalingFactor1: decimals1Res.result,
69
87
  reserve0,
70
88
  reserve1,
71
89
  blockTimestampLast,
@@ -99,14 +117,19 @@ export async function fetchFPMMPoolDetails(publicClient, chainId, pool) {
99
117
  export async function fetchVirtualPoolDetails(publicClient, pool) {
100
118
  const address = pool.poolAddr;
101
119
  try {
102
- const [reservesResult, protocolFee, metadataResult] = await Promise.all([
103
- publicClient.readContract({ address, abi: VIRTUAL_POOL_ABI, functionName: 'getReserves' }),
104
- publicClient.readContract({ address, abi: VIRTUAL_POOL_ABI, functionName: 'protocolFee' }),
105
- publicClient.readContract({ address, abi: VIRTUAL_POOL_ABI, functionName: 'metadata' }),
106
- ]);
107
- const [reserve0, reserve1, blockTimestampLast] = reservesResult;
108
- const [dec0, dec1] = metadataResult;
109
- const spreadBps = protocolFee;
120
+ const results = await publicClient.multicall({
121
+ contracts: [
122
+ { address, abi: VIRTUAL_POOL_ABI, functionName: 'getReserves' },
123
+ { address, abi: VIRTUAL_POOL_ABI, functionName: 'protocolFee' },
124
+ { address, abi: VIRTUAL_POOL_ABI, functionName: 'metadata' },
125
+ ],
126
+ });
127
+ if (results[0].status === 'failure' || results[1].status === 'failure' || results[2].status === 'failure') {
128
+ throw new Error('One or more virtual pool reads failed');
129
+ }
130
+ const [reserve0, reserve1, blockTimestampLast] = results[0].result;
131
+ const [dec0, dec1] = results[2].result;
132
+ const spreadBps = results[1].result;
110
133
  return {
111
134
  ...pool,
112
135
  poolType: 'Virtual',
@@ -20,28 +20,26 @@ export async function fetchFPMMPools(publicClient, chainId) {
20
20
  if (poolAddresses.length === 0) {
21
21
  return [];
22
22
  }
23
- const poolDataPromises = poolAddresses.map(async (poolAddress) => {
24
- const [token0, token1] = await Promise.all([
25
- publicClient.readContract({
26
- address: poolAddress,
27
- abi: FPMM_ABI,
28
- functionName: 'token0',
29
- }),
30
- publicClient.readContract({
31
- address: poolAddress,
32
- abi: FPMM_ABI,
33
- functionName: 'token1',
34
- }),
35
- ]);
23
+ // Batch all token0/token1 reads into a single multicall
24
+ const contracts = poolAddresses.flatMap((poolAddress) => [
25
+ { address: poolAddress, abi: FPMM_ABI, functionName: 'token0' },
26
+ { address: poolAddress, abi: FPMM_ABI, functionName: 'token1' },
27
+ ]);
28
+ const results = await publicClient.multicall({ contracts });
29
+ return poolAddresses.map((poolAddress, i) => {
30
+ const token0Result = results[i * 2];
31
+ const token1Result = results[i * 2 + 1];
32
+ if (token0Result.status === 'failure' || token1Result.status === 'failure') {
33
+ throw new Error(`Failed to read token addresses for pool ${poolAddress}`);
34
+ }
36
35
  return {
37
36
  factoryAddr: fpmmFactoryAddress,
38
37
  poolAddr: poolAddress,
39
- token0: token0,
40
- token1: token1,
38
+ token0: token0Result.result,
39
+ token1: token1Result.result,
41
40
  poolType: PoolType.FPMM,
42
41
  };
43
42
  });
44
- return await Promise.all(poolDataPromises);
45
43
  }
46
44
  catch (error) {
47
45
  throw new Error(`Failed to fetch FPMM pools: ${error.message}`);
@@ -82,16 +80,22 @@ export async function fetchVirtualPools(publicClient, chainId) {
82
80
  tokenPairToExchangeId.set(`${t0}:${t1}`, exchange.exchangeId);
83
81
  }
84
82
  }
85
- // For each pool, read its token pair and match to an exchangeId
86
- const poolPromises = poolAddresses.map(async (poolAddress) => {
87
- const [token0, token1] = (await publicClient.readContract({
88
- address: poolAddress,
89
- abi: VIRTUAL_POOL_ABI,
90
- functionName: 'tokens',
91
- }));
83
+ // Batch all tokens() reads into a single multicall
84
+ const contracts = poolAddresses.map((poolAddress) => ({
85
+ address: poolAddress,
86
+ abi: VIRTUAL_POOL_ABI,
87
+ functionName: 'tokens',
88
+ }));
89
+ const results = await publicClient.multicall({ contracts });
90
+ return poolAddresses.map((poolAddress, i) => {
91
+ const result = results[i];
92
+ if (result.status === 'failure') {
93
+ throw new Error(`Failed to read token addresses for virtual pool ${poolAddress}`);
94
+ }
95
+ const [token0, token1] = result.result;
92
96
  const [sorted0, sorted1] = sortTokenAddresses(token0, token1);
93
97
  const exchangeId = tokenPairToExchangeId.get(`${sorted0}:${sorted1}`);
94
- const pool = {
98
+ return {
95
99
  factoryAddr: virtualPoolFactoryAddress,
96
100
  poolAddr: poolAddress,
97
101
  token0: sorted0,
@@ -99,9 +103,7 @@ export async function fetchVirtualPools(publicClient, chainId) {
99
103
  poolType: PoolType.Virtual,
100
104
  exchangeId,
101
105
  };
102
- return pool;
103
106
  });
104
- return await Promise.all(poolPromises);
105
107
  }
106
108
  catch (error) {
107
109
  throw new Error(`Failed to fetch Virtual pools: ${error.message}`);
@@ -86,15 +86,8 @@ export class TokenService {
86
86
  * @returns Array of stable tokens
87
87
  */
88
88
  async getStableTokens(includeSupply = true) {
89
- console.log("\r\n =======================================");
90
- console.log("ReserveV2: " + this.isReserveV2());
91
- console.log('=======================================');
92
89
  const reserveAddress = getContractAddress(this.chainId, RESERVE);
93
90
  const tokenAddresses = await this.getStableTokenAddresses(reserveAddress);
94
- console.log('\r\n =======================================');
95
- console.log('Stable tokens: ');
96
- console.log(tokenAddresses);
97
- console.log('=======================================');
98
91
  // Fetch metadata and totalSupply for all tokens concurrently
99
92
  const tokens = await Promise.all(tokenAddresses.map(async (address) => {
100
93
  const [metadata, totalSupply] = await Promise.all([
@@ -38,7 +38,7 @@ const monadTestnet = defineChain({
38
38
  },
39
39
  blockExplorers: {
40
40
  default: {
41
- name: 'Monad Explorer',
41
+ name: 'Monad Testnet Explorer',
42
42
  url: 'https://testnet.monadexplorer.com',
43
43
  },
44
44
  },
@@ -25,18 +25,17 @@ export async function getPoolCostPercent(pool, publicClient) {
25
25
  * FPMM pools use lpFee + protocolFee in basis points (10000 = 100%)
26
26
  */
27
27
  async function getFPMMCostPercent(poolAddress, publicClient) {
28
- const [lpFee, protocolFee] = await Promise.all([
29
- publicClient.readContract({
30
- address: poolAddress,
31
- abi: FPMM_ABI,
32
- functionName: 'lpFee',
33
- }),
34
- publicClient.readContract({
35
- address: poolAddress,
36
- abi: FPMM_ABI,
37
- functionName: 'protocolFee',
38
- }),
39
- ]);
28
+ const results = await publicClient.multicall({
29
+ contracts: [
30
+ { address: poolAddress, abi: FPMM_ABI, functionName: 'lpFee' },
31
+ { address: poolAddress, abi: FPMM_ABI, functionName: 'protocolFee' },
32
+ ],
33
+ });
34
+ if (results[0].status === 'failure' || results[1].status === 'failure') {
35
+ throw new Error(`Failed to read fees for pool ${poolAddress}`);
36
+ }
37
+ const lpFee = results[0].result;
38
+ const protocolFee = results[1].result;
40
39
  // Convert from basis points to percentage using BigInt arithmetic to avoid precision loss
41
40
  const totalBasisPoints = lpFee + protocolFee;
42
41
  const scaled = totalBasisPoints * 1000000n;
@@ -46,12 +45,13 @@ async function getFPMMCostPercent(poolAddress, publicClient) {
46
45
  * Calculate cost for Virtual pools
47
46
  */
48
47
  async function getVirtualPoolCostPercent(poolAddress, publicClient) {
49
- const protocolFee = await publicClient.readContract({
50
- address: poolAddress,
51
- abi: VIRTUAL_POOL_ABI,
52
- functionName: 'protocolFee',
48
+ const results = await publicClient.multicall({
49
+ contracts: [{ address: poolAddress, abi: VIRTUAL_POOL_ABI, functionName: 'protocolFee' }],
53
50
  });
51
+ if (results[0].status === 'failure') {
52
+ throw new Error(`Failed to read protocolFee for pool ${poolAddress}`);
53
+ }
54
54
  // Convert from basis points to percentage using BigInt arithmetic to avoid precision loss
55
- const scaled = protocolFee * 1000000n;
55
+ const scaled = results[0].result * 1000000n;
56
56
  return Number(scaled / 100n) / 1e6;
57
57
  }
@@ -13,42 +13,62 @@ async function fetchFPMMPoolDetails(publicClient, chainId, pool) {
13
13
  try {
14
14
  // Known liquidity strategy addresses for this chain
15
15
  const knownStrategies = getKnownLiquidityStrategies(chainId);
16
- // Fetch core data
17
- const [reservesResult, decimals0, decimals1, lpFee, protocolFee, rebalanceIncentive, rebalanceThresholdAbove, rebalanceThresholdBelow, ...strategyResults] = await Promise.all([
18
- publicClient.readContract({ address, abi: abis_1.FPMM_ABI, functionName: 'getReserves' }),
19
- publicClient.readContract({ address, abi: abis_1.FPMM_ABI, functionName: 'decimals0' }),
20
- publicClient.readContract({ address, abi: abis_1.FPMM_ABI, functionName: 'decimals1' }),
21
- publicClient.readContract({ address, abi: abis_1.FPMM_ABI, functionName: 'lpFee' }),
22
- publicClient.readContract({ address, abi: abis_1.FPMM_ABI, functionName: 'protocolFee' }),
23
- publicClient.readContract({ address, abi: abis_1.FPMM_ABI, functionName: 'rebalanceIncentive' }),
24
- publicClient.readContract({ address, abi: abis_1.FPMM_ABI, functionName: 'rebalanceThresholdAbove' }),
25
- publicClient.readContract({ address, abi: abis_1.FPMM_ABI, functionName: 'rebalanceThresholdBelow' }),
26
- ...knownStrategies.map((strategyAddr) => publicClient.readContract({
16
+ // Build all contract reads for a single multicall
17
+ const coreContracts = [
18
+ { address, abi: abis_1.FPMM_ABI, functionName: 'getReserves' },
19
+ { address, abi: abis_1.FPMM_ABI, functionName: 'decimals0' },
20
+ { address, abi: abis_1.FPMM_ABI, functionName: 'decimals1' },
21
+ { address, abi: abis_1.FPMM_ABI, functionName: 'lpFee' },
22
+ { address, abi: abis_1.FPMM_ABI, functionName: 'protocolFee' },
23
+ { address, abi: abis_1.FPMM_ABI, functionName: 'rebalanceIncentive' },
24
+ { address, abi: abis_1.FPMM_ABI, functionName: 'rebalanceThresholdAbove' },
25
+ { address, abi: abis_1.FPMM_ABI, functionName: 'rebalanceThresholdBelow' },
26
+ ...knownStrategies.map((strategyAddr) => ({
27
27
  address,
28
28
  abi: abis_1.FPMM_ABI,
29
29
  functionName: 'liquidityStrategy',
30
30
  args: [strategyAddr],
31
31
  })),
32
- ]);
33
- const [reserve0, reserve1, blockTimestampLast] = reservesResult;
34
- const lpFeeBps = lpFee;
35
- const protocolFeeBps = protocolFee;
36
- const rebalanceIncentiveBps = rebalanceIncentive;
37
- const thresholdAboveBps = rebalanceThresholdAbove;
38
- const thresholdBelowBps = rebalanceThresholdBelow;
39
- // Find the active liquidity strategy (first match wins)
40
- const activeIndex = strategyResults.findIndex((result) => result === true);
32
+ // Include getRebalancingState in the same multicall (allowFailure handles FXMarketClosed)
33
+ { address, abi: abis_1.FPMM_ABI, functionName: 'getRebalancingState' },
34
+ ];
35
+ const results = await publicClient.multicall({ contracts: coreContracts });
36
+ // Parse core results (first 8 are fixed)
37
+ const reservesRes = results[0];
38
+ const decimals0Res = results[1];
39
+ const decimals1Res = results[2];
40
+ const lpFeeRes = results[3];
41
+ const protocolFeeRes = results[4];
42
+ const rebalanceIncentiveRes = results[5];
43
+ const thresholdAboveRes = results[6];
44
+ const thresholdBelowRes = results[7];
45
+ // Check core results
46
+ if (reservesRes.status === 'failure' ||
47
+ decimals0Res.status === 'failure' ||
48
+ decimals1Res.status === 'failure' ||
49
+ lpFeeRes.status === 'failure' ||
50
+ protocolFeeRes.status === 'failure' ||
51
+ rebalanceIncentiveRes.status === 'failure' ||
52
+ thresholdAboveRes.status === 'failure' ||
53
+ thresholdBelowRes.status === 'failure') {
54
+ throw new Error('One or more core pool reads failed');
55
+ }
56
+ const [reserve0, reserve1, blockTimestampLast] = reservesRes.result;
57
+ const lpFeeBps = lpFeeRes.result;
58
+ const protocolFeeBps = protocolFeeRes.result;
59
+ const rebalanceIncentiveBps = rebalanceIncentiveRes.result;
60
+ const thresholdAboveBps = thresholdAboveRes.result;
61
+ const thresholdBelowBps = thresholdBelowRes.result;
62
+ // Parse strategy results (indices 8 .. 8+N-1)
63
+ const strategyResults = results.slice(8, 8 + knownStrategies.length);
64
+ const activeIndex = strategyResults.findIndex((r) => r.status === 'success' && r.result === true);
41
65
  const liquidityStrategy = activeIndex >= 0 ? knownStrategies[activeIndex] : null;
42
- // Fetch pricing separately — graceful degradation when FX market is closed
66
+ // Parse getRebalancingState (last result) — graceful degradation when FX market is closed
67
+ const rebalancingRes = results[8 + knownStrategies.length];
43
68
  let pricing = null;
44
69
  let inBand = null;
45
- try {
46
- const rebalancingStateResult = await publicClient.readContract({
47
- address,
48
- abi: abis_1.FPMM_ABI,
49
- functionName: 'getRebalancingState',
50
- });
51
- const [oraclePriceNum, oraclePriceDen, reservePriceNum, reservePriceDen, reservePriceAboveOraclePrice, rebalanceThreshold, priceDifference,] = rebalancingStateResult;
70
+ if (rebalancingRes.status === 'success') {
71
+ const [oraclePriceNum, oraclePriceDen, reservePriceNum, reservePriceDen, reservePriceAboveOraclePrice, rebalanceThreshold, priceDifference,] = rebalancingRes.result;
52
72
  pricing = {
53
73
  oraclePriceNum,
54
74
  oraclePriceDen,
@@ -62,14 +82,12 @@ async function fetchFPMMPoolDetails(publicClient, chainId, pool) {
62
82
  };
63
83
  inBand = priceDifference < BigInt(rebalanceThreshold);
64
84
  }
65
- catch {
66
- // getRebalancingState() failed (likely FXMarketClosed) — pricing stays null
67
- }
85
+ // If rebalancingRes.status === 'failure' (likely FXMarketClosed) — pricing stays null
68
86
  return {
69
87
  ...pool,
70
88
  poolType: 'FPMM',
71
- scalingFactor0: decimals0,
72
- scalingFactor1: decimals1,
89
+ scalingFactor0: decimals0Res.result,
90
+ scalingFactor1: decimals1Res.result,
73
91
  reserve0,
74
92
  reserve1,
75
93
  blockTimestampLast,
@@ -103,14 +121,19 @@ async function fetchFPMMPoolDetails(publicClient, chainId, pool) {
103
121
  async function fetchVirtualPoolDetails(publicClient, pool) {
104
122
  const address = pool.poolAddr;
105
123
  try {
106
- const [reservesResult, protocolFee, metadataResult] = await Promise.all([
107
- publicClient.readContract({ address, abi: abis_1.VIRTUAL_POOL_ABI, functionName: 'getReserves' }),
108
- publicClient.readContract({ address, abi: abis_1.VIRTUAL_POOL_ABI, functionName: 'protocolFee' }),
109
- publicClient.readContract({ address, abi: abis_1.VIRTUAL_POOL_ABI, functionName: 'metadata' }),
110
- ]);
111
- const [reserve0, reserve1, blockTimestampLast] = reservesResult;
112
- const [dec0, dec1] = metadataResult;
113
- const spreadBps = protocolFee;
124
+ const results = await publicClient.multicall({
125
+ contracts: [
126
+ { address, abi: abis_1.VIRTUAL_POOL_ABI, functionName: 'getReserves' },
127
+ { address, abi: abis_1.VIRTUAL_POOL_ABI, functionName: 'protocolFee' },
128
+ { address, abi: abis_1.VIRTUAL_POOL_ABI, functionName: 'metadata' },
129
+ ],
130
+ });
131
+ if (results[0].status === 'failure' || results[1].status === 'failure' || results[2].status === 'failure') {
132
+ throw new Error('One or more virtual pool reads failed');
133
+ }
134
+ const [reserve0, reserve1, blockTimestampLast] = results[0].result;
135
+ const [dec0, dec1] = results[2].result;
136
+ const spreadBps = results[1].result;
114
137
  return {
115
138
  ...pool,
116
139
  poolType: 'Virtual',
@@ -24,28 +24,26 @@ async function fetchFPMMPools(publicClient, chainId) {
24
24
  if (poolAddresses.length === 0) {
25
25
  return [];
26
26
  }
27
- const poolDataPromises = poolAddresses.map(async (poolAddress) => {
28
- const [token0, token1] = await Promise.all([
29
- publicClient.readContract({
30
- address: poolAddress,
31
- abi: abis_1.FPMM_ABI,
32
- functionName: 'token0',
33
- }),
34
- publicClient.readContract({
35
- address: poolAddress,
36
- abi: abis_1.FPMM_ABI,
37
- functionName: 'token1',
38
- }),
39
- ]);
27
+ // Batch all token0/token1 reads into a single multicall
28
+ const contracts = poolAddresses.flatMap((poolAddress) => [
29
+ { address: poolAddress, abi: abis_1.FPMM_ABI, functionName: 'token0' },
30
+ { address: poolAddress, abi: abis_1.FPMM_ABI, functionName: 'token1' },
31
+ ]);
32
+ const results = await publicClient.multicall({ contracts });
33
+ return poolAddresses.map((poolAddress, i) => {
34
+ const token0Result = results[i * 2];
35
+ const token1Result = results[i * 2 + 1];
36
+ if (token0Result.status === 'failure' || token1Result.status === 'failure') {
37
+ throw new Error(`Failed to read token addresses for pool ${poolAddress}`);
38
+ }
40
39
  return {
41
40
  factoryAddr: fpmmFactoryAddress,
42
41
  poolAddr: poolAddress,
43
- token0: token0,
44
- token1: token1,
42
+ token0: token0Result.result,
43
+ token1: token1Result.result,
45
44
  poolType: types_1.PoolType.FPMM,
46
45
  };
47
46
  });
48
- return await Promise.all(poolDataPromises);
49
47
  }
50
48
  catch (error) {
51
49
  throw new Error(`Failed to fetch FPMM pools: ${error.message}`);
@@ -86,16 +84,22 @@ async function fetchVirtualPools(publicClient, chainId) {
86
84
  tokenPairToExchangeId.set(`${t0}:${t1}`, exchange.exchangeId);
87
85
  }
88
86
  }
89
- // For each pool, read its token pair and match to an exchangeId
90
- const poolPromises = poolAddresses.map(async (poolAddress) => {
91
- const [token0, token1] = (await publicClient.readContract({
92
- address: poolAddress,
93
- abi: abis_1.VIRTUAL_POOL_ABI,
94
- functionName: 'tokens',
95
- }));
87
+ // Batch all tokens() reads into a single multicall
88
+ const contracts = poolAddresses.map((poolAddress) => ({
89
+ address: poolAddress,
90
+ abi: abis_1.VIRTUAL_POOL_ABI,
91
+ functionName: 'tokens',
92
+ }));
93
+ const results = await publicClient.multicall({ contracts });
94
+ return poolAddresses.map((poolAddress, i) => {
95
+ const result = results[i];
96
+ if (result.status === 'failure') {
97
+ throw new Error(`Failed to read token addresses for virtual pool ${poolAddress}`);
98
+ }
99
+ const [token0, token1] = result.result;
96
100
  const [sorted0, sorted1] = (0, sortUtils_1.sortTokenAddresses)(token0, token1);
97
101
  const exchangeId = tokenPairToExchangeId.get(`${sorted0}:${sorted1}`);
98
- const pool = {
102
+ return {
99
103
  factoryAddr: virtualPoolFactoryAddress,
100
104
  poolAddr: poolAddress,
101
105
  token0: sorted0,
@@ -103,9 +107,7 @@ async function fetchVirtualPools(publicClient, chainId) {
103
107
  poolType: types_1.PoolType.Virtual,
104
108
  exchangeId,
105
109
  };
106
- return pool;
107
110
  });
108
- return await Promise.all(poolPromises);
109
111
  }
110
112
  catch (error) {
111
113
  throw new Error(`Failed to fetch Virtual pools: ${error.message}`);
@@ -89,15 +89,8 @@ class TokenService {
89
89
  * @returns Array of stable tokens
90
90
  */
91
91
  async getStableTokens(includeSupply = true) {
92
- console.log("\r\n =======================================");
93
- console.log("ReserveV2: " + this.isReserveV2());
94
- console.log('=======================================');
95
92
  const reserveAddress = (0, constants_1.getContractAddress)(this.chainId, constants_1.RESERVE);
96
93
  const tokenAddresses = await this.getStableTokenAddresses(reserveAddress);
97
- console.log('\r\n =======================================');
98
- console.log('Stable tokens: ');
99
- console.log(tokenAddresses);
100
- console.log('=======================================');
101
94
  // Fetch metadata and totalSupply for all tokens concurrently
102
95
  const tokens = await Promise.all(tokenAddresses.map(async (address) => {
103
96
  const [metadata, totalSupply] = await Promise.all([
@@ -42,7 +42,7 @@ const monadTestnet = (0, viem_1.defineChain)({
42
42
  },
43
43
  blockExplorers: {
44
44
  default: {
45
- name: 'Monad Explorer',
45
+ name: 'Monad Testnet Explorer',
46
46
  url: 'https://testnet.monadexplorer.com',
47
47
  },
48
48
  },
@@ -28,18 +28,17 @@ async function getPoolCostPercent(pool, publicClient) {
28
28
  * FPMM pools use lpFee + protocolFee in basis points (10000 = 100%)
29
29
  */
30
30
  async function getFPMMCostPercent(poolAddress, publicClient) {
31
- const [lpFee, protocolFee] = await Promise.all([
32
- publicClient.readContract({
33
- address: poolAddress,
34
- abi: abis_1.FPMM_ABI,
35
- functionName: 'lpFee',
36
- }),
37
- publicClient.readContract({
38
- address: poolAddress,
39
- abi: abis_1.FPMM_ABI,
40
- functionName: 'protocolFee',
41
- }),
42
- ]);
31
+ const results = await publicClient.multicall({
32
+ contracts: [
33
+ { address: poolAddress, abi: abis_1.FPMM_ABI, functionName: 'lpFee' },
34
+ { address: poolAddress, abi: abis_1.FPMM_ABI, functionName: 'protocolFee' },
35
+ ],
36
+ });
37
+ if (results[0].status === 'failure' || results[1].status === 'failure') {
38
+ throw new Error(`Failed to read fees for pool ${poolAddress}`);
39
+ }
40
+ const lpFee = results[0].result;
41
+ const protocolFee = results[1].result;
43
42
  // Convert from basis points to percentage using BigInt arithmetic to avoid precision loss
44
43
  const totalBasisPoints = lpFee + protocolFee;
45
44
  const scaled = totalBasisPoints * 1000000n;
@@ -49,13 +48,14 @@ async function getFPMMCostPercent(poolAddress, publicClient) {
49
48
  * Calculate cost for Virtual pools
50
49
  */
51
50
  async function getVirtualPoolCostPercent(poolAddress, publicClient) {
52
- const protocolFee = await publicClient.readContract({
53
- address: poolAddress,
54
- abi: virtualPool_1.VIRTUAL_POOL_ABI,
55
- functionName: 'protocolFee',
51
+ const results = await publicClient.multicall({
52
+ contracts: [{ address: poolAddress, abi: virtualPool_1.VIRTUAL_POOL_ABI, functionName: 'protocolFee' }],
56
53
  });
54
+ if (results[0].status === 'failure') {
55
+ throw new Error(`Failed to read protocolFee for pool ${poolAddress}`);
56
+ }
57
57
  // Convert from basis points to percentage using BigInt arithmetic to avoid precision loss
58
- const scaled = protocolFee * 1000000n;
58
+ const scaled = results[0].result * 1000000n;
59
59
  return Number(scaled / 100n) / 1e6;
60
60
  }
61
61
  //# sourceMappingURL=costUtils.js.map
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mento-protocol/mento-sdk",
3
3
  "description": "Official SDK for interacting with the Mento Protocol",
4
- "version": "3.1.0-beta.1",
4
+ "version": "3.1.0-beta.2",
5
5
  "license": "MIT",
6
6
  "author": "Mento Labs",
7
7
  "keywords": [