Skip to content
Draft
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
20 changes: 20 additions & 0 deletions src/lxly/bridge/abi/bridge-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* Bridge Adapter ABI
*
* Polygon ZkEVM Bridge Adapter contract ABI for custom ERC20 bridging
*/

export const bridgeAdapterAbi = [
{
inputs: [
{ internalType: 'address', name: 'recipient', type: 'address' },
{ internalType: 'uint256', name: 'amount', type: 'uint256' },
{ internalType: 'uint32', name: 'destinationNetworkId', type: 'uint32' },

Copilot AI Sep 8, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ABI defines destinationNetworkId as uint32 but the TypeScript interface uses number. Consider using a more specific type or adding validation to ensure the number fits within uint32 range (0 to 4,294,967,295).

Copilot uses AI. Check for mistakes.
{ internalType: 'bool', name: 'forceUpdateGlobalExitRoot', type: 'bool' },
],
name: 'bridgeToken',
outputs: [],
stateMutability: 'nonpayable',
type: 'function',
},
] as const;
8 changes: 8 additions & 0 deletions src/lxly/bridge/abi/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Bridge ABI Exports
*
* Central export point for all bridge-related ABIs
*/

export { bridgeAbi } from './bridge';
export { bridgeAdapterAbi } from './bridge-adapter';
61 changes: 61 additions & 0 deletions src/lxly/bridge/bridge-adapter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/**
* Bridge Adapter Implementation
*
* Custom ERC20 bridging functionality using bridge adapter contract
*/

import { encodeFunctionData, type Address } from 'viem';
import { ValidationUtils } from '../utils';
import { BaseContract } from '../base/contract';
import type { BridgeTokenParams, TransactionParams } from '../../types';
import { bridgeAdapterAbi } from './abi/bridge-adapter';

export interface BridgeAdapterConfig {
bridgeAdapterAddress: string;
rpcUrl: string;
chainId: number;
}

export class BridgeAdapter extends BaseContract {
private bridgeAdapterAddress: string;

constructor(config: BridgeAdapterConfig) {
super({ rpcUrl: config.rpcUrl, chainId: config.chainId });
this.bridgeAdapterAddress = config.bridgeAdapterAddress;
}

/**
* Build bridge token transaction for custom ERC20
*/
async buildBridgeToken(
params: BridgeTokenParams,
from?: string
): Promise<TransactionParams> {
ValidationUtils.validateAddress(params.recipient, 'Recipient address');
ValidationUtils.validateAmount(params.amount, 'Amount');

const data = encodeFunctionData({
abi: bridgeAdapterAbi,
functionName: 'bridgeToken',
args: [
params.recipient as Address,
BigInt(params.amount),

Copilot AI Sep 8, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The amount parameter is validated as a string but directly converted to BigInt without error handling. Consider adding validation to ensure the string is a valid numeric format before conversion, or handle potential conversion errors.

Suggested change
BigInt(params.amount),
(() => {
// Ensure params.amount is a valid non-negative integer string
if (!/^\d+$/.test(params.amount)) {
throw new Error(`Amount must be a valid non-negative integer string, got: "${params.amount}"`);
}
try {
return BigInt(params.amount);
} catch (e) {
throw new Error(`Failed to convert amount to BigInt: ${e instanceof Error ? e.message : String(e)}`);
}
})(),

Copilot uses AI. Check for mistakes.
params.destinationNetworkId,
params.forceUpdateGlobalExitRoot,
],
});

const [nonce, gas] = await Promise.all([
this.getNonce(from),
this.estimateGas(data, this.bridgeAdapterAddress, from),
]);

return {
from,
to: this.bridgeAdapterAddress,
data,
gas,
nonce,
};
}
}
9 changes: 9 additions & 0 deletions src/lxly/bridge/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/**
* Bridge Exports
*
* Central export point for all bridge-related functionality
*/

export { Bridge } from './bridge';
export { BridgeAdapter } from './bridge-adapter';
export { BridgeUtil } from './util';
1 change: 1 addition & 0 deletions src/lxly/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface LxlyConfig {

export { ERC20 } from './tokens/erc20';
export { Bridge } from './bridge/bridge';
export { BridgeAdapter } from './bridge/bridge-adapter';
export { BridgeUtil } from './bridge/util';

export class LxlyClient {
Expand Down
36 changes: 36 additions & 0 deletions src/lxly/tokens/erc20.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
buildTransferFrom as buildTransferFromTx,
} from './build';
import { Bridge } from '../bridge/bridge';
import { BridgeAdapter } from '../bridge/bridge-adapter';
import { chainRegistry } from '../../chains/registry';
import { getAbi } from '../services/abi';

Expand Down Expand Up @@ -174,6 +175,41 @@ export class ERC20 extends BaseContract {
);
}

/**
* Bridge custom ERC20 token to another network using bridge adapter
*/
async bridgeToken(
recipient: string,
amount: string,
destinationNetworkId: number,
bridgeAdapterAddress: string,
forceUpdateGlobalExitRoot = true,
from?: string
): Promise<TransactionParams> {
ValidationUtils.validateAddress(recipient, 'Recipient address');
ValidationUtils.validateAmount(amount, 'Amount');
ValidationUtils.validateAddress(
bridgeAdapterAddress,
'Bridge adapter address'
);

const bridgeAdapter = new BridgeAdapter({
bridgeAdapterAddress,
rpcUrl: this.config.rpcUrl,
chainId: this.config.chainId,
});

return bridgeAdapter.buildBridgeToken(
{
recipient,
amount,
destinationNetworkId,
forceUpdateGlobalExitRoot,
},
from
);
}

/**
* Get wrapped version of this token on destination network
*/
Expand Down
7 changes: 7 additions & 0 deletions src/types/bridge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ export interface BridgeAssetParams {
permitData?: string;
}

export interface BridgeTokenParams {
recipient: string;
amount: string;
destinationNetworkId: number;
forceUpdateGlobalExitRoot: boolean;
}

export interface BridgeOptions {
forceUpdateGlobalExitRoot?: boolean;
permitData?: string;
Expand Down
Loading