From 929bff555e775b8de7076f06848aac57cbc1cb06 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Thu, 29 Aug 2024 11:35:16 +0700 Subject: [PATCH 1/4] feat: proxy beacon --- src/services/evm/crawl_contract_evm.service.ts | 8 ++++---- src/services/evm/helpers/contract_helper.ts | 6 +++++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/services/evm/crawl_contract_evm.service.ts b/src/services/evm/crawl_contract_evm.service.ts index a0ad66578..c656f338a 100644 --- a/src/services/evm/crawl_contract_evm.service.ts +++ b/src/services/evm/crawl_contract_evm.service.ts @@ -301,22 +301,22 @@ export default class CrawlSmartContractEVMService extends BullableService { this.contractHelper.detectProxyContractByByteCode( addr, byteCode, - EIPProxyContractSupportByteCode.EIP_1967_IMPLEMENTATION.SLOT + EIPProxyContractSupportByteCode.EIP_1967_IMPLEMENTATION.TYPE ), this.contractHelper.detectProxyContractByByteCode( addr, byteCode, - EIPProxyContractSupportByteCode.EIP_1822_IMPLEMENTATION.SLOT + EIPProxyContractSupportByteCode.EIP_1822_IMPLEMENTATION.TYPE ), this.contractHelper.detectProxyContractByByteCode( addr, byteCode, - EIPProxyContractSupportByteCode.OPEN_ZEPPELIN_IMPLEMENTATION.SLOT + EIPProxyContractSupportByteCode.OPEN_ZEPPELIN_IMPLEMENTATION.TYPE ), this.contractHelper.detectProxyContractByByteCode( addr, byteCode, - EIPProxyContractSupportByteCode.EIP_1167_IMPLEMENTATION.SLOT + EIPProxyContractSupportByteCode.EIP_1167_IMPLEMENTATION.TYPE ), ]).catch(() => Promise.resolve(null)); }) diff --git a/src/services/evm/helpers/contract_helper.ts b/src/services/evm/helpers/contract_helper.ts index 991240802..718f852f2 100644 --- a/src/services/evm/helpers/contract_helper.ts +++ b/src/services/evm/helpers/contract_helper.ts @@ -20,13 +20,17 @@ export class ContractHelper { public async detectProxyContractByByteCode( contractAddress: string, byteCode: string, - byteCodeSlot: string, + type: string, blockHeight?: number | string ): Promise { const resultReturn: DetectEVMProxyContract = { logicContractAddress: '', EIP: '', }; + const byteCodeSlot = Object.values(EIPProxyContractSupportByteCode).find( + (e) => e.TYPE === type + )?.SLOT; + if (!byteCodeSlot) throw Error('Type proxy not supported'); const result = byteCode.includes(byteCodeSlot); if (!result) throw Error('Not proxy contract!'); From 8821c7564bf7eb4763020827df28d2c91063aebf Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Fri, 30 Aug 2024 10:42:37 +0700 Subject: [PATCH 2/4] feat: proxy beacon --- src/models/evm_smart_contract.ts | 17 ++++++++ src/services/evm/constant.ts | 8 ++-- .../evm/crawl_contract_evm.service.ts | 39 +++++++++++++---- src/services/evm/helpers/contract_helper.ts | 42 +++++++++++++------ 4 files changed, 82 insertions(+), 24 deletions(-) diff --git a/src/models/evm_smart_contract.ts b/src/models/evm_smart_contract.ts index 4d7005103..fd63ad394 100644 --- a/src/models/evm_smart_contract.ts +++ b/src/models/evm_smart_contract.ts @@ -60,6 +60,7 @@ export class EVMSmartContract extends BaseModel { PROXY_EIP_1822: 'PROXY_EIP_1822', PROXY_OPEN_ZEPPELIN_IMPLEMENTATION: 'PROXY_OPEN_ZEPPELIN_IMPLEMENTATION', PROXY_EIP_1167: 'PROXY_EIP_1167', + PROXY_EIP_1967_BEACON: 'PROXY_EIP_1967_BEACON', }; } @@ -82,4 +83,20 @@ export class EVMSmartContract extends BaseModel { }, }; } + + static BEACON_ABI = [ + { + inputs: [], + name: 'implementation', + outputs: [ + { + internalType: 'address', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + ]; } diff --git a/src/services/evm/constant.ts b/src/services/evm/constant.ts index 9588e70fe..bdf370d9d 100644 --- a/src/services/evm/constant.ts +++ b/src/services/evm/constant.ts @@ -56,10 +56,10 @@ export const EIPProxyContractSupportByteCode = { TYPE: EVMSmartContract.TYPES.PROXY_EIP_1967, }, // TODO: support beacon soon. - // EIP_1967_BEACON: { - // SLOT: 'a3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50', // eip1967.proxy.beacon - // TYPE: EVMSmartContract.TYPES.PROXY_EIP_1967, - // }, + EIP_1967_BEACON: { + SLOT: 'a3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50', // eip1967.proxy.beacon + TYPE: EVMSmartContract.TYPES.PROXY_EIP_1967_BEACON, + }, // EIP_1967_ADMIN: { // SLOT: 'b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103', // eip1967.proxy.admin // TYPE: EVMSmartContract.TYPES.PROXY_EIP_1967, diff --git a/src/services/evm/crawl_contract_evm.service.ts b/src/services/evm/crawl_contract_evm.service.ts index c656f338a..a5be6b44b 100644 --- a/src/services/evm/crawl_contract_evm.service.ts +++ b/src/services/evm/crawl_contract_evm.service.ts @@ -2,7 +2,7 @@ import { Service } from '@ourparentcenter/moleculer-decorators-extended'; import { whatsabi } from '@shazow/whatsabi'; import _, { Dictionary } from 'lodash'; import { ServiceBroker } from 'moleculer'; -import { GetBytecodeReturnType, PublicClient, keccak256 } from 'viem'; +import { GetBytecodeReturnType, PublicClient, keccak256, toHex } from 'viem'; import config from '../../../config.json' assert { type: 'json' }; import BullableService, { QueueHandler } from '../../base/bullable.service'; import knex from '../../common/utils/db_connection'; @@ -11,6 +11,7 @@ import { BlockCheckpoint, EVMSmartContract, EVMTransaction, + EvmEvent, EvmInternalTransaction, } from '../../models'; import { @@ -22,6 +23,9 @@ import { } from './constant'; import { ContractHelper } from './helpers/contract_helper'; +export const PROXY_EVENT_TOPIC0 = { + BEACON_UPGRADED: keccak256(toHex('BeaconUpgraded(address)')), +}; @Service({ name: SERVICE.V1.CrawlSmartContractEVM.key, version: 1, @@ -169,8 +173,22 @@ export default class CrawlSmartContractEVMService extends BullableService { ); const bytecodesByAddress: _.Dictionary = await this.getBytecodeContracts(notFoundAddresses); + const beaconContracts = ( + await EvmEvent.query() + .where('evm_tx_id', '>=', fromTx.id) + .andWhere('evm_tx_id', '<=', toTx.id) + .andWhere('topic0', PROXY_EVENT_TOPIC0.BEACON_UPGRADED) + .select('address', 'topic1') + ).map((e) => ({ + address: e.address, + beacon: `0x${e.topic1.slice(26)}`, + })); const proxyInfoByAddress: _.Dictionary = - await this.getContractsProxyInfo(notFoundAddresses, bytecodesByAddress); + await this.getContractsProxyInfo( + notFoundAddresses, + bytecodesByAddress, + beaconContracts + ); notFoundAddresses.forEach((address: string) => { const code = bytecodesByAddress[address]; // check if this address has code -> is smart contract @@ -288,7 +306,11 @@ export default class CrawlSmartContractEVMService extends BullableService { async getContractsProxyInfo( addrs: string[], - bytecodes: _.Dictionary + bytecodes: _.Dictionary, + beaconContracts: { + address: string; + beacon: string; + }[] ) { const result: _.Dictionary = {}; const proxiesInfo = await Promise.all( @@ -301,22 +323,25 @@ export default class CrawlSmartContractEVMService extends BullableService { this.contractHelper.detectProxyContractByByteCode( addr, byteCode, - EIPProxyContractSupportByteCode.EIP_1967_IMPLEMENTATION.TYPE + EIPProxyContractSupportByteCode.EIP_1967_IMPLEMENTATION.SLOT ), this.contractHelper.detectProxyContractByByteCode( addr, byteCode, - EIPProxyContractSupportByteCode.EIP_1822_IMPLEMENTATION.TYPE + EIPProxyContractSupportByteCode.EIP_1822_IMPLEMENTATION.SLOT ), this.contractHelper.detectProxyContractByByteCode( addr, byteCode, - EIPProxyContractSupportByteCode.OPEN_ZEPPELIN_IMPLEMENTATION.TYPE + EIPProxyContractSupportByteCode.OPEN_ZEPPELIN_IMPLEMENTATION.SLOT ), this.contractHelper.detectProxyContractByByteCode( addr, byteCode, - EIPProxyContractSupportByteCode.EIP_1167_IMPLEMENTATION.TYPE + EIPProxyContractSupportByteCode.EIP_1167_IMPLEMENTATION.SLOT + ), + this.contractHelper.detectBeaconProxyContract( + beaconContracts.find((e) => e.address === addr)?.beacon ), ]).catch(() => Promise.resolve(null)); }) diff --git a/src/services/evm/helpers/contract_helper.ts b/src/services/evm/helpers/contract_helper.ts index 718f852f2..fa063c529 100644 --- a/src/services/evm/helpers/contract_helper.ts +++ b/src/services/evm/helpers/contract_helper.ts @@ -1,5 +1,6 @@ import _ from 'lodash'; -import { PublicClient } from 'viem'; +import { EvmEvent, EVMSmartContract } from 'src/models'; +import { getContract, PublicClient } from 'viem'; import '../../../../fetch-polyfill.js'; import { DetectEVMProxyContract, @@ -9,6 +10,7 @@ import { NULL_BYTE_CODE, ZERO_ADDRESS, } from '../constant'; +import { PROXY_EVENT_TOPIC0 } from '../crawl_contract_evm.service.js'; export class ContractHelper { private viemClient: PublicClient; @@ -20,17 +22,13 @@ export class ContractHelper { public async detectProxyContractByByteCode( contractAddress: string, byteCode: string, - type: string, + byteCodeSlot: string, blockHeight?: number | string ): Promise { const resultReturn: DetectEVMProxyContract = { logicContractAddress: '', EIP: '', }; - const byteCodeSlot = Object.values(EIPProxyContractSupportByteCode).find( - (e) => e.TYPE === type - )?.SLOT; - if (!byteCodeSlot) throw Error('Type proxy not supported'); const result = byteCode.includes(byteCodeSlot); if (!result) throw Error('Not proxy contract!'); @@ -66,6 +64,24 @@ export class ContractHelper { return resultReturn; } + public async detectBeaconProxyContract( + beacon?: string + ): Promise { + if (!beacon) { + throw Error('Not beacon contract!'); + } + const contract = getContract({ + address: beacon as `0x${string}`, + abi: EVMSmartContract.BEACON_ABI, + client: this.viemClient, + }); + const implementation = (await contract.read.implementation()) as string; + return { + logicContractAddress: implementation, + EIP: EIPProxyContractSupportByteCode.EIP_1967_BEACON.TYPE, + }; + } + // Detect contract is proxy contract or not public async isContractProxy( contractAddress: string, @@ -82,6 +98,12 @@ export class ContractHelper { if (!byteCode) { return null; } + const beaconContract = ( + await EvmEvent.query() + .where('address', contractAddress) + .andWhere('topic0', PROXY_EVENT_TOPIC0.BEACON_UPGRADED) + .select('topic1') + ).map((e) => `0x${e.topic1.slice(26)}`); try { if (byteCodeSlot) { result = await this.detectProxyContractByByteCode( @@ -104,13 +126,7 @@ export class ContractHelper { EIPProxyContractSupportByteCode.EIP_1967_IMPLEMENTATION.SLOT, blockHeight ), - // TODO: support beacon soon. - // this.detectProxyContractByByteCode( - // contractAddress, - // byteCode, - // EIPProxyContractSupportByteCode.EIP_1967_BEACON.SLOT, - // blockHeight - // ), + this.detectBeaconProxyContract(beaconContract[0]), this.detectProxyContractByByteCode( contractAddress, byteCode, From e4476e8031bce41a0a34b422d031b1681c8c8992 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Fri, 30 Aug 2024 11:03:58 +0700 Subject: [PATCH 3/4] feat: proxy beacon --- src/models/evm_smart_contract.ts | 5 +++++ src/services/evm/crawl_contract_evm.service.ts | 7 ++----- src/services/evm/helpers/contract_helper.ts | 5 ++--- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/models/evm_smart_contract.ts b/src/models/evm_smart_contract.ts index fd63ad394..6dbc616b6 100644 --- a/src/models/evm_smart_contract.ts +++ b/src/models/evm_smart_contract.ts @@ -1,5 +1,6 @@ /* eslint-disable import/no-cycle */ import { Model } from 'objection'; +import { keccak256, toHex } from 'viem'; import BaseModel from './base'; import { EvmProxyHistory } from './evm_proxy_history'; @@ -84,6 +85,10 @@ export class EVMSmartContract extends BaseModel { }; } + static PROXY_EVENT_TOPIC0 = { + BEACON_UPGRADED: keccak256(toHex('BeaconUpgraded(address)')), + }; + static BEACON_ABI = [ { inputs: [], diff --git a/src/services/evm/crawl_contract_evm.service.ts b/src/services/evm/crawl_contract_evm.service.ts index a5be6b44b..95782d4c2 100644 --- a/src/services/evm/crawl_contract_evm.service.ts +++ b/src/services/evm/crawl_contract_evm.service.ts @@ -2,7 +2,7 @@ import { Service } from '@ourparentcenter/moleculer-decorators-extended'; import { whatsabi } from '@shazow/whatsabi'; import _, { Dictionary } from 'lodash'; import { ServiceBroker } from 'moleculer'; -import { GetBytecodeReturnType, PublicClient, keccak256, toHex } from 'viem'; +import { GetBytecodeReturnType, PublicClient, keccak256 } from 'viem'; import config from '../../../config.json' assert { type: 'json' }; import BullableService, { QueueHandler } from '../../base/bullable.service'; import knex from '../../common/utils/db_connection'; @@ -23,9 +23,6 @@ import { } from './constant'; import { ContractHelper } from './helpers/contract_helper'; -export const PROXY_EVENT_TOPIC0 = { - BEACON_UPGRADED: keccak256(toHex('BeaconUpgraded(address)')), -}; @Service({ name: SERVICE.V1.CrawlSmartContractEVM.key, version: 1, @@ -177,7 +174,7 @@ export default class CrawlSmartContractEVMService extends BullableService { await EvmEvent.query() .where('evm_tx_id', '>=', fromTx.id) .andWhere('evm_tx_id', '<=', toTx.id) - .andWhere('topic0', PROXY_EVENT_TOPIC0.BEACON_UPGRADED) + .andWhere('topic0', EVMSmartContract.PROXY_EVENT_TOPIC0.BEACON_UPGRADED) .select('address', 'topic1') ).map((e) => ({ address: e.address, diff --git a/src/services/evm/helpers/contract_helper.ts b/src/services/evm/helpers/contract_helper.ts index fa063c529..f029311b8 100644 --- a/src/services/evm/helpers/contract_helper.ts +++ b/src/services/evm/helpers/contract_helper.ts @@ -1,6 +1,6 @@ import _ from 'lodash'; -import { EvmEvent, EVMSmartContract } from 'src/models'; import { getContract, PublicClient } from 'viem'; +import { EvmEvent, EVMSmartContract } from '../../../models'; import '../../../../fetch-polyfill.js'; import { DetectEVMProxyContract, @@ -10,7 +10,6 @@ import { NULL_BYTE_CODE, ZERO_ADDRESS, } from '../constant'; -import { PROXY_EVENT_TOPIC0 } from '../crawl_contract_evm.service.js'; export class ContractHelper { private viemClient: PublicClient; @@ -101,7 +100,7 @@ export class ContractHelper { const beaconContract = ( await EvmEvent.query() .where('address', contractAddress) - .andWhere('topic0', PROXY_EVENT_TOPIC0.BEACON_UPGRADED) + .andWhere('topic0', EVMSmartContract.PROXY_EVENT_TOPIC0.BEACON_UPGRADED) .select('topic1') ).map((e) => `0x${e.topic1.slice(26)}`); try { From 880582ea3a141e2628a1989ca2f017a5a94e0df6 Mon Sep 17 00:00:00 2001 From: phamphong9981 Date: Fri, 30 Aug 2024 17:19:03 +0700 Subject: [PATCH 4/4] refactor: review --- src/services/evm/helpers/contract_helper.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/services/evm/helpers/contract_helper.ts b/src/services/evm/helpers/contract_helper.ts index f029311b8..785090b83 100644 --- a/src/services/evm/helpers/contract_helper.ts +++ b/src/services/evm/helpers/contract_helper.ts @@ -97,12 +97,16 @@ export class ContractHelper { if (!byteCode) { return null; } - const beaconContract = ( + let beaconContract = ( await EvmEvent.query() .where('address', contractAddress) .andWhere('topic0', EVMSmartContract.PROXY_EVENT_TOPIC0.BEACON_UPGRADED) + .first() .select('topic1') - ).map((e) => `0x${e.topic1.slice(26)}`); + )?.topic1.slice(26); + if (beaconContract) { + beaconContract = `0x${beaconContract}`; + } try { if (byteCodeSlot) { result = await this.detectProxyContractByByteCode( @@ -125,7 +129,7 @@ export class ContractHelper { EIPProxyContractSupportByteCode.EIP_1967_IMPLEMENTATION.SLOT, blockHeight ), - this.detectBeaconProxyContract(beaconContract[0]), + this.detectBeaconProxyContract(beaconContract), this.detectProxyContractByByteCode( contractAddress, byteCode,