Skip to content
Merged
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
6 changes: 6 additions & 0 deletions .changeset/test-performance-optimization.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@hashgraph/asset-tokenization-contracts": patch
---

- Optimize test fixture deployment speed (96% improvement). Improved contract test performance from 47 seconds to 2 seconds per fixture by fixing inefficient batch processing and removing unnecessary network delays on instant-mining networks (Hardhat/local).
- Remove duplicated contract interface fragments in test files (ERC3643, clearing, protectedPartitions tests).
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@
"clean:full": "npm run clean:workspaces && npm run clean:deps:full && npm run clean:root && echo '\\n✅ Complete cleanup finished — workspaces, dependencies, and root junk removed'",
"lint:staged:js": "eslint --cache",
"lint:staged:sol": "solhint --config packages/ats/contracts/.solhint.json",
"format:staged": "prettier --check",
"format:staged": "prettier --write",
"format:staged:check": "prettier --check",
"pre-commit": "lint-staged",
"commitlint": "commitlint --edit",
"prepare": "husky",
Expand Down
1 change: 0 additions & 1 deletion packages/ats/contracts/scripts/cli/hardhat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ async function main() {
console.log(` Equity Config Version: ${output.configurations.equity.version}`);
console.log(` Bond Config Version: ${output.configurations.bond.version}`);
console.log(` Total Contracts: ${output.summary.totalContracts}`);
console.log(` Deployment Time: ${output.summary.deploymentTime}ms`);

process.exit(0);
} catch (error) {
Expand Down
1 change: 0 additions & 1 deletion packages/ats/contracts/scripts/cli/standalone.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ async function main() {
console.log(` Equity Config Version: ${output.configurations.equity.version}`);
console.log(` Bond Config Version: ${output.configurations.bond.version}`);
console.log(` Total Contracts: ${output.summary.totalContracts}`);
console.log(` Deployment Time: ${output.summary.deploymentTime}ms`);

process.exit(0);
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,9 @@ const BOND_FACETS = [
* @param blrContract - BusinessLogicResolver contract instance
* @param facetAddresses - Map of facet names to their deployed addresses
* @param useTimeTravel - Whether to use TimeTravel variants (default: false)
* @param partialBatchDeploy - Whether this is a partial batch deployment (default: false)
* @param batchSize - Number of facets per batch (default: DEFAULT_BATCH_SIZE)
* @param confirmations - Number of confirmations to wait for (default: 0 for test environments)
* @returns Promise resolving to operation result
*
* @example
Expand All @@ -122,7 +125,10 @@ const BOND_FACETS = [
* 'BondUSAFacet': '0xdef...',
* // ... more facets
* },
* false
* false,
* false,
* 15,
* 0
* )
*
* if (result.success) {
Expand All @@ -139,6 +145,7 @@ export async function createBondConfiguration(
useTimeTravel: boolean = false,
partialBatchDeploy: boolean = false,
batchSize: number = DEFAULT_BATCH_SIZE,
confirmations: number = 0,
): Promise<OperationResult<ConfigurationData, ConfigurationError>> {
// Get facet names based on time travel mode
// Include TimeTravelFacet when useTimeTravel=true to provide time manipulation functions
Expand Down Expand Up @@ -169,5 +176,6 @@ export async function createBondConfiguration(
facets,
partialBatchDeploy,
batchSize,
confirmations,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@ const EQUITY_FACETS = [
* @param blrContract - BusinessLogicResolver contract instance
* @param facetAddresses - Map of facet names to their deployed addresses
* @param useTimeTravel - Whether to use TimeTravel variants (default: false)
* @param partialBatchDeploy - Whether this is a partial batch deployment (default: false)
* @param batchSize - Number of facets per batch (default: DEFAULT_BATCH_SIZE)
* @param confirmations - Number of confirmations to wait for (default: 0 for test environments)
* @returns Promise resolving to operation result
*
* @example
Expand All @@ -122,7 +125,10 @@ const EQUITY_FACETS = [
* 'EquityUSAFacet': '0x123...',
* // ... more facets
* },
* false
* false,
* false,
* 15,
* 0
* )
*
* if (result.success) {
Expand All @@ -139,6 +145,7 @@ export async function createEquityConfiguration(
useTimeTravel: boolean = false,
partialBatchDeploy: boolean = false,
batchSize: number = DEFAULT_BATCH_SIZE,
confirmations: number = 0,
): Promise<OperationResult<ConfigurationData, ConfigurationError>> {
// Get facet names based on time travel mode
// Include TimeTravelFacet when useTimeTravel=true to provide time manipulation functions
Expand Down Expand Up @@ -169,5 +176,6 @@ export async function createEquityConfiguration(
facets,
partialBatchDeploy,
batchSize,
confirmations,
});
}
1 change: 1 addition & 0 deletions packages/ats/contracts/scripts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ export type {
ResumeOptions,
} from "./infrastructure/types/checkpoint";
export { CheckpointManager } from "./infrastructure/checkpoint/CheckpointManager";
export { NullCheckpointManager } from "./infrastructure/checkpoint/NullCheckpointManager";
export type { CreateCheckpointParams } from "./infrastructure/checkpoint/CheckpointManager";
export {
checkpointToDeploymentOutput,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
// SPDX-License-Identifier: Apache-2.0

/**
* Null checkpoint manager for test environments.
*
* Provides no-op implementations of checkpoint operations to eliminate
* filesystem I/O overhead during test execution. All checkpoint state is
* maintained in memory only.
*
* Use this manager when `ignoreCheckpoint: true` is specified in deployment
* options to prevent unnecessary disk writes and improve test performance.
*
* @module infrastructure/checkpoint/NullCheckpointManager
*/

import type { DeploymentCheckpoint, CheckpointStatus } from "../types/checkpoint";
import { CheckpointManager } from "./CheckpointManager";

/**
* No-op checkpoint manager for test environments.
*
* Extends CheckpointManager but overrides all filesystem operations to be
* no-ops. Checkpoints are created in memory but never persisted to disk.
*
* **Performance Benefits:**
* - Eliminates filesystem I/O during test execution
* - Prevents checkpoint file accumulation
* - Avoids race conditions in parallel test execution
* - Reduces test initialization overhead by ~500-1000ms per test
*
* @example
* ```typescript
* // In deployment workflow
* const checkpointManager = ignoreCheckpoint
* ? new NullCheckpointManager()
* : new CheckpointManager(checkpointDir);
*
* // Checkpoint operations work but don't touch filesystem
* const checkpoint = checkpointManager.createCheckpoint({ ... });
* await checkpointManager.saveCheckpoint(checkpoint); // No-op
* ```
*/
export class NullCheckpointManager extends CheckpointManager {
/**
* Create a null checkpoint manager.
*
* Directory parameter is accepted for API compatibility but ignored.
*/
constructor(checkpointsDir?: string) {
super(checkpointsDir);
}

/**
* No-op save operation.
*
* Checkpoint is not written to disk. This eliminates filesystem I/O
* overhead during test execution while maintaining API compatibility.
*
* @param checkpoint - Checkpoint to save (ignored)
*/
async saveCheckpoint(checkpoint: DeploymentCheckpoint): Promise<void> {
// No-op - don't write to disk
// Update lastUpdate for consistency with in-memory state
checkpoint.lastUpdate = new Date().toISOString();
}

/**
* Always returns null (no checkpoints exist on disk).
*
* @param _checkpointId - Checkpoint ID to load (ignored)
* @returns null (checkpoints are never persisted)
*/
async loadCheckpoint(_checkpointId: string): Promise<DeploymentCheckpoint | null> {
return null; // No checkpoints exist
}

/**
* Always returns empty array (no checkpoints exist on disk).
*
* @param _network - Network name (ignored)
* @param _status - Status filter (ignored)
* @returns Empty array (no checkpoints to find)
*/
async findCheckpoints(_network: string, _status?: CheckpointStatus): Promise<DeploymentCheckpoint[]> {
return []; // No checkpoints exist
}

/**
* No-op delete operation.
*
* @param _checkpointId - Checkpoint ID to delete (ignored)
*/
async deleteCheckpoint(_checkpointId: string): Promise<void> {
// No-op - nothing to delete
}

/**
* No-op cleanup operation.
*
* @param _network - Network name (ignored)
* @param _daysToKeep - Days to keep (ignored)
* @returns 0 (no checkpoints to clean up)
*/
async cleanupOldCheckpoints(_network: string, _daysToKeep?: number): Promise<number> {
return 0; // No checkpoints to clean up
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,6 @@ export function checkpointToDeploymentOutput(checkpoint: DeploymentCheckpoint):
// Calculate deployment time
const endTime = new Date(checkpoint.lastUpdate).getTime();
const start = new Date(startTime).getTime();
const deploymentTime = endTime - start;

// Calculate total gas used (sum from all deployments)
let totalGasUsed = 0;
Expand Down Expand Up @@ -114,7 +113,7 @@ export function checkpointToDeploymentOutput(checkpoint: DeploymentCheckpoint):
totalContracts: 3 + steps.facets.size, // ProxyAdmin + BLR + Factory + facets
totalFacets: steps.facets.size,
totalConfigurations: 2,
deploymentTime,
deploymentTime: endTime - start,
gasUsed: totalGasUsed.toString(),
success: checkpoint.status === "completed",
},
Expand Down
3 changes: 2 additions & 1 deletion packages/ats/contracts/scripts/infrastructure/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export {

export { getNetworkConfig, getAllNetworks } from "./config";

export { getDeploymentConfig, isLocalNetwork, DEPLOYMENT_CONFIGS } from "./networkConfig";
export { getDeploymentConfig, isLocalNetwork, isInstantMiningNetwork, DEPLOYMENT_CONFIGS } from "./networkConfig";
export type { DeploymentConfig } from "./networkConfig";

// ============================================================================
Expand Down Expand Up @@ -207,6 +207,7 @@ export { getSelector } from "./utils/selector";
// ============================================================================

export { CheckpointManager } from "./checkpoint/CheckpointManager";
export { NullCheckpointManager } from "./checkpoint/NullCheckpointManager";
export type { CreateCheckpointParams } from "./checkpoint/CheckpointManager";

export {
Expand Down
25 changes: 18 additions & 7 deletions packages/ats/contracts/scripts/infrastructure/networkConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -170,19 +170,30 @@ export function getDeploymentConfig(network: string): DeploymentConfig {
}

/**
* Check if network is a simulated local environment.
* These networks are instant and don't need retries (hardhat, local).
* Note: hedera-local is excluded - it's a real network running locally.
* Check if network uses instant mining (simulated local environment).
* These networks process transactions instantly and don't need delays or batching.
*
* Instant networks: hardhat, local
* NOT instant: hedera-local (simulates real network behavior)
*
* @param network - Network name
* @returns true if network is local (hardhat/local only - not hedera-local)
* @returns true if network uses instant mining (hardhat/local only)
*
* @example
* ```typescript
* isLocalNetwork("hardhat") // true
* isLocalNetwork("hedera-testnet") // false
* isInstantMiningNetwork("hardhat") // true - instant mining
* isInstantMiningNetwork("local") // true - instant mining
* isInstantMiningNetwork("hedera-local") // false - simulates real network
* isInstantMiningNetwork("hedera-testnet") // false - real network
* ```
*/
export function isLocalNetwork(network: string): boolean {
export function isInstantMiningNetwork(network: string): boolean {
return network === "hardhat" || network === "local";
}

/**
* @deprecated Use isInstantMiningNetwork() instead for better clarity
*/
export function isLocalNetwork(network: string): boolean {
return isInstantMiningNetwork(network);
}
Loading