Skip to content

ABIService.getABI() ignores network and always returns testnet ABIs (mainnet contract calls use wrong ABI) #64

Description

@Nexory

Summary

ABIService.getABI() always returns the testnet ABI regardless of the configured network. The mainnet ABI import is commented out and the network value passed to the constructor is never used. As a result, when the SDK is configured for mainnet, contracts are instantiated with testnet ABIs — and for any contract whose mainnet/testnet ABI differs, this produces incorrectly-encoded calldata.

Location

src/services/abi_service.ts:

import testnetAbis from "../abis/testnet";
// import mainnetAbis from "../abis/mainnet";   <-- commented out

export class ABIService {
    network: string;

    constructor(network: string) {
        this.network = network;   // stored but never used
    }

    getABI(contractName: string) {
        return testnetAbis[contractName];   // always testnet
    }
}

Why this reaches mainnet contract calls

getABI is the live ABI-loading path:

  • src/utils/web3_side_chain_client.ts:42this.abiManager = new ABIService(this.network) (network is passed in, from config.network)
  • src/utils/base_token.ts:38const abi = contractParam.abi || this.client.abiManager.getABI(contractParam.name)
  • src/lxly/bridge_extension.ts:15BridgeExtension sets name: 'BridgeExtension' and passes no explicit abi, so it resolves via getABI('BridgeExtension').

config.network is the canonical 'mainnet' / 'testnet' string (see src/config.ts proof-API map and examples/config.jsnetwork: process.env.NETWORK || 'testnet').

Concrete divergence

BridgeExtension.bridgeAndCall differs between the two ABIs:

  • testnet inputs: token, amount, permitData, destinationNetwork, callAddress, callData
  • mainnet inputs: token, amount, destinationNetwork, callAddress, fallbackAddress, callData, forceUpdateGlobalExitRoot (no permitData)

On mainnet, BridgeExtension is instantiated with the testnet ABI, so bridgeAndCall encodes against the wrong signature → revert or mis-interpreted arguments (e.g. a permitData bytes value encoded into the destinationNetwork uint32 position). src/lxly/bridge_extension.ts:73-85 builds the call with the permitData argument present, matching the testnet shape.

Both BridgeExtension ABIs exist and are exported (src/abis/index.ts, src/abis/{mainnet,testnet}/index.ts), and mainnet is a supported target (error_type.ts AllowedOnMainnet, README describes the Polygon LxLy mainnet bridge) — so this looks like incomplete network wiring rather than an intentional testnet-only design.

Suggested fix

Re-enable the mainnet import and select by the network the service is already given:

import testnetAbis from "../abis/testnet";
import mainnetAbis from "../abis/mainnet";

getABI(contractName: string) {
    const abis = this.network === 'mainnet' ? mainnetAbis : testnetAbis;
    return abis[contractName];
}

Note this changes ABI resolution for all mainnet contract interactions, so it's worth validating each mainnet ABI against the deployed contracts before release — hence filing as an issue rather than a direct PR. Happy to open a PR if the approach looks right.

Environment

  • repo @ current main (2026-04-22)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions