Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: replaced the SEVM library with a regex-based approach for the interface matching mechanism in ERC Registry project #1115

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions tools/erc-repository-indexer/docs/registry_design.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,7 @@ classDiagram

c. **Perform Interface Signature Matching**:

- Analyze the bytecode for compliance with ERC-20 or ERC-721 standards using the [Contract.isErc()](https://github.com/acuarica/evm/blob/402028ca8c3a33dbb8498f0200d9af2efbf4f792/src/index.ts#L153-L158) method from the [SEVM library](https://www.npmjs.com/package/sevm). This method matches function signatures to confirm compliance with the ERC-20 and ERC-721 standards.
- **Limitations**: The tool may fail to identify contracts that were not compiled using Solidity or Vyper. This limitation should be considered when interpreting the results.
- Perform bytecode analysis for ERC-20 and ERC-721 compliance using a regex-based approach. This method utilizes positive lookahead assertions to match all requisite function selectors and event topics directly within the bytecode, ensuring accurate interface validation.

d. **Handle Pagination**:

Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
},
"dependencies": {
"dotenv": "^16.4.5",
"ethers": "^6.13.4",
"sevm": "^0.7.3"
"ethers": "^6.13.4"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
*
*/

import { Contract } from 'sevm';
import { ContractScannerService } from './contractScanner';
import constants from '../utils/constants';
import { ethers } from 'ethers';
Expand Down Expand Up @@ -101,9 +100,7 @@ export class ByteCodeAnalyzer {

console.log(`Analyzing contract: contractId=${contract.contract_id}`);

const sevmContract = new Contract(contractBytecode);

if (sevmContract.isERC(ERCID.ERC20)) {
if (this.isErc(ERCID.ERC20, contractBytecode)) {
const ercTokenInfoObject = await this.analyzeErcContract(
ERCID.ERC20,
contract,
Expand All @@ -115,7 +112,7 @@ export class ByteCodeAnalyzer {
}
}

if (sevmContract.isERC(ERCID.ERC721)) {
if (this.isErc(ERCID.ERC721, contractBytecode)) {
const ercTokenInfoObject = await this.analyzeErcContract(
ERCID.ERC721,
contract,
Expand Down Expand Up @@ -232,4 +229,17 @@ export class ByteCodeAnalyzer {
...ercTokenInfoObject,
} as ERC20OutputInterface | ERC721OutputInterface;
}

/**
* Checks if the given bytecode matches the specified ERC standard based on its signature.
*
* @param {ERCID} ercId - The identifier for the ERC standard (e.g., ERC-20, ERC-721).
* @param {string} bytecode - The bytecode of the contract to be checked.
* @returns {boolean} - Returns true if the bytecode matches the signature pattern for the specified ERC standard, otherwise false.
*/
private isErc(ercId: ERCID, bytecode: string): boolean {
const ercSignatureRegexPattern =
constants.ERC_STANDARD_SIGNATURE_REGEX[ercId];
return ercSignatureRegexPattern.test(bytecode);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,58 @@ export default {
sighash: '0x95d89b41',
},
],
ERC_STANDARD_SIGNATURE_REGEX: {
/**
* The regex pattern for identifying an ERC-20 bytecode.
* Built on top of the following method and event signatures:
*
* Selectors (Methods):
* - 'dd62ed3e': allowance(address _owner, address _spender) view returns (uint256 remaining)
* - '095ea7b3': approve(address _spender, uint256 _value) returns (bool success)
* - '70a08231': balanceOf(address _owner) view returns (uint256 balance)
* - '18160ddd': totalSupply() view returns (uint256)
* - 'a9059cbb': transfer(address _to, uint256 _value) returns (bool success)
* - '23b872dd': transferFrom(address _from, address _to, uint256 _value) returns (bool success)
*
* Topics (Events):
* - '8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925': Approval(address indexed _owner, address indexed _spender, uint256 _value)
* - 'ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef': Transfer(address indexed _from, address indexed _to, uint256 _value)
*
* source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.1.0/contracts/token/ERC20/IERC20.sol
*
* note: The `(?=...)` (positive lookahead operator) is used here to ensure that ALL the necessary selectors and topics exist at least once in the bytecode.
* It doesn't consume characters but asserts that the given pattern (selector or topic) can be found somewhere in the bytecode.
*/
ERC20:
/(?=.*dd62ed3e)(?=.*095ea7b3)(?=.*70a08231)(?=.*18160ddd)(?=.*a9059cbb)(?=.*23b872dd)(?=.*8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925)(?=.*ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef)/,

/**
* The regex pattern for identifying an ERC-721 bytecode.
* Built on top of the following method and event signatures:
*
* Selectors (Methods):
* - '095ea7b3': approve(address _approved, uint256 _tokenId) payable
* - '70a08231': balanceOf(address _owner) view returns (uint256)
* - '081812fc': getApproved(uint256 _tokenId) view returns (address)
* - 'e985e9c5': isApprovedForAll(address _owner, address _operator) view returns (bool)
* - '6352211e': ownerOf(uint256 _tokenId) view returns (address)
* - '42842e0e': safeTransferFrom(address _from, address _to, uint256 _tokenId) payable
* - 'b88d4fde': safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) payable
* - 'a22cb465': setApprovalForAll(address _operator, bool _approved)
* - '01ffc9a7': supportsInterface(bytes4 interfaceID) view returns (bool)
* - '23b872dd': transferFrom(address _from, address _to, uint256 _tokenId) payable
*
* Topics (Events):
* - '8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925': Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId)
* - '17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31': ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved)
* - 'ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef': Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId)
*
* source: https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v5.1.0/contracts/token/ERC721/IERC721.sol
*
* note: The `(?=...)` (positive lookahead operator) is used here to ensure that ALL the necessary selectors and topics exist at least once in the bytecode.
* It doesn't consume characters but asserts that the given pattern (selector or topic) can be found somewhere in the bytecode.
*/
ERC721:
/(?=.*095ea7b3)(?=.*70a08231)(?=.*081812fc)(?=.*e985e9c5)(?=.*6352211e)(?=.*42842e0e)(?=.*b88d4fde)(?=.*a22cb465)(?=.*01ffc9a7)(?=.*23b872dd)(?=.*8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925)(?=.*17307eab39ab6107e8899845ad3d59bd9653f200f220920489ca2b5937696c31)(?=.*ddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef)/,
},
};
Loading
Loading