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:42 → this.abiManager = new ABIService(this.network) (network is passed in, from config.network)
src/utils/base_token.ts:38 → const abi = contractParam.abi || this.client.abiManager.getABI(contractParam.name)
src/lxly/bridge_extension.ts:15 → BridgeExtension 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.js → network: 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)
Summary
ABIService.getABI()always returns the testnet ABI regardless of the configured network. The mainnet ABI import is commented out and thenetworkvalue 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:Why this reaches mainnet contract calls
getABIis the live ABI-loading path:src/utils/web3_side_chain_client.ts:42→this.abiManager = new ABIService(this.network)(network is passed in, fromconfig.network)src/utils/base_token.ts:38→const abi = contractParam.abi || this.client.abiManager.getABI(contractParam.name)src/lxly/bridge_extension.ts:15→BridgeExtensionsetsname: 'BridgeExtension'and passes no explicitabi, so it resolves viagetABI('BridgeExtension').config.networkis the canonical'mainnet'/'testnet'string (seesrc/config.tsproof-API map andexamples/config.js→network: process.env.NETWORK || 'testnet').Concrete divergence
BridgeExtension.bridgeAndCalldiffers between the two ABIs:token, amount, permitData, destinationNetwork, callAddress, callDatatoken, amount, destinationNetwork, callAddress, fallbackAddress, callData, forceUpdateGlobalExitRoot(nopermitData)On mainnet,
BridgeExtensionis instantiated with the testnet ABI, sobridgeAndCallencodes against the wrong signature → revert or mis-interpreted arguments (e.g. apermitDatabytes value encoded into thedestinationNetworkuint32 position).src/lxly/bridge_extension.ts:73-85builds the call with thepermitDataargument present, matching the testnet shape.Both
BridgeExtensionABIs exist and are exported (src/abis/index.ts,src/abis/{mainnet,testnet}/index.ts), and mainnet is a supported target (error_type.tsAllowedOnMainnet, 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:
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