Skip to content

Commit

Permalink
feat: proxy beacon
Browse files Browse the repository at this point in the history
  • Loading branch information
phamphong9981 committed Aug 30, 2024
1 parent 929bff5 commit 8821c75
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 24 deletions.
17 changes: 17 additions & 0 deletions src/models/evm_smart_contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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',
};
}

Expand All @@ -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',
},
];
}
8 changes: 4 additions & 4 deletions src/services/evm/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
39 changes: 32 additions & 7 deletions src/services/evm/crawl_contract_evm.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -11,6 +11,7 @@ import {
BlockCheckpoint,
EVMSmartContract,
EVMTransaction,
EvmEvent,
EvmInternalTransaction,
} from '../../models';
import {
Expand All @@ -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,
Expand Down Expand Up @@ -169,8 +173,22 @@ export default class CrawlSmartContractEVMService extends BullableService {
);
const bytecodesByAddress: _.Dictionary<GetBytecodeReturnType> =
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<DetectEVMProxyContract | null> =
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
Expand Down Expand Up @@ -288,7 +306,11 @@ export default class CrawlSmartContractEVMService extends BullableService {

async getContractsProxyInfo(
addrs: string[],
bytecodes: _.Dictionary<GetBytecodeReturnType>
bytecodes: _.Dictionary<GetBytecodeReturnType>,
beaconContracts: {
address: string;
beacon: string;
}[]
) {
const result: _.Dictionary<DetectEVMProxyContract | null> = {};
const proxiesInfo = await Promise.all(
Expand 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));
})
Expand Down
42 changes: 29 additions & 13 deletions src/services/evm/helpers/contract_helper.ts
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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;
Expand All @@ -20,17 +22,13 @@ export class ContractHelper {
public async detectProxyContractByByteCode(
contractAddress: string,
byteCode: string,
type: string,
byteCodeSlot: string,
blockHeight?: number | string
): Promise<DetectEVMProxyContract> {
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!');
Expand Down Expand Up @@ -66,6 +64,24 @@ export class ContractHelper {
return resultReturn;
}

public async detectBeaconProxyContract(
beacon?: string
): Promise<DetectEVMProxyContract> {
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,
Expand All @@ -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(
Expand All @@ -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,
Expand Down

0 comments on commit 8821c75

Please sign in to comment.