Skip to content

Commit

Permalink
[web] Refactor with safeAsyncFlatMap where possible (#278)
Browse files Browse the repository at this point in the history
  • Loading branch information
junhoyeo authored Oct 9, 2022
1 parent b6b13fa commit 3bdc447
Show file tree
Hide file tree
Showing 10 changed files with 220 additions and 212 deletions.
2 changes: 1 addition & 1 deletion packages/bento-common/utils/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
export { Base64 } from './Base64';
export { safePromiseAll } from './safePromiseAll';
export { safePromiseAll, safeAsyncFlatMap } from './safer-promises';
export { shortenAddress } from './shortenAddress';
4 changes: 0 additions & 4 deletions packages/bento-common/utils/safePromiseAll.ts

This file was deleted.

17 changes: 17 additions & 0 deletions packages/bento-common/utils/safer-promises.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export const safePromiseAll = async <T extends any>(promises: Promise<T>[]) =>
(await Promise.allSettled(promises)).flatMap((res) =>
res.status === 'fulfilled' ? res.value : [],
);

export const safeAsyncFlatMap = async <T extends any, U extends any>(
array: T[],
callback: (value: T, index: number, array: T[]) => Promise<[] | U | U[]>,
): Promise<U[]> => {
const result = await Promise.allSettled(array.map(callback));
return result.flatMap((res) => {
if (res.status === 'fulfilled') {
return res.value;
}
return [];
});
};
6 changes: 3 additions & 3 deletions packages/bento-web/pages/api/auth/verify/[walletType].ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,9 @@ const handler = async (req: APIRequest, res: NextApiResponse) => {
.select('id, address, networks, user_id')
.eq('address', walletAddress)
.eq('user_id', user.id);
const previousNetworks = (prevNetworkQuery.data ?? [])
.map((v) => v.networks)
.flat();
const previousNetworks = (prevNetworkQuery.data ?? []).flatMap(
(v) => v.networks,
);
const mergedNetworks = Array.from(
new Set([...previousNetworks, ...networks]),
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { CosmosSDKBasedNetworks, safePromiseAll } from '@bento/common';
import { CosmosSDKBasedNetworks, safeAsyncFlatMap } from '@bento/common';
import {
Bech32Address,
CosmosHubChain,
Expand Down Expand Up @@ -45,47 +45,43 @@ const handler = async (req: APIRequest, res: NextApiResponse) => {
coinMarketCapId?: number;
balance: number;
price?: number;
}[] = (
await safePromiseAll(
wallets.flatMap(async (walletAddress) => {
const bech32Address = Bech32Address.fromBech32(walletAddress);
}[] = await safeAsyncFlatMap(wallets, async (walletAddress) => {
const bech32Address = Bech32Address.fromBech32(walletAddress);

if (['cosmos-hub', 'osmosis'].includes(network)) {
const chain = chains[network];
const chainBech32Address = bech32Address.toBech32(
chain.bech32Config.prefix,
);
if (['cosmos-hub', 'osmosis'].includes(network)) {
const chain = chains[network];
const chainBech32Address = bech32Address.toBech32(
chain.bech32Config.prefix,
);

const getTokenBalances = async (): Promise<TokenBalance[]> =>
'getTokenBalances' in chain
? chain.getTokenBalances?.(chainBech32Address) ?? []
: [];
const [balance, delegations, tokenBalances] = await Promise.all([
chain.getBalance(chainBech32Address).catch(() => 0),
chain.getDelegations(chainBech32Address).catch(() => 0),
getTokenBalances(),
]);
const getTokenBalances = async (): Promise<TokenBalance[]> =>
'getTokenBalances' in chain
? chain.getTokenBalances?.(chainBech32Address) ?? []
: [];
const [balance, delegations, tokenBalances] = await Promise.all([
chain.getBalance(chainBech32Address).catch(() => 0),
chain.getDelegations(chainBech32Address).catch(() => 0),
getTokenBalances(),
]);

return [
{
walletAddress: chainBech32Address,
platform: network,
return [
{
walletAddress: chainBech32Address,
platform: network,

symbol: chain.currency.symbol,
name: chain.currency.name,
logo: chain.currency.logo,
coinGeckoId: chain.currency.coinGeckoId,
balance,
delegations,
price: undefined,
},
...tokenBalances,
];
}
return [];
}),
)
).flat();
symbol: chain.currency.symbol,
name: chain.currency.name,
logo: chain.currency.logo,
coinGeckoId: chain.currency.coinGeckoId,
balance,
delegations,
price: undefined,
},
...tokenBalances,
];
}
return [];
});

res.status(200).json(result);
};
Expand Down
110 changes: 53 additions & 57 deletions packages/bento-web/pages/api/balances/evm/[network]/[walletAddress].ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { EVMBasedNetworks, safePromiseAll } from '@bento/common';
import { EVMBasedNetworks, safeAsyncFlatMap } from '@bento/common';
import {
AvalancheChain,
BNBChain,
Expand Down Expand Up @@ -56,65 +56,61 @@ const handler = async (req: APIRequest, res: NextApiResponse) => {
coinMarketCapId?: number;
balance: number;
price?: number;
}[] = (
await safePromiseAll(
wallets.map(async (walletAddress) => {
if (SUPPORTED_CHAINS.includes(network)) {
const chain = chains[network]!;
const getTokenBalances = async (): Promise<TokenBalance[]> => {
try {
const items =
'getTokenBalances' in chain
? await chain.getTokenBalances(walletAddress)
: [];
await redisClient
.set(
`token-balances:${network}:${walletAddress}`,
JSON.stringify(items),
)
.catch((err) => console.error(err));
return items;
} catch (error) {
console.error(
'Error occurred while fetching token balances (likely Covalent API error)',
error,
);
const out = await redisClient
.get(`token-balances:${network}:${walletAddress}`)
.catch((err) => {
console.error(err);
});
if (out) {
return JSON.parse(out);
}
return [];
}
};
}[] = await safeAsyncFlatMap(wallets, async (walletAddress) => {
if (SUPPORTED_CHAINS.includes(network)) {
const chain = chains[network]!;
const getTokenBalances = async (): Promise<TokenBalance[]> => {
try {
const items =
'getTokenBalances' in chain
? await chain.getTokenBalances(walletAddress)
: [];
await redisClient
.set(
`token-balances:${network}:${walletAddress}`,
JSON.stringify(items),
)
.catch((err) => console.error(err));
return items;
} catch (error) {
console.error(
'Error occurred while fetching token balances (likely Covalent API error)',
error,
);
const out = await redisClient
.get(`token-balances:${network}:${walletAddress}`)
.catch((err) => {
console.error(err);
});
if (out) {
return JSON.parse(out);
}
return [];
}
};

const [balance, tokenBalances] = await Promise.all([
chain.getBalance(walletAddress).catch(() => 0),
getTokenBalances().catch(() => []),
]);
const [balance, tokenBalances] = await Promise.all([
chain.getBalance(walletAddress).catch(() => 0),
getTokenBalances().catch(() => []),
]);

return [
{
walletAddress,
platform: network,
return [
{
walletAddress,
platform: network,

symbol: chain.currency.symbol,
name: chain.currency.name,
logo: chain.currency.logo,
coinGeckoId: chain.currency.coinGeckoId,
balance,
// price: currencyPrice,
},
...tokenBalances,
];
}
return [];
}),
)
).flat();
symbol: chain.currency.symbol,
name: chain.currency.name,
logo: chain.currency.logo,
coinGeckoId: chain.currency.coinGeckoId,
balance,
// price: currencyPrice,
},
...tokenBalances,
];
}
return [];
});

const coinMarketCapIds = result
.flatMap((x) => (!!x.coinMarketCapId ? x.coinMarketCapId : []))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { safePromiseAll } from '@bento/common';
import { safeAsyncFlatMap } from '@bento/common';
import { SolanaChain, TokenBalance } from '@bento/core';
import type { NextApiRequest, NextApiResponse } from 'next';

Expand Down Expand Up @@ -36,62 +36,55 @@ const handler = async (req: APIRequest, res: NextApiResponse) => {
coinMarketCapId?: number;
balance: number;
price?: number;
}[] = (
await safePromiseAll(
wallets.map(async (walletAddress) => {
const getTokenBalances = async (): Promise<TokenBalance[]> => {
try {
const items =
'getTokenBalances' in chain
? await chain.getTokenBalances(walletAddress)
: [];
await redisClient
.set(
`token-balances:solana:${walletAddress}`,
JSON.stringify(items),
)
.catch((err) => console.error(err));
return items;
} catch (error) {
console.error(
'Error occurred while fetching token balances (likely Covalent API error)',
error,
);
const out = await redisClient
.get(`token-balances:solana:${walletAddress}`)
.catch((err) => {
console.error(err);
});
if (out) {
return JSON.parse(out);
}
return [];
}
};
}[] = await safeAsyncFlatMap(wallets, async (walletAddress) => {
const getTokenBalances = async (): Promise<TokenBalance[]> => {
try {
const items =
'getTokenBalances' in chain
? await chain.getTokenBalances(walletAddress)
: [];
await redisClient
.set(`token-balances:solana:${walletAddress}`, JSON.stringify(items))
.catch((err) => console.error(err));
return items;
} catch (error) {
console.error(
'Error occurred while fetching token balances (likely Covalent API error)',
error,
);
const out = await redisClient
.get(`token-balances:solana:${walletAddress}`)
.catch((err) => {
console.error(err);
});
if (out) {
return JSON.parse(out);
}
return [];
}
};

const [balance, tokenBalances] = await Promise.all([
chain.getBalance(walletAddress).catch(() => 0),
getTokenBalances().catch(() => []),
[],
]);
const [balance, tokenBalances] = await Promise.all([
chain.getBalance(walletAddress).catch(() => 0),
getTokenBalances().catch(() => []),
[],
]);

return [
{
walletAddress,
platform: 'solana',
return [
{
walletAddress,
platform: 'solana',

symbol: chain.currency.symbol,
name: chain.currency.name,
logo: chain.currency.logo,
coinGeckoId: chain.currency.coinGeckoId,
balance,
price: undefined,
},
...tokenBalances,
];
}),
)
).flat();
symbol: chain.currency.symbol,
name: chain.currency.name,
logo: chain.currency.logo,
coinGeckoId: chain.currency.coinGeckoId,
balance,
price: undefined,
},
...tokenBalances,
];
});

await redisClient.disconnect();
res.status(200).json(result);
Expand Down
Loading

0 comments on commit 3bdc447

Please sign in to comment.