Giter Site home page Giter Site logo

mycryptohq / eth-scan Goto Github PK

View Code? Open in Web Editor NEW
187.0 187.0 80.0 2.46 MB

An efficient Ether and token balance scanner library

JavaScript 0.53% TypeScript 89.48% Solidity 10.00%
balance erc-20 ether ethereum ethersjs scanner smart-contract tokens web3

eth-scan's People

Contributors

alebanzas avatar dependabot-preview[bot] avatar dependabot[bot] avatar frederikbolding avatar mrluit avatar mrtenz avatar pablocastellano avatar yabirgb avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

eth-scan's Issues

Error on BSC: Failed to get data from contract: Error: Input must be hexadecimal

Getting balances works great on ETH, but if I switch to BSC and pass a list of valid contract addresses, i see:

Failed to get data from contract: Error: Input must be hexadecimal

I use ethersproject/address to validate each address, and I'm using popular known contract addresses like BUSD, but it still fails.

Any ideas?

New eth-scan broken for some tokens

The problem

At rotki we use the old eth-scan version: https://etherscan.io/address/0x86F25b64e1Fe4C5162cDEeD5245575D32eC549db

Tried and true, works every time.

@yabirgb deployed the new one in optimism, so thought we could try and use it in mainnet too.

While trying to accomplish that it turns out that there is some edge-cases for which it's broken.

While for many tokens it works fine and returns True/false plus the uint256 in bytes for the token balance, there is a few tokens where it returns True plus a humongous byte string.

One such token is 0x0e880118C29F095143dDA28e64d95333A9e75A47 (https://etherscan.io/address/0x0e880118C29F095143dDA28e64d95333A9e75A47)

If you try to query balance for any user it spits out the huge byte string. But probably best example is someone who actually owns a bit of it.

So tokensBalance for owner: 0xeE620a0991D57f464aaD452789a4564Ba51245E8 and contracts: 0x0e880118C29F095143dDA28e64d95333A9e75A47

Try via: https://etherscan.io/address/0x08A8fDBddc160A7d5b957256b903dCAb1aE512C5#readContract

Result can be seen below

2022-12-08_15-20

Some extra thoughts

I can't help but feel that the original very first contract is superior in every way. Only downside is that it has view only the EtherBalances and the other calls are not view while they should have been.

But for this new one the following problems exist:

  1. Adding success/false for each underlying token call does not help much. As a consumer app I only care about the balance and not success/false of the balanceOf. If the call failed, I would always assume 0 balance anyway. So this adds unecessary complexity and reduces the amount of possible tokens we can have in a roundtrip to a node due to size limitations.
  2. Having success/false also for etherBalances does not make sense since it's always true. So it takes up extra space and reduces the amount of possible addresses we can have in a roundtrip to a node due to size limitations.
  3. Having the result in bytes. I really don't get this. The original contract had it in uint256 which was exactly what you need. We are always dealing with uint256. Either token balances or eth balances. What was the reason to do this? This adds complexity in the consumer side, as we need to convert from byte string to int for every single token. Slows things down quite a lot considering our calls are in chunks of thousands of tokens.

It's quite possible I am missing something, but I would love some explanation into the thinking that went into these changes.

Explanation on the reduction of amount of possible addresses: Same code that was querying exact same amount of tokens for a given address fails both with etherscan proxy (they close the connection) and with my own node (times out). Works fine with old contract.

If with the new contract I send less addresses in each batch then it works. So in the end the new contract forces us to make more calls to a node.

Minimal project with your example code throws an exception about importing uuid-random-es.js

Hi there!

I really want to use your library in my project, but am currently experiencing the following exception:

**package.json:
{
"name": "eth-scanner",
"version": "1.0.0",
"main": "./lib/index.js",
"bin": {
"chain-inspector": "./lib/index.js"
},
"license": "MIT",
"dependencies": {
"@mycrypto/eth-scan": "^3.5.2"
},
"devDependencies": {
"@types/node": "^16.9.6",
"ts-node": "^10.2.1",
"typescript": "^4.4.3"
}
}

**tsconfig.json:
{
"compilerOptions": {
"target": "es2019",
"module": "commonjs",
"lib": ["es6", "es2015", "dom"],
"declaration": true,
"outDir": "lib",
"rootDir": "src",
"strict": true,
"types": ["node"],
"esModuleInterop": true,
"resolveJsonModule": true,
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,
}
}

**index.ts:
#!/usr/bin/env node
import { getEtherBalances } from '@mycrypto/eth-scan';

(async() => {

getEtherBalances('http://192.168.155:8545', [
    '0x9a0decaffb07fb500ff7e5d253b16892dbec006a',
    '0xeb65f72a2f5464157288ac15f1bb56c56e6be375',
    '0x1b96c634f9e9fcfb76932e165984901701352ffd',
    '0x740539b55ee5dc58efffb88fea44a9008f8daa6f',
    '0x95d9e32dc03770699a6a5e5858165b174d500015'
  ]).then(console.log);

})();

**result:
Error [ERR_REQUIRE_ESM]: require() of ES Module /Users/gibbinator/code/eth-scanner-2/node_modules/uuid-random-es/dist/uuid-random-es.js from /Users/gibbinator/code/eth-scanner-2/node_modules/@mycrypto/eth-scan/lib/cjs/providers/http.js not supported.

Thanks very much,
Gibbinator

Bug risk in : async function should have await expression

DESCRIPTION

A function that does not contain any await expressions should not be async (except for some edge cases in TypeScript which are discussed below). Asynchronous functions in JavaScript behave differently than other functions in two important ways:

The return value is always a Promise.
You can use the await operator inside them.
Functions are made async so that we can use the await operator inside them. Consider this example:

async function fetchData(processDataItem) {
const response = await fetch(DATA_URL);
const data = await response.json();

return data.map(processDataItem);

}
Asynchronous functions that don't use await might be an unintentional result of refactoring.

Note: This issue ignores async generator functions. Generators yield rather than return a value and async generators might yield all the values of another async generator without ever actually needing to use await.

In TypeScript, one might feel the need to make a function async to comply with type signatures defined by an interface. Ideally, the code should be refactored to get rid of such restrictions, but sometimes that isn't feasible (For example, when we are implementing an interface defined in a 3rd party library like Next.js).

This situation can easily be circumvented by returning the value with a call to Promise.resolve:

interface HasAsyncFunc {
getNum: () => Promise
}

// Not recommended:
const o: HasAsyncFunc = {
async getNum() { return 1 }
}

// Recommended:
const o: HasAsyncFunc = {
// We only use Promise.resolve to adhere to the type
// of the surrounding object.
getNum() { return Promise.resolve(1) }
}
It is also advised to add a comment near the redundant promise to make the intent clear.

BAD PRACTICE

async function fetchData(): string {
// readFileSync is a synchronous function that blocks
// the main thread, and thus does not need to be awaited
return fs.readFileSync("data.txt", "utf-8");
}

performAction(async () => { console.log("no awaits in here") });

RECOMMENDED

async function fetchDataAsync(): Promise {
return await fs.readFile("data.txt", "utf-8")
}

performAction(async () => { await writeToFile(data) });

// Allow empty functions.
async function no_op() {}

Look here to fix it:

Found async function without any await expressions

src/api.ts

  • @param results
  • @param encodeData
    */
    export const retryCalls = async (
    provider: ProviderLike,
    addresses: string | string[],
    contracts: string | string[],
    results: Result[],
    encodeData: (address: string) => string
    ): Promise<Result[]> => {
    return Promise.all(
    results.map(async (result, index) => {
    if (result[0]) {
    return result;
    }
    const address = typeof addresses === 'string' ? addresses : addresses[index];
    const contractAddress = typeof contracts === 'string' ? contracts : contracts[index];
    const data = encodeData(address);
    try {
    const newResult = await call(provider, contractAddress, data);
    return [true, newResult] as [boolean, Uint8Array];
    } catch {
    // noop
    }
    return result;
    })
    );
    };

Found async function without any await expressions

src/providers/eip-1193.ts

return (provider as EIP1193ProviderLike)?.request !== undefined;

},

send: async (provider: EIP1193ProviderLike, method: string, params: unknown[]): Promise => {
const payload = getPayload(method, params);
return provider.request(payload);
}
};

export default provider;

Found async function without any await expressions

src/providers/provider.test.ts

jest.mock('./eip-1193', () => ({
isProvider: jest.fn(),
send: jest.fn().mockImplementation(async () => '0x00')
}));

jest.mock('./ethers', () => ({
Found async function without any await expressions
src/providers/provider.test.ts

jest.mock('./ethers', () => ({
isProvider: jest.fn(),
send: jest.fn().mockImplementation(async () => '0x00')
}));

jest.mock('./http', () => ({
Found async function without any await expressions
src/providers/provider.test.ts

jest.mock('./http', () => ({
isProvider: jest.fn(),
send: jest.fn().mockImplementation(async () => '0x00')
}));

jest.mock('./web3', () => ({

1
2

BSC

I have discovered this library through this StackExchange question.

Your last comment said:

The contract is currently not deployed on BSC, but assuming BSC is compatible with EVM smart contracts, it should work as well if the contract was deployed.

Have you by any chance deployed equivalent contract to BSC network?

Export BalanceMap interface

To avoid having to redefine the return type, it would be nice to be able to import the BalanceMap interface

Anti-pattern : Prefer that unbound methods are called with their expected scope

DESCRIPTION

Warning is raised when a method is used outside of a method call.

Class functions don't preserve the class scope when passed as standalone variables.

BAD PRACTICE

class MyClass {
public log(): void {
console.log(this);
}
}

const instance = new MyClass();

// This logs the global scope (window/global), not the class instance
const myLog = instance.log;
myLog();

// This log might later be called with an incorrect scope
const { log } = instance;

RECOMMENDED

class MyClass {
public logUnbound(): void {
console.log(this);
}

public logBound = () => console.log(this);
}

const instance = new MyClass();

// logBound will always be bound with the correct scope
const { logBound } = instance;
logBound();

// .bind and lambdas will also add a correct scope
const dotBindLog = instance.logBound.bind(instance);
const innerLog = () => instance.logBound();

Look here to fix it, and remember to sponsor or give me a star for my work :)

Avoid referencing unbound methods which may cause unintentional scoping of this. If your function does not access this, you can annotate it with this: void, or consider using an arrow function instead
src/providers/ethers.test.ts

import EthersProvider from './ethers';

const { createFixtureLoader, provider } = waffle;
const { isProvider, send } = EthersProvider;

const loadFixture = createFixtureLoader(provider.getWallets(), provider);
Avoid referencing unbound methods which may cause unintentional scoping of this. If your function does not access this, you can annotate it with this: void, or consider using an arrow function instead
src/providers/ethers.test.ts

import EthersProvider from './ethers';

const { createFixtureLoader, provider } = waffle;
const { isProvider, send } = EthersProvider;

const loadFixture = createFixtureLoader(provider.getWallets(), provider);
Avoid referencing unbound methods which may cause unintentional scoping of this. If your function does not access this, you can annotate it with this: void, or consider using an arrow function instead
src/providers/http.test.ts

import HttpProvider from './http';

const { createFixtureLoader, provider } = waffle;
const { isProvider, send } = HttpProvider;

const loadFixture = createFixtureLoader(provider.getWallets(), provider);

1
2
3

Save gas, precompute hashed value

I'm not expert in how the EVM works internally and counts gaz. I just think the method abi.encodeWithSignature is internally computing a Keccak256 hash, which is very computational intensive. That costs 36 units of gaz, and a basic static value would be way more efficient.
Why not replacing the balanceOf(address) method (and same L54) with its corresponding hash key 70a08231, and also building the argument in a lazy way, using a simple packing way ?

getTokenBalances & getTokensBalances | Not functional

Description

When executing either getTokensBalances or getTokenBalances with either a Web3 provider or a RPC endpoint, the following error occurs @ eth-scan.ts:85

Connection via metamask

Error: invalid address (argument="address", value=undefined, code=INVALID_ARGUMENT, version=address/5.0.0-beta.134) (argument=null, value=undefined, code=INVALID_ARGUMENT, version=abi/5.0.0-beta.145)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.