From f05706b37a714bad2cd4ee0a14613a6226f5a4b9 Mon Sep 17 00:00:00 2001 From: zoeyTM Date: Fri, 1 Nov 2024 01:41:35 -0400 Subject: [PATCH 1/3] allow ignition modules as a dependency for futures --- .../core/src/ignition-module-serializer.ts | 59 ++++++++++++++----- packages/core/src/internal/batcher.ts | 23 +++++++- packages/core/src/internal/module-builder.ts | 40 +++++++++++++ packages/core/src/internal/module.ts | 2 +- .../utils/adjacency-list-converter.ts | 13 ++-- packages/core/src/types/module-builder.ts | 28 ++++----- packages/core/src/types/module.ts | 22 +++---- packages/core/src/types/serialization.ts | 2 +- packages/core/test/batcher.ts | 10 +++- packages/core/test/call.ts | 10 +++- packages/core/test/contract.ts | 12 +++- packages/core/test/contractAt.ts | 10 +++- packages/core/test/contractAtFromArtifact.ts | 10 +++- packages/core/test/contractFromArtifact.ts | 10 +++- packages/core/test/encodeFunctionCall.ts | 12 +++- packages/core/test/library.ts | 10 +++- packages/core/test/libraryFromArtifact.ts | 10 +++- packages/core/test/send.ts | 12 +++- packages/core/test/staticCall.ts | 10 +++- packages/core/test/types/module.ts | 2 +- packages/ui/examples/ComplexModule.js | 12 +++- packages/ui/src/main.css | 6 ++ .../components/deployment-flow.tsx | 13 +++- packages/ui/src/utils/to-mermaid.ts | 7 ++- 24 files changed, 269 insertions(+), 76 deletions(-) diff --git a/packages/core/src/ignition-module-serializer.ts b/packages/core/src/ignition-module-serializer.ts index 11808b00b..a04b0fe62 100644 --- a/packages/core/src/ignition-module-serializer.ts +++ b/packages/core/src/ignition-module-serializer.ts @@ -136,7 +136,9 @@ export class IgnitionModuleSerializer { moduleId: future.module.id, type: future.type, dependencies: Array.from(future.dependencies).map((d) => - this._convertFutureToFutureToken(d) + isFuture(d) + ? this._convertFutureToFutureToken(d) + : this._convertModuleToModuleToken(d) ), contractName: future.contractName, constructorArgs: future.constructorArgs.map((arg) => @@ -161,7 +163,9 @@ export class IgnitionModuleSerializer { moduleId: future.module.id, type: future.type, dependencies: Array.from(future.dependencies).map((d) => - this._convertFutureToFutureToken(d) + isFuture(d) + ? this._convertFutureToFutureToken(d) + : this._convertModuleToModuleToken(d) ), contractName: future.contractName, artifact: future.artifact, @@ -187,7 +191,9 @@ export class IgnitionModuleSerializer { moduleId: future.module.id, type: future.type, dependencies: Array.from(future.dependencies).map((d) => - this._convertFutureToFutureToken(d) + isFuture(d) + ? this._convertFutureToFutureToken(d) + : this._convertModuleToModuleToken(d) ), contractName: future.contractName, from: isRuntimeValue(future.from) @@ -204,7 +210,9 @@ export class IgnitionModuleSerializer { moduleId: future.module.id, type: future.type, dependencies: Array.from(future.dependencies).map((d) => - this._convertFutureToFutureToken(d) + isFuture(d) + ? this._convertFutureToFutureToken(d) + : this._convertModuleToModuleToken(d) ), contractName: future.contractName, artifact: future.artifact, @@ -222,7 +230,9 @@ export class IgnitionModuleSerializer { moduleId: future.module.id, type: future.type, dependencies: Array.from(future.dependencies).map((d) => - this._convertFutureToFutureToken(d) + isFuture(d) + ? this._convertFutureToFutureToken(d) + : this._convertModuleToModuleToken(d) ), contract: this._convertFutureToFutureToken(future.contract), functionName: future.functionName, @@ -245,7 +255,9 @@ export class IgnitionModuleSerializer { moduleId: future.module.id, type: future.type, dependencies: Array.from(future.dependencies).map((d) => - this._convertFutureToFutureToken(d) + isFuture(d) + ? this._convertFutureToFutureToken(d) + : this._convertModuleToModuleToken(d) ), contract: this._convertFutureToFutureToken(future.contract), functionName: future.functionName, @@ -264,7 +276,9 @@ export class IgnitionModuleSerializer { moduleId: future.module.id, type: future.type, dependencies: Array.from(future.dependencies).map((d) => - this._convertFutureToFutureToken(d) + isFuture(d) + ? this._convertFutureToFutureToken(d) + : this._convertModuleToModuleToken(d) ), contract: this._convertFutureToFutureToken(future.contract), functionName: future.functionName, @@ -279,7 +293,9 @@ export class IgnitionModuleSerializer { moduleId: future.module.id, type: future.type, dependencies: Array.from(future.dependencies).map((d) => - this._convertFutureToFutureToken(d) + isFuture(d) + ? this._convertFutureToFutureToken(d) + : this._convertModuleToModuleToken(d) ), contractName: future.contractName, address: isFuture(future.address) @@ -298,7 +314,9 @@ export class IgnitionModuleSerializer { moduleId: future.module.id, type: future.type, dependencies: Array.from(future.dependencies).map((d) => - this._convertFutureToFutureToken(d) + isFuture(d) + ? this._convertFutureToFutureToken(d) + : this._convertModuleToModuleToken(d) ), contractName: future.contractName, artifact: future.artifact, @@ -318,7 +336,9 @@ export class IgnitionModuleSerializer { moduleId: future.module.id, type: future.type, dependencies: Array.from(future.dependencies).map((d) => - this._convertFutureToFutureToken(d) + isFuture(d) + ? this._convertFutureToFutureToken(d) + : this._convertModuleToModuleToken(d) ), futureToReadFrom: this._convertFutureToFutureToken( future.futureToReadFrom @@ -336,7 +356,9 @@ export class IgnitionModuleSerializer { moduleId: future.module.id, type: future.type, dependencies: Array.from(future.dependencies).map((d) => - this._convertFutureToFutureToken(d) + isFuture(d) + ? this._convertFutureToFutureToken(d) + : this._convertModuleToModuleToken(d) ), to: isFuture(future.to) ? this._convertFutureToFutureToken(future.to) @@ -475,7 +497,14 @@ export class IgnitionModuleDeserializer { ); for (const dependencyId of serializedFuture.dependencies) { - const dependency = this._lookup(futuresLookup, dependencyId.futureId); + let dependency: Future | IgnitionModule; + + if (dependencyId._kind === "FutureToken") { + dependency = this._lookup(futuresLookup, dependencyId.futureId); + } else { + dependency = this._lookup(modulesLookup, dependencyId.moduleId); + } + future.dependencies.add(dependency); } @@ -548,8 +577,10 @@ export class IgnitionModuleDeserializer { for (const serializedFuture of serializedFutures) { for (const dependencyToken of serializedFuture.dependencies) { - const dependency = serializedFuturesMap[dependencyToken.futureId]; - graph.get(dependency)!.add(serializedFuture); + if (dependencyToken._kind === "FutureToken") { + const dependency = serializedFuturesMap[dependencyToken.futureId]; + graph.get(dependency)!.add(serializedFuture); + } } } diff --git a/packages/core/src/internal/batcher.ts b/packages/core/src/internal/batcher.ts index 3c4ef0b90..ef5264ae7 100644 --- a/packages/core/src/internal/batcher.ts +++ b/packages/core/src/internal/batcher.ts @@ -130,7 +130,28 @@ export class Batcher { ): boolean { const dependencies = batchState.adjacencyList.getDependenciesFor(futureId); - return [...dependencies].every( + return [...dependencies].every((depId) => { + if (/#/.test(depId)) { + return batchState.visitState[depId] === VisitStatus.VISITED; + } + + return this._checkModuleDependencyIsComplete(depId, batchState); + }); + } + + /** + * This is needed because moduleIds are not present in the visit state + * causing an infinite loop when checking whether a depenedency is visited if that dependency is a module. + */ + private static _checkModuleDependencyIsComplete( + moduleId: string, + batchState: BatchState + ) { + const dependencies = Object.keys(batchState.visitState).filter((futureId) => + futureId.startsWith(moduleId) + ); + + return dependencies.every( (depId) => batchState.visitState[depId] === VisitStatus.VISITED ); } diff --git a/packages/core/src/internal/module-builder.ts b/packages/core/src/internal/module-builder.ts index 40d3d7f60..5dd313193 100644 --- a/packages/core/src/internal/module-builder.ts +++ b/packages/core/src/internal/module-builder.ts @@ -299,6 +299,10 @@ class IgnitionModuleBuilderImplementation< for (const afterFuture of options.after ?? []) { future.dependencies.add(afterFuture); + + if (!isFuture(afterFuture)) { + this._module.submodules.add(afterFuture); + } } for (const libraryFuture of Object.values(options.libraries)) { @@ -356,6 +360,10 @@ class IgnitionModuleBuilderImplementation< for (const afterFuture of options.after ?? []) { future.dependencies.add(afterFuture); + + if (!isFuture(afterFuture)) { + this._module.submodules.add(afterFuture); + } } for (const libraryFuture of Object.values(options.libraries)) { @@ -426,6 +434,10 @@ class IgnitionModuleBuilderImplementation< for (const afterFuture of options.after ?? []) { future.dependencies.add(afterFuture); + + if (!isFuture(afterFuture)) { + this._module.submodules.add(afterFuture); + } } for (const libraryFuture of Object.values(options.libraries)) { @@ -470,6 +482,10 @@ class IgnitionModuleBuilderImplementation< for (const afterFuture of options.after ?? []) { future.dependencies.add(afterFuture); + + if (!isFuture(afterFuture)) { + this._module.submodules.add(afterFuture); + } } for (const libraryFuture of Object.values(options.libraries)) { @@ -543,6 +559,10 @@ class IgnitionModuleBuilderImplementation< for (const afterFuture of options.after ?? []) { future.dependencies.add(afterFuture); + + if (!isFuture(afterFuture)) { + this._module.submodules.add(afterFuture); + } } this._module.futures.add(future); @@ -607,6 +627,10 @@ class IgnitionModuleBuilderImplementation< for (const afterFuture of options.after ?? []) { future.dependencies.add(afterFuture); + + if (!isFuture(afterFuture)) { + this._module.submodules.add(afterFuture); + } } this._module.futures.add(future); @@ -669,6 +693,10 @@ class IgnitionModuleBuilderImplementation< for (const afterFuture of options.after ?? []) { future.dependencies.add(afterFuture); + + if (!isFuture(afterFuture)) { + this._module.submodules.add(afterFuture); + } } this._module.futures.add(future); @@ -774,6 +802,10 @@ class IgnitionModuleBuilderImplementation< for (const afterFuture of options.after ?? []) { future.dependencies.add(afterFuture); + + if (!isFuture(afterFuture)) { + this._module.submodules.add(afterFuture); + } } if (isFuture(address)) { @@ -819,6 +851,10 @@ class IgnitionModuleBuilderImplementation< for (const afterFuture of options.after ?? []) { future.dependencies.add(afterFuture); + + if (!isFuture(afterFuture)) { + this._module.submodules.add(afterFuture); + } } if (isFuture(address)) { @@ -953,6 +989,10 @@ class IgnitionModuleBuilderImplementation< for (const afterFuture of options.after ?? []) { future.dependencies.add(afterFuture); + + if (!isFuture(afterFuture)) { + this._module.submodules.add(afterFuture); + } } this._module.futures.add(future); diff --git a/packages/core/src/internal/module.ts b/packages/core/src/internal/module.ts index afa58b40c..7ce4820f0 100644 --- a/packages/core/src/internal/module.ts +++ b/packages/core/src/internal/module.ts @@ -27,7 +27,7 @@ import { const customInspectSymbol = Symbol.for("util.inspect.custom"); abstract class BaseFutureImplementation { - public readonly dependencies: Set = new Set(); + public readonly dependencies: Set = new Set(); constructor( public readonly id: string, diff --git a/packages/core/src/internal/utils/adjacency-list-converter.ts b/packages/core/src/internal/utils/adjacency-list-converter.ts index e2993f441..7a10ebfcd 100644 --- a/packages/core/src/internal/utils/adjacency-list-converter.ts +++ b/packages/core/src/internal/utils/adjacency-list-converter.ts @@ -1,3 +1,4 @@ +import { isFuture } from "../../type-guards"; import { Future } from "../../types/module"; import { AdjacencyList } from "./adjacency-list"; @@ -12,11 +13,13 @@ export class AdjacencyListConverter { for (const dependency of future.dependencies) { dependencyGraph.addDependency({ from: future.id, to: dependency.id }); - this._optionallyAddDependenciesSubmoduleSiblings( - dependencyGraph, - future, - dependency - ); + if (isFuture(dependency)) { + this._optionallyAddDependenciesSubmoduleSiblings( + dependencyGraph, + future, + dependency + ); + } } } diff --git a/packages/core/src/types/module-builder.ts b/packages/core/src/types/module-builder.ts index 4a8221af2..4daad3661 100644 --- a/packages/core/src/types/module-builder.ts +++ b/packages/core/src/types/module-builder.ts @@ -35,9 +35,9 @@ export interface ContractOptions { id?: string; /** - * The futures to execute before this one. + * The futures or Ignition modules to execute before this one. */ - after?: Future[]; + after?: Array; /** * The libraries to link to the contract. @@ -71,9 +71,9 @@ export interface LibraryOptions { id?: string; /** - * The futures to execute before this one. + * The futures or Ignition modules to execute before this one. */ - after?: Future[]; + after?: Array; /** * The libraries to link to the contract. @@ -98,9 +98,9 @@ export interface CallOptions { id?: string; /** - * The futures to execute before this one. + * The futures or Ignition modules to execute before this one. */ - after?: Future[]; + after?: Array; /** * The value in wei to send with the transaction. @@ -129,9 +129,9 @@ export interface StaticCallOptions { id?: string; /** - * The futures to execute before this one. + * The futures or Ignition modules to execute before this one. */ - after?: Future[]; + after?: Array; /** * The account to send the transaction from. @@ -151,9 +151,9 @@ export interface EncodeFunctionCallOptions { id?: string; /** - * The futures to execute before this one. + * The futures or Ignition modules to execute before this one. */ - after?: Future[]; + after?: Array; } /** @@ -168,9 +168,9 @@ export interface ContractAtOptions { id?: string; /** - * The futures to execute before this one. + * The futures or Ignition modules to execute before this one. */ - after?: Future[]; + after?: Array; } /** @@ -204,9 +204,9 @@ export interface ReadEventArgumentOptions { */ export interface SendDataOptions { /** - * The futures to execute before this one. + * The futures or Ignition modules to execute before this one. */ - after?: Future[]; + after?: Array; /** * The account to send the transaction from. diff --git a/packages/core/src/types/module.ts b/packages/core/src/types/module.ts index 55732b36b..45160b154 100644 --- a/packages/core/src/types/module.ts +++ b/packages/core/src/types/module.ts @@ -134,7 +134,7 @@ export interface NamedArtifactContractDeploymentFuture< type: FutureType.NAMED_ARTIFACT_CONTRACT_DEPLOYMENT; id: string; module: IgnitionModule; - dependencies: Set; + dependencies: Set; contractName: ContractNameT; constructorArgs: ArgumentType[]; libraries: Record>; @@ -156,7 +156,7 @@ export interface ContractDeploymentFuture { type: FutureType.CONTRACT_DEPLOYMENT; id: string; module: IgnitionModule; - dependencies: Set; + dependencies: Set; contractName: string; artifact: Artifact; constructorArgs: ArgumentType[]; @@ -180,7 +180,7 @@ export interface NamedArtifactLibraryDeploymentFuture< type: FutureType.NAMED_ARTIFACT_LIBRARY_DEPLOYMENT; id: string; module: IgnitionModule; - dependencies: Set; + dependencies: Set; contractName: LibraryNameT; libraries: Record>; from: string | AccountRuntimeValue | undefined; @@ -196,7 +196,7 @@ export interface LibraryDeploymentFuture { type: FutureType.LIBRARY_DEPLOYMENT; id: string; module: IgnitionModule; - dependencies: Set; + dependencies: Set; contractName: string; artifact: Artifact; libraries: Record>; @@ -215,7 +215,7 @@ export interface ContractCallFuture< type: FutureType.CONTRACT_CALL; id: string; module: IgnitionModule; - dependencies: Set; + dependencies: Set; contract: ContractFuture; functionName: FunctionNameT; args: ArgumentType[]; @@ -239,7 +239,7 @@ export interface StaticCallFuture< type: FutureType.STATIC_CALL; id: string; module: IgnitionModule; - dependencies: Set; + dependencies: Set; contract: ContractFuture; functionName: FunctionNameT; nameOrIndex: string | number; @@ -259,7 +259,7 @@ export interface EncodeFunctionCallFuture< type: FutureType.ENCODE_FUNCTION_CALL; id: string; module: IgnitionModule; - dependencies: Set; + dependencies: Set; contract: ContractFuture; functionName: FunctionNameT; args: ArgumentType[]; @@ -274,7 +274,7 @@ export interface NamedArtifactContractAtFuture { type: FutureType.NAMED_ARTIFACT_CONTRACT_AT; id: string; module: IgnitionModule; - dependencies: Set; + dependencies: Set; contractName: ContractNameT; address: | string @@ -292,7 +292,7 @@ export interface ContractAtFuture { type: FutureType.CONTRACT_AT; id: string; module: IgnitionModule; - dependencies: Set; + dependencies: Set; contractName: string; address: | string @@ -311,7 +311,7 @@ export interface ReadEventArgumentFuture { type: FutureType.READ_EVENT_ARGUMENT; id: string; module: IgnitionModule; - dependencies: Set; + dependencies: Set; futureToReadFrom: | NamedArtifactContractDeploymentFuture | ContractDeploymentFuture @@ -332,7 +332,7 @@ export interface SendDataFuture { type: FutureType.SEND_DATA; id: string; module: IgnitionModule; - dependencies: Set; + dependencies: Set; to: | string | AddressResolvableFuture diff --git a/packages/core/src/types/serialization.ts b/packages/core/src/types/serialization.ts index 025c3c9d7..72289b675 100644 --- a/packages/core/src/types/serialization.ts +++ b/packages/core/src/types/serialization.ts @@ -64,7 +64,7 @@ export interface ModuleToken { export interface BaseSerializedFuture { id: string; type: FutureType; - dependencies: FutureToken[]; + dependencies: Array; moduleId: string; } diff --git a/packages/core/test/batcher.ts b/packages/core/test/batcher.ts index 8d2c25d75..e735708e1 100644 --- a/packages/core/test/batcher.ts +++ b/packages/core/test/batcher.ts @@ -40,9 +40,14 @@ describe("batcher", () => { }); it("should batch through dependencies", () => { + const otherModule = buildModule("Module2", (m) => { + const example = m.contract("Example"); + return { example }; + }); + const ignitionModule = buildModule("Module1", (m) => { const contract1 = m.contract("Contract1"); - const contract2 = m.contract("Contract2"); + const contract2 = m.contract("Contract2", [], { after: [otherModule] }); const contract3 = m.contract("Contract3", [contract1, contract2]); @@ -58,7 +63,8 @@ describe("batcher", () => { }); assertBatching({ ignitionModule }, [ - ["Module1#Contract1", "Module1#Contract2"], + ["Module1#Contract1", "Module2#Example"], + ["Module1#Contract2"], ["Module1#Contract3"], ["Module1#Contract4", "Module1#Contract5"], ]); diff --git a/packages/core/test/call.ts b/packages/core/test/call.ts index 568e6b644..6dd4debea 100644 --- a/packages/core/test/call.ts +++ b/packages/core/test/call.ts @@ -87,11 +87,16 @@ describe("call", () => { }); it("should be able to pass one contract as an after dependency of a call", () => { + const otherModule = buildModule("Module2", (m) => { + const example = m.contract("Example"); + return { example }; + }); + const moduleWithDependentContracts = buildModule("Module1", (m) => { const example = m.contract("Example"); const another = m.contract("Another"); - m.call(example, "test", [], { after: [another] }); + m.call(example, "test", [], { after: [another, otherModule] }); return { example, another }; }); @@ -114,9 +119,10 @@ describe("call", () => { assert.fail("Not a named contract deployment"); } - assert.equal(callFuture.dependencies.size, 2); + assert.equal(callFuture.dependencies.size, 3); assert(callFuture.dependencies.has(exampleFuture!)); assert(callFuture.dependencies.has(anotherFuture!)); + assert(callFuture.dependencies.has(otherModule!)); }); it("should be able to pass value as an option", () => { diff --git a/packages/core/test/contract.ts b/packages/core/test/contract.ts index 8de703491..395447ef3 100644 --- a/packages/core/test/contract.ts +++ b/packages/core/test/contract.ts @@ -78,9 +78,16 @@ describe("contract", () => { }); it("should be able to pass one contract as an after dependency of another", () => { + const otherModule = buildModule("Module2", (m) => { + const example = m.contract("Example"); + return { example }; + }); + const moduleWithDependentContracts = buildModule("Module1", (m) => { const example = m.contract("Example"); - const another = m.contract("Another", [], { after: [example] }); + const another = m.contract("Another", [], { + after: [example, otherModule], + }); return { example, another }; }); @@ -101,8 +108,9 @@ describe("contract", () => { assert.fail("Not a named contract deployment"); } - assert.equal(anotherFuture.dependencies.size, 1); + assert.equal(anotherFuture.dependencies.size, 2); assert(anotherFuture.dependencies.has(exampleFuture!)); + assert(anotherFuture.dependencies.has(otherModule!)); }); it("should be able to pass a library as a dependency of a contract", () => { diff --git a/packages/core/test/contractAt.ts b/packages/core/test/contractAt.ts index 4bc328abe..aacfb2308 100644 --- a/packages/core/test/contractAt.ts +++ b/packages/core/test/contractAt.ts @@ -47,10 +47,15 @@ describe("contractAt", () => { }); it("should be able to pass an after dependency", () => { + const otherModule = buildModule("Module2", (m) => { + const example = m.contract("Example"); + return { example }; + }); + const moduleWithDependentContracts = buildModule("Module1", (m) => { const example = m.contract("Example"); const another = m.contractAt("Another", exampleAddress, { - after: [example], + after: [example, otherModule], }); return { example, another }; @@ -61,8 +66,9 @@ describe("contractAt", () => { const exampleFuture = moduleWithDependentContracts.results.example; const anotherFuture = moduleWithDependentContracts.results.another; - assert.equal(anotherFuture.dependencies.size, 1); + assert.equal(anotherFuture.dependencies.size, 2); assert(anotherFuture.dependencies.has(exampleFuture!)); + assert(anotherFuture.dependencies.has(otherModule)); }); it("should be able to pass a static call future as the address", () => { diff --git a/packages/core/test/contractAtFromArtifact.ts b/packages/core/test/contractAtFromArtifact.ts index 79ffa04a2..fe2483860 100644 --- a/packages/core/test/contractAtFromArtifact.ts +++ b/packages/core/test/contractAtFromArtifact.ts @@ -48,10 +48,15 @@ describe("contractAtFromArtifact", () => { }); it("should be able to pass an after dependency", () => { + const otherModule = buildModule("Module2", (m) => { + const example = m.contract("Example"); + return { example }; + }); + const moduleWithDependentContracts = buildModule("Module1", (m) => { const example = m.contract("Example"); const another = m.contractAt("Another", fakeArtifact, exampleAddress, { - after: [example], + after: [example, otherModule], }); return { example, another }; @@ -62,8 +67,9 @@ describe("contractAtFromArtifact", () => { const exampleFuture = moduleWithDependentContracts.results.example; const anotherFuture = moduleWithDependentContracts.results.another; - assert.equal(anotherFuture.dependencies.size, 1); + assert.equal(anotherFuture.dependencies.size, 2); assert(anotherFuture.dependencies.has(exampleFuture!)); + assert(anotherFuture.dependencies.has(otherModule)); }); it("should be able to pass a static call future as the address", () => { diff --git a/packages/core/test/contractFromArtifact.ts b/packages/core/test/contractFromArtifact.ts index bf938c39c..024588200 100644 --- a/packages/core/test/contractFromArtifact.ts +++ b/packages/core/test/contractFromArtifact.ts @@ -70,10 +70,15 @@ describe("contractFromArtifact", () => { }); it("should be able to pass an after dependency", () => { + const otherModule = buildModule("Module2", (m) => { + const example = m.contract("Example"); + return { example }; + }); + const moduleWithDependentContracts = buildModule("Module1", (m) => { const example = m.contract("Example"); const another = m.contract("Another", fakeArtifact, [], { - after: [example], + after: [example, otherModule], }); return { example, another }; @@ -84,8 +89,9 @@ describe("contractFromArtifact", () => { const exampleFuture = moduleWithDependentContracts.results.example; const anotherFuture = moduleWithDependentContracts.results.another; - assert.equal(anotherFuture.dependencies.size, 1); + assert.equal(anotherFuture.dependencies.size, 2); assert(anotherFuture.dependencies.has(exampleFuture!)); + assert(anotherFuture.dependencies.has(otherModule)); }); it("should be able to pass a library as a dependency of a contract", () => { diff --git a/packages/core/test/encodeFunctionCall.ts b/packages/core/test/encodeFunctionCall.ts index 3d240913f..6dc23cea2 100644 --- a/packages/core/test/encodeFunctionCall.ts +++ b/packages/core/test/encodeFunctionCall.ts @@ -87,11 +87,18 @@ describe("encodeFunctionCall", () => { }); it("should be able to pass one contract as an after dependency of a call", () => { + const otherModule = buildModule("Module2", (m) => { + const example = m.contract("Example"); + return { example }; + }); + const moduleWithDependentContracts = buildModule("Module1", (m) => { const example = m.contract("Example"); const another = m.contract("Another"); - m.encodeFunctionCall(example, "test", [], { after: [another] }); + m.encodeFunctionCall(example, "test", [], { + after: [another, otherModule], + }); return { example, another }; }); @@ -114,9 +121,10 @@ describe("encodeFunctionCall", () => { assert.fail("Not a named encode function call future"); } - assert.equal(callFuture.dependencies.size, 2); + assert.equal(callFuture.dependencies.size, 3); assert(callFuture.dependencies.has(exampleFuture!)); assert(callFuture.dependencies.has(anotherFuture!)); + assert(callFuture.dependencies.has(otherModule!)); }); describe("Arguments", () => { diff --git a/packages/core/test/library.ts b/packages/core/test/library.ts index 88191fdb1..6ec758db0 100644 --- a/packages/core/test/library.ts +++ b/packages/core/test/library.ts @@ -45,9 +45,14 @@ describe("library", () => { }); it("should be able to pass one library as an after dependency of another", () => { + const otherModule = buildModule("Module2", (m) => { + const example = m.contract("Example"); + return { example }; + }); + const moduleWithDependentContracts = buildModule("Module1", (m) => { const example = m.library("Example"); - const another = m.library("Another", { after: [example] }); + const another = m.library("Another", { after: [example, otherModule] }); return { example, another }; }); @@ -68,8 +73,9 @@ describe("library", () => { assert.fail("Not a named library deployment"); } - assert.equal(anotherFuture.dependencies.size, 1); + assert.equal(anotherFuture.dependencies.size, 2); assert(anotherFuture.dependencies.has(exampleFuture!)); + assert(anotherFuture.dependencies.has(otherModule!)); }); it("should be able to pass a library as a dependency of a library", () => { diff --git a/packages/core/test/libraryFromArtifact.ts b/packages/core/test/libraryFromArtifact.ts index d63d214c2..c5dfe3375 100644 --- a/packages/core/test/libraryFromArtifact.ts +++ b/packages/core/test/libraryFromArtifact.ts @@ -41,10 +41,15 @@ describe("libraryFromArtifact", () => { }); it("should be able to pass an after dependency", () => { + const otherModule = buildModule("Module2", (m) => { + const example = m.contract("Example"); + return { example }; + }); + const moduleWithDependentContracts = buildModule("Module1", (m) => { const example = m.library("Example"); const another = m.library("Another", fakeArtifact, { - after: [example], + after: [example, otherModule], }); return { example, another }; @@ -55,8 +60,9 @@ describe("libraryFromArtifact", () => { const exampleFuture = moduleWithDependentContracts.results.example; const anotherFuture = moduleWithDependentContracts.results.another; - assert.equal(anotherFuture.dependencies.size, 1); + assert.equal(anotherFuture.dependencies.size, 2); assert(anotherFuture.dependencies.has(exampleFuture!)); + assert(anotherFuture.dependencies.has(otherModule!)); }); it("should be able to pass a library as a dependency of a library", () => { diff --git a/packages/core/test/send.ts b/packages/core/test/send.ts index b61b6868b..ee2846ca6 100644 --- a/packages/core/test/send.ts +++ b/packages/core/test/send.ts @@ -79,9 +79,16 @@ describe("send", () => { }); it("should be able to pass one contract as an after dependency of a send", () => { + const otherModule = buildModule("Module2", (m) => { + const example = m.contract("Example"); + return { example }; + }); + const moduleWithDependentContracts = buildModule("Module1", (m) => { const example = m.contract("Example"); - m.send("test_send", exampleAddress, 0n, "", { after: [example] }); + m.send("test_send", exampleAddress, 0n, "", { + after: [example, otherModule], + }); return { example }; }); @@ -100,8 +107,9 @@ describe("send", () => { assert.fail("Not a send data future"); } - assert.equal(sendFuture.dependencies.size, 1); + assert.equal(sendFuture.dependencies.size, 2); assert(sendFuture.dependencies.has(exampleFuture!)); + assert(sendFuture.dependencies.has(otherModule!)); }); it("should be able to pass a value", () => { diff --git a/packages/core/test/staticCall.ts b/packages/core/test/staticCall.ts index 1ce966d63..3da0fb56a 100644 --- a/packages/core/test/staticCall.ts +++ b/packages/core/test/staticCall.ts @@ -88,11 +88,16 @@ describe("static call", () => { }); it("should be able to pass one contract as an after dependency of a static call", () => { + const otherModule = buildModule("Module2", (m) => { + const example = m.contract("Example"); + return { example }; + }); + const moduleWithDependentContracts = buildModule("Module1", (m) => { const example = m.contract("Example"); const another = m.contract("Another"); - m.staticCall(example, "test", [], 0, { after: [another] }); + m.staticCall(example, "test", [], 0, { after: [another, otherModule] }); return { example, another }; }); @@ -115,9 +120,10 @@ describe("static call", () => { assert.fail("Not a named contract deployment"); } - assert.equal(callFuture.dependencies.size, 2); + assert.equal(callFuture.dependencies.size, 3); assert(callFuture.dependencies.has(exampleFuture!)); assert(callFuture.dependencies.has(anotherFuture!)); + assert(callFuture.dependencies.has(otherModule!)); }); it("should be able to pass its result into another call", () => { diff --git a/packages/core/test/types/module.ts b/packages/core/test/types/module.ts index 3adbad465..0eb14c6ae 100644 --- a/packages/core/test/types/module.ts +++ b/packages/core/test/types/module.ts @@ -17,7 +17,7 @@ interface BaseFuture { id: string; type: FutureType; module: IgnitionModule; - dependencies: Set; + dependencies: Set; } interface BaseRuntimeValue { diff --git a/packages/ui/examples/ComplexModule.js b/packages/ui/examples/ComplexModule.js index 519235d30..64db7dd2e 100644 --- a/packages/ui/examples/ComplexModule.js +++ b/packages/ui/examples/ComplexModule.js @@ -2,8 +2,18 @@ import { buildModule } from "@nomicfoundation/ignition-core"; const fakeArtifact = ["fake artifact"]; +const fakeModuleDependency = buildModule("FakeModule", (m) => { + const contract = m.contract("FakeContract", [1, 2, 3]); + + m.call(contract, "initialize", [4, 5, 6]); + + return {}; +}); + const uniswap = buildModule("Uniswap", (m) => { - const router = m.contract("UniswapRouter", [1, 2, 3]); + const router = m.contract("UniswapRouter", [1, 2, 3], { + after: [fakeModuleDependency], + }); m.call(router, "configure", [3, 4, 5]); diff --git a/packages/ui/src/main.css b/packages/ui/src/main.css index 5d0a72307..f46bfd003 100644 --- a/packages/ui/src/main.css +++ b/packages/ui/src/main.css @@ -43,6 +43,12 @@ body { max-width: 391px !important; } +span.future-to-module-arrow { + font-weight: 900 !important; + font-size: medium !important; + letter-spacing: -2px; +} + /* mermaid styles */ .mermaid { diff --git a/packages/ui/src/pages/visualization-overview/components/deployment-flow.tsx b/packages/ui/src/pages/visualization-overview/components/deployment-flow.tsx index 419ab844c..23c13e8b9 100644 --- a/packages/ui/src/pages/visualization-overview/components/deployment-flow.tsx +++ b/packages/ui/src/pages/visualization-overview/components/deployment-flow.tsx @@ -100,12 +100,19 @@ const FlowTooltip: React.FC = () => (
Diagram reference
- Future to future dependency + Future to future dependency         - ---> + + -----------> + +
+ Future to module dependency      + + ----> +
Module to module dependency     - - - -> + - - ->
); diff --git a/packages/ui/src/utils/to-mermaid.ts b/packages/ui/src/utils/to-mermaid.ts index 02d681db7..f37dcfd87 100644 --- a/packages/ui/src/utils/to-mermaid.ts +++ b/packages/ui/src/utils/to-mermaid.ts @@ -22,12 +22,15 @@ export function toMermaid( ...new Set( getAllFuturesForModule(ignitionModule) .flatMap((f) => - Array.from(f.dependencies).map((d) => [ + Array.from(f.dependencies).map<[string, string, boolean]>((d) => [ toEscapedId(f.id), toEscapedId(d.id), + /#/.test(d.id), ]) ) - .map(([from, to]) => `${from} --> ${to}`) + .map( + ([from, to, isFuture]) => `${from} ${isFuture ? "-->" : "==>"} ${to}` + ) ), ].join("\n"); From dd39beebe1acd4284bb852df450de2ab84a6ed8b Mon Sep 17 00:00:00 2001 From: zoeyTM Date: Wed, 20 Nov 2024 01:45:32 -0500 Subject: [PATCH 2/3] fix existing library bug and add comment for known issue --- .../components/deployment-flow.tsx | 7 +++++++ .../visualization-overview/components/future-header.tsx | 9 --------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/ui/src/pages/visualization-overview/components/deployment-flow.tsx b/packages/ui/src/pages/visualization-overview/components/deployment-flow.tsx index 23c13e8b9..99f6f5871 100644 --- a/packages/ui/src/pages/visualization-overview/components/deployment-flow.tsx +++ b/packages/ui/src/pages/visualization-overview/components/deployment-flow.tsx @@ -93,6 +93,13 @@ const VisualizeDiv = styled.div` width: 100%; `; +// TODO: when we added the future-to-module dependency, we created a non-ideal situation where +// a module-to-module dependency arrow still gets added to the mermaid graph, even if the dependant module +// is only used as a future-to-module dependency. This is because the dependant module has to get added to the +// parent module as a submodule, and we currently don't have a way of distinguishing at this point in the code between +// a submodule that is exclusively used as a future-to-module dependency (i.e. in { after: [...] }) +// and a submodule that is used as a module-to-module dependency (i.e. in m.useModule(...)). +// This is a known issue that we have decided to revisit at a later point in time because the solution is not trivial. const FlowTooltip: React.FC = () => ( diff --git a/packages/ui/src/pages/visualization-overview/components/future-header.tsx b/packages/ui/src/pages/visualization-overview/components/future-header.tsx index 211991dd6..ccbe4753a 100644 --- a/packages/ui/src/pages/visualization-overview/components/future-header.tsx +++ b/packages/ui/src/pages/visualization-overview/components/future-header.tsx @@ -8,15 +8,6 @@ export const FutureHeader: React.FC<{ setCurrentlyHovered: (id: string) => void; future: Future; }> = ({ isLibrary, toggled, displayText, setCurrentlyHovered, future }) => { - if (isLibrary) { - return ( - -
- {displayText} - - ); - } - return ( {isLibrary ?
: } From cf758da522a6adbfc9c0be1a6bdd09121fce9cd2 Mon Sep 17 00:00:00 2001 From: zoeyTM Date: Sun, 15 Dec 2024 04:18:27 -0500 Subject: [PATCH 3/3] add comment regarding testing for module id --- packages/core/src/internal/batcher.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/core/src/internal/batcher.ts b/packages/core/src/internal/batcher.ts index ef5264ae7..26bb2348d 100644 --- a/packages/core/src/internal/batcher.ts +++ b/packages/core/src/internal/batcher.ts @@ -131,6 +131,7 @@ export class Batcher { const dependencies = batchState.adjacencyList.getDependenciesFor(futureId); return [...dependencies].every((depId) => { + // We distinguish between module and future ids here, as the future's always have `#` and the modules don't. if (/#/.test(depId)) { return batchState.visitState[depId] === VisitStatus.VISITED; }