Skip to Content
Code Wikirepos — cfx-solidity

repos — cfx-solidity

repos/cfx-solidity

Solidity pipeline for Conflux development — standard ABIs, contract bindings, and compiler tooling. Independent carve-out from cfx-core, designed for framework-stable consumption.

Overview

This monorepo contains two primary packages:

  1. @cfxdevkit/abis: Standard EVM ABI shapes (ERC-20/721/1155/4626, Multicall3) and Swappi V2 interfaces.
  2. @cfxdevkit/contracts: Typed helpers for reading, writing, deploying, and bridging contracts on Conflux eSpace and Core Space.

Both packages are published separately but developed in tandem. The abis package has zero dependencies on the cfxdevkit framework, making it safe to depend on from any layer (including @cfxdevkit/cdk). The contracts layer builds atop abis and @cfxdevkit/cdk to provide a convenient surface for contract interactions.

Package: @cfxdevkit/abis

Standard EVM ABI shapes re-exported from viem under framework-stable aliases. Includes core token standards, common extensions, and Conflux-specific Swappi V2 bindings.

Key Features

  • Canonical Sources: ABIs are imported directly from viem (which tracks official EIPs) to ensure alignment with upstream standards.
  • Framework-Stable Aliases: Both uppercase constants (e.g., ERC20_ABI) and camelCase aliases (e.g., erc20Abi) are exported for consistency.
  • Extended Interfaces: Includes OpenZeppelin 5.x extensions commonly used in DevKit templates (e.g., ERC20_EXTENDED_ABI with mint, burn, pausable, and role management).
  • Swappi V2 Support: Full Uniswap V2-compatible ABIs for Swappi on Conflux eSpace, including factory, pair, and router interfaces, plus deployed addresses for mainnet (1030) and testnet (71).
  • Zero cfxdevkit Dependencies: Safe to use from @cfxdevkit/cdk or any other layer without pulling in contract execution machinery.

File Structure

src/ ├─ erc20.ts # ERC-20, ERC-2612 (permit), and extended variants ├─ erc721.ts # ERC-721, enumerable, royalty (ERC-2981), and extended variants ├─ erc1155.ts # Full ERC-1155 interface ├─ erc4626.ts # ERC-4626 tokenized vault ├─ erc165.ts # Minimal ERC-165 interface detection ├─ multicall3.ts # Multicall3 ABI and canonical address └─ swappi/ # Swappi V2 (Uniswap V2 fork) for Conflux eSpace ├─ factory.ts # IUniswapV2Factory ├─ pair.ts # IUniswapV2Pair └─ router.ts # IUniswapV2Router02

Usage

import { ERC20_ABI, erc20Abi } from '@cfxdevkit/abis'; // Both point to the same viem-sourced ABI console.log(ERC20_ABI === erc20Abi); // true import { SWAPPI_FACTORY_ABI, SWAPPI_FACTORY_ADDRESS } from '@cfxdevkit/abis'; // Use with viem to create a contract instance import { createPublicClient, http } from 'viem'; const client = createPublicClient({ chain: mainnet, // or your Conflux eSpace chain transport: http(), }); const factory = client.getContract({ address: SWAPPI_FACTORY_ADDRESS[1030], // mainnet abi: SWAPPI_FACTORY_ABI, });

Testing

Run pnpm test in the packages/abis directory to validate:

  • ABI shapes are arrays
  • CamelCase aliases point to uppercase constants
  • Required functions are present in each ABI
  • Multicall3 address matches the canonical deployment

Package: @cfxdevkit/contracts

Standard contract bindings and a thin read/write/deploy surface for @cfxdevkit/cdk. Provides typed helpers for interacting with ERC-20/721/1155 contracts, multicall3, bridging, and deployment on both Conflux eSpace and Core Space.

Key Features

  • Chain-Agnostic Helpers: Functions automatically select the correct RPC method (eth_call/eth_sendRawTransaction for eSpace, cfx_call/cfx_sendRawTransaction for Core) based on client.family.
  • Typed Surface: Leverages TypeScript and viem for ABI-safe encoding/decoding.
  • Modular Design: Split into focused sub-packages:
    • abis: Back-compat re-exports of @cfxdevkit/abis
    • read: Typed contract reads (readContract)
    • write: Typed contract writes (sendWrite, prepareWrite)
    • deploy: Contract deployment (deployContract)
    • erc20: Nested ERC-20 specific helpers
    • bridge: Cross-space transfers between Conflux Core and eSpace
    • errors: Stable error codes for programmatic handling
  • No Framework Lock-in: While built for @cfxdevkit/cdk, only depends on its client/signer interfaces (not implementation).

File Structure

src/ ├─ abis/ # Back-compat re-exports of @cfxdevkit/abis ├─ read/ # readContract (eSpace/Core Space aware) ├─ write/ # sendWrite, prepareWrite, receipt handling ├─ deploy/ # deployContract (Core/eSpace specific logic) ├─ erc20/ # Typed ERC-20 helpers (balanceOf, transfer, etc.) ├─ bridge/ # Core <-> eSpace transfers via CrossSpaceCall ├─ errors/ # ContractsError with stable codes └─ index.ts # Barrel export (discouraged for tree-shaking)

Key Functions

readContract

Typed contract read that works on both chains:

import { readContract } from '@cfxdevkit/contracts/read'; import { ERC20_ABI } from '@cfxdevkit/abis'; const balance = await readContract({ client, // from @cfxdevkit/cdk address: '0x...', // eSpace or 'cfx:...' for Core abi: ERC20_ABI, functionName: 'balanceOf', args: ['0x...'], });

sendWrite

Typed contract write with automatic gas/fee estimation:

import { sendWrite } from '@cfxdevkit/contracts/write'; import { ERC20_ABI } from '@cfxdevkit/abis'; const { hash } = await sendWrite({ client, signer, // from @cfxdevkit/cdk address: '0x...', abi: ERC20_ABI, functionName: 'transfer', args: ['0x...', 1n * 10n ** 18n], waitForReceipt: true, // optional });

deployContract

Deploys a contract bytecode with ABI, handling Core/eSpace differences:

import { deployContract } from '@cfxdevkit/contracts/deploy'; import { ERC20_ABI } from '@cfxdevkit/abis'; const { hash, address } = await deployContract({ client, signer, abi: ERC20_ABI, bytecode: '0x...', // compiled contract bytecode args: [initialSupply], // constructor args waitForReceipt: true, });

erc20

Nested helpers for common ERC-20 operations:

import { erc20 } from '@cfxdevkit/contracts/erc20'; const name = await erc20.name({ client, address: tokenAddress }); const balance = await erc20.balanceOf( { client, address: tokenAddress }, userAddress ); const { hash } = await erc20.transfer( { client, address: tokenAddress, signer }, recipient, amount );

bridge

Cross-space transfers between Conflux Core and eSpace:

import { transferToEspace, getMappedBalance } from '@cfxdevkit/contracts/bridge'; // Core to eSpace await transferToEspace({ client, // Core Space client signer, // dual-address signer to: eSpaceAddress, // 0x... address value: amount, }); // Query mapped balance on Core const balance = await getMappedBalance({ client, coreHexAddress: coreAddress, // 0x... Core address });

Error Handling

All errors are ContractsError instances with a stable code field:

import { ContractsError } from '@cfxdevkit/contracts/errors'; try { await sendWrite(...); } catch (err) { if (err instanceof ContractsError) { switch (err.code) { case 'contracts/reverted': // handle revert break; case 'contracts/receipt-timeout': // handle timeout break; // ... } } }

Testing

Run pnpm test in packages/contracts to validate:

  • Chain-specific RPC calls (eSpace vs Core)
  • Proper address validation (0x hex for eSpace, base32 for Core)
  • Encoding/decoding correctness
  • Error wrapping and propagation
  • Deployment flows (mocked signers/clients)

Usage Example: Full ERC-20 Flow

import { createPublicClient, http } from 'viem'; import { privateKeyToAccount } from '@cfxdevkit/cdk'; import { erc20 } from '@cfxdevkit/contracts/erc20'; // Setup clients (example for Conflux eSpace testnet) const espaceClient = createPublicClient({ chain: { id: 71, name: 'Conflux eSpace Testnet', nativeToken: { symbol: 'CFX', decimals: 18 }, rpc: { http: ['http://espace-testnet.rpc'] }, }, transport: http(), }); const coreClient = /* ... similar for Core Space ... */; // Setup signer (dual-address for Cross Space calls) const account = privateKeyToAccount('0x...'); // must have coreAddress set for bridge // Read token metadata const [name, symbol, decimals] = await Promise.all([ erc20.name({ client: espaceClient, address: tokenAddress }), erc20.symbol({ client: espaceClient, address: tokenAddress }), erc20.decimals({ client: espaceClient, address: tokenAddress }), ]); // Check balance const balance = await erc20.balanceOf( { client: espaceClient, address: tokenAddress }, account.address ); // Transfer tokens const { hash } = await erc20.transfer( { client: espaceClient, address: tokenAddress, signer: account }, recipient, amount );

Development

Prerequisites

  • Node.js >=22
  • pnpm >=10

Commands

# Install dependencies pnpm install # Build all packages pnpm run -r build # Run tests pnpm run -r test # Typecheck pnpm run -r typecheck # Lint pnpm run -r lint

Dependency Validation

The module includes scripts to verify workspace consistency:

  • scripts/check-deps.mjs: Ensures all workspace:* dependencies are declared within the repo
  • scripts/validate-workspace.mjs: Confirms pnpm-workspace.template.yaml matches actual package structure

Architecture Notes

Chain Abstraction

The contracts layer abstracts over Conflux’s two execution environments:

  • eSpace: Ethereum-compatible VM (uses eth_* RPC)
  • Core Space: Native Conflux VM (uses cfx_* RPC)

Helpers like readContract and sendWrite inspect client.family to dispatch the correct RPC methods, while address validation ensures eSpace uses 0x hex and Core Space uses base32 (cfx:.../cfxtest:...).

Why Separate ABIs Package?

The @cfxdevkit/abis package exists to:

  1. Provide a stable, zero-dependency source of ABI shapes
  2. Allow @cfxdevkit/cdk and other low-level layers to depend on ABIs without pulling in contract execution logic
  3. Enable independent versioning of ABI definitions from contract helpers

Swappi V2 Specifics

Swappi V2 is a Uniswap V2 fork deployed on Conflux eSpace. The ABIs follow the canonical interfaces, but addresses are chain-specific:

  • Mainnet (1030): Factory, Router, and WCFX addresses as defined in swappi.ts
  • Testnet (71): Separate set of testnet addresses Always verify addresses against official Swappi documentation  before production use.

See Also

Last updated on