Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
90aad8a
feat: implement contract diff check tool (#587)
s2imonovic Oct 6, 2025
6b9d7c0
ci: add develop branch to trigger CI on push (#591)
lumtis Oct 13, 2025
e62852c
fix: remove ZETA onCall() from UniversalContract (#597)
s2imonovic Oct 20, 2025
51631dc
chore: fix X account (#600)
CryptoFewka Oct 21, 2025
09ac0d6
chore: set package name to protocol-contracts-evm (#596)
fadeev Oct 22, 2025
1ef5a5b
feat: multiple evm calls in single tx (#603)
s2imonovic Oct 22, 2025
f310b0a
refactor: add imports to UniversalContract + remove payable modifier …
s2imonovic Oct 22, 2025
71a0bf7
feat: (GatewayZEVM) burn tokens when paying for GAS during outbounds …
s2imonovic Oct 23, 2025
307038e
Merge remote-tracking branch 'origin/main' into release/v15
s2imonovic Oct 23, 2025
95c820d
fix: generate
s2imonovic Oct 23, 2025
d48d1ba
chore: rename go module into `protocol-contracts-evm` (#595)
lumtis Oct 23, 2025
a920f13
conflicts
lumtis Oct 23, 2025
c9a6cc8
fix: generate
s2imonovic Oct 23, 2025
e2e9cdc
chore: fix lint generation (#609)
lumtis Oct 24, 2025
556ed43
chore: fix review comments + add testnet configs (#592)
s2imonovic Oct 27, 2025
8d44a76
Merge remote-tracking branch 'origin/develop' into release/v15
s2imonovic Oct 30, 2025
159516f
feat: enable Sui mainnet support for withdrawAndCall (#614)
s2imonovic Oct 30, 2025
0dfdcad
Merge remote-tracking branch 'origin/develop' into release/v15
s2imonovic Oct 30, 2025
55ef5a1
Merge remote-tracking branch 'origin/main' into release/v15
s2imonovic Oct 30, 2025
4a051a2
fix: generate
s2imonovic Oct 30, 2025
4d6625a
fix: generate
s2imonovic Oct 30, 2025
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: 0 additions & 3 deletions contracts/zevm/GatewayZEVM.sol
Original file line number Diff line number Diff line change
Expand Up @@ -246,9 +246,6 @@ contract GatewayZEVM is
if (callOptions.gasLimit < MIN_GAS_LIMIT) revert InsufficientGasLimit();
if (message.length + revertOptions.revertMessage.length > MAX_MESSAGE_SIZE) revert MessageSizeExceeded();

// Sui mainnet not supported for now
require(IZRC20(zrc20).CHAIN_ID() != 105);

uint256 gasFee = _withdrawZRC20WithGasLimit(amount, zrc20, callOptions.gasLimit);
emit WithdrawnAndCalled(
msg.sender,
Expand Down
2 changes: 1 addition & 1 deletion data/addresses.testnet.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"type": "zetaToken"
},
{
"address": "0x6b2fe12c605d64e14ca69f9aba51550593ba92ff43376d0a6cc26a5ca226f9bd,0x6fc08f682551e52c2cc34362a20f744ba6a3d8d17f6583fa2f774887c4079700",
"address": "0x28acc3a03af7658e52456617ac5ba6933ebf8dfb03469697b3673577a4262e24,0x6fc08f682551e52c2cc34362a20f744ba6a3d8d17f6583fa2f774887c4079700,0x01f724edef5461e280e533649b08c191a39af5f677025a226664a6626373b393,0x6b2fe12c605d64e14ca69f9aba51550593ba92ff43376d0a6cc26a5ca226f9bd,0x6b2fe12c605d64e14ca69f9aba51550593ba92ff43376d0a6cc26a5ca226f9bd",
"category": "omnichain",
"chain_id": 103,
"chain_name": "sui_testnet",
Expand Down
2 changes: 1 addition & 1 deletion pkg/gatewayevmzevm.t.sol/gatewayevmzevmtest.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/gatewayzevm.sol/gatewayzevm.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/gatewayzevm.t.sol/gatewayzevminboundtest.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/gatewayzevm.t.sol/gatewayzevmoutboundtest.go

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion pkg/zrc20.t.sol/zrc20test.go

Large diffs are not rendered by default.

127 changes: 123 additions & 4 deletions scripts/contractDiff/contractDiffTool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,42 @@ const NETWORK_CONFIGS: Record<string, NetworkConfig> = {
apiUrl: "https://zetachain.blockscout.com/api",
explorerType: "blockscout",
},
sepolia_testnet: {
name: "Ethereum Sepolia",
apiUrl: "https://api.etherscan.io/v2/api?chainid=11155111",
apiKeyEnv: process.env.ETHERSCAN_API_KEY!,
explorerType: "etherscan",
},
bsc_testnet: {
name: "BNB Smart Chain Testnet",
apiUrl: "https://api.etherscan.io/v2/api?chainid=97",
apiKeyEnv: process.env.ETHERSCAN_API_KEY!,
explorerType: "etherscan",
},
amoy_testnet: {
name: "Polygon Amoy",
apiUrl: "https://api.etherscan.io/v2/api?chainid=80002",
apiKeyEnv: process.env.ETHERSCAN_API_KEY!,
explorerType: "etherscan",
},
base_sepolia: {
name: "Base Sepolia",
apiUrl: "https://api.etherscan.io/v2/api?chainid=84532",
apiKeyEnv: process.env.ETHERSCAN_API_KEY!,
explorerType: "etherscan",
},
arbitrum_sepolia: {
name: "Arbitrum Sepolia",
apiUrl: "https://api.etherscan.io/v2/api?chainid=421614",
apiKeyEnv: process.env.ETHERSCAN_API_KEY!,
explorerType: "etherscan",
},
avalanche_testnet: {
name: "Avalanche Fuji",
apiUrl: "https://api.etherscan.io/v2/api?chainid=43113",
apiKeyEnv: process.env.ETHERSCAN_API_KEY!,
explorerType: "etherscan",
}
}

interface ContractSource {
Expand Down Expand Up @@ -94,6 +130,15 @@ interface BlockscoutResponse {
abi?: string;
}

/**
* Fetches contract source code from Blockscout explorer API.
* Converts the Blockscout response format to match Etherscan's format for consistency.
*
* @param address - The contract address to fetch
* @param apiUrl - The Blockscout API base URL
* @returns ContractSource object with source code and metadata
* @throws Error if contract is not verified or fetch fails
*/
async function fetchFromBlockscout(
address: string,
apiUrl: string
Expand Down Expand Up @@ -145,18 +190,27 @@ async function fetchFromBlockscout(
} catch (error) {
if (axios.isAxiosError(error)) {
if (error.response?.status === 404) {
throw new Error("Contract not found or not verified on Blockscout");
throw new Error(`Contract not found or not verified on Blockscout: ${error.message}`);
}
throw new Error(`Failed to fetch contract from Blockscout: ${error.message}`);
}
throw error;
}
}

/**
* Fetches contract source code from Etherscan-compatible API.
*
* @param address - The contract address to fetch
* @param apiUrl - The Etherscan API URL with chainid
* @param apiKey - The Etherscan API key for authentication
* @returns ContractSource object with source code and metadata
* @throws Error if contract is not verified or fetch fails
*/
async function fetchFromEtherscan(
address: string,
apiUrl: string,
apiKey: string
address: string,
apiUrl: string,
apiKey: string
): Promise<ContractSource> {
try {
const response = await axios.get<EtherscanResponse>(apiUrl, {
Expand Down Expand Up @@ -194,6 +248,15 @@ async function fetchFromEtherscan(
}
}

/**
* Fetches contract source code from the appropriate explorer based on network configuration.
* Routes to either Blockscout or Etherscan API based on the explorer type.
*
* @param address - The contract address to fetch
* @param network - The network identifier
* @returns ContractSource object with source code and metadata
* @throws Error if network is unknown or fetch fails
*/
async function fetchContractSource(
address: string,
network: string
Expand All @@ -214,6 +277,13 @@ async function fetchContractSource(
}
}

/**
* Extracts the declaration order of contracts, interfaces, and libraries from Solidity source code.
* Used to maintain consistent ordering when flattening multi-file contracts.
*
* @param sourceCode - The Solidity source code to analyze
* @returns Array of contract/interface/library names in order of declaration
*/
function extractDeclarationOrder(sourceCode: string): string[] {
const order: string[] = [];

Expand All @@ -228,6 +298,13 @@ function extractDeclarationOrder(sourceCode: string): string[] {
return order;
}

/**
* Finds the file content that contains a specific contract, interface, or library declaration.
*
* @param sources - Object containing file paths/names as keys and file content as values
* @param contractName - The name of the contract/interface/library to find
* @returns The file content containing the contract, or null if not found
*/
function findFileByContractName(sources: any, contractName: string): string | null {
for (const [, fileData] of Object.entries(sources)) {
const content = typeof fileData === "object" && fileData !== null
Expand All @@ -243,6 +320,13 @@ function findFileByContractName(sources: any, contractName: string): string | nu
return null;
}

/**
* Removes duplicate SPDX license identifiers and pragma statements from flattened code.
* Keeps only the first occurrence of each to avoid compilation errors.
*
* @param code - The Solidity source code to clean
* @returns Cleaned source code with duplicate headers removed
*/
function cleanDuplicateHeaders(code: string): string {
const lines = code.split('\n');
const result: string[] = [];
Expand Down Expand Up @@ -276,6 +360,14 @@ function cleanDuplicateHeaders(code: string): string {
return result.join('\n');
}

/**
* Flattens multi-file contract source code into a single file.
* Handles both standard JSON format and double-braced format from explorers.
* Files are concatenated in alphabetical order and duplicate headers are removed.
*
* @param sourceCode - The source code
* @returns Flattened source code as a single string
*/
function flattenSourceCode(sourceCode: string): string {
if (sourceCode.startsWith("{{") || sourceCode.startsWith("{")) {
try {
Expand Down Expand Up @@ -308,6 +400,14 @@ function flattenSourceCode(sourceCode: string): string {
return sourceCode;
}

/**
* Reorders multi-file contract source to match the declaration order of a single-file version.
* This ensures consistent ordering when comparing contracts that have different file structures.
*
* @param singleFileCode - The single-file version used as reference for ordering
* @param multiFileCode - The multi-file version source code to reorder
* @returns Reordered and flattened source code matching the single-file order
*/
function reorderMultiFileToMatchSingleFile(
singleFileCode: string,
multiFileCode: string
Expand Down Expand Up @@ -366,6 +466,15 @@ function reorderMultiFileToMatchSingleFile(
}
}

/**
* Saves contract source code to a file in the specified output directory.
* Creates the directory if it doesn't exist.
*
* @param content - The contract source code to save
* @param fileName - The name of the file to create
* @param outputDir - The directory where the file should be saved
* @returns The full path to the saved file
*/
function saveContractToFile(
content: string,
fileName: string,
Expand All @@ -381,6 +490,16 @@ function saveContractToFile(
return filePath;
}

/**
* Main function to fetch, flatten, and compare two contract implementations.
* Fetches both contracts, handles single vs multi-file structures,
* flattens them for comparison, and saves the results to disk.
*
* @param oldAddress - The address of the old/previous contract implementation
* @param newAddress - The address of the new/updated contract implementation
* @param network - The network identifier where the contracts are deployed
* @throws Error if fetching or processing fails
*/
export async function fetchAndFlattenContract(
oldAddress: string,
newAddress: string,
Expand Down
2 changes: 1 addition & 1 deletion types/factories/GatewayZEVM__factory.ts

Large diffs are not rendered by default.