From da3dcbd6545314e021e31b04cf5141057e2c031b Mon Sep 17 00:00:00 2001 From: Max Dumas Date: Wed, 8 Dec 2021 13:46:52 -0500 Subject: [PATCH 1/6] First pass at adapting to user Zapper API --- bin/get-balances.ts | 66 +++++++++++++++++++++++++++------------------ package.json | 2 ++ src/client.ts | 21 +++++++++++++-- src/types.ts | 1 + yarn.lock | 51 +++++++++++++++++++++++++++++++++++ 5 files changed, 113 insertions(+), 28 deletions(-) diff --git a/bin/get-balances.ts b/bin/get-balances.ts index 2252dfe..d14ae6f 100755 --- a/bin/get-balances.ts +++ b/bin/get-balances.ts @@ -1,10 +1,13 @@ #!/usr/bin/env node --experimental-json-modules --loader ts-node/esm import assert from 'node:assert'; +import { encode } from 'querystring'; +import EventSource from 'eventsource'; -import ethers from 'ethers'; +import { CryptoBalance } from '../src/types.js'; -import { LunchMoneyEthereumWalletConnection, createEthereumWalletClient } from '../src/main.js'; +const ZAPPER_FI_API_URL = 'https://api.zapper.fi/v1/balances'; +const ZAPPER_FI_API_KEY = '96e0cc51-a62e-42ca-acee-910ea7d2a241'; const requireEnv = (key: string): string => { const value = process.env[key]; @@ -12,31 +15,42 @@ const requireEnv = (key: string): string => { return value; }; -const apiKey = requireEnv('LM_ETHERSCAN_API_KEY'); -const walletAddress = requireEnv('LM_ETHEREUM_WALLET_ADDRESS'); - -const provider = ethers.providers.getDefaultProvider('homestead', { - etherscan: apiKey, - // TODO: Get these other keys for redundancy and performance - // infura: YOUR_INFURA_PROJECT_ID, - // Or if using a project secret: - // infura: { - // projectId: YOUR_INFURA_PROJECT_ID, - // projectSecret: YOUR_INFURA_PROJECT_SECRET, - // }, - // alchemy: YOUR_ALCHEMY_API_KEY, - // pocket: YOUR_POCKET_APPLICATION_KEY - // Or if using an application secret key: - // pocket: { - // applicationId: , - // applicationSecretKey: - // } +const ETH_ADDRESS = requireEnv('ETH_ADDRESS'); +const ETH_ADDRESS_INDEX = ETH_ADDRESS.toLowerCase(); + +const qs = encode({ + 'addresses[]': ETH_ADDRESS, + api_key: ZAPPER_FI_API_KEY, }); -const client = createEthereumWalletClient(provider); +const url = ZAPPER_FI_API_URL + '?' + qs; + +console.log(url); + +const es = new EventSource(url); + +const hasAnyNonZeroBalances = (balanceData: any) => balanceData.balances[ETH_ADDRESS_INDEX].products.length > 0; -const resp = await LunchMoneyEthereumWalletConnection.getBalances({ walletAddress }, { client }); +es.addEventListener('balance', (ev) => { + const data = JSON.parse(ev.data); + if (hasAnyNonZeroBalances(data)) { + const p = data.balances[ETH_ADDRESS_INDEX].products; -for (const { asset, amount } of resp.balances) { - console.log(`${asset}: ${amount}`); -} + const balances: CryptoBalance[] = p.flatMap((p: any) => + p.assets.flatMap((a: any) => + a.tokens.map((t: any) => ({ + asset: t.symbol, + amount: t.balance, + amounInUSD: t.balanceUSD, + })), + ), + ); + + console.log(balances); + } +}); + +es.addEventListener('end', () => { + console.log('Finished stream. Closing connection.'); + es.close(); +}); diff --git a/package.json b/package.json index ebc5013..569140c 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ }, "devDependencies": { "@types/chai": "^4", + "@types/eventsource": "^1.1.7", "@types/mocha": "^9.0.0", "@types/node": "^16.6.1", "@types/sinon": "^10.0.2", @@ -72,6 +73,7 @@ "dependencies": { "@mycrypto/eth-scan": "^3.4.4", "ethers": "^5.4.6", + "eventsource": "^1.1.0", "mem": "^8.1.1" } } diff --git a/src/client.ts b/src/client.ts index 71da1be..e6b0aa2 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,9 +1,19 @@ -import * as ethscan from '@mycrypto/eth-scan'; +import ethscan from '@mycrypto/eth-scan'; import * as ethers from 'ethers'; +import EventSource from 'eventsource'; import mem from 'mem'; +import { encode } from 'querystring'; + +// import eventsource from "eventsource"; import tokenList1inch from '../fixtures/1inch.json'; +console.log(ethscan); + +const ZAPPER_FI_API_URL = 'https://api.zapper.fi/v1/balances'; +const ZAPPER_FI_API_KEY = '96e0cc51-a62e-42ca-acee-910ea7d2a241'; +const ETH_ADDRESS = '0x8E29007951cE79c151dd070b51e30168E9663c13'; + export interface EthereumWalletClient { getWeiBalance(walletAddress: string): Promise; getTokensBalance(walletAddress: string, tokenContractAddresses: string[]): Promise>; @@ -14,7 +24,14 @@ export const createEthereumWalletClient = (provider: ethers.providers.BaseProvid return (await provider.getBalance(walletAddress)).toBigInt(); }, async getTokensBalance(walletAddress, tokenContractAddresses) { - return ethscan.getTokensBalance(provider, walletAddress, tokenContractAddresses); + const es = new EventSource( + ZAPPER_FI_API_URL + + '?' + + encode({ + 'addresses[]': ETH_ADDRESS, + api_key: ZAPPER_FI_API_KEY, + }), + ); }, }); diff --git a/src/types.ts b/src/types.ts index b24d9a4..3f621fa 100644 --- a/src/types.ts +++ b/src/types.ts @@ -17,6 +17,7 @@ export type LunchMoneyCryptoConnectionContext = {}; export interface CryptoBalance { asset: string; amount: string; + amountInUSD: string; } export const providerNames = ['coinbase', 'coinbase_pro', 'kraken', 'binance', 'wallet_ethereum'] as const; diff --git a/yarn.lock b/yarn.lock index 9de7cd2..509454a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -537,6 +537,7 @@ __metadata: dependencies: "@mycrypto/eth-scan": ^3.4.4 "@types/chai": ^4 + "@types/eventsource": ^1.1.7 "@types/mocha": ^9.0.0 "@types/node": ^16.6.1 "@types/sinon": ^10.0.2 @@ -547,6 +548,7 @@ __metadata: eslint-config-prettier: ^8.3.0 eslint-plugin-prettier: ^3.4.0 ethers: ^5.4.6 + eventsource: ^1.1.0 husky: ^7.0.0 lint-staged: ^11.1.2 mem: ^8.1.1 @@ -683,6 +685,13 @@ __metadata: languageName: node linkType: hard +"@types/eventsource@npm:^1.1.7": + version: 1.1.7 + resolution: "@types/eventsource@npm:1.1.7" + checksum: 887e8999fedf9b080403e5df13367ac07d9dc185efad495426818b0db336ca01540a1004ef8a5302799594529158196ffa1f42ec1385e4e78e7541337a1ebe4f + languageName: node + linkType: hard + "@types/json-schema@npm:^7.0.7": version: 7.0.9 resolution: "@types/json-schema@npm:7.0.9" @@ -1759,6 +1768,15 @@ __metadata: languageName: node linkType: hard +"eventsource@npm:^1.1.0": + version: 1.1.0 + resolution: "eventsource@npm:1.1.0" + dependencies: + original: ^1.0.0 + checksum: 78338b7e75ec471cb793efb3319e0c4d2bf00fb638a2e3f888ad6d98cd1e3d4492a29f554c0921c7b2ac5130c3a732a1a0056739f6e2f548d714aec685e5da7e + languageName: node + linkType: hard + "execa@npm:^5.0.0": version: 5.1.1 resolution: "execa@npm:5.1.1" @@ -2920,6 +2938,15 @@ fsevents@~2.3.2: languageName: node linkType: hard +"original@npm:^1.0.0": + version: 1.0.2 + resolution: "original@npm:1.0.2" + dependencies: + url-parse: ^1.4.3 + checksum: 8dca9311dab50c8953366127cb86b7c07bf547d6aa6dc6873a75964b7563825351440557e5724d9c652c5e99043b8295624f106af077f84bccf19592e421beb9 + languageName: node + linkType: hard + "p-defer@npm:^1.0.0": version: 1.0.0 resolution: "p-defer@npm:1.0.0" @@ -3098,6 +3125,13 @@ fsevents@~2.3.2: languageName: node linkType: hard +"querystringify@npm:^2.1.1": + version: 2.2.0 + resolution: "querystringify@npm:2.2.0" + checksum: 5641ea231bad7ef6d64d9998faca95611ed4b11c2591a8cae741e178a974f6a8e0ebde008475259abe1621cb15e692404e6b6626e927f7b849d5c09392604b15 + languageName: node + linkType: hard + "queue-microtask@npm:^1.2.2": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -3159,6 +3193,13 @@ fsevents@~2.3.2: languageName: node linkType: hard +"requires-port@npm:^1.0.0": + version: 1.0.0 + resolution: "requires-port@npm:1.0.0" + checksum: eee0e303adffb69be55d1a214e415cf42b7441ae858c76dfc5353148644f6fd6e698926fc4643f510d5c126d12a705e7c8ed7e38061113bdf37547ab356797ff + languageName: node + linkType: hard + "resolve-from@npm:^4.0.0": version: 4.0.0 resolution: "resolve-from@npm:4.0.0" @@ -3707,6 +3748,16 @@ typescript@^4.3.5: languageName: node linkType: hard +"url-parse@npm:^1.4.3": + version: 1.5.3 + resolution: "url-parse@npm:1.5.3" + dependencies: + querystringify: ^2.1.1 + requires-port: ^1.0.0 + checksum: c6b32fff835e43f3b1b4150239f459744f0ab1a908841dbfecbfc79bf67f4d6c8d9af1841d0c6d814d45bfa08525cc29312a0bef31db7aa894306b3db07e4ee0 + languageName: node + linkType: hard + "util-deprecate@npm:~1.0.1": version: 1.0.2 resolution: "util-deprecate@npm:1.0.2" From a7a18ac7a22fbdee4cba8fcd1ee5693b91aa3e1c Mon Sep 17 00:00:00 2001 From: Max Dumas Date: Wed, 8 Dec 2021 15:18:07 -0500 Subject: [PATCH 2/6] Fully-working script to get balances from Zapper --- bin/get-balances.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/bin/get-balances.ts b/bin/get-balances.ts index d14ae6f..420c181 100755 --- a/bin/get-balances.ts +++ b/bin/get-balances.ts @@ -1,12 +1,15 @@ #!/usr/bin/env node --experimental-json-modules --loader ts-node/esm import assert from 'node:assert'; -import { encode } from 'querystring'; +import { encode } from 'node:querystring'; import EventSource from 'eventsource'; import { CryptoBalance } from '../src/types.js'; const ZAPPER_FI_API_URL = 'https://api.zapper.fi/v1/balances'; +// This API key is public and shared with all users. This API is publicly +// available, free of charge. See here for details: +// https://docs.zapper.fi/zapper-api/endpoints const ZAPPER_FI_API_KEY = '96e0cc51-a62e-42ca-acee-910ea7d2a241'; const requireEnv = (key: string): string => { @@ -25,12 +28,12 @@ const qs = encode({ const url = ZAPPER_FI_API_URL + '?' + qs; -console.log(url); - const es = new EventSource(url); const hasAnyNonZeroBalances = (balanceData: any) => balanceData.balances[ETH_ADDRESS_INDEX].products.length > 0; +let allBalances: CryptoBalance[] = []; + es.addEventListener('balance', (ev) => { const data = JSON.parse(ev.data); if (hasAnyNonZeroBalances(data)) { @@ -40,17 +43,18 @@ es.addEventListener('balance', (ev) => { p.assets.flatMap((a: any) => a.tokens.map((t: any) => ({ asset: t.symbol, - amount: t.balance, - amounInUSD: t.balanceUSD, + amount: t.balance.toString(), + amountInUSD: t.balanceUSD.toString(), })), ), ); - console.log(balances); + allBalances = allBalances.concat(balances); } }); es.addEventListener('end', () => { console.log('Finished stream. Closing connection.'); + console.log(allBalances.sort((a, b) => parseFloat(a.amountInUSD) - parseFloat(b.amountInUSD))); es.close(); }); From 5179f4954f16fa81bf1f294c0a9c425a195bfb72 Mon Sep 17 00:00:00 2001 From: Max Dumas Date: Wed, 8 Dec 2021 15:39:25 -0500 Subject: [PATCH 3/6] Move code to main file --- bin/get-balances.ts | 58 +---- package.json | 2 - src/main.ts | 89 ++++--- yarn.lock | 622 +------------------------------------------- 4 files changed, 72 insertions(+), 699 deletions(-) diff --git a/bin/get-balances.ts b/bin/get-balances.ts index 420c181..bfe76c3 100755 --- a/bin/get-balances.ts +++ b/bin/get-balances.ts @@ -1,16 +1,8 @@ #!/usr/bin/env node --experimental-json-modules --loader ts-node/esm import assert from 'node:assert'; -import { encode } from 'node:querystring'; -import EventSource from 'eventsource'; -import { CryptoBalance } from '../src/types.js'; - -const ZAPPER_FI_API_URL = 'https://api.zapper.fi/v1/balances'; -// This API key is public and shared with all users. This API is publicly -// available, free of charge. See here for details: -// https://docs.zapper.fi/zapper-api/endpoints -const ZAPPER_FI_API_KEY = '96e0cc51-a62e-42ca-acee-910ea7d2a241'; +import { LunchMoneyEthereumWalletConnection } from '../src/main.js'; const requireEnv = (key: string): string => { const value = process.env[key]; @@ -19,42 +11,14 @@ const requireEnv = (key: string): string => { }; const ETH_ADDRESS = requireEnv('ETH_ADDRESS'); -const ETH_ADDRESS_INDEX = ETH_ADDRESS.toLowerCase(); - -const qs = encode({ - 'addresses[]': ETH_ADDRESS, - api_key: ZAPPER_FI_API_KEY, -}); - -const url = ZAPPER_FI_API_URL + '?' + qs; - -const es = new EventSource(url); - -const hasAnyNonZeroBalances = (balanceData: any) => balanceData.balances[ETH_ADDRESS_INDEX].products.length > 0; - -let allBalances: CryptoBalance[] = []; - -es.addEventListener('balance', (ev) => { - const data = JSON.parse(ev.data); - if (hasAnyNonZeroBalances(data)) { - const p = data.balances[ETH_ADDRESS_INDEX].products; - - const balances: CryptoBalance[] = p.flatMap((p: any) => - p.assets.flatMap((a: any) => - a.tokens.map((t: any) => ({ - asset: t.symbol, - amount: t.balance.toString(), - amountInUSD: t.balanceUSD.toString(), - })), - ), - ); - - allBalances = allBalances.concat(balances); - } -}); -es.addEventListener('end', () => { - console.log('Finished stream. Closing connection.'); - console.log(allBalances.sort((a, b) => parseFloat(a.amountInUSD) - parseFloat(b.amountInUSD))); - es.close(); -}); +(async function () { + console.log( + await LunchMoneyEthereumWalletConnection.getBalances( + { + walletAddress: ETH_ADDRESS, + }, + {}, + ), + ); +})(); diff --git a/package.json b/package.json index 569140c..ec6b677 100644 --- a/package.json +++ b/package.json @@ -71,8 +71,6 @@ ] }, "dependencies": { - "@mycrypto/eth-scan": "^3.4.4", - "ethers": "^5.4.6", "eventsource": "^1.1.0", "mem": "^8.1.1" } diff --git a/src/main.ts b/src/main.ts index 4c1e4fb..43fa6ed 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,13 +2,20 @@ import { LunchMoneyCryptoConnection, LunchMoneyCryptoConnectionContext, LunchMoneyCryptoConnectionConfig, + CryptoBalance, } from './types.js'; -import { loadTokenList, EthereumWalletClient } from './client.js'; -import * as ethers from 'ethers'; +import { encode } from 'node:querystring'; +import EventSource from 'eventsource'; export { LunchMoneyCryptoConnection } from './types.js'; -export { createEthereumWalletClient, EthereumWalletClient } from './client.js'; +// export { createEthereumWalletClient, EthereumWalletClient } from './client.js'; + +const ZAPPER_FI_API_URL = 'https://api.zapper.fi/v1/balances'; +// This API key is public and shared with all users. This API is publicly +// available, free of charge. See here for details: +// https://docs.zapper.fi/zapper-api/endpoints +const ZAPPER_FI_API_KEY = '96e0cc51-a62e-42ca-acee-910ea7d2a241'; /** The minimum balance (in wei) that a token should have in order to be * considered for returning as a balance. */ @@ -20,9 +27,7 @@ interface LunchMoneyEthereumWalletConnectionConfig extends LunchMoneyCryptoConne negligibleBalanceThreshold?: number; } -interface LunchMoneyEthereumWalletConnectionContext extends LunchMoneyCryptoConnectionContext { - client: EthereumWalletClient; -} +type LunchMoneyEthereumWalletConnectionContext = LunchMoneyCryptoConnectionContext; export const LunchMoneyEthereumWalletConnection: LunchMoneyCryptoConnection< LunchMoneyEthereumWalletConnectionConfig, @@ -31,28 +36,54 @@ export const LunchMoneyEthereumWalletConnection: LunchMoneyCryptoConnection< async initiate(config, context) { return this.getBalances(config, context); }, - async getBalances({ walletAddress, negligibleBalanceThreshold = NEGLIGIBLE_BALANCE_THRESHOLD }, { client }) { - const weiBalance = await client.getWeiBalance(walletAddress); - - const tokenList = await loadTokenList(); - const map = await client.getTokensBalance( - walletAddress, - tokenList.map((t) => t.address), - ); - - const balances = tokenList - .map((t) => ({ - asset: t.symbol, - amount: map[t.address] ?? 0, - })) - .concat({ asset: 'ETH', amount: weiBalance }) - .filter((b) => b.amount > negligibleBalanceThreshold) - .map(({ asset, amount }) => ({ asset, amount: ethers.utils.formatEther(amount) })) - .sort((a, b) => a.asset.localeCompare(b.asset)); - - return { - providerName: 'wallet_ethereum', - balances, - }; + async getBalances({ walletAddress, negligibleBalanceThreshold = NEGLIGIBLE_BALANCE_THRESHOLD }, {}) { + // For some reason the address that is returned in the response is + // lowercased. + const walletAddressIndex = walletAddress.toLowerCase(); + + const qs = encode({ + 'addresses[]': walletAddress, + api_key: ZAPPER_FI_API_KEY, + }); + + const es = new EventSource(ZAPPER_FI_API_URL + '?' + qs); + + const hasAnyNonZeroBalances = (balanceData: any) => balanceData.balances[walletAddressIndex].products.length > 0; + + let balances: CryptoBalance[] = []; + + es.addEventListener('balance', (ev) => { + const data = JSON.parse(ev.data); + if (hasAnyNonZeroBalances(data)) { + const p = data.balances[walletAddressIndex].products; + + const appBalances: CryptoBalance[] = p.flatMap((p: any) => + p.assets.flatMap((a: any) => + a.tokens.map((t: any) => ({ + asset: t.symbol, + amount: t.balance.toString(), + amountInUSD: t.balanceUSD.toString(), + })), + ), + ); + + balances = balances.concat(appBalances); + } + }); + + return new Promise((resolve, reject) => { + es.addEventListener('end', () => { + es.close(); + resolve({ + providerName: 'wallet_ethereum', + balances, + }); + }); + + es.addEventListener('error', (ev) => { + es.close(); + reject(ev); + }); + }); }, }; diff --git a/yarn.lock b/yarn.lock index 509454a..f8d25f0 100644 --- a/yarn.lock +++ b/yarn.lock @@ -74,445 +74,6 @@ __metadata: languageName: node linkType: hard -"@ethersproject/abi@npm:5.4.1": - version: 5.4.1 - resolution: "@ethersproject/abi@npm:5.4.1" - dependencies: - "@ethersproject/address": ^5.4.0 - "@ethersproject/bignumber": ^5.4.0 - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/constants": ^5.4.0 - "@ethersproject/hash": ^5.4.0 - "@ethersproject/keccak256": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - "@ethersproject/properties": ^5.4.0 - "@ethersproject/strings": ^5.4.0 - checksum: 75460eee1d4eeaa438a43a003343dfcd2a8c3b1b7e0d8a3dd31c9200375d52daeea7022622696d1fec54913ec3b72423057236677907793ec134d2601a061fec - languageName: node - linkType: hard - -"@ethersproject/abi@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/abi@npm:5.4.0" - dependencies: - "@ethersproject/address": ^5.4.0 - "@ethersproject/bignumber": ^5.4.0 - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/constants": ^5.4.0 - "@ethersproject/hash": ^5.4.0 - "@ethersproject/keccak256": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - "@ethersproject/properties": ^5.4.0 - "@ethersproject/strings": ^5.4.0 - checksum: b299aab954e43da5abfaf1ba015405c1d9f9d87f83e1c37d09c4c402bb98abac9aedecdb52fd17b1389a272caddc8341a9410babb6536a56e0b77004b5839173 - languageName: node - linkType: hard - -"@ethersproject/abstract-provider@npm:5.4.1, @ethersproject/abstract-provider@npm:^5.4.0": - version: 5.4.1 - resolution: "@ethersproject/abstract-provider@npm:5.4.1" - dependencies: - "@ethersproject/bignumber": ^5.4.0 - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - "@ethersproject/networks": ^5.4.0 - "@ethersproject/properties": ^5.4.0 - "@ethersproject/transactions": ^5.4.0 - "@ethersproject/web": ^5.4.0 - checksum: dd4b8b0b58a58b094377e657e46cb59231bed89abdaa774cad6b0a21015d797283d50585fd4e7f1f9dfda66feede0aed8725183839f4527067427902720e360b - languageName: node - linkType: hard - -"@ethersproject/abstract-signer@npm:5.4.1, @ethersproject/abstract-signer@npm:^5.4.0": - version: 5.4.1 - resolution: "@ethersproject/abstract-signer@npm:5.4.1" - dependencies: - "@ethersproject/abstract-provider": ^5.4.0 - "@ethersproject/bignumber": ^5.4.0 - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - "@ethersproject/properties": ^5.4.0 - checksum: 5fe9c3978d9c1ca9a5f47ed8afb2d8d00c97e4304114e72ea149816c9a607979022bff01176cb9b293ac8753158399ae7bdf57901f3deb8f7cb050e06070ad1e - languageName: node - linkType: hard - -"@ethersproject/address@npm:5.4.0, @ethersproject/address@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/address@npm:5.4.0" - dependencies: - "@ethersproject/bignumber": ^5.4.0 - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/keccak256": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - "@ethersproject/rlp": ^5.4.0 - checksum: c7ad0cffa86a24c5e4c176d4c08b99a35551abd72520ccc55de9c94ef45737f6082c2a784586360915f17802008154c995990f8f35b0f6c2b6738b80b766c0a4 - languageName: node - linkType: hard - -"@ethersproject/base64@npm:5.4.0, @ethersproject/base64@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/base64@npm:5.4.0" - dependencies: - "@ethersproject/bytes": ^5.4.0 - checksum: 40e14debc4534005cc8a1594074dab272c49d84fb3a6b4c0eedd6144211312a8ad97009603381e90f6ddfeaf5f227c0e8e56d6753562981a9ae6ccfcb8430d4e - languageName: node - linkType: hard - -"@ethersproject/basex@npm:5.4.0, @ethersproject/basex@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/basex@npm:5.4.0" - dependencies: - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/properties": ^5.4.0 - checksum: 0a364834f29b27bb04c91698cd2612f07f96f73709af20d99a4b96babc75baf770e84ac01fa2c6b34f0ee3e04c35566d601c6cf1933973844c1a0b785f6f0da5 - languageName: node - linkType: hard - -"@ethersproject/bignumber@npm:5.4.1, @ethersproject/bignumber@npm:^5.4.0": - version: 5.4.1 - resolution: "@ethersproject/bignumber@npm:5.4.1" - dependencies: - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - bn.js: ^4.11.9 - checksum: ba154500635ed4545ba2e2a06ed1b4f07738956e131f1a4c0fc706f8eed80d89c9cc93fdf1a71f95e4f943e29faf8b3644cc1226c51f9280d26dc6783089f976 - languageName: node - linkType: hard - -"@ethersproject/bytes@npm:5.4.0, @ethersproject/bytes@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/bytes@npm:5.4.0" - dependencies: - "@ethersproject/logger": ^5.4.0 - checksum: be8678ec713858e6d944defc78b9950ab83a9cde22c51ea2658ee4a5aa176f1ce9c36dd6630a2a17dcf6bd098b1b33b7b1d3b8fe2edb8840af4028567ca68775 - languageName: node - linkType: hard - -"@ethersproject/constants@npm:5.4.0, @ethersproject/constants@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/constants@npm:5.4.0" - dependencies: - "@ethersproject/bignumber": ^5.4.0 - checksum: 736f601d54b1f427cf5451ac463c5470929aac590a287ec40fa5d5f69582f2695b6bc4b476228d63030fb7f215a302fc6db690b2d55038207910232e17b09d89 - languageName: node - linkType: hard - -"@ethersproject/contracts@npm:5.4.1": - version: 5.4.1 - resolution: "@ethersproject/contracts@npm:5.4.1" - dependencies: - "@ethersproject/abi": ^5.4.0 - "@ethersproject/abstract-provider": ^5.4.0 - "@ethersproject/abstract-signer": ^5.4.0 - "@ethersproject/address": ^5.4.0 - "@ethersproject/bignumber": ^5.4.0 - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/constants": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - "@ethersproject/properties": ^5.4.0 - "@ethersproject/transactions": ^5.4.0 - checksum: da817280c44244bf66c9d6136110e4baecad2e30780143598078081b8b34b6a93023ab83a601746d37336a5fceabbcaa267b3b09cf297a7a105949e5b21f255b - languageName: node - linkType: hard - -"@ethersproject/hash@npm:5.4.0, @ethersproject/hash@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/hash@npm:5.4.0" - dependencies: - "@ethersproject/abstract-signer": ^5.4.0 - "@ethersproject/address": ^5.4.0 - "@ethersproject/bignumber": ^5.4.0 - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/keccak256": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - "@ethersproject/properties": ^5.4.0 - "@ethersproject/strings": ^5.4.0 - checksum: 20c48688568cc6bb3643bdc69626d0358d1d45994d2f4e38ee01823c0f0dbb47920e74e751bbbfe7c02995a7fd5afe4e2594c0a301fb579339cb3e908141f7b2 - languageName: node - linkType: hard - -"@ethersproject/hdnode@npm:5.4.0, @ethersproject/hdnode@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/hdnode@npm:5.4.0" - dependencies: - "@ethersproject/abstract-signer": ^5.4.0 - "@ethersproject/basex": ^5.4.0 - "@ethersproject/bignumber": ^5.4.0 - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - "@ethersproject/pbkdf2": ^5.4.0 - "@ethersproject/properties": ^5.4.0 - "@ethersproject/sha2": ^5.4.0 - "@ethersproject/signing-key": ^5.4.0 - "@ethersproject/strings": ^5.4.0 - "@ethersproject/transactions": ^5.4.0 - "@ethersproject/wordlists": ^5.4.0 - checksum: cfd3b1512800877223960ad4e8ce6eacc09486b6e07d74f109c37f6211a3bb77c528c684d3a2dadff933f12d5b8d9676659ef65baa9c5eab257d255d78743dbd - languageName: node - linkType: hard - -"@ethersproject/json-wallets@npm:5.4.0, @ethersproject/json-wallets@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/json-wallets@npm:5.4.0" - dependencies: - "@ethersproject/abstract-signer": ^5.4.0 - "@ethersproject/address": ^5.4.0 - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/hdnode": ^5.4.0 - "@ethersproject/keccak256": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - "@ethersproject/pbkdf2": ^5.4.0 - "@ethersproject/properties": ^5.4.0 - "@ethersproject/random": ^5.4.0 - "@ethersproject/strings": ^5.4.0 - "@ethersproject/transactions": ^5.4.0 - aes-js: 3.0.0 - scrypt-js: 3.0.1 - checksum: 8bd933253212199d6594cc2b7777b0e110724e756a9693f4d07f3549355eb599a7ede98b52bd96d2b0f8985ed52ad5a6ff2148fc0aa5a78812535636e4e9dffa - languageName: node - linkType: hard - -"@ethersproject/keccak256@npm:5.4.0, @ethersproject/keccak256@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/keccak256@npm:5.4.0" - dependencies: - "@ethersproject/bytes": ^5.4.0 - js-sha3: 0.5.7 - checksum: b76d2818734fb55b80c3b7fc07e761a594b721840c276a52b92e457e4f460802c2f6fe6e684ef444a1d28565351b5859a6e202751fafe111bccef2e596bf9a1e - languageName: node - linkType: hard - -"@ethersproject/logger@npm:5.4.1": - version: 5.4.1 - resolution: "@ethersproject/logger@npm:5.4.1" - checksum: 40ade2d1d9408cf786239d850aa6c1bd41403d68f8ab8974643836406efdae1a9419f25a184037ed7c2ee9dcb09896a09d078eed6dd424351f025b1e2eaf19e3 - languageName: node - linkType: hard - -"@ethersproject/logger@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/logger@npm:5.4.0" - checksum: dae12037fe91668b14e2e55fe6e6404d57f8e6a632788610bf8dab8553efad2343d10c61d556c5697ca6646ad628e4802e48d92da4f4011d97ff2d1875bb4de5 - languageName: node - linkType: hard - -"@ethersproject/networks@npm:5.4.2, @ethersproject/networks@npm:^5.4.0": - version: 5.4.2 - resolution: "@ethersproject/networks@npm:5.4.2" - dependencies: - "@ethersproject/logger": ^5.4.0 - checksum: 08b794f537fe291a566d930877a4a05a18543537755d323b634a7e4818032e5c8bd4f14bb97dafce4d3e4149ecea499b59e4f75a07fd554cab87c3a7fbeb0a0c - languageName: node - linkType: hard - -"@ethersproject/pbkdf2@npm:5.4.0, @ethersproject/pbkdf2@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/pbkdf2@npm:5.4.0" - dependencies: - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/sha2": ^5.4.0 - checksum: 596c6b8f920a147eb8f7c1b36d6119be3bf0b31c591cc844b3067300a7ee605d9b03959309a58a37c1ab64e112b4edf838dcac658c5163c5d17966700936c615 - languageName: node - linkType: hard - -"@ethersproject/properties@npm:5.4.1": - version: 5.4.1 - resolution: "@ethersproject/properties@npm:5.4.1" - dependencies: - "@ethersproject/logger": ^5.4.0 - checksum: 9c1fc83e26e0b2d7c26c1fb49e14cf5a08598bc06e9b1b5b2f67c9368febb6a35066e445debfc4f600e2a6516fc01baf42d65c23f79b32e634afd6a5b5dd9faf - languageName: node - linkType: hard - -"@ethersproject/properties@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/properties@npm:5.4.0" - dependencies: - "@ethersproject/logger": ^5.4.0 - checksum: 618ff237e5f21853752acf0ec7cff80d681aeb2a6b1812ed83bb99a6dcf9a86c1afef90e1669f2d766aab0daa7e21692fb03cf84e0cda355cb4f6b12eb9b059f - languageName: node - linkType: hard - -"@ethersproject/providers@npm:5.4.5": - version: 5.4.5 - resolution: "@ethersproject/providers@npm:5.4.5" - dependencies: - "@ethersproject/abstract-provider": ^5.4.0 - "@ethersproject/abstract-signer": ^5.4.0 - "@ethersproject/address": ^5.4.0 - "@ethersproject/basex": ^5.4.0 - "@ethersproject/bignumber": ^5.4.0 - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/constants": ^5.4.0 - "@ethersproject/hash": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - "@ethersproject/networks": ^5.4.0 - "@ethersproject/properties": ^5.4.0 - "@ethersproject/random": ^5.4.0 - "@ethersproject/rlp": ^5.4.0 - "@ethersproject/sha2": ^5.4.0 - "@ethersproject/strings": ^5.4.0 - "@ethersproject/transactions": ^5.4.0 - "@ethersproject/web": ^5.4.0 - bech32: 1.1.4 - ws: 7.4.6 - checksum: 09692fb053453c2a7e5d566e42d4542cd4225ea829d9177b6227c09559b11c041b4be5958d550ece3fb1d4677e67e45a4604604fd9f5d1b8dcbd04b8c259c186 - languageName: node - linkType: hard - -"@ethersproject/random@npm:5.4.0, @ethersproject/random@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/random@npm:5.4.0" - dependencies: - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - checksum: 7bf0150b639faac4f9c8c234083d2f1b5d3fbd530ab64d5602240c8873d856418e1512f41b8c39b771118ff7d06d257dd0ad473aca9e3886535baeeb5ac6b7e8 - languageName: node - linkType: hard - -"@ethersproject/rlp@npm:5.4.0, @ethersproject/rlp@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/rlp@npm:5.4.0" - dependencies: - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - checksum: d6c45c17f8062a93737267aca52c16725fa197706b5dade1f806f9be3e498d3f70101cad631dc979d7db94ee170f225fed03900df35676cfe6ba86b16136f046 - languageName: node - linkType: hard - -"@ethersproject/sha2@npm:5.4.0, @ethersproject/sha2@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/sha2@npm:5.4.0" - dependencies: - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - hash.js: 1.1.7 - checksum: 253a5ad3894c3a0bcd1d7af6dc19fbd80680732bf05c708b9c27c26ffc0a8c2be04eaeabf98be6be18e7181a130b142ac391aceac705382cea7f24876f012671 - languageName: node - linkType: hard - -"@ethersproject/signing-key@npm:5.4.0, @ethersproject/signing-key@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/signing-key@npm:5.4.0" - dependencies: - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - "@ethersproject/properties": ^5.4.0 - bn.js: ^4.11.9 - elliptic: 6.5.4 - hash.js: 1.1.7 - checksum: aa5666aec046f57cd6b66044b2ea34cd72e481b209cadbf7a2d09e1eb9123e0817398e0e31dad22acef72fa9e06faf78026169c82f800826bcf4fbbed2d7d5a3 - languageName: node - linkType: hard - -"@ethersproject/solidity@npm:5.4.0": - version: 5.4.0 - resolution: "@ethersproject/solidity@npm:5.4.0" - dependencies: - "@ethersproject/bignumber": ^5.4.0 - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/keccak256": ^5.4.0 - "@ethersproject/sha2": ^5.4.0 - "@ethersproject/strings": ^5.4.0 - checksum: 62f8756a9b9e15073b4785f8880a3c248eba6397f33b6de1bd4ed7f83a362c5a61dfe5b2e602b30aec0b0f46efc27cbdd796de43b2c1b62db9e3d2fed43ec90a - languageName: node - linkType: hard - -"@ethersproject/strings@npm:5.4.0, @ethersproject/strings@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/strings@npm:5.4.0" - dependencies: - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/constants": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - checksum: f62ab89803f6cbc9c91093589e8e117b0c0857d8642d7d85bef1807f3ced7757127d3f271bf0bd7f644e02a31282842c1339f678b7f8a79c88ad3326d775db2a - languageName: node - linkType: hard - -"@ethersproject/transactions@npm:5.4.0, @ethersproject/transactions@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/transactions@npm:5.4.0" - dependencies: - "@ethersproject/address": ^5.4.0 - "@ethersproject/bignumber": ^5.4.0 - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/constants": ^5.4.0 - "@ethersproject/keccak256": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - "@ethersproject/properties": ^5.4.0 - "@ethersproject/rlp": ^5.4.0 - "@ethersproject/signing-key": ^5.4.0 - checksum: 7407c93301fe634bf74150e1b4dc0636246ee4022ae988c003e1a52d0ad5cc6a693a6cb763b2280cb856f76e314b8650206cb1308a7a679e63bfbe8604294fe3 - languageName: node - linkType: hard - -"@ethersproject/units@npm:5.4.0": - version: 5.4.0 - resolution: "@ethersproject/units@npm:5.4.0" - dependencies: - "@ethersproject/bignumber": ^5.4.0 - "@ethersproject/constants": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - checksum: 47054df485b4b648a982186d280f0eef9489acbd0d1a4036cc8f9170b8c3c91552b5d8d21213cef206e76b475fc83c9318d87c50b4931e0495198637181f068d - languageName: node - linkType: hard - -"@ethersproject/wallet@npm:5.4.0": - version: 5.4.0 - resolution: "@ethersproject/wallet@npm:5.4.0" - dependencies: - "@ethersproject/abstract-provider": ^5.4.0 - "@ethersproject/abstract-signer": ^5.4.0 - "@ethersproject/address": ^5.4.0 - "@ethersproject/bignumber": ^5.4.0 - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/hash": ^5.4.0 - "@ethersproject/hdnode": ^5.4.0 - "@ethersproject/json-wallets": ^5.4.0 - "@ethersproject/keccak256": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - "@ethersproject/properties": ^5.4.0 - "@ethersproject/random": ^5.4.0 - "@ethersproject/signing-key": ^5.4.0 - "@ethersproject/transactions": ^5.4.0 - "@ethersproject/wordlists": ^5.4.0 - checksum: 537bea004798951476b15752fad672d919e3130aeacaff3cf2497d466f1346e5fa15b6cd716c0c879ce68bc7f33e1bd355ce11032a94e9d30eef0666b6fd1ddf - languageName: node - linkType: hard - -"@ethersproject/web@npm:5.4.0, @ethersproject/web@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/web@npm:5.4.0" - dependencies: - "@ethersproject/base64": ^5.4.0 - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - "@ethersproject/properties": ^5.4.0 - "@ethersproject/strings": ^5.4.0 - checksum: 09954d42aaa91690448b8dacfb16d82351138f13c2bf05ab18c4e03c5c5c1df625bc094d781665ef5b034a24f5c0c305d6b01f4f5e6bf082e30933b3788d69ce - languageName: node - linkType: hard - -"@ethersproject/wordlists@npm:5.4.0, @ethersproject/wordlists@npm:^5.4.0": - version: 5.4.0 - resolution: "@ethersproject/wordlists@npm:5.4.0" - dependencies: - "@ethersproject/bytes": ^5.4.0 - "@ethersproject/hash": ^5.4.0 - "@ethersproject/logger": ^5.4.0 - "@ethersproject/properties": ^5.4.0 - "@ethersproject/strings": ^5.4.0 - checksum: db9fb8994a27cec040a44efa4a9c16af6443db71e809012a7dfa05c2ea2162c699a7f5e5ca9683580a490e90c55718aa110fa1eb79749a359f85794cc43d67b6 - languageName: node - linkType: hard - -"@findeth/abi@npm:^0.7.1": - version: 0.7.1 - resolution: "@findeth/abi@npm:0.7.1" - checksum: 2657a946b912c7a837f4d6ddf2027c090e5c8737c8c33178fee6246d136af02e4621ef5aea43c2fe738d5ab8c95a8faef52d1f89e15c924888acbbcdfcee863b - languageName: node - linkType: hard - "@humanwhocodes/config-array@npm:^0.5.0": version: 0.5.0 resolution: "@humanwhocodes/config-array@npm:0.5.0" @@ -535,7 +96,6 @@ __metadata: version: 0.0.0-use.local resolution: "@lunch-money/ethereum-to-lunch-money@workspace:." dependencies: - "@mycrypto/eth-scan": ^3.4.4 "@types/chai": ^4 "@types/eventsource": ^1.1.7 "@types/mocha": ^9.0.0 @@ -547,7 +107,6 @@ __metadata: eslint: ^7.32.0 eslint-config-prettier: ^8.3.0 eslint-plugin-prettier: ^3.4.0 - ethers: ^5.4.6 eventsource: ^1.1.0 husky: ^7.0.0 lint-staged: ^11.1.2 @@ -560,16 +119,6 @@ __metadata: languageName: unknown linkType: soft -"@mycrypto/eth-scan@npm:^3.4.4": - version: 3.4.4 - resolution: "@mycrypto/eth-scan@npm:3.4.4" - dependencies: - "@findeth/abi": ^0.7.1 - isomorphic-unfetch: ^3.1.0 - checksum: c7d2ac94eb7265b4060de1a81ebc8a6b92f7a22f4a78036ca79de195e4a7c30b6d4513b36cf28c7767e8f0d35018350f82b8b1f89b596ac6ed04a942ff290603 - languageName: node - linkType: hard - "@nodelib/fs.scandir@npm:2.1.5": version: 2.1.5 resolution: "@nodelib/fs.scandir@npm:2.1.5" @@ -876,13 +425,6 @@ __metadata: languageName: node linkType: hard -"aes-js@npm:3.0.0": - version: 3.0.0 - resolution: "aes-js@npm:3.0.0" - checksum: 251e26d533cd1a915b44896b17d5ed68c24a02484cfdd2e74ec700a309267db96651ea4eb657bf20aac32a3baa61f6e34edf8e2fec2de440a655da9942d334b8 - languageName: node - linkType: hard - "agent-base@npm:6, agent-base@npm:^6.0.2": version: 6.0.2 resolution: "agent-base@npm:6.0.2" @@ -1070,13 +612,6 @@ __metadata: languageName: node linkType: hard -"bech32@npm:1.1.4": - version: 1.1.4 - resolution: "bech32@npm:1.1.4" - checksum: 0e98db619191548390d6f09ff68b0253ba7ae6a55db93dfdbb070ba234c1fd3308c0606fbcc95fad50437227b10011e2698b89f0181f6e7f845c499bd14d0f4b - languageName: node - linkType: hard - "binary-extensions@npm:^2.0.0": version: 2.2.0 resolution: "binary-extensions@npm:2.2.0" @@ -1084,13 +619,6 @@ __metadata: languageName: node linkType: hard -"bn.js@npm:^4.11.9": - version: 4.12.0 - resolution: "bn.js@npm:4.12.0" - checksum: 39afb4f15f4ea537b55eaf1446c896af28ac948fdcf47171961475724d1bb65118cca49fa6e3d67706e4790955ec0e74de584e45c8f1ef89f46c812bee5b5a12 - languageName: node - linkType: hard - "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -1110,13 +638,6 @@ __metadata: languageName: node linkType: hard -"brorand@npm:^1.1.0": - version: 1.1.0 - resolution: "brorand@npm:1.1.0" - checksum: 8a05c9f3c4b46572dec6ef71012b1946db6cae8c7bb60ccd4b7dd5a84655db49fe043ecc6272e7ef1f69dc53d6730b9e2a3a03a8310509a3d797a618cbee52be - languageName: node - linkType: hard - "browser-stdout@npm:1.3.1": version: 1.3.1 resolution: "browser-stdout@npm:1.3.1" @@ -1466,21 +987,6 @@ __metadata: languageName: node linkType: hard -"elliptic@npm:6.5.4": - version: 6.5.4 - resolution: "elliptic@npm:6.5.4" - dependencies: - bn.js: ^4.11.9 - brorand: ^1.1.0 - hash.js: ^1.0.0 - hmac-drbg: ^1.0.1 - inherits: ^2.0.4 - minimalistic-assert: ^1.0.1 - minimalistic-crypto-utils: ^1.0.1 - checksum: d56d21fd04e97869f7ffcc92e18903b9f67f2d4637a23c860492fbbff5a3155fd9ca0184ce0c865dd6eb2487d234ce9551335c021c376cd2d3b7cb749c7d10f4 - languageName: node - linkType: hard - "emoji-regex@npm:^8.0.0": version: 8.0.0 resolution: "emoji-regex@npm:8.0.0" @@ -1730,44 +1236,6 @@ __metadata: languageName: node linkType: hard -"ethers@npm:^5.4.6": - version: 5.4.6 - resolution: "ethers@npm:5.4.6" - dependencies: - "@ethersproject/abi": 5.4.1 - "@ethersproject/abstract-provider": 5.4.1 - "@ethersproject/abstract-signer": 5.4.1 - "@ethersproject/address": 5.4.0 - "@ethersproject/base64": 5.4.0 - "@ethersproject/basex": 5.4.0 - "@ethersproject/bignumber": 5.4.1 - "@ethersproject/bytes": 5.4.0 - "@ethersproject/constants": 5.4.0 - "@ethersproject/contracts": 5.4.1 - "@ethersproject/hash": 5.4.0 - "@ethersproject/hdnode": 5.4.0 - "@ethersproject/json-wallets": 5.4.0 - "@ethersproject/keccak256": 5.4.0 - "@ethersproject/logger": 5.4.1 - "@ethersproject/networks": 5.4.2 - "@ethersproject/pbkdf2": 5.4.0 - "@ethersproject/properties": 5.4.1 - "@ethersproject/providers": 5.4.5 - "@ethersproject/random": 5.4.0 - "@ethersproject/rlp": 5.4.0 - "@ethersproject/sha2": 5.4.0 - "@ethersproject/signing-key": 5.4.0 - "@ethersproject/solidity": 5.4.0 - "@ethersproject/strings": 5.4.0 - "@ethersproject/transactions": 5.4.0 - "@ethersproject/units": 5.4.0 - "@ethersproject/wallet": 5.4.0 - "@ethersproject/web": 5.4.0 - "@ethersproject/wordlists": 5.4.0 - checksum: 3750ca9532b1413ef2b1bd611f4a8c74aa6413cbe47dd443d52430e4457a1343ea84c334e99018f816ea19e2848bb885146421c86101fe4570292b43c7762081 - languageName: node - linkType: hard - "eventsource@npm:^1.1.0": version: 1.1.0 resolution: "eventsource@npm:1.1.0" @@ -2064,16 +1532,6 @@ fsevents@~2.3.2: languageName: node linkType: hard -"hash.js@npm:1.1.7, hash.js@npm:^1.0.0, hash.js@npm:^1.0.3": - version: 1.1.7 - resolution: "hash.js@npm:1.1.7" - dependencies: - inherits: ^2.0.3 - minimalistic-assert: ^1.0.1 - checksum: e350096e659c62422b85fa508e4b3669017311aa4c49b74f19f8e1bc7f3a54a584fdfd45326d4964d6011f2b2d882e38bea775a96046f2a61b7779a979629d8f - languageName: node - linkType: hard - "he@npm:1.2.0": version: 1.2.0 resolution: "he@npm:1.2.0" @@ -2083,17 +1541,6 @@ fsevents@~2.3.2: languageName: node linkType: hard -"hmac-drbg@npm:^1.0.1": - version: 1.0.1 - resolution: "hmac-drbg@npm:1.0.1" - dependencies: - hash.js: ^1.0.3 - minimalistic-assert: ^1.0.0 - minimalistic-crypto-utils: ^1.0.1 - checksum: bd30b6a68d7f22d63f10e1888aee497d7c2c5c0bb469e66bbdac99f143904d1dfe95f8131f95b3e86c86dd239963c9d972fcbe147e7cffa00e55d18585c43fe0 - languageName: node - linkType: hard - "http-cache-semantics@npm:^4.1.0": version: 4.1.0 resolution: "http-cache-semantics@npm:4.1.0" @@ -2211,7 +1658,7 @@ fsevents@~2.3.2: languageName: node linkType: hard -"inherits@npm:2, inherits@npm:^2.0.3, inherits@npm:^2.0.4, inherits@npm:~2.0.3": +"inherits@npm:2, inherits@npm:~2.0.3": version: 2.0.4 resolution: "inherits@npm:2.0.4" checksum: 4a48a733847879d6cf6691860a6b1e3f0f4754176e4d71494c41f3475553768b10f84b5ce1d40fbd0e34e6bfbb864ee35858ad4dd2cf31e02fc4a154b724d7f1 @@ -2350,23 +1797,6 @@ fsevents@~2.3.2: languageName: node linkType: hard -"isomorphic-unfetch@npm:^3.1.0": - version: 3.1.0 - resolution: "isomorphic-unfetch@npm:3.1.0" - dependencies: - node-fetch: ^2.6.1 - unfetch: ^4.2.0 - checksum: 82b92fe4ec2823a81ab0fc0d11bd94d710e6f9a940d56b3cba31896d4345ec9ffc7949f4ff31ebcae84f6b95f7ebf3474c4c7452b834eb4078ea3f2c37e459c5 - languageName: node - linkType: hard - -"js-sha3@npm:0.5.7": - version: 0.5.7 - resolution: "js-sha3@npm:0.5.7" - checksum: 973a28ea4b26cc7f12d2ab24f796e24ee4a71eef45a6634a052f6eb38cf8b2333db798e896e6e094ea6fa4dfe8e42a2a7942b425cf40da3f866623fd05bb91ea - languageName: node - linkType: hard - "js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -2645,20 +2075,6 @@ fsevents@~2.3.2: languageName: node linkType: hard -"minimalistic-assert@npm:^1.0.0, minimalistic-assert@npm:^1.0.1": - version: 1.0.1 - resolution: "minimalistic-assert@npm:1.0.1" - checksum: cc7974a9268fbf130fb055aff76700d7e2d8be5f761fb5c60318d0ed010d839ab3661a533ad29a5d37653133385204c503bfac995aaa4236f4e847461ea32ba7 - languageName: node - linkType: hard - -"minimalistic-crypto-utils@npm:^1.0.1": - version: 1.0.1 - resolution: "minimalistic-crypto-utils@npm:1.0.1" - checksum: 6e8a0422b30039406efd4c440829ea8f988845db02a3299f372fceba56ffa94994a9c0f2fd70c17f9969eedfbd72f34b5070ead9656a34d3f71c0bd72583a0ed - languageName: node - linkType: hard - "minimatch@npm:3.0.4, minimatch@npm:^3.0.4": version: 3.0.4 resolution: "minimatch@npm:3.0.4" @@ -2826,13 +2242,6 @@ fsevents@~2.3.2: languageName: node linkType: hard -"node-fetch@npm:^2.6.1": - version: 2.6.1 - resolution: "node-fetch@npm:2.6.1" - checksum: 91075bedd57879117e310fbcc36983ad5d699e522edb1ebcdc4ee5294c982843982652925c3532729fdc86b2d64a8a827797a745f332040d91823c8752ee4d7c - languageName: node - linkType: hard - "node-gyp@npm:latest": version: 8.1.0 resolution: "node-gyp@npm:8.1.0" @@ -3281,13 +2690,6 @@ fsevents@~2.3.2: languageName: node linkType: hard -"scrypt-js@npm:3.0.1": - version: 3.0.1 - resolution: "scrypt-js@npm:3.0.1" - checksum: b7c7d1a68d6ca946f2fbb0778e0c4ec63c65501b54023b2af7d7e9f48fdb6c6580d6f7675cd53bda5944c5ebc057560d5a6365079752546865defb3b79dea454 - languageName: node - linkType: hard - "semver-compare@npm:^1.0.0": version: 1.0.0 resolution: "semver-compare@npm:1.0.0" @@ -3714,13 +3116,6 @@ typescript@^4.3.5: languageName: node linkType: hard -"unfetch@npm:^4.2.0": - version: 4.2.0 - resolution: "unfetch@npm:4.2.0" - checksum: 6a4b2557e1d921eaa80c4425ce27a404945ec26491ed06e62598f333996a91a44c7908cb26dc7c2746d735762b13276cf4aa41829b4c8f438dde63add3045d7a - languageName: node - linkType: hard - "unique-filename@npm:^1.1.1": version: 1.1.1 resolution: "unique-filename@npm:1.1.1" @@ -3835,21 +3230,6 @@ typescript@^4.3.5: languageName: node linkType: hard -"ws@npm:7.4.6": - version: 7.4.6 - resolution: "ws@npm:7.4.6" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 3a990b32ed08c72070d5e8913e14dfcd831919205be52a3ff0b4cdd998c8d554f167c9df3841605cde8b11d607768cacab3e823c58c96a5c08c987e093eb767a - languageName: node - linkType: hard - "y18n@npm:^5.0.5": version: 5.0.8 resolution: "y18n@npm:5.0.8" From 44bbc6498c7af6d381963d7647aaadfd3dda4362 Mon Sep 17 00:00:00 2001 From: Max Dumas Date: Wed, 8 Dec 2021 16:11:02 -0500 Subject: [PATCH 4/6] Adapt client code --- bin/get-balances.ts | 4 +- bin/refresh-token-list.sh | 7 --- src/client.ts | 104 +++++++++++++++++++++++++------------- src/main.ts | 82 ++++++++++-------------------- 4 files changed, 99 insertions(+), 98 deletions(-) delete mode 100755 bin/refresh-token-list.sh diff --git a/bin/get-balances.ts b/bin/get-balances.ts index bfe76c3..4d6efe5 100755 --- a/bin/get-balances.ts +++ b/bin/get-balances.ts @@ -2,7 +2,7 @@ import assert from 'node:assert'; -import { LunchMoneyEthereumWalletConnection } from '../src/main.js'; +import { LunchMoneyEthereumWalletConnection, createZapperAPIClient } from '../src/main.js'; const requireEnv = (key: string): string => { const value = process.env[key]; @@ -18,7 +18,7 @@ const ETH_ADDRESS = requireEnv('ETH_ADDRESS'); { walletAddress: ETH_ADDRESS, }, - {}, + { client: createZapperAPIClient() }, ), ); })(); diff --git a/bin/refresh-token-list.sh b/bin/refresh-token-list.sh deleted file mode 100755 index c97ba1d..0000000 --- a/bin/refresh-token-list.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/bash -set -euo pipefail -IFS=$'\n\t' - -DIR=$(dirname "$0") - -curl https://tokens.1inch.eth.link/ -o $(realpath $DIR/../fixtures/1inch.json) diff --git a/src/client.ts b/src/client.ts index e6b0aa2..df2b224 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,49 +1,83 @@ -import ethscan from '@mycrypto/eth-scan'; -import * as ethers from 'ethers'; import EventSource from 'eventsource'; -import mem from 'mem'; import { encode } from 'querystring'; -// import eventsource from "eventsource"; - -import tokenList1inch from '../fixtures/1inch.json'; - -console.log(ethscan); - const ZAPPER_FI_API_URL = 'https://api.zapper.fi/v1/balances'; +// This API key is public and shared with all users. This API is publicly +// available, free of charge. See here for details: +// https://docs.zapper.fi/zapper-api/endpoints const ZAPPER_FI_API_KEY = '96e0cc51-a62e-42ca-acee-910ea7d2a241'; -const ETH_ADDRESS = '0x8E29007951cE79c151dd070b51e30168E9663c13'; -export interface EthereumWalletClient { - getWeiBalance(walletAddress: string): Promise; - getTokensBalance(walletAddress: string, tokenContractAddresses: string[]): Promise>; +export interface ZapperAPIClient { + getTokenBalances(walletAddresses: string[]): Promise; } -export const createEthereumWalletClient = (provider: ethers.providers.BaseProvider): EthereumWalletClient => ({ - async getWeiBalance(walletAddress) { - return (await provider.getBalance(walletAddress)).toBigInt(); - }, - async getTokensBalance(walletAddress, tokenContractAddresses) { - const es = new EventSource( - ZAPPER_FI_API_URL + - '?' + - encode({ - 'addresses[]': ETH_ADDRESS, - api_key: ZAPPER_FI_API_KEY, - }), - ); +export const createZapperAPIClient = (): ZapperAPIClient => ({ + async getTokenBalances(walletAddresses) { + const qs = encode({ + 'addresses[]': walletAddresses.join(','), + api_key: ZAPPER_FI_API_KEY, + }); + + const es = new EventSource(ZAPPER_FI_API_URL + '?' + qs); + + let balances: ZapperTokenBalancesResponse[] = []; + + es.addEventListener('balance', (ev) => { + const data: ZapperTokenBalancesResponse = JSON.parse(ev.data); + balances = balances.concat(data); + }); + + return new Promise((resolve, reject) => { + es.addEventListener('end', () => { + es.close(); + resolve(balances); + }); + + es.addEventListener('error', (ev) => { + es.close(); + reject(ev); + }); + }); }, }); -interface Token { +interface ZapperTokenBalancesResponse { + network: string; + appId: string; + balances: Record; +} + +interface ZapperBalance { + products: ZapperProduct[]; + meta: ZapperMeta; +} + +interface ZapperMeta { + label: string; + value: number; + type: string; +} + +interface ZapperProduct { + label: string; + assets: ZapperAsset[]; +} + +interface ZapperAsset { + type: string; + balanceUSD: number; + tokens: ZapperToken[]; +} + +interface ZapperToken { + type: string; + network: string; address: string; - chainId: number; - name: string; - symbol: string; decimals: number; - logoURI: string; + symbol: string; + price: number; + hide: boolean; + balance: number; + balanceRaw: string; + balanceUSD: number; } - -export const loadTokenList = mem(async (): Promise => { - return tokenList1inch.tokens; -}); diff --git a/src/main.ts b/src/main.ts index 43fa6ed..57a64d6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -2,24 +2,16 @@ import { LunchMoneyCryptoConnection, LunchMoneyCryptoConnectionContext, LunchMoneyCryptoConnectionConfig, - CryptoBalance, } from './types.js'; -import { encode } from 'node:querystring'; -import EventSource from 'eventsource'; +import { createZapperAPIClient, ZapperAPIClient } from './client.js'; export { LunchMoneyCryptoConnection } from './types.js'; -// export { createEthereumWalletClient, EthereumWalletClient } from './client.js'; +export { createZapperAPIClient, ZapperAPIClient } from './client.js'; -const ZAPPER_FI_API_URL = 'https://api.zapper.fi/v1/balances'; -// This API key is public and shared with all users. This API is publicly -// available, free of charge. See here for details: -// https://docs.zapper.fi/zapper-api/endpoints -const ZAPPER_FI_API_KEY = '96e0cc51-a62e-42ca-acee-910ea7d2a241'; - -/** The minimum balance (in wei) that a token should have in order to be +/** The minimum balance (in USD) that a token should have in order to be * considered for returning as a balance. */ -const NEGLIGIBLE_BALANCE_THRESHOLD = 1000; +const NEGLIGIBLE_BALANCE_THRESHOLD = 0.01; interface LunchMoneyEthereumWalletConnectionConfig extends LunchMoneyCryptoConnectionConfig { /** The unique ID of the user's wallet address on the blockchain. */ @@ -27,7 +19,9 @@ interface LunchMoneyEthereumWalletConnectionConfig extends LunchMoneyCryptoConne negligibleBalanceThreshold?: number; } -type LunchMoneyEthereumWalletConnectionContext = LunchMoneyCryptoConnectionContext; +interface LunchMoneyEthereumWalletConnectionContext extends LunchMoneyCryptoConnectionContext { + client: ZapperAPIClient; +} export const LunchMoneyEthereumWalletConnection: LunchMoneyCryptoConnection< LunchMoneyEthereumWalletConnectionConfig, @@ -36,54 +30,34 @@ export const LunchMoneyEthereumWalletConnection: LunchMoneyCryptoConnection< async initiate(config, context) { return this.getBalances(config, context); }, - async getBalances({ walletAddress, negligibleBalanceThreshold = NEGLIGIBLE_BALANCE_THRESHOLD }, {}) { + async getBalances( + { walletAddress, negligibleBalanceThreshold = NEGLIGIBLE_BALANCE_THRESHOLD }, + { client = createZapperAPIClient() }, + ) { // For some reason the address that is returned in the response is // lowercased. const walletAddressIndex = walletAddress.toLowerCase(); - const qs = encode({ - 'addresses[]': walletAddress, - api_key: ZAPPER_FI_API_KEY, - }); - - const es = new EventSource(ZAPPER_FI_API_URL + '?' + qs); - - const hasAnyNonZeroBalances = (balanceData: any) => balanceData.balances[walletAddressIndex].products.length > 0; - - let balances: CryptoBalance[] = []; + const res = await client.getTokenBalances([walletAddress]); - es.addEventListener('balance', (ev) => { - const data = JSON.parse(ev.data); - if (hasAnyNonZeroBalances(data)) { - const p = data.balances[walletAddressIndex].products; - - const appBalances: CryptoBalance[] = p.flatMap((p: any) => - p.assets.flatMap((a: any) => - a.tokens.map((t: any) => ({ + const balances = res + .flatMap((x) => + x.balances[walletAddressIndex].products.flatMap((p) => + p.assets.flatMap((a) => + a.tokens.map((t) => ({ asset: t.symbol, - amount: t.balance.toString(), - amountInUSD: t.balanceUSD.toString(), + amount: t.balance, + amountInUSD: t.balanceUSD, })), ), - ); - - balances = balances.concat(appBalances); - } - }); - - return new Promise((resolve, reject) => { - es.addEventListener('end', () => { - es.close(); - resolve({ - providerName: 'wallet_ethereum', - balances, - }); - }); - - es.addEventListener('error', (ev) => { - es.close(); - reject(ev); - }); - }); + ), + ) + .filter((t) => t.amountInUSD > negligibleBalanceThreshold) + .map((t) => ({ ...t, amount: t.amount.toString(), amountInUSD: t.amountInUSD.toString() })); + + return { + providerName: 'wallet_ethereum', + balances, + }; }, }; From d37f1a37e4a619946671ca3fa2eca135638df48e Mon Sep 17 00:00:00 2001 From: Max Dumas Date: Wed, 8 Dec 2021 16:26:55 -0500 Subject: [PATCH 5/6] Fix tests --- src/client.ts | 4 +-- test/main.spec.ts | 68 +++++++++++++++++++++++++++++++++++------------ 2 files changed, 53 insertions(+), 19 deletions(-) diff --git a/src/client.ts b/src/client.ts index df2b224..f888018 100644 --- a/src/client.ts +++ b/src/client.ts @@ -41,7 +41,7 @@ export const createZapperAPIClient = (): ZapperAPIClient => ({ }, }); -interface ZapperTokenBalancesResponse { +export interface ZapperTokenBalancesResponse { network: string; appId: string; balances: Record; @@ -49,7 +49,7 @@ interface ZapperTokenBalancesResponse { interface ZapperBalance { products: ZapperProduct[]; - meta: ZapperMeta; + meta: ZapperMeta[]; } interface ZapperMeta { diff --git a/test/main.spec.ts b/test/main.spec.ts index 4f18df2..c68af6b 100644 --- a/test/main.spec.ts +++ b/test/main.spec.ts @@ -2,6 +2,7 @@ import { assert } from 'chai'; import sinon from 'sinon'; import { LunchMoneyEthereumWalletConnection as underTest } from '../src/main.js'; +import { ZapperTokenBalancesResponse } from '../src/client.js'; describe('LunchMoneyEthereumWalletConnection', () => { const dummyConfig = { @@ -10,18 +11,51 @@ describe('LunchMoneyEthereumWalletConnection', () => { }; const mockClient = { - getWeiBalance: sinon.stub(), - getTokensBalance: sinon.stub(), + getTokenBalances: sinon.stub<[], Promise>(), }; const dummyContext = { client: mockClient, }; + const makeBalances = (balances: { asset: string; amountInUSD: number; amount: number }[]) => [ + { + network: 'ethereum', + appId: 'tokens', + balances: { + [dummyConfig.walletAddress]: { + meta: [], + products: [ + { + label: 'Tokens', + assets: [ + { + balanceUSD: 0, + type: 'tokens', + tokens: balances.map((t) => ({ + address: '0xbar', + type: 'token', + balanceUSD: t.amountInUSD, + balance: t.amount, + balanceRaw: '', + decimals: 9, + hide: false, + network: 'ethereum', + price: 1337, + symbol: t.asset, + })), + }, + ], + }, + ], + }, + }, + }, + ]; + describe('getBalances', () => { describe('when the wallet has an ETH amount less than the neglible balance threshold', () => { it('does not output the ETH balance amount', async () => { - mockClient.getWeiBalance.resolves(50); - mockClient.getTokensBalance.resolves({}); + mockClient.getTokenBalances.resolves(makeBalances([{ asset: 'ETH', amount: 0, amountInUSD: 0 }])); const response = await underTest.getBalances(dummyConfig, dummyContext); @@ -34,22 +68,20 @@ describe('LunchMoneyEthereumWalletConnection', () => { describe('when the wallet has an ETH amount more than the neglible balance threshold', () => { it('outputs the ETH balance amount', async () => { - mockClient.getWeiBalance.resolves(1000); - mockClient.getTokensBalance.resolves({}); + mockClient.getTokenBalances.resolves(makeBalances([{ asset: 'ETH', amount: 1, amountInUSD: 150 }])); const response = await underTest.getBalances(dummyConfig, dummyContext); assert.deepEqual(response, { providerName: 'wallet_ethereum', - balances: [{ asset: 'ETH', amount: '0.000000000000001' }], + balances: [{ asset: 'ETH', amount: '1', amountInUSD: '150' }], }); }); }); describe('when the wallet contains no tokens', () => { it('outputs nothing', async () => { - mockClient.getWeiBalance.resolves(0); - mockClient.getTokensBalance.resolves({}); + mockClient.getTokenBalances.resolves([]); const response = await underTest.getBalances(dummyConfig, dummyContext); @@ -62,19 +94,21 @@ describe('LunchMoneyEthereumWalletConnection', () => { describe('when the wallet contains tokens', () => { it('outputs the tokens which have balances above the negligible balance threshold', async () => { - mockClient.getWeiBalance.resolves(50); - mockClient.getTokensBalance.resolves({ - '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48': 1000, // USDC - '0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2': 10, // MKR - '0xa1d65E8fB6e87b60FECCBc582F7f97804B725521': 10000, // DXD - }); + mockClient.getTokenBalances.resolves( + makeBalances([ + { asset: 'ETH', amount: 0, amountInUSD: 200 }, + { asset: 'USDC', amount: 0, amountInUSD: 300 }, + { asset: 'WBTC', amount: 0, amountInUSD: 250 }, + ]), + ); const response = await underTest.getBalances(dummyConfig, dummyContext); assert.strictEqual(response.providerName, 'wallet_ethereum'); assert.sameDeepMembers(response.balances, [ - { asset: 'USDC', amount: '0.000000000000001' }, - { asset: 'DXD', amount: '0.00000000000001' }, + { asset: 'ETH', amount: '0', amountInUSD: '200' }, + { asset: 'USDC', amount: '0', amountInUSD: '300' }, + { asset: 'WBTC', amount: '0', amountInUSD: '250' }, ]); }); }); From c4998debf8d9467260f4a64690e9d564f72d576d Mon Sep 17 00:00:00 2001 From: Max Dumas Date: Wed, 8 Dec 2021 16:33:09 -0500 Subject: [PATCH 6/6] Make client truly optional --- bin/get-balances.ts | 2 +- src/main.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/get-balances.ts b/bin/get-balances.ts index 4d6efe5..f1d380a 100755 --- a/bin/get-balances.ts +++ b/bin/get-balances.ts @@ -18,7 +18,7 @@ const ETH_ADDRESS = requireEnv('ETH_ADDRESS'); { walletAddress: ETH_ADDRESS, }, - { client: createZapperAPIClient() }, + {}, ), ); })(); diff --git a/src/main.ts b/src/main.ts index 57a64d6..fb3c1eb 100644 --- a/src/main.ts +++ b/src/main.ts @@ -20,7 +20,7 @@ interface LunchMoneyEthereumWalletConnectionConfig extends LunchMoneyCryptoConne } interface LunchMoneyEthereumWalletConnectionContext extends LunchMoneyCryptoConnectionContext { - client: ZapperAPIClient; + client?: ZapperAPIClient; } export const LunchMoneyEthereumWalletConnection: LunchMoneyCryptoConnection<