0
\$\begingroup\$

This code flags potential Ethereum Virtual Machine (EVM) proxy contracts. Almost all proxy contracts are not auditable, since the code can be changed at any moment, and so should be avoided by users.

This nodejs code does simple bytecode analysis. It produces false positives (most often the one red dot) that need to be checked manually by private or public analysis to determine if they are in fact proxies. This is because the CALL opcode has dual use cases and can be used to create proxies and also used to transfer ETH.

Also smart contract bytecode often contains metadata that is decoded by getOpcodes() to produce false opcodes that may contain some of the opcodes that are searched for when detecting the proxies, thereby also producing false positives. Surrounding metadata opcodes should be some INVALID opcodes that will help determine that it is in fact metadata.

The contracts marked as decentralised contain immutable source code that can be audited with certainty and so provide a much higher level of trust for users.

If you have any suggestions for improving the code that would be great. Other ways to detect proxies are through source code analysis (there are ways to obtain verified source code) which might be the next step if this project is to be taken further.

detect-proxy.mjs

import dotenv from 'dotenv';dotenv.config()
import EVM from 'evm'
import Web3 from 'web3'
import { markdownTable } from 'markdown-table'

async function check(address, name='') {
  const web3 = new Web3('https://mainnet.infura.io/v3/'+process.env.INFURA_API_KEY)
  const r = []// result
  await web3.eth.getCode(web3.utils.toChecksumAddress(address)).then(code=>{
    const evm = new EVM.EVM(code)
    let o = evm.getOpcodes()
    r.push(o.filter(o=>'DELEGATECALL'==o.name).length||'')// opcode F4
    r.push(o.filter(o=>'CALLCODE'==o.name).length||'')// opcode F2
    r.push(o.filter(o=>'CALL'==o.name).length||'')// opcode F1
  })
  return r
}

let tokens = (await(await fetch('https://api.ethplorer.io/getTop?criteria=cap&apiKey='+(process.env.ETHPLORER_API_KEY||'freekey'))).json()).tokens
tokens = tokens.slice(0,10)
const table = [['DELEGATECALL', 'CALLCODE', 'CALL', 'Address', 'Name', 'Symbol', 'Decentralisation']];
for (const token of tokens) {
  const r = await check(token.address, token.symbol)// opcode counts
  let d = '✅ Decentralised'// decentralised?
  if (r[0]+r[1]+r[2]>0) {
    if (r[0]>0) d = '🔴🔴🔴'
    else if (r[1]>0) d = '🔴🔴'
    else if (r[2]>0) d = '🔴'
    d += ' Potentially Centralised'
  }
  table.push([
    ...r,
    token.address,
    token.name,
    token.symbol,
    d,
  ])
}
console.log(markdownTable(table))

package.json

{
  "dependencies": {
    "dotenv": "^16.0.3",
    "evm": "^0.2.0",
    "markdown-table": "^3.0.3",
    "web3": "^1.8.2"
  }
}

.env

INFURA_API_KEY=<your infura api key>
ETHPLORER_API_KEY=freekey

Run the code using

$ npm install
$ node detect-proxy.mjs
\$\endgroup\$

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Browse other questions tagged or ask your own question.