/** * Tests for ContractFactory * * Verifies contract creation with protocol-specific ABIs */ describe('ContractFactory', () => { it('should have ContractFactory module available', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); expect(fs.existsSync(contractFactoryPath)).toBe(true); }); it('should be exported from helpers index', () => { const fs = require('fs'); const path = require('path'); const indexPath = path.join(__dirname, '../../src/helpers/index.ts'); const indexContent = fs.readFileSync(indexPath, 'utf-8'); expect(indexContent).toContain('ContractFactory'); }); describe('Module structure', () => { it('should import required dependencies', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain( "import { Contract, ContractRunner } from 'ethers'", ); expect(content).toContain("import { AbiManager } from './AbiManager'"); expect(content).toContain( "import { ProtocolDetector } from './ProtocolDetector'", ); }); it('should define ContractFactory class', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('export class ContractFactory'); }); it('should have constructor accepting signer and optional dependencies', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('constructor'); expect(content).toContain('signer: ContractRunner'); expect(content).toContain('abiManager?: AbiManager'); expect(content).toContain('protocolDetector?: ProtocolDetector'); expect(content).toContain('networkConfig?: NetworkConfig'); }); }); describe('createPoolContract', () => { it('should define createPoolContract method', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('createPoolContract'); expect(content).toContain('poolAddress: string'); expect(content).toContain('beaconName: string'); }); it('should use AbiManager to get pool ABI', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('this.abiManager.getPoolAbi(beaconName)'); }); it('should create ethers.Contract with pool ABI', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('new Contract'); expect(content).toContain('poolAddress'); expect(content).toContain('poolAbi'); }); }); describe('createFactoryContract', () => { it('should define createFactoryContract method', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('createFactoryContract'); expect(content).toContain('factoryAddress: string'); expect(content).toContain('beaconName: string'); }); it('should use AbiManager to get factory ABI', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('this.abiManager.getFactoryAbi(beaconName)'); }); it('should create ethers.Contract with factory ABI', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('factoryAbi'); }); }); describe('createQuoterContract', () => { it('should define createQuoterContract method', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('createQuoterContract'); // beaconName is optional and drives dynamic resolution const hasQuoterAddress = content.includes('beaconName?:') || content.includes('beaconName?: string') || content.includes('if (!beaconName)'); expect(hasQuoterAddress).toBe(true); // beaconName is also optional now for flexibility const hasBeaconName = content.includes('beaconName') && content.includes('string'); expect(hasBeaconName).toBe(true); }); it('should use AbiManager to get quoter V2 ABI', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('this.quoterFactory.createQuoterV2(beaconName'); }); it('should create ethers.Contract with quoter ABI', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('createQuoterV2'); }); }); describe('createVaultContract', () => { it('should define createVaultContract method', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('createVaultContract'); expect(content).toContain('vaultAddress: string'); expect(content).toContain('beaconName: string'); expect(content).toContain('isUpgraded'); }); it('should use AbiManager to get vault ABI with upgrade flag', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain( 'this.abiManager.getVaultAbi(beaconName, isUpgraded)', ); }); it('should default isUpgraded to true', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('isUpgraded = true'); }); it('should create ethers.Contract with vault ABI', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('vaultAbi'); }); }); describe('createSteerPeripheryContract', () => { it('should define createSteerPeripheryContract method', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('createSteerPeripheryContract'); expect(content).toContain('peripheryAddress: string'); }); it('should use AbiManager to get Steer Periphery ABI', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('this.abiManager.getSteerPeripheryAbi()'); }); it('should create ethers.Contract with periphery ABI', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('peripheryAbi'); }); }); describe('detectVaultUpgrade', () => { it('should define detectVaultUpgrade method', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('detectVaultUpgrade'); expect(content).toContain('vaultAddress: string'); expect(content).toContain('beaconName: string'); expect(content).toContain('Promise'); }); it('should attempt to call totalFees0() to detect upgrade', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('totalFees0()'); }); it('should return true if totalFees0() succeeds', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('return true'); }); it('should return false if totalFees0() fails', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('catch'); expect(content).toContain('return false'); }); }); describe('isMultiPositionVault', () => { it('should define isMultiPositionVault method', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('isMultiPositionVault'); expect(content).toContain('beaconName: string'); expect(content).toContain('boolean'); }); it('should check for "multi" in beacon name', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('toLowerCase()'); expect(content).toContain("'multi'"); }); it('should check for "aerodrome" in beacon name', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain("'aerodrome'"); }); }); describe('Method signatures', () => { it('should accept optional signer parameter in all create methods', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); // Count occurrences of optional signer parameter const signerParamMatches = content.match(/signer\?: ContractRunner/g); expect(signerParamMatches).not.toBeNull(); expect(signerParamMatches!.length).toBeGreaterThanOrEqual(5); // All 5 create methods }); it('should return Contract type from all create methods', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); // Verify return types expect(content).toContain('): Contract'); }); }); describe('Integration with helper modules', () => { it('should use AbiManager for all ABI resolution', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('this.abiManager.getPoolAbi'); expect(content).toContain('this.abiManager.getFactoryAbi'); expect(content).toContain('this.quoterFactory.createQuoterV2'); expect(content).toContain('this.abiManager.getVaultAbi'); expect(content).toContain('this.abiManager.getSteerPeripheryAbi'); }); it('should initialize AbiManager if not provided', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('abiManager || new AbiManager()'); }); it('should initialize ProtocolDetector if not provided', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('protocolDetector || new ProtocolDetector()'); }); }); describe('Documentation', () => { it('should have JSDoc comments for class', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('/**'); expect(content).toContain('* ContractFactory'); }); it('should have JSDoc comments for all public methods', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('* Create a pool contract'); expect(content).toContain('* Create a factory contract'); expect(content).toContain('* Create a quoter'); expect(content).toContain('* Create a vault contract'); expect(content).toContain('* Create a Steer Periphery'); }); }); describe('Network Config Integration', () => { it('should accept optional networkConfig in constructor', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('constructor'); expect(content).toContain('networkConfig'); }); it('should create QuoterFactory when networkConfig provided', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('QuoterFactory'); }); it('should make quoterAddress optional in createQuoterContract', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); // beaconName is optional; dynamic resolution is via QuoterFactory const hasOptionalAddress = content.includes('beaconName?:') || content.includes('beaconName?: string') || content.includes('if (!beaconName)'); expect(hasOptionalAddress).toBe(true); }); it('should resolve quoter address dynamically when not provided', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); // Should use QuoterFactory when address not provided const hasDynamicResolution = content.includes('quoterFactory') || content.includes('createQuoterV2'); expect(hasDynamicResolution).toBe(true); }); it('should maintain backward compatibility with explicit address', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); // Should still accept quoterAddress parameter expect(content).toContain('quoterAddress'); expect(content).toContain('createQuoterContract'); }); it('should throw error when address needed but networkConfig missing', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); // Should have error handling for missing networkConfig const hasErrorHandling = content.includes('throw new Error') || content.includes('if (!networkConfig') || content.includes('if (!this.quoterFactory'); expect(hasErrorHandling).toBe(true); }); it('should import QuoterFactory', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); expect(content).toContain('QuoterFactory'); }); it('should import NetworkConfig type', () => { const fs = require('fs'); const path = require('path'); const contractFactoryPath = path.join( __dirname, '../../src/helpers/ContractFactory.ts', ); const content = fs.readFileSync(contractFactoryPath, 'utf-8'); const importsNetworkConfig = content.includes('NetworkConfig') || content.includes('./NetworkConfig'); expect(importsNetworkConfig).toBe(true); }); }); });