Connecting to Existing Contracts
The most common use case is connecting to already-deployed contracts.Basic Connection
import { GenericSwapFacet__factory } from '@lifi/contract-types';
import { ethers } from 'ethers';
// Read-only connection with provider
const provider = new ethers.providers.JsonRpcProvider(RPC_URL);
const facet = GenericSwapFacet__factory.connect(
'0xDIAMOND_ADDRESS',
provider
);
// Now you can call view functions
const result = await facet.someViewFunction();
Connection with Signer
// Read-write connection with signer
const signer = provider.getSigner();
const facet = GenericSwapFacet__factory.connect(
'0xDIAMOND_ADDRESS',
signer
);
// Now you can send transactions
const tx = await facet.swapTokensGeneric(
transactionId,
integrator,
referrer,
receiver,
minAmount,
swapData
);
await tx.wait();
Multiple Facets on Same Diamond
import {
GenericSwapFacet__factory,
DiamondLoupeFacet__factory,
OwnershipFacet__factory
} from '@lifi/contract-types';
const DIAMOND_ADDRESS = '0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE';
// Connect to multiple facets of the same diamond
const genericSwap = GenericSwapFacet__factory.connect(DIAMOND_ADDRESS, signer);
const loupe = DiamondLoupeFacet__factory.connect(DIAMOND_ADDRESS, provider);
const ownership = OwnershipFacet__factory.connect(DIAMOND_ADDRESS, signer);
// Use each facet for its specific functionality
const owner = await ownership.owner();
const facets = await loupe.facets();
const swapTx = await genericSwap.swapTokensGeneric(...);
Working with Events
Listening to Events
const facet = GenericSwapFacet__factory.connect(address, provider);
// Listen to specific event
facet.on('LiFiSwappedGeneric', (
transactionId,
integrator,
referrer,
fromAssetId,
toAssetId,
fromAmount,
toAmount,
event
) => {
console.log('Swap executed:', {
transactionId,
from: fromAssetId,
to: toAssetId,
amount: ethers.utils.formatUnits(toAmount, 18)
});
});
Filtering Events
// Create event filter
const filter = facet.filters.LiFiSwappedGeneric(
null, // transactionId
'MyIntegrator', // integrator
null, // referrer
null, // fromAssetId
null, // toAssetId
null, // fromAmount
null // toAmount
);
// Query past events
const events = await facet.queryFilter(filter, fromBlock, toBlock);
for (const event of events) {
console.log('Event:', event.args);
}
Waiting for Specific Events
// Send transaction
const tx = await facet.swapTokensGeneric(...);
// Wait for transaction and get events
const receipt = await tx.wait();
const swapEvents = receipt.events?.filter(
e => e.event === 'LiFiSwappedGeneric'
);
for (const event of swapEvents) {
console.log('Swap event:', event.args);
}
Encoding Function Calls
Encode Call Data
const facet = GenericSwapFacet__factory.connect(address, signer);
// Encode function call without sending transaction
const calldata = facet.interface.encodeFunctionData('swapTokensGeneric', [
transactionId,
integrator,
referrer,
receiver,
minAmount,
swapData
]);
console.log('Encoded calldata:', calldata);
Decode Call Data
const decoded = facet.interface.decodeFunctionData(
'swapTokensGeneric',
calldata
);
console.log('Decoded parameters:', decoded);
Get Function Selector
const selector = facet.interface.getSighash('swapTokensGeneric');
console.log('Function selector:', selector); // e.g., '0x12345678'
Estimate Gas
const facet = GenericSwapFacet__factory.connect(address, signer);
// Estimate gas for transaction
const gasEstimate = await facet.estimateGas.swapTokensGeneric(
transactionId,
integrator,
referrer,
receiver,
minAmount,
swapData,
{ value: ethers.utils.parseEther('0.1') } // If payable
);
console.log('Estimated gas:', gasEstimate.toString());
// Use estimate when sending transaction
const tx = await facet.swapTokensGeneric(
transactionId,
integrator,
referrer,
receiver,
minAmount,
swapData,
{
gasLimit: gasEstimate.mul(120).div(100), // Add 20% buffer
value: ethers.utils.parseEther('0.1')
}
);
Call Static (Simulation)
const facet = GenericSwapFacet__factory.connect(address, signer);
// Simulate transaction without sending it
try {
await facet.callStatic.swapTokensGeneric(
transactionId,
integrator,
referrer,
receiver,
minAmount,
swapData
);
console.log('Transaction would succeed');
} catch (error) {
console.error('Transaction would fail:', error);
}
Accessing ABI and Bytecode
Get ABI
import { GenericSwapFacet__factory } from '@lifi/contract-types';
const abi = GenericSwapFacet__factory.abi;
console.log('Contract ABI:', abi);
// Save ABI to file
import fs from 'fs';
fs.writeFileSync(
'GenericSwapFacet.json',
JSON.stringify(abi, null, 2)
);
Get Bytecode
const bytecode = GenericSwapFacet__factory.bytecode;
console.log('Contract bytecode:', bytecode);
Multi-Chain Usage
const chains = {
ethereum: {
rpc: 'https://eth-mainnet.g.alchemy.com/v2/...',
diamond: '0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE'
},
polygon: {
rpc: 'https://polygon-mainnet.g.alchemy.com/v2/...',
diamond: '0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE'
},
arbitrum: {
rpc: 'https://arb-mainnet.g.alchemy.com/v2/...',
diamond: '0x1231DEB6f5749EF6cE6943a275A1D3E7486F4EaE'
}
};
// Connect to same facet on multiple chains
const connections = Object.entries(chains).map(([name, config]) => {
const provider = new ethers.providers.JsonRpcProvider(config.rpc);
const facet = GenericSwapFacet__factory.connect(config.diamond, provider);
return { name, facet };
});
// Query all chains in parallel
const results = await Promise.all(
connections.map(async ({ name, facet }) => {
try {
// Call some view function
const data = await facet.someViewFunction();
return { name, data };
} catch (error) {
return { name, error };
}
})
);
Error Handling
import { GenericSwapFacet__factory } from '@lifi/contract-types';
const facet = GenericSwapFacet__factory.connect(address, signer);
try {
const tx = await facet.swapTokensGeneric(
transactionId,
integrator,
referrer,
receiver,
minAmount,
swapData
);
const receipt = await tx.wait();
console.log('Transaction successful:', receipt.transactionHash);
} catch (error: any) {
// Handle different error types
if (error.code === 'INSUFFICIENT_FUNDS') {
console.error('Insufficient funds for transaction');
} else if (error.code === 'UNPREDICTABLE_GAS_LIMIT') {
console.error('Transaction would fail, check parameters');
} else if (error.reason) {
console.error('Contract reverted:', error.reason);
} else {
console.error('Transaction failed:', error.message);
}
}
Advanced Patterns
Factory Helper Class
import { Signer, providers } from 'ethers';
import {
GenericSwapFacet,
GenericSwapFacet__factory,
DiamondLoupeFacet,
DiamondLoupeFacet__factory
} from '@lifi/contract-types';
class LiFiDiamond {
public genericSwap: GenericSwapFacet;
public loupe: DiamondLoupeFacet;
constructor(
address: string,
signerOrProvider: Signer | providers.Provider
) {
this.genericSwap = GenericSwapFacet__factory.connect(
address,
signerOrProvider
);
this.loupe = DiamondLoupeFacet__factory.connect(
address,
signerOrProvider
);
}
async getFacets() {
return await this.loupe.facets();
}
async performSwap(params: SwapParams) {
return await this.genericSwap.swapTokensGeneric(
params.transactionId,
params.integrator,
params.referrer,
params.receiver,
params.minAmount,
params.swapData
);
}
}
// Usage
const diamond = new LiFiDiamond(DIAMOND_ADDRESS, signer);
const facets = await diamond.getFacets();
const tx = await diamond.performSwap(swapParams);
Contract Verification Helper
import { DiamondLoupeFacet__factory } from '@lifi/contract-types';
async function verifyDiamondHasFacet(
diamondAddress: string,
facetAddress: string,
provider: providers.Provider
): Promise<boolean> {
const loupe = DiamondLoupeFacet__factory.connect(diamondAddress, provider);
const facets = await loupe.facetAddresses();
return facets.map(f => f.toLowerCase()).includes(facetAddress.toLowerCase());
}
// Usage
const hasGenericSwap = await verifyDiamondHasFacet(
DIAMOND_ADDRESS,
GENERIC_SWAP_FACET_ADDRESS,
provider
);