diff --git a/packages/web/package.json b/packages/web/package.json index d566e5d..43b2118 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -18,6 +18,7 @@ "@reduxjs/toolkit": "^1.8.3", "@tippyjs/react": "^4.2.6", "aws-amplify": "^4.3.30", + "coingecko-api": "^1.0.10", "dayjs": "^1.11.4", "ethers": "^5.6.9", "global": "^4.4.0", diff --git a/packages/web/src/pages/api/query/erc20.ts b/packages/web/src/pages/api/query/erc20.ts index 23e8c6f..3dc27a3 100644 --- a/packages/web/src/pages/api/query/erc20.ts +++ b/packages/web/src/pages/api/query/erc20.ts @@ -35,7 +35,7 @@ export default async function handler( try { const s3data = await S3.getObject({ Bucket: BUCKET_NAME, - Key: `onchain/${account.toLowerCase()}/${chain}/ranking`, + Key: `onchain/${account.toLowerCase()}/${chain}/erc20`, }).promise(); const scanResult: ScanERC20Result = s3data.Body? JSON.parse(s3data.Body.toString()):{ account, diff --git a/packages/web/src/pages/api/query/networth.ts b/packages/web/src/pages/api/query/networth.ts new file mode 100644 index 0000000..9ce6df3 --- /dev/null +++ b/packages/web/src/pages/api/query/networth.ts @@ -0,0 +1,63 @@ +// Next.js API route support: https://nextjs.org/docs/api-routes/introduction +import type { NextApiRequest, NextApiResponse } from 'next'; +import { utils } from 'ethers'; +import { S3, ERROR_MESSAGE, BUCKET_NAME, ScanError, ERC20Asset, priceToUsdByTokenAddress, provider } from 'scan-helper'; + + +type NetworthResult = { + account: string, + networth: number, +} + +const WETH_ADDR = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; +const ETH_DECIMALS = 18; + +export default async function handler( + req: NextApiRequest, + res: NextApiResponse< NetworthResult | ScanError> +) { + const { account } = req.query; + if ( + typeof account !== 'string' || + !utils.isAddress(account) + ) { + res.status(500).json({ + account, + message: ERROR_MESSAGE.INVALID_ADDRESS, + } as ScanError); + return; + } + const erc20Assets: ERC20Asset[] = await S3.getObject({ + Bucket: BUCKET_NAME, + Key: `onchain/${account.toLowerCase()}/ether/erc20`, + }).promise() + .then(s3data => { + const scanResult = s3data.Body? JSON.parse(s3data.Body.toString()): []; + return scanResult.erc20assets?? scanResult; + }) + .catch(err => { + return []; + }); + const ethBalance = utils.formatUnits(await provider.getBalance(account), ETH_DECIMALS); + const tokenAddressList = erc20Assets.map(asset => asset.contractAddress); + const balanceList = [...erc20Assets.map(asset => asset.balance), ethBalance]; + const result: any = await priceToUsdByTokenAddress([...tokenAddressList, WETH_ADDR]); + const priceMap = result.data; + const worthList = tokenAddressList.map((addr, idx) => { + if (priceMap[addr]) { + parseInt(balanceList[idx]); + return priceMap[addr]['usd'] * parseInt(balanceList[idx]); + } else { + return 0; + } + }); + + const networth = worthList.reduce( + (previousValue, currentValue) => previousValue + currentValue, 0, + ); + + res.status(200).json({ + account, + networth + } as NetworthResult); +} diff --git a/packages/web/src/pages/api/recommend/index.ts b/packages/web/src/pages/api/recommend/index.ts index 4155f53..7c131b3 100644 --- a/packages/web/src/pages/api/recommend/index.ts +++ b/packages/web/src/pages/api/recommend/index.ts @@ -8,7 +8,7 @@ import { import { RecResult, getCorrelation, TABLE_NAME, docClient } from 'rec-helper'; const TABLE_SIZE = 3000; -const SCAN_RANGE = 100; +const SCAN_RANGE = 30; const RANDOM_NUMBER_RANGE = TABLE_SIZE - SCAN_RANGE; const ONE_DAY_INTERVAL = 86400000; diff --git a/packages/web/src/rec-helper.ts b/packages/web/src/rec-helper.ts index 9eec443..c1163aa 100644 --- a/packages/web/src/rec-helper.ts +++ b/packages/web/src/rec-helper.ts @@ -34,5 +34,8 @@ export const getCorrelation = ( ranking1: Frequency[], ranking2: Frequency[]) : number => { + const map1 = new Map(ranking1.map(fre => [fre.address, fre.frequency])); + const map2 = new Map(ranking2.map(fre => [fre.address, fre.frequency])); + // const multi = map1.keys() return ranking2.length; }; \ No newline at end of file diff --git a/packages/web/src/scan-helper.ts b/packages/web/src/scan-helper.ts index e4387ae..6ae39eb 100644 --- a/packages/web/src/scan-helper.ts +++ b/packages/web/src/scan-helper.ts @@ -1,5 +1,6 @@ import AWS from 'aws-sdk'; import { ethers, utils } from 'ethers'; +const CoinGecko = require('coingecko-api'); export const BUCKET_NAME = '3card'; @@ -7,7 +8,7 @@ AWS.config.update({ accessKeyId: process.env.NEXT_PUBLIC_AWS_ACCESS_KEY_ID, secretAccessKey: process.env.NEXT_PUBLIC_AWS_SECRET_ACCESS_KEY }); -console.log('api key: ,', process.env.NEXT_PUBLIC_AWS_ACCESS_KEY_ID) +// console.log('api key: ,', process.env.NEXT_PUBLIC_AWS_ACCESS_KEY_ID) export const S3 = new AWS.S3(); export const provider = new ethers.providers.JsonRpcProvider( @@ -178,6 +179,17 @@ export class EnsFetcher { } } +export const CoinGeckoClient = new CoinGecko(); + +export async function priceToUsdByTokenAddress(tokenAddresses: string[]) { + //only ethereum chain address is available + const data = await CoinGeckoClient.simple.fetchTokenPrice({ + contract_addresses: tokenAddresses, + vs_currencies: 'usd' + }) + return data; +} + export const ADDRESS_TAGS = new Map([ ['0x7a250d5630b4cf539739df2c5dacb4c659f2488d', 'Uniswap V2 Router'], ['0x7be8076f4ea4a4ad08075c2508e481d6c946d12b', 'Opensea V1'], diff --git a/yarn.lock b/yarn.lock index dbba46c..d0a6ce8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5722,6 +5722,11 @@ clsx@^1.1.0: resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== +coingecko-api@^1.0.10: + version "1.0.10" + resolved "https://registry.yarnpkg.com/coingecko-api/-/coingecko-api-1.0.10.tgz#ac8694d5999f00727fe55f0078ce2917603076b2" + integrity sha512-7YLLC85+daxAw5QlBWoHVBVpJRwoPr4HtwanCr8V/WRjoyHTa1Lb9DQAvv4MDJZHiz4no6HGnDQnddtjV35oRA== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"