From 692c83fcc7f83228d4d191b4326d9a3bf36d9a8c Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Thu, 17 Apr 2025 03:37:11 +0100 Subject: [PATCH 1/5] feat: deployment files exported, added a getter of Ethers contracts --- contracts/deployments/contractsEthers.ts | 268 ++++++++++++++++++ contracts/deployments/contractsViem.ts | 65 +++++ contracts/deployments/index.ts | 12 + contracts/package.json | 2 + contracts/scripts/utils/contracts.ts | 31 +- .../integration/getContractsEthers.test.ts | 259 +++++++++++++++++ contracts/test/utils/getActualAddress.test.ts | 22 ++ contracts/test/utils/getActualAddress.ts | 23 ++ yarn.lock | 76 ++++- 9 files changed, 749 insertions(+), 9 deletions(-) create mode 100644 contracts/deployments/contractsEthers.ts create mode 100644 contracts/deployments/contractsViem.ts create mode 100644 contracts/deployments/index.ts create mode 100644 contracts/test/integration/getContractsEthers.test.ts create mode 100644 contracts/test/utils/getActualAddress.test.ts create mode 100644 contracts/test/utils/getActualAddress.ts diff --git a/contracts/deployments/contractsEthers.ts b/contracts/deployments/contractsEthers.ts new file mode 100644 index 000000000..3f99b13b3 --- /dev/null +++ b/contracts/deployments/contractsEthers.ts @@ -0,0 +1,268 @@ +import { ethers } from "ethers"; +import { arbitrum, arbitrumSepolia } from "viem/chains"; +import { + klerosCoreConfig as devnetCoreConfig, + sortitionModuleConfig as devnetSortitionConfig, + disputeKitClassicConfig as devnetDkcConfig, + disputeResolverConfig as devnetDrConfig, + disputeTemplateRegistryConfig as devnetDtrConfig, + evidenceModuleConfig as devnetEvidenceConfig, + policyRegistryConfig as devnetPolicyRegistryConfig, + transactionBatcherConfig as devnetBatcherConfig, + chainlinkRngConfig as devnetChainlinkRngConfig, + blockHashRngConfig as devnetBlockHashRngConfig, + pnkConfig as devnetPnkConfig, + klerosCoreSnapshotProxyConfig as devnetSnapshotProxyConfig, + klerosCoreUniversityConfig as devnetCoreUniversityConfig, + sortitionModuleUniversityConfig as devnetSortitionUniversityConfig, + disputeKitClassicUniversityConfig as devnetDkcUniversityConfig, + disputeResolverUniversityConfig as devnetDrUniversityConfig, + klerosCoreUniversityProxyConfig as devnetCoreUniversityProxyConfig, +} from "./devnet.viem"; +import { + klerosCoreConfig as testnetCoreConfig, + sortitionModuleConfig as testnetSortitionConfig, + disputeKitClassicConfig as testnetDkcConfig, + disputeResolverConfig as testnetDrConfig, + disputeTemplateRegistryConfig as testnetDtrConfig, + evidenceModuleConfig as testnetEvidenceConfig, + policyRegistryConfig as testnetPolicyRegistryConfig, + transactionBatcherConfig as testnetBatcherConfig, + chainlinkRngConfig as testnetChainlinkRngConfig, + blockHashRngConfig as testnetBlockHashRngConfig, + pnkConfig as testnetPnkConfig, + klerosCoreSnapshotProxyConfig as testnetSnapshotProxyConfig, +} from "./testnet.viem"; +import { + klerosCoreNeoConfig as mainnetCoreConfig, + sortitionModuleNeoConfig as mainnetSortitionConfig, + disputeKitClassicNeoConfig as mainnetDkcConfig, + disputeResolverNeoConfig as mainnetDrConfig, + disputeTemplateRegistryConfig as mainnetDtrConfig, + evidenceModuleConfig as mainnetEvidenceConfig, + policyRegistryConfig as mainnetPolicyRegistryConfig, + transactionBatcherConfig as mainnetBatcherConfig, + chainlinkRngConfig as mainnetChainlinkRngConfig, + randomizerRngConfig as mainnetRandomizerRngConfig, + blockHashRngConfig as mainnetBlockHashRngConfig, + pnkConfig as mainnetPnkConfig, + klerosCoreSnapshotProxyConfig as mainnetSnapshotProxyConfig, +} from "./mainnet.viem"; +import { + KlerosCore, + KlerosCore__factory, + SortitionModule, + SortitionModule__factory, + DisputeKitClassic, + DisputeKitClassic__factory, + DisputeResolver, + DisputeResolver__factory, + DisputeTemplateRegistry, + DisputeTemplateRegistry__factory, + EvidenceModule, + EvidenceModule__factory, + PolicyRegistry, + PolicyRegistry__factory, + TransactionBatcher, + TransactionBatcher__factory, + ChainlinkRNG, + ChainlinkRNG__factory, + RandomizerRNG, + RandomizerRNG__factory, + BlockHashRNG, + BlockHashRNG__factory, + PNK, + PNK__factory, + KlerosCoreSnapshotProxy, + KlerosCoreSnapshotProxy__factory, + KlerosCoreUniversity, + KlerosCoreUniversity__factory, + SortitionModuleUniversity, + SortitionModuleUniversity__factory, + KlerosCoreNeo, + KlerosCoreNeo__factory, + SortitionModuleNeo, + SortitionModuleNeo__factory, +} from "../typechain-types"; + +const deployments = { + devnet: { + chainId: arbitrumSepolia.id, + }, + university: { + chainId: arbitrumSepolia.id, + }, + testnet: { + chainId: arbitrumSepolia.id, + }, + mainnetNeo: { + chainId: arbitrum.id, + }, +} as const; + +type DeploymentName = keyof typeof deployments; + +type ContractConfig = { + address: Record; + abi: readonly any[]; +}; + +function getAddress(config: ContractConfig, chainId: number): `0x${string}` { + const address = config.address[chainId]; + if (!address) throw new Error(`No address found for chainId ${chainId}`); + return address; +} + +type CommonFactoriesConfigs = { + dkcConfig: ContractConfig; + drConfig: ContractConfig; + dtrConfig: ContractConfig; + evidenceConfig: ContractConfig; + policyRegistryConfig: ContractConfig; + batcherConfig: ContractConfig; + chainlinkRngConfig?: ContractConfig; + randomizerRngConfig?: ContractConfig; + blockHashRngConfig: ContractConfig; + pnkConfig: ContractConfig; + snapshotProxyConfig: ContractConfig; +}; + +type CommonFactories = { + disputeKitClassic: DisputeKitClassic; + disputeResolver: DisputeResolver; + disputeTemplateRegistry: DisputeTemplateRegistry; + evidence: EvidenceModule; + policyRegistry: PolicyRegistry; + transactionBatcher: TransactionBatcher; + chainlinkRng: ChainlinkRNG | null; + randomizerRng: RandomizerRNG | null; + blockHashRng: BlockHashRNG; + pnk: PNK; + klerosCoreSnapshotProxy: KlerosCoreSnapshotProxy; +}; + +function getCommonFactories( + configs: CommonFactoriesConfigs, + provider: ethers.Provider, + chainId: number +): CommonFactories { + return { + disputeKitClassic: DisputeKitClassic__factory.connect(getAddress(configs.dkcConfig, chainId), provider), + disputeResolver: DisputeResolver__factory.connect(getAddress(configs.drConfig, chainId), provider), + disputeTemplateRegistry: DisputeTemplateRegistry__factory.connect(getAddress(configs.dtrConfig, chainId), provider), + evidence: EvidenceModule__factory.connect(getAddress(configs.evidenceConfig, chainId), provider), + policyRegistry: PolicyRegistry__factory.connect(getAddress(configs.policyRegistryConfig, chainId), provider), + transactionBatcher: TransactionBatcher__factory.connect(getAddress(configs.batcherConfig, chainId), provider), + chainlinkRng: configs.chainlinkRngConfig + ? ChainlinkRNG__factory.connect(getAddress(configs.chainlinkRngConfig, chainId), provider) + : null, + randomizerRng: configs.randomizerRngConfig + ? RandomizerRNG__factory.connect(getAddress(configs.randomizerRngConfig, chainId), provider) + : null, + blockHashRng: BlockHashRNG__factory.connect(getAddress(configs.blockHashRngConfig, chainId), provider), + pnk: PNK__factory.connect(getAddress(configs.pnkConfig, chainId), provider), + klerosCoreSnapshotProxy: KlerosCoreSnapshotProxy__factory.connect( + getAddress(configs.snapshotProxyConfig, chainId), + provider + ), + }; +} + +export const getContracts = async (provider: ethers.Provider, deployment: DeploymentName) => { + const { chainId } = deployments[deployment]; + let klerosCore: KlerosCore | KlerosCoreNeo | KlerosCoreUniversity; + let sortition: SortitionModule | SortitionModuleNeo | SortitionModuleUniversity; + let commonFactories: CommonFactories; + + switch (deployment) { + case "devnet": { + klerosCore = KlerosCore__factory.connect(getAddress(devnetCoreConfig, chainId), provider); + sortition = SortitionModule__factory.connect(getAddress(devnetSortitionConfig, chainId), provider); + commonFactories = getCommonFactories( + { + dkcConfig: devnetDkcConfig, + drConfig: devnetDrConfig, + dtrConfig: devnetDtrConfig, + evidenceConfig: devnetEvidenceConfig, + policyRegistryConfig: devnetPolicyRegistryConfig, + batcherConfig: devnetBatcherConfig, + chainlinkRngConfig: devnetChainlinkRngConfig, + blockHashRngConfig: devnetBlockHashRngConfig, + pnkConfig: devnetPnkConfig, + snapshotProxyConfig: devnetSnapshotProxyConfig, + }, + provider, + chainId + ); + break; + } + case "university": { + klerosCore = KlerosCoreUniversity__factory.connect(getAddress(devnetCoreUniversityConfig, chainId), provider); + sortition = SortitionModuleUniversity__factory.connect( + getAddress(devnetSortitionUniversityConfig, chainId), + provider + ); + commonFactories = getCommonFactories( + { + dkcConfig: devnetDkcUniversityConfig, + drConfig: devnetDrUniversityConfig, + dtrConfig: devnetDtrConfig, + evidenceConfig: devnetEvidenceConfig, + policyRegistryConfig: devnetPolicyRegistryConfig, + batcherConfig: devnetBatcherConfig, + chainlinkRngConfig: devnetChainlinkRngConfig, + blockHashRngConfig: devnetBlockHashRngConfig, + pnkConfig: devnetPnkConfig, + snapshotProxyConfig: devnetSnapshotProxyConfig, + }, + provider, + chainId + ); + break; + } + case "testnet": + klerosCore = KlerosCore__factory.connect(getAddress(testnetCoreConfig, chainId), provider); + sortition = SortitionModule__factory.connect(getAddress(testnetSortitionConfig, chainId), provider); + commonFactories = getCommonFactories( + { + dkcConfig: testnetDkcConfig, + drConfig: testnetDrConfig, + dtrConfig: testnetDtrConfig, + evidenceConfig: testnetEvidenceConfig, + policyRegistryConfig: testnetPolicyRegistryConfig, + batcherConfig: testnetBatcherConfig, + chainlinkRngConfig: testnetChainlinkRngConfig, + blockHashRngConfig: testnetBlockHashRngConfig, + pnkConfig: testnetPnkConfig, + snapshotProxyConfig: testnetSnapshotProxyConfig, + }, + provider, + chainId + ); + break; + case "mainnetNeo": + klerosCore = KlerosCoreNeo__factory.connect(getAddress(mainnetCoreConfig, chainId), provider); + sortition = SortitionModuleNeo__factory.connect(getAddress(mainnetSortitionConfig, chainId), provider); + commonFactories = getCommonFactories( + { + dkcConfig: mainnetDkcConfig, + drConfig: mainnetDrConfig, + dtrConfig: mainnetDtrConfig, + evidenceConfig: mainnetEvidenceConfig, + policyRegistryConfig: mainnetPolicyRegistryConfig, + batcherConfig: mainnetBatcherConfig, + chainlinkRngConfig: mainnetChainlinkRngConfig, + randomizerRngConfig: mainnetRandomizerRngConfig, + blockHashRngConfig: mainnetBlockHashRngConfig, + pnkConfig: mainnetPnkConfig, + snapshotProxyConfig: mainnetSnapshotProxyConfig, + }, + provider, + chainId + ); + break; + default: + throw new Error(`Unsupported deployment: ${deployment}`); + } + return { klerosCore, sortition, ...commonFactories }; +}; diff --git a/contracts/deployments/contractsViem.ts b/contracts/deployments/contractsViem.ts new file mode 100644 index 000000000..27a4f9c5e --- /dev/null +++ b/contracts/deployments/contractsViem.ts @@ -0,0 +1,65 @@ +import { arbitrum, arbitrumSepolia } from "viem/chains"; +import { disputeTemplateRegistryConfig as devnetDtrConfig, klerosCoreConfig as devnetCoreConfig } from "./devnet.viem"; +import { + disputeTemplateRegistryConfig as mainnetDtrConfig, + klerosCoreNeoConfig as mainnetCoreConfig, +} from "./mainnet.viem"; +import { type PublicClient, type WalletClient } from "viem"; + +export const Cores = { + BASE: "BASE", + NEO: "NEO", + UNIVERSITY: "UNIVERSITY", +} as const; + +export type Core = (typeof Cores)[keyof typeof Cores]; + +export const getContracts = ({ + publicClient, + walletClient, + chainId, + coreType, +}: { + publicClient: PublicClient; + walletClient?: WalletClient; + chainId: number; + coreType: Core; +}) => { + throw new Error("Not implemented"); + switch (chainId) { + case arbitrum.id: + return { + disputeTemplateRegistry: { + address: mainnetDtrConfig.address[chainId], + publicClient, + walletClient, + abi: mainnetDtrConfig.abi, + }, + klerosCore: { + address: mainnetCoreConfig.address[chainId], + publicClient, + walletClient, + abi: mainnetCoreConfig.abi, + }, + }; + case arbitrumSepolia.id: + return { + disputeTemplateRegistry: { + address: devnetDtrConfig.address[chainId], + publicClient, + walletClient, + abi: devnetDtrConfig.abi, + }, + klerosCore: { + address: devnetCoreConfig.address[chainId], + publicClient, + walletClient, + abi: devnetCoreConfig.abi, + }, + }; + default: + throw new Error(`Unsupported chainId: ${chainId}`); + } +}; + +// TODO: do the same for Viem ? diff --git a/contracts/deployments/index.ts b/contracts/deployments/index.ts new file mode 100644 index 000000000..2dc723a46 --- /dev/null +++ b/contracts/deployments/index.ts @@ -0,0 +1,12 @@ +// Typechain Ethers v6 artifacts +export * as arbitrum from "./arbitrum"; +export * as arbitrumSepolia from "./arbitrumSepolia"; +export * as arbitrumSepoliaDevnet from "./arbitrumSepoliaDevnet"; + +// Viem artifacts +export * as devnetViem from "./devnet.viem"; +export * as mainnetViem from "./mainnet.viem"; +export * as testnetViem from "./testnet.viem"; + +export { getContracts as getContractsEthers } from "./contractsEthers"; +export { getContracts as getContractsViem } from "./contractsViem"; diff --git a/contracts/package.json b/contracts/package.json index 641cac636..d4d55deba 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -84,6 +84,7 @@ "@types/chai": "^4.3.20", "@types/mocha": "^10.0.10", "@types/node": "^20.17.6", + "@types/sinon": "^17.0.4", "@wagmi/cli": "^2.2.0", "abitype": "^0.10.3", "chai": "^4.5.0", @@ -109,6 +110,7 @@ "prettier": "^3.3.3", "prettier-plugin-solidity": "^1.4.2", "shelljs": "^0.8.5", + "sinon": "^20.0.0", "solhint-plugin-prettier": "^0.1.0", "solidity-coverage": "^0.8.13", "ts-node": "^10.9.2", diff --git a/contracts/scripts/utils/contracts.ts b/contracts/scripts/utils/contracts.ts index 933f889c8..31321fbfd 100644 --- a/contracts/scripts/utils/contracts.ts +++ b/contracts/scripts/utils/contracts.ts @@ -27,6 +27,11 @@ export const Cores = { export type Core = (typeof Cores)[keyof typeof Cores]; +/** + * Get contract names by specifying the coreType (BASE, NEO, UNIVERSITY). + * @param coreType - Core type + * @returns Contract names + */ export const getContractNames = (coreType: Core) => { const coreSpecificNames = { [Cores.NEO]: { @@ -65,34 +70,34 @@ export const getContractNames = (coreType: Core) => { }; }; +/** + * Get contracts by specifying the coreType (BASE, NEO, UNIVERSITY). + * @param hre - Hardhat runtime environment + * @param coreType - Core type + * @returns Contracts + */ export const getContracts = async (hre: HardhatRuntimeEnvironment, coreType: Core) => { const { ethers } = hre; let core: KlerosCore | KlerosCoreNeo | KlerosCoreUniversity; let sortition: SortitionModule | SortitionModuleNeo | SortitionModuleUniversity; - let disputeKitClassic: DisputeKitClassic; - let disputeResolver: DisputeResolver; switch (coreType) { case Cores.NEO: core = await ethers.getContract(getContractNames(coreType).core); sortition = await ethers.getContract(getContractNames(coreType).sortition); - disputeKitClassic = await ethers.getContract(getContractNames(coreType).disputeKitClassic); - disputeResolver = await ethers.getContract(getContractNames(coreType).disputeResolver); break; case Cores.BASE: core = await ethers.getContract(getContractNames(coreType).core); sortition = await ethers.getContract(getContractNames(coreType).sortition); - disputeKitClassic = await ethers.getContract(getContractNames(coreType).disputeKitClassic); - disputeResolver = await ethers.getContract(getContractNames(coreType).disputeResolver); break; case Cores.UNIVERSITY: core = await ethers.getContract(getContractNames(coreType).core); sortition = await ethers.getContract(getContractNames(coreType).sortition); - disputeKitClassic = await ethers.getContract(getContractNames(coreType).disputeKitClassic); - disputeResolver = await ethers.getContract(getContractNames(coreType).disputeResolver); break; default: throw new Error("Invalid core type, must be one of BASE, NEO, or UNIVERSITY"); } + const disputeKitClassic = await ethers.getContract(getContractNames(coreType).disputeKitClassic); + const disputeResolver = await ethers.getContract(getContractNames(coreType).disputeResolver); const disputeTemplateRegistry = await ethers.getContract( getContractNames(coreType).disputeTemplateRegistry ); @@ -123,6 +128,11 @@ export const getContracts = async (hre: HardhatRuntimeEnvironment, coreType: Cor }; }; +/** + * Get contracts by inferring the coreType (BASE, NEO, UNIVERSITY) from the network, most convenient for most cases. + * @param hre - Hardhat runtime environment + * @returns Contracts + */ export const getContractsFromNetwork = async (hre: HardhatRuntimeEnvironment) => { const { network } = hre; if (network.name === "arbitrumSepoliaDevnet" || network.name === "arbitrumSepolia") { @@ -134,6 +144,11 @@ export const getContractsFromNetwork = async (hre: HardhatRuntimeEnvironment) => } }; +/** + * Get contract names by inferring the coreType (BASE, NEO, UNIVERSITY) from the network, most convenient for most cases. + * @param hre - Hardhat runtime environment + * @returns Contract names + */ export const getContractNamesFromNetwork = async (hre: HardhatRuntimeEnvironment) => { const { network } = hre; if (network.name === "arbitrumSepoliaDevnet" || network.name === "arbitrumSepolia") { diff --git a/contracts/test/integration/getContractsEthers.test.ts b/contracts/test/integration/getContractsEthers.test.ts new file mode 100644 index 000000000..84a265a5d --- /dev/null +++ b/contracts/test/integration/getContractsEthers.test.ts @@ -0,0 +1,259 @@ +import { expect } from "chai"; +import { ethers } from "ethers"; +import { arbitrum, arbitrumSepolia } from "viem/chains"; +import { getContracts } from "../../deployments/contractsEthers"; +import { + KlerosCore__factory, + KlerosCoreNeo__factory, + KlerosCoreUniversity__factory, + SortitionModule__factory, + SortitionModuleNeo__factory, + SortitionModuleUniversity__factory, + DisputeKitClassic__factory, + DisputeResolver__factory, + DisputeTemplateRegistry__factory, + EvidenceModule__factory, + PolicyRegistry__factory, + TransactionBatcher__factory, + ChainlinkRNG__factory, + RandomizerRNG__factory, + BlockHashRNG__factory, + PNK__factory, + KlerosCoreSnapshotProxy__factory, +} from "../../typechain-types"; +import { getActualAddress } from "../utils/getActualAddress"; + +// Network names for deployments +const NETWORKS = { + DEVNET: "arbitrumSepoliaDevnet", + TESTNET: "arbitrumSepolia", + MAINNET: "arbitrum", +} as const; + +type NetworkType = (typeof NETWORKS)[keyof typeof NETWORKS]; + +type ContractMapping = { + [K in keyof Awaited>]: { + name: string; + optional?: boolean; + }; +}; + +const baseContractMapping: ContractMapping = { + klerosCore: { name: "KlerosCore" }, + sortition: { name: "SortitionModule" }, + disputeKitClassic: { name: "DisputeKitClassic" }, + disputeResolver: { name: "DisputeResolver" }, + disputeTemplateRegistry: { name: "DisputeTemplateRegistry" }, + evidence: { name: "EvidenceModule" }, + policyRegistry: { name: "PolicyRegistry" }, + transactionBatcher: { name: "TransactionBatcher" }, + chainlinkRng: { name: "ChainlinkRNG", optional: true }, + randomizerRng: { name: "RandomizerRNG", optional: true }, + blockHashRng: { name: "BlockHashRNG" }, + pnk: { name: "PNK" }, + klerosCoreSnapshotProxy: { name: "KlerosCoreSnapshotProxy" }, +}; + +const universityContractMapping: ContractMapping = { + klerosCore: { name: "KlerosCoreUniversity" }, + sortition: { name: "SortitionModuleUniversity" }, + disputeKitClassic: { name: "DisputeKitClassicUniversity" }, + disputeResolver: { name: "DisputeResolverUniversity" }, + disputeTemplateRegistry: { name: "DisputeTemplateRegistry" }, + evidence: { name: "EvidenceModule" }, + policyRegistry: { name: "PolicyRegistry" }, + transactionBatcher: { name: "TransactionBatcher" }, + chainlinkRng: { name: "ChainlinkRNG", optional: true }, + randomizerRng: { name: "RandomizerRNG", optional: true }, + blockHashRng: { name: "BlockHashRNG" }, + pnk: { name: "PNK" }, + klerosCoreSnapshotProxy: { name: "KlerosCoreSnapshotProxy" }, +}; + +const neoContractMapping: ContractMapping = { + klerosCore: { name: "KlerosCoreNeo" }, + sortition: { name: "SortitionModuleNeo" }, + disputeKitClassic: { name: "DisputeKitClassicNeo" }, + disputeResolver: { name: "DisputeResolverNeo" }, + disputeTemplateRegistry: { name: "DisputeTemplateRegistry" }, + evidence: { name: "EvidenceModule" }, + policyRegistry: { name: "PolicyRegistry" }, + transactionBatcher: { name: "TransactionBatcher" }, + chainlinkRng: { name: "ChainlinkRNG", optional: false }, + randomizerRng: { name: "RandomizerRNG", optional: false }, + blockHashRng: { name: "BlockHashRNG" }, + pnk: { name: "PNK" }, + klerosCoreSnapshotProxy: { name: "KlerosCoreSnapshotProxy" }, +}; + +describe("getContracts", () => { + // Use real providers for each network + const arbitrumSepoliaProvider = new ethers.JsonRpcProvider("https://sepolia-rollup.arbitrum.io/rpc"); + const arbitrumProvider = new ethers.JsonRpcProvider("https://arb1.arbitrum.io/rpc"); + + function getConstructor( + factory: { connect: (address: string, provider: ethers.Provider) => T }, + provider: ethers.Provider + ) { + return factory.connect("0x0", provider).constructor; + } + + function verifyCommonContractInstances( + contracts: Awaited>, + provider: ethers.Provider + ) { + expect(contracts.disputeKitClassic).to.be.instanceOf(getConstructor(DisputeKitClassic__factory, provider)); + expect(contracts.disputeResolver).to.be.instanceOf(getConstructor(DisputeResolver__factory, provider)); + expect(contracts.disputeTemplateRegistry).to.be.instanceOf( + getConstructor(DisputeTemplateRegistry__factory, provider) + ); + expect(contracts.evidence).to.be.instanceOf(getConstructor(EvidenceModule__factory, provider)); + expect(contracts.policyRegistry).to.be.instanceOf(getConstructor(PolicyRegistry__factory, provider)); + expect(contracts.transactionBatcher).to.be.instanceOf(getConstructor(TransactionBatcher__factory, provider)); + expect(contracts.blockHashRng).to.be.instanceOf(getConstructor(BlockHashRNG__factory, provider)); + expect(contracts.pnk).to.be.instanceOf(getConstructor(PNK__factory, provider)); + expect(contracts.klerosCoreSnapshotProxy).to.be.instanceOf( + getConstructor(KlerosCoreSnapshotProxy__factory, provider) + ); + if (contracts.chainlinkRng) { + expect(contracts.chainlinkRng).to.be.instanceOf(getConstructor(ChainlinkRNG__factory, provider)); + } + if (contracts.randomizerRng) { + expect(contracts.randomizerRng).to.be.instanceOf(getConstructor(RandomizerRNG__factory, provider)); + } + } + + // Helper to verify contract addresses + async function verifyContractAddress(address: Promise) { + const resolvedAddress = await address; + expect(resolvedAddress).to.match(/^0x[a-fA-F0-9]{40}$/); + expect(resolvedAddress).to.not.equal("0x0000000000000000000000000000000000000000"); + } + + // Helper to verify all contract addresses + async function verifyAllContractAddresses(contracts: Awaited>) { + await verifyContractAddress(contracts.klerosCore.getAddress()); + await verifyContractAddress(contracts.sortition.getAddress()); + await verifyContractAddress(contracts.disputeKitClassic.getAddress()); + await verifyContractAddress(contracts.disputeResolver.getAddress()); + await verifyContractAddress(contracts.disputeTemplateRegistry.getAddress()); + await verifyContractAddress(contracts.evidence.getAddress()); + await verifyContractAddress(contracts.policyRegistry.getAddress()); + await verifyContractAddress(contracts.transactionBatcher.getAddress()); + if (contracts.chainlinkRng) { + await verifyContractAddress(contracts.chainlinkRng.getAddress()); + } + if (contracts.randomizerRng) { + await verifyContractAddress(contracts.randomizerRng.getAddress()); + } + await verifyContractAddress(contracts.blockHashRng.getAddress()); + await verifyContractAddress(contracts.pnk.getAddress()); + await verifyContractAddress(contracts.klerosCoreSnapshotProxy.getAddress()); + } + + // Helper to verify contract addresses against deployment files + async function verifyDeployedAddresses( + contracts: Awaited>, + network: NetworkType, + contractMapping: ContractMapping + ) { + for (const [key, { name, optional }] of Object.entries(contractMapping)) { + const contract = contracts[key as keyof typeof contracts]; + if (contract === null) { + if (!optional) { + throw new Error(`Required contract ${name} is null`); + } + continue; + } + expect(await contract.getAddress()).to.equal(await getActualAddress(network, name)); + } + } + + it("should return correct contract instances for devnet", async () => { + const contracts = await getContracts(arbitrumSepoliaProvider, "devnet"); + + // Verify chain ID + const network = await arbitrumSepoliaProvider.getNetwork(); + expect(network.chainId).to.equal(arbitrumSepolia.id); + + // Verify contract instances + expect(contracts.klerosCore).to.be.instanceOf(getConstructor(KlerosCore__factory, arbitrumSepoliaProvider)); + expect(contracts.sortition).to.be.instanceOf(getConstructor(SortitionModule__factory, arbitrumSepoliaProvider)); + verifyCommonContractInstances(contracts, arbitrumSepoliaProvider); + expect(contracts.chainlinkRng).to.not.be.null; + expect(contracts.randomizerRng).to.be.null; + + // Verify all contract addresses + await verifyAllContractAddresses(contracts); + await verifyDeployedAddresses(contracts, NETWORKS.DEVNET, baseContractMapping); + }); + + it("should return correct contract instances for university", async () => { + const contracts = await getContracts(arbitrumSepoliaProvider, "university"); + + // Verify chain ID + const network = await arbitrumSepoliaProvider.getNetwork(); + expect(network.chainId).to.equal(arbitrumSepolia.id); + + // Verify contract instances + expect(contracts.klerosCore).to.be.instanceOf( + getConstructor(KlerosCoreUniversity__factory, arbitrumSepoliaProvider) + ); + expect(contracts.sortition).to.be.instanceOf( + getConstructor(SortitionModuleUniversity__factory, arbitrumSepoliaProvider) + ); + verifyCommonContractInstances(contracts, arbitrumSepoliaProvider); + expect(contracts.chainlinkRng).to.not.be.null; + expect(contracts.randomizerRng).to.be.null; + + // Verify all contract addresses + await verifyAllContractAddresses(contracts); + await verifyDeployedAddresses(contracts, NETWORKS.DEVNET, universityContractMapping); + }); + + it("should return correct contract instances for testnet", async () => { + const contracts = await getContracts(arbitrumSepoliaProvider, "testnet"); + + // Verify chain ID + const network = await arbitrumSepoliaProvider.getNetwork(); + expect(network.chainId).to.equal(arbitrumSepolia.id); + + // Verify contract instances + expect(contracts.klerosCore).to.be.instanceOf(getConstructor(KlerosCore__factory, arbitrumSepoliaProvider)); + expect(contracts.sortition).to.be.instanceOf(getConstructor(SortitionModule__factory, arbitrumSepoliaProvider)); + verifyCommonContractInstances(contracts, arbitrumSepoliaProvider); + expect(contracts.chainlinkRng).to.not.be.null; + expect(contracts.randomizerRng).to.be.null; + + // Verify all contract addresses + await verifyAllContractAddresses(contracts); + await verifyDeployedAddresses(contracts, NETWORKS.TESTNET, baseContractMapping); + }); + + it("should return correct contract instances for mainnetNeo", async () => { + const contracts = await getContracts(arbitrumProvider, "mainnetNeo"); + + // Verify chain ID + const network = await arbitrumProvider.getNetwork(); + expect(network.chainId).to.equal(arbitrum.id); + + // Verify contract instances + expect(contracts.klerosCore).to.be.instanceOf(getConstructor(KlerosCoreNeo__factory, arbitrumProvider)); + expect(contracts.sortition).to.be.instanceOf(getConstructor(SortitionModuleNeo__factory, arbitrumProvider)); + verifyCommonContractInstances(contracts, arbitrumProvider); + expect(contracts.chainlinkRng).to.not.be.null; + expect(contracts.randomizerRng).to.not.be.null; + + // Verify all contract addresses + await verifyAllContractAddresses(contracts); + await verifyDeployedAddresses(contracts, NETWORKS.MAINNET, neoContractMapping); + }); + + it("should throw error for unsupported deployment", async () => { + // @ts-expect-error Testing invalid deployment + await expect(getContracts(arbitrumSepoliaProvider, "invalid")).to.be.rejectedWith( + /Unsupported deployment|Cannot destructure property/ + ); + }); +}); diff --git a/contracts/test/utils/getActualAddress.test.ts b/contracts/test/utils/getActualAddress.test.ts new file mode 100644 index 000000000..c80569a07 --- /dev/null +++ b/contracts/test/utils/getActualAddress.test.ts @@ -0,0 +1,22 @@ +import { expect } from "chai"; +import { getActualAddress } from "./getActualAddress"; + +describe("getActualAddress", () => { + it("should return the correct address for KlerosCore on arbitrumSepoliaDevnet", async () => { + const address = await getActualAddress("arbitrumSepoliaDevnet", "KlerosCore"); + expect(address).to.match(/^0x[a-fA-F0-9]{40}$/); + expect(address).to.not.equal("0x0000000000000000000000000000000000000000"); + }); + + it("should throw error for non-existent network", async () => { + await expect(getActualAddress("nonexistentNetwork", "KlerosCore")).to.be.rejectedWith( + "No deployment file found for KlerosCore on nonexistentNetwork" + ); + }); + + it("should throw error for non-existent contract", async () => { + await expect(getActualAddress("arbitrumSepoliaDevnet", "NonExistentContract")).to.be.rejectedWith( + "No deployment file found for NonExistentContract on arbitrumSepoliaDevnet" + ); + }); +}); diff --git a/contracts/test/utils/getActualAddress.ts b/contracts/test/utils/getActualAddress.ts new file mode 100644 index 000000000..8e38e16b3 --- /dev/null +++ b/contracts/test/utils/getActualAddress.ts @@ -0,0 +1,23 @@ +/** + * Get the deployed address of a contract from its deployment JSON file + * @param network The network name (e.g., "arbitrumSepoliaDevnet") + * @param contractName The contract name (e.g., "KlerosCore") + * @returns The deployed contract address + * @throws Error if the deployment file doesn't exist or has no address + */ +export async function getActualAddress(network: string, contractName: string): Promise { + try { + const deployment = await import(`../../deployments/${network}/${contractName}.json`, { + assert: { type: "json" }, + }); + if (!deployment.default.address) { + throw new Error(`No address found in deployment file for ${contractName} on ${network}`); + } + return deployment.default.address; + } catch (error) { + if (error instanceof Error && error.message.includes("Cannot find module")) { + throw new Error(`No deployment file found for ${contractName} on ${network}`); + } + throw error; + } +} diff --git a/yarn.lock b/yarn.lock index 631f38963..6aa34039e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5565,6 +5565,7 @@ __metadata: "@types/chai": "npm:^4.3.20" "@types/mocha": "npm:^10.0.10" "@types/node": "npm:^20.17.6" + "@types/sinon": "npm:^17.0.4" "@wagmi/cli": "npm:^2.2.0" abitype: "npm:^0.10.3" chai: "npm:^4.5.0" @@ -5590,6 +5591,7 @@ __metadata: prettier: "npm:^3.3.3" prettier-plugin-solidity: "npm:^1.4.2" shelljs: "npm:^0.8.5" + sinon: "npm:^20.0.0" solhint-plugin-prettier: "npm:^0.1.0" solidity-coverage: "npm:^0.8.13" ts-node: "npm:^10.9.2" @@ -8589,6 +8591,24 @@ __metadata: languageName: node linkType: hard +"@sinonjs/commons@npm:^3.0.1": + version: 3.0.1 + resolution: "@sinonjs/commons@npm:3.0.1" + dependencies: + type-detect: "npm:4.0.8" + checksum: 10/a0af217ba7044426c78df52c23cedede6daf377586f3ac58857c565769358ab1f44ebf95ba04bbe38814fba6e316ca6f02870a009328294fc2c555d0f85a7117 + languageName: node + linkType: hard + +"@sinonjs/fake-timers@npm:^13.0.5": + version: 13.0.5 + resolution: "@sinonjs/fake-timers@npm:13.0.5" + dependencies: + "@sinonjs/commons": "npm:^3.0.1" + checksum: 10/11ee417968fc4dce1896ab332ac13f353866075a9d2a88ed1f6258f17cc4f7d93e66031b51fcddb8c203aa4d53fd980b0ae18aba06269f4682164878a992ec3f + languageName: node + linkType: hard + "@sinonjs/fake-timers@npm:^8.0.1": version: 8.1.0 resolution: "@sinonjs/fake-timers@npm:8.1.0" @@ -8598,6 +8618,17 @@ __metadata: languageName: node linkType: hard +"@sinonjs/samsam@npm:^8.0.1": + version: 8.0.2 + resolution: "@sinonjs/samsam@npm:8.0.2" + dependencies: + "@sinonjs/commons": "npm:^3.0.1" + lodash.get: "npm:^4.4.2" + type-detect: "npm:^4.1.0" + checksum: 10/58ca9752e8e835a09ed275f8edf8da2720fe95c0c02f6bcb90ad7f86fdceb393f35f744194b705dd94216228646ec0aedbb814e245eb869b940dcf1266b7a533 + languageName: node + linkType: hard + "@socket.io/component-emitter@npm:~3.1.0": version: 3.1.0 resolution: "@socket.io/component-emitter@npm:3.1.0" @@ -9978,6 +10009,22 @@ __metadata: languageName: node linkType: hard +"@types/sinon@npm:^17.0.4": + version: 17.0.4 + resolution: "@types/sinon@npm:17.0.4" + dependencies: + "@types/sinonjs__fake-timers": "npm:*" + checksum: 10/286c34e66e3573673ba59a332ac81189e20dd591c5c5360c8ff3ed83a59a60bdb1d4c8f13ab8863a4d5ce636282e4b11c640b87f398663eee152988ca09b1933 + languageName: node + linkType: hard + +"@types/sinonjs__fake-timers@npm:*": + version: 8.1.5 + resolution: "@types/sinonjs__fake-timers@npm:8.1.5" + checksum: 10/3a0b285fcb8e1eca435266faa27ffff206608b69041022a42857274e44d9305822e85af5e7a43a9fae78d2ab7dc0fcb49f3ae3bda1fa81f0203064dbf5afd4f6 + languageName: node + linkType: hard + "@types/sockjs@npm:^0.3.33": version: 0.3.33 resolution: "@types/sockjs@npm:0.3.33" @@ -15793,6 +15840,13 @@ __metadata: languageName: node linkType: hard +"diff@npm:^7.0.0": + version: 7.0.0 + resolution: "diff@npm:7.0.0" + checksum: 10/e9b8e48d054c9c0c093c65ce8e2637af94b35f2427001607b14e5e0589e534ea3413a7f91ebe6d7c5a1494ace49cb7c7c3972f442ddd96a4767ff091999a082e + languageName: node + linkType: hard + "diffie-hellman@npm:^5.0.3": version: 5.0.3 resolution: "diffie-hellman@npm:5.0.3" @@ -22967,6 +23021,13 @@ __metadata: languageName: node linkType: hard +"lodash.get@npm:^4.4.2": + version: 4.4.2 + resolution: "lodash.get@npm:4.4.2" + checksum: 10/2a4925f6e89bc2c010a77a802d1ba357e17ed1ea03c2ddf6a146429f2856a216663e694a6aa3549a318cbbba3fd8b7decb392db457e6ac0b83dc745ed0a17380 + languageName: node + linkType: hard + "lodash.isarguments@npm:^3.1.0": version: 3.1.0 resolution: "lodash.isarguments@npm:3.1.0" @@ -30112,6 +30173,19 @@ __metadata: languageName: node linkType: hard +"sinon@npm:^20.0.0": + version: 20.0.0 + resolution: "sinon@npm:20.0.0" + dependencies: + "@sinonjs/commons": "npm:^3.0.1" + "@sinonjs/fake-timers": "npm:^13.0.5" + "@sinonjs/samsam": "npm:^8.0.1" + diff: "npm:^7.0.0" + supports-color: "npm:^7.2.0" + checksum: 10/825cb36a58c0510cec03d9bef4fe66a12baf0e0cfdf1600423e3da1e6d57a03fe8161f4859340ea13d4c42e63da1724a260ef4c5ce119dc9ee075ad93b6e8bdd + languageName: node + linkType: hard + "sirv@npm:^2.0.4": version: 2.0.4 resolution: "sirv@npm:2.0.4" @@ -31300,7 +31374,7 @@ __metadata: languageName: node linkType: hard -"supports-color@npm:^7.0.0, supports-color@npm:^7.1.0": +"supports-color@npm:^7.0.0, supports-color@npm:^7.1.0, supports-color@npm:^7.2.0": version: 7.2.0 resolution: "supports-color@npm:7.2.0" dependencies: From c1323032add32b32040cd2184cee22fcc3dd9b61 Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Thu, 17 Apr 2025 16:41:46 +0100 Subject: [PATCH 2/5] feat: packaging to both CJS and ESM --- contracts/deployments/contractsViem.ts | 140 ++++++++++++++++--------- contracts/deployments/index.ts | 4 + contracts/package.json | 36 ++++++- contracts/scripts/publish.sh | 36 ++++++- contracts/tsconfig-release.json | 1 - contracts/wagmi.config.devnet.ts | 2 +- contracts/wagmi.config.mainnet.ts | 2 +- contracts/wagmi.config.testnet.ts | 2 +- 8 files changed, 161 insertions(+), 62 deletions(-) diff --git a/contracts/deployments/contractsViem.ts b/contracts/deployments/contractsViem.ts index 27a4f9c5e..5492c6118 100644 --- a/contracts/deployments/contractsViem.ts +++ b/contracts/deployments/contractsViem.ts @@ -1,65 +1,107 @@ import { arbitrum, arbitrumSepolia } from "viem/chains"; -import { disputeTemplateRegistryConfig as devnetDtrConfig, klerosCoreConfig as devnetCoreConfig } from "./devnet.viem"; import { - disputeTemplateRegistryConfig as mainnetDtrConfig, - klerosCoreNeoConfig as mainnetCoreConfig, -} from "./mainnet.viem"; -import { type PublicClient, type WalletClient } from "viem"; + klerosCoreConfig as devnetCoreConfig, + sortitionModuleConfig as devnetSortitionConfig, + disputeKitClassicConfig as devnetDkcConfig, + disputeResolverConfig as devnetDrConfig, + disputeTemplateRegistryConfig as devnetDtrConfig, + evidenceModuleConfig as devnetEvidenceConfig, + policyRegistryConfig as devnetPolicyRegistryConfig, + transactionBatcherConfig as devnetBatcherConfig, + chainlinkRngConfig as devnetChainlinkRngConfig, + blockHashRngConfig as devnetBlockHashRngConfig, + pnkConfig as devnetPnkConfig, + klerosCoreSnapshotProxyConfig as devnetSnapshotProxyConfig, + klerosCoreUniversityConfig as devnetCoreUniversityConfig, + sortitionModuleUniversityConfig as devnetSortitionUniversityConfig, + disputeKitClassicUniversityConfig as devnetDkcUniversityConfig, + disputeResolverUniversityConfig as devnetDrUniversityConfig, + klerosCoreUniversityProxyConfig as devnetCoreUniversityProxyConfig, +} from "./devnet.viem"; +import { type PublicClient, type WalletClient, getContract } from "viem"; -export const Cores = { - BASE: "BASE", - NEO: "NEO", - UNIVERSITY: "UNIVERSITY", +const deployments = { + devnet: { + chainId: arbitrumSepolia.id, + }, + university: { + chainId: arbitrumSepolia.id, + }, + testnet: { + chainId: arbitrumSepolia.id, + }, + mainnetNeo: { + chainId: arbitrum.id, + }, } as const; -export type Core = (typeof Cores)[keyof typeof Cores]; +type DeploymentName = keyof typeof deployments; + +type ContractConfig = { + address: Record; + abi: readonly any[]; +}; + +type ContractConfigs = { + klerosCore?: { + address: `0x${string}`; + abi: readonly any[]; + }; +}; + +function getAddress(config: ContractConfig, chainId: number): `0x${string}` { + const address = config.address[chainId]; + if (!address) throw new Error(`No address found for chainId ${chainId}`); + return address; +} + +export const getConfigs = ({ deployment }: { deployment: DeploymentName }): ContractConfigs => { + const { chainId } = deployments[deployment]; + let contractConfigs: ContractConfigs = {}; + switch (deployment) { + case "devnet": + contractConfigs = { + klerosCore: { + address: getAddress(devnetCoreConfig, chainId), + abi: devnetCoreConfig.abi, + }, + }; + break; + default: + throw new Error(`Unsupported deployment: ${deployment}`); + } + return contractConfigs; +}; export const getContracts = ({ publicClient, walletClient, - chainId, - coreType, + deployment, }: { publicClient: PublicClient; walletClient?: WalletClient; - chainId: number; - coreType: Core; + deployment: DeploymentName; }) => { - throw new Error("Not implemented"); - switch (chainId) { - case arbitrum.id: - return { - disputeTemplateRegistry: { - address: mainnetDtrConfig.address[chainId], - publicClient, - walletClient, - abi: mainnetDtrConfig.abi, - }, - klerosCore: { - address: mainnetCoreConfig.address[chainId], - publicClient, - walletClient, - abi: mainnetCoreConfig.abi, - }, - }; - case arbitrumSepolia.id: - return { - disputeTemplateRegistry: { - address: devnetDtrConfig.address[chainId], - publicClient, - walletClient, - abi: devnetDtrConfig.abi, - }, - klerosCore: { - address: devnetCoreConfig.address[chainId], - publicClient, - walletClient, - abi: devnetCoreConfig.abi, - }, - }; + const clientConfig = { + client: { + public: publicClient, + wallet: walletClient, + }, + }; + let klerosCore; + switch (deployment) { + case "devnet": + const contractConfigs = getConfigs({ deployment }); + if (!contractConfigs.klerosCore) throw new Error("KlerosCore config not found"); + klerosCore = getContract({ + ...contractConfigs.klerosCore, + ...clientConfig, + }); + break; default: - throw new Error(`Unsupported chainId: ${chainId}`); + throw new Error(`Unsupported deployment: ${deployment}`); } + return { + klerosCore, + }; }; - -// TODO: do the same for Viem ? diff --git a/contracts/deployments/index.ts b/contracts/deployments/index.ts index 2dc723a46..3b46603f0 100644 --- a/contracts/deployments/index.ts +++ b/contracts/deployments/index.ts @@ -8,5 +8,9 @@ export * as devnetViem from "./devnet.viem"; export * as mainnetViem from "./mainnet.viem"; export * as testnetViem from "./testnet.viem"; +// Typechain-types +export * from "../typechain-types"; + +// Contracts getters export { getContracts as getContractsEthers } from "./contractsEthers"; export { getContracts as getContractsViem } from "./contractsViem"; diff --git a/contracts/package.json b/contracts/package.json index d4d55deba..6a69e2bca 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -2,7 +2,33 @@ "name": "@kleros/kleros-v2-contracts", "version": "0.8.1", "description": "Smart contracts for Kleros version 2", - "main": "typechain-types/index.ts", + "main": "./dist/cjs/index.js", + "module": "./dist/esm/index.js", + "types": "./dist/types/index.d.ts", + "exports": { + ".": { + "types": "./dist/types/index.d.ts", + "import": "./dist/esm/index.js", + "require": "./dist/cjs/index.js", + "default": "./dist/esm/index.js" + } + }, + "files": [ + "types", + "esm", + "cjs", + "arbitration", + "gateway", + "kleros-v1", + "libraries", + "proxy", + "rng", + "token", + "utils", + "typechain-types", + "deployments", + "index.ts" + ], "repository": "git@github.com:kleros/kleros-v2.git", "homepage": "https://github.com/kleros/kleros-v2/tree/master/contracts#readme", "author": "Kleros", @@ -64,10 +90,10 @@ "release:minor": "scripts/publish.sh minor", "release:major": "scripts/publish.sh major", "tenderly-verify": "hardhat tenderly:verify", - "build:all": "yarn rimraf ./dist && yarn build:cjs && yarn build:esm && yarn build:types", - "build:cjs": "tsc --project tsconfig.json --module commonjs --outDir ./dist/cjs && echo '{\"type\": \"commonjs\"}' > ./dist/cjs/package.json", - "build:esm": "tsc --project tsconfig.json --module es2020 --outDir ./dist/esm && echo '{\"type\": \"module\"}' > ./dist/esm/package.json", - "build:types": "tsc --project tsconfig.json --module esnext --declarationDir ./dist/types --emitDeclarationOnly --declaration --declarationMap" + "build:all": "rm -rf ./dist && yarn build:cjs && yarn build:esm && yarn build:types", + "build:cjs": "tsc --project tsconfig-release.json --module CommonJS --moduleResolution Node --outDir ./dist/cjs && echo '{\"type\": \"commonjs\"}' > ./dist/cjs/package.json", + "build:esm": "tsc --project tsconfig-release.json --module NodeNext --moduleResolution NodeNext --outDir ./dist/esm && echo '{\"type\": \"module\"}' > ./dist/esm/package.json", + "build:types": "tsc --project tsconfig-release.json --declarationDir ./dist/types --emitDeclarationOnly --declaration --declarationMap" }, "devDependencies": { "@defi-wonderland/natspec-smells": "^1.1.5", diff --git a/contracts/scripts/publish.sh b/contracts/scripts/publish.sh index cef61141d..b1bd85ad7 100755 --- a/contracts/scripts/publish.sh +++ b/contracts/scripts/publish.sh @@ -1,4 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash + +shopt -s extglob SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" @@ -20,24 +22,46 @@ trap _finally EXIT #-------------------------------------- -yarn version $1 +if [[ "$PWD" != */contracts ]]; then + echo "Error: This script must be run from the contracts directory" + exit 1 +fi + +# Recompile the contracts +yarn clean +yarn build + +# Rebuild the typechain without mocks +rm -rf artifacts/src/**/*[mM]ock* +find artifacts/src -name "*.dbg.json" -type f -delete +rm -rf typechain-types +yarn typechain --out-dir typechain-types --glob 'artifacts/src/**/*.json' --target ethers-v6 +# Generate the viem artifacts yarn viem:generate-devnet yarn viem:generate-testnet yarn viem:generate-mainnet + +# Generate the Hardhat artifacts yarn export:devnet yarn export:testnet yarn export:mainnet +# Build the dist rm -rf dist mkdir dist +yarn build:all -yarn tsc --project tsconfig-release.json --outDir ./dist +# Copy the README and contracts cp -pr README.md src/ dist/ + +# Remove unwanted files rm -rf dist/config rm -rf dist/deploy rm -rf dist/scripts rm -rf dist/test +rm -rf dist/**/mock +rm -rf dist/**/*Mock* rm -rf dist/hardhat.config* rm -rf dist/deployments/**/solcInputs rm -rf dist/deployments/localhost @@ -45,6 +69,10 @@ rm -rf dist/deployments/hardhat rm -rf dist/deployments/hardhat.viem.ts jq 'del(.scripts.prepare)' package.json > dist/package.json +# Bump the version +yarn version $1 + +# Publish the package cd dist -npm publish +npm publish --dry-run cd - diff --git a/contracts/tsconfig-release.json b/contracts/tsconfig-release.json index 7464d1b94..a94f4d6f6 100644 --- a/contracts/tsconfig-release.json +++ b/contracts/tsconfig-release.json @@ -4,7 +4,6 @@ "declaration": true }, "include": [ - "./src", "./typechain-types", "./deployments" ], diff --git a/contracts/wagmi.config.devnet.ts b/contracts/wagmi.config.devnet.ts index d562d4b66..7c8009cc1 100644 --- a/contracts/wagmi.config.devnet.ts +++ b/contracts/wagmi.config.devnet.ts @@ -1,5 +1,5 @@ import { Config, defineConfig } from "@wagmi/cli"; -import IHomeGateway from "@kleros/kleros-v2-contracts/artifacts/src/gateway/interfaces/IHomeGateway.sol/IHomeGateway.json" assert { type: "json" }; +import IHomeGateway from "./artifacts/src/gateway/interfaces/IHomeGateway.sol/IHomeGateway.json" assert { type: "json" }; import { getAbi, readArtifacts, merge } from "./scripts/wagmiHelpers"; const getConfig = async (): Promise => { diff --git a/contracts/wagmi.config.mainnet.ts b/contracts/wagmi.config.mainnet.ts index b48c7f5d6..aa321eba0 100644 --- a/contracts/wagmi.config.mainnet.ts +++ b/contracts/wagmi.config.mainnet.ts @@ -1,5 +1,5 @@ import { Config, defineConfig } from "@wagmi/cli"; -import IHomeGateway from "@kleros/kleros-v2-contracts/artifacts/src/gateway/interfaces/IHomeGateway.sol/IHomeGateway.json" assert { type: "json" }; +import IHomeGateway from "./artifacts/src/gateway/interfaces/IHomeGateway.sol/IHomeGateway.json" assert { type: "json" }; import { getAbi, readArtifacts, merge } from "./scripts/wagmiHelpers"; const getConfig = async (): Promise => { diff --git a/contracts/wagmi.config.testnet.ts b/contracts/wagmi.config.testnet.ts index aa1c4b108..93a00cdd0 100644 --- a/contracts/wagmi.config.testnet.ts +++ b/contracts/wagmi.config.testnet.ts @@ -1,5 +1,5 @@ import { Config, defineConfig } from "@wagmi/cli"; -import IHomeGateway from "@kleros/kleros-v2-contracts/artifacts/src/gateway/interfaces/IHomeGateway.sol/IHomeGateway.json" assert { type: "json" }; +import IHomeGateway from "./artifacts/src/gateway/interfaces/IHomeGateway.sol/IHomeGateway.json" assert { type: "json" }; import { getAbi, readArtifacts, merge } from "./scripts/wagmiHelpers"; const getConfig = async (): Promise => { From 1aeb0a23c6e475860c78fc0bac22f810b2808993 Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Thu, 17 Apr 2025 18:20:04 +0100 Subject: [PATCH 3/5] feat: contracts getter for Viem --- contracts/deployments/contractsEthers.ts | 1 - contracts/deployments/contractsViem.ts | 262 ++++++++++++++++-- .../test/integration/getContractsViem.test.ts | 220 +++++++++++++++ 3 files changed, 456 insertions(+), 27 deletions(-) create mode 100644 contracts/test/integration/getContractsViem.test.ts diff --git a/contracts/deployments/contractsEthers.ts b/contracts/deployments/contractsEthers.ts index 3f99b13b3..7653fc7a5 100644 --- a/contracts/deployments/contractsEthers.ts +++ b/contracts/deployments/contractsEthers.ts @@ -17,7 +17,6 @@ import { sortitionModuleUniversityConfig as devnetSortitionUniversityConfig, disputeKitClassicUniversityConfig as devnetDkcUniversityConfig, disputeResolverUniversityConfig as devnetDrUniversityConfig, - klerosCoreUniversityProxyConfig as devnetCoreUniversityProxyConfig, } from "./devnet.viem"; import { klerosCoreConfig as testnetCoreConfig, diff --git a/contracts/deployments/contractsViem.ts b/contracts/deployments/contractsViem.ts index 5492c6118..fc181dc83 100644 --- a/contracts/deployments/contractsViem.ts +++ b/contracts/deployments/contractsViem.ts @@ -16,8 +16,36 @@ import { sortitionModuleUniversityConfig as devnetSortitionUniversityConfig, disputeKitClassicUniversityConfig as devnetDkcUniversityConfig, disputeResolverUniversityConfig as devnetDrUniversityConfig, - klerosCoreUniversityProxyConfig as devnetCoreUniversityProxyConfig, } from "./devnet.viem"; +import { + klerosCoreConfig as testnetCoreConfig, + sortitionModuleConfig as testnetSortitionConfig, + disputeKitClassicConfig as testnetDkcConfig, + disputeResolverConfig as testnetDrConfig, + disputeTemplateRegistryConfig as testnetDtrConfig, + evidenceModuleConfig as testnetEvidenceConfig, + policyRegistryConfig as testnetPolicyRegistryConfig, + transactionBatcherConfig as testnetBatcherConfig, + chainlinkRngConfig as testnetChainlinkRngConfig, + blockHashRngConfig as testnetBlockHashRngConfig, + pnkConfig as testnetPnkConfig, + klerosCoreSnapshotProxyConfig as testnetSnapshotProxyConfig, +} from "./testnet.viem"; +import { + klerosCoreNeoConfig as mainnetCoreConfig, + sortitionModuleNeoConfig as mainnetSortitionConfig, + disputeKitClassicNeoConfig as mainnetDkcConfig, + disputeResolverNeoConfig as mainnetDrConfig, + disputeTemplateRegistryConfig as mainnetDtrConfig, + evidenceModuleConfig as mainnetEvidenceConfig, + policyRegistryConfig as mainnetPolicyRegistryConfig, + transactionBatcherConfig as mainnetBatcherConfig, + chainlinkRngConfig as mainnetChainlinkRngConfig, + randomizerRngConfig as mainnetRandomizerRngConfig, + blockHashRngConfig as mainnetBlockHashRngConfig, + pnkConfig as mainnetPnkConfig, + klerosCoreSnapshotProxyConfig as mainnetSnapshotProxyConfig, +} from "./mainnet.viem"; import { type PublicClient, type WalletClient, getContract } from "viem"; const deployments = { @@ -42,11 +70,25 @@ type ContractConfig = { abi: readonly any[]; }; -type ContractConfigs = { - klerosCore?: { - address: `0x${string}`; - abi: readonly any[]; - }; +type ContractInstance = { + address: `0x${string}`; + abi: readonly any[]; +}; + +type ContractInstances = { + klerosCore: ContractInstance; + sortition: ContractInstance; + disputeKitClassic: ContractInstance; + disputeResolver: ContractInstance; + disputeTemplateRegistry: ContractInstance; + evidence: ContractInstance; + policyRegistry: ContractInstance; + transactionBatcher: ContractInstance; + chainlinkRng?: ContractInstance; + randomizerRng?: ContractInstance; + blockHashRng: ContractInstance; + pnk: ContractInstance; + klerosCoreSnapshotProxy: ContractInstance; }; function getAddress(config: ContractConfig, chainId: number): `0x${string}` { @@ -55,22 +97,134 @@ function getAddress(config: ContractConfig, chainId: number): `0x${string}` { return address; } -export const getConfigs = ({ deployment }: { deployment: DeploymentName }): ContractConfigs => { +function getContractConfig({ config, chainId }: { config: ContractConfig; chainId: number }): ContractInstance { + return { + address: getAddress(config, chainId), + abi: config.abi, + }; +} + +function getCommonConfigs({ + chainId, + configs, +}: { + chainId: number; + configs: { + klerosCore: ContractConfig; + sortition: ContractConfig; + disputeKitClassic: ContractConfig; + disputeResolver: ContractConfig; + disputeTemplateRegistry: ContractConfig; + evidence: ContractConfig; + policyRegistry: ContractConfig; + transactionBatcher: ContractConfig; + blockHashRng: ContractConfig; + pnk: ContractConfig; + klerosCoreSnapshotProxy: ContractConfig; + chainlinkRng?: ContractConfig; + randomizerRng?: ContractConfig; + }; +}): ContractInstances { + const base: ContractInstances = { + klerosCore: getContractConfig({ config: configs.klerosCore, chainId }), + sortition: getContractConfig({ config: configs.sortition, chainId }), + disputeKitClassic: getContractConfig({ config: configs.disputeKitClassic, chainId }), + disputeResolver: getContractConfig({ config: configs.disputeResolver, chainId }), + disputeTemplateRegistry: getContractConfig({ config: configs.disputeTemplateRegistry, chainId }), + evidence: getContractConfig({ config: configs.evidence, chainId }), + policyRegistry: getContractConfig({ config: configs.policyRegistry, chainId }), + transactionBatcher: getContractConfig({ config: configs.transactionBatcher, chainId }), + blockHashRng: getContractConfig({ config: configs.blockHashRng, chainId }), + pnk: getContractConfig({ config: configs.pnk, chainId }), + klerosCoreSnapshotProxy: getContractConfig({ config: configs.klerosCoreSnapshotProxy, chainId }), + }; + + if (configs.chainlinkRng) base.chainlinkRng = getContractConfig({ config: configs.chainlinkRng, chainId }); + + if (configs.randomizerRng) base.randomizerRng = getContractConfig({ config: configs.randomizerRng, chainId }); + + return base; +} + +export const getConfigs = ({ deployment }: { deployment: DeploymentName }): ContractInstances => { const { chainId } = deployments[deployment]; - let contractConfigs: ContractConfigs = {}; switch (deployment) { case "devnet": - contractConfigs = { - klerosCore: { - address: getAddress(devnetCoreConfig, chainId), - abi: devnetCoreConfig.abi, + return getCommonConfigs({ + chainId, + configs: { + klerosCore: devnetCoreConfig, + sortition: devnetSortitionConfig, + disputeKitClassic: devnetDkcConfig, + disputeResolver: devnetDrConfig, + disputeTemplateRegistry: devnetDtrConfig, + evidence: devnetEvidenceConfig, + policyRegistry: devnetPolicyRegistryConfig, + transactionBatcher: devnetBatcherConfig, + blockHashRng: devnetBlockHashRngConfig, + pnk: devnetPnkConfig, + klerosCoreSnapshotProxy: devnetSnapshotProxyConfig, + chainlinkRng: devnetChainlinkRngConfig, }, + }); + + case "university": + return { + klerosCore: getContractConfig({ config: devnetCoreUniversityConfig, chainId }), + sortition: getContractConfig({ config: devnetSortitionUniversityConfig, chainId }), + disputeKitClassic: getContractConfig({ config: devnetDkcUniversityConfig, chainId }), + disputeResolver: getContractConfig({ config: devnetDrUniversityConfig, chainId }), + disputeTemplateRegistry: getContractConfig({ config: devnetDtrConfig, chainId }), // FIXME: should not be shared with devnet + evidence: getContractConfig({ config: devnetEvidenceConfig, chainId }), // Not arbitrator specific + policyRegistry: getContractConfig({ config: devnetPolicyRegistryConfig, chainId }), // Not arbitrator specific + transactionBatcher: getContractConfig({ config: devnetBatcherConfig, chainId }), // Not arbitrator specific + blockHashRng: getContractConfig({ config: devnetBlockHashRngConfig, chainId }), // Not used in university + pnk: getContractConfig({ config: devnetPnkConfig, chainId }), // Not arbitrator specific + klerosCoreSnapshotProxy: getContractConfig({ config: devnetSnapshotProxyConfig, chainId }), // Not used in university }; - break; + + case "testnet": + return getCommonConfigs({ + chainId, + configs: { + klerosCore: testnetCoreConfig, + sortition: testnetSortitionConfig, + disputeKitClassic: testnetDkcConfig, + disputeResolver: testnetDrConfig, + disputeTemplateRegistry: testnetDtrConfig, + evidence: testnetEvidenceConfig, + policyRegistry: testnetPolicyRegistryConfig, + transactionBatcher: testnetBatcherConfig, + blockHashRng: testnetBlockHashRngConfig, + pnk: testnetPnkConfig, + klerosCoreSnapshotProxy: testnetSnapshotProxyConfig, + chainlinkRng: testnetChainlinkRngConfig, + }, + }); + + case "mainnetNeo": + return getCommonConfigs({ + chainId, + configs: { + klerosCore: mainnetCoreConfig, + sortition: mainnetSortitionConfig, + disputeKitClassic: mainnetDkcConfig, + disputeResolver: mainnetDrConfig, + disputeTemplateRegistry: mainnetDtrConfig, + evidence: mainnetEvidenceConfig, + policyRegistry: mainnetPolicyRegistryConfig, + transactionBatcher: mainnetBatcherConfig, + blockHashRng: mainnetBlockHashRngConfig, + pnk: mainnetPnkConfig, + klerosCoreSnapshotProxy: mainnetSnapshotProxyConfig, + chainlinkRng: mainnetChainlinkRngConfig, + randomizerRng: mainnetRandomizerRngConfig, + }, + }); + default: throw new Error(`Unsupported deployment: ${deployment}`); } - return contractConfigs; }; export const getContracts = ({ @@ -88,20 +242,76 @@ export const getContracts = ({ wallet: walletClient, }, }; - let klerosCore; - switch (deployment) { - case "devnet": - const contractConfigs = getConfigs({ deployment }); - if (!contractConfigs.klerosCore) throw new Error("KlerosCore config not found"); - klerosCore = getContract({ - ...contractConfigs.klerosCore, + const contractConfigs = getConfigs({ deployment }); + const klerosCore = getContract({ + ...contractConfigs.klerosCore, + ...clientConfig, + }); + const sortition = getContract({ + ...contractConfigs.sortition, + ...clientConfig, + }); + const disputeKitClassic = getContract({ + ...contractConfigs.disputeKitClassic, + ...clientConfig, + }); + const disputeResolver = getContract({ + ...contractConfigs.disputeResolver, + ...clientConfig, + }); + const disputeTemplateRegistry = getContract({ + ...contractConfigs.disputeTemplateRegistry, + ...clientConfig, + }); + const evidence = getContract({ + ...contractConfigs.evidence, + ...clientConfig, + }); + const policyRegistry = getContract({ + ...contractConfigs.policyRegistry, + ...clientConfig, + }); + const transactionBatcher = getContract({ + ...contractConfigs.transactionBatcher, + ...clientConfig, + }); + const chainlinkRng = contractConfigs.chainlinkRng + ? getContract({ + ...contractConfigs.chainlinkRng, ...clientConfig, - }); - break; - default: - throw new Error(`Unsupported deployment: ${deployment}`); - } + }) + : undefined; + const randomizerRng = contractConfigs.randomizerRng + ? getContract({ + ...contractConfigs.randomizerRng, + ...clientConfig, + }) + : undefined; + const blockHashRng = getContract({ + ...contractConfigs.blockHashRng, + ...clientConfig, + }); + const pnk = getContract({ + ...contractConfigs.pnk, + ...clientConfig, + }); + const klerosCoreSnapshotProxy = getContract({ + ...contractConfigs.klerosCoreSnapshotProxy, + ...clientConfig, + }); return { klerosCore, + sortition, + disputeKitClassic, + disputeResolver, + disputeTemplateRegistry, + evidence, + policyRegistry, + transactionBatcher, + chainlinkRng, + randomizerRng, + blockHashRng, + pnk, + klerosCoreSnapshotProxy, }; }; diff --git a/contracts/test/integration/getContractsViem.test.ts b/contracts/test/integration/getContractsViem.test.ts new file mode 100644 index 000000000..0e4d3e801 --- /dev/null +++ b/contracts/test/integration/getContractsViem.test.ts @@ -0,0 +1,220 @@ +import { expect } from "chai"; +import { createPublicClient, createWalletClient, http, Address } from "viem"; +import { arbitrum, arbitrumSepolia } from "viem/chains"; +import { getContracts } from "../../deployments/contractsViem"; +import { getActualAddress } from "../utils/getActualAddress"; + +// Network names for deployments +const NETWORKS = { + DEVNET: "arbitrumSepoliaDevnet", + TESTNET: "arbitrumSepolia", + MAINNET: "arbitrum", +} as const; + +type NetworkType = (typeof NETWORKS)[keyof typeof NETWORKS]; + +type ContractMapping = { + [K in keyof ReturnType]: { + name: string; + optional?: boolean; + }; +}; + +const baseContractMapping: ContractMapping = { + klerosCore: { name: "KlerosCore" }, + sortition: { name: "SortitionModule" }, + disputeKitClassic: { name: "DisputeKitClassic" }, + disputeResolver: { name: "DisputeResolver" }, + disputeTemplateRegistry: { name: "DisputeTemplateRegistry" }, + evidence: { name: "EvidenceModule" }, + policyRegistry: { name: "PolicyRegistry" }, + transactionBatcher: { name: "TransactionBatcher" }, + chainlinkRng: { name: "ChainlinkRNG", optional: true }, + randomizerRng: { name: "RandomizerRNG", optional: true }, + blockHashRng: { name: "BlockHashRNG" }, + pnk: { name: "PNK" }, + klerosCoreSnapshotProxy: { name: "KlerosCoreSnapshotProxy" }, +}; + +const universityContractMapping: ContractMapping = { + klerosCore: { name: "KlerosCoreUniversity" }, + sortition: { name: "SortitionModuleUniversity" }, + disputeKitClassic: { name: "DisputeKitClassicUniversity" }, + disputeResolver: { name: "DisputeResolverUniversity" }, + disputeTemplateRegistry: { name: "DisputeTemplateRegistry" }, + evidence: { name: "EvidenceModule" }, + policyRegistry: { name: "PolicyRegistry" }, + transactionBatcher: { name: "TransactionBatcher" }, + chainlinkRng: { name: "ChainlinkRNG", optional: true }, + randomizerRng: { name: "RandomizerRNG", optional: true }, + blockHashRng: { name: "BlockHashRNG" }, + pnk: { name: "PNK" }, + klerosCoreSnapshotProxy: { name: "KlerosCoreSnapshotProxy" }, +}; + +const neoContractMapping: ContractMapping = { + klerosCore: { name: "KlerosCoreNeo" }, + sortition: { name: "SortitionModuleNeo" }, + disputeKitClassic: { name: "DisputeKitClassicNeo" }, + disputeResolver: { name: "DisputeResolverNeo" }, + disputeTemplateRegistry: { name: "DisputeTemplateRegistry" }, + evidence: { name: "EvidenceModule" }, + policyRegistry: { name: "PolicyRegistry" }, + transactionBatcher: { name: "TransactionBatcher" }, + chainlinkRng: { name: "ChainlinkRNG", optional: false }, + randomizerRng: { name: "RandomizerRNG", optional: false }, + blockHashRng: { name: "BlockHashRNG" }, + pnk: { name: "PNK" }, + klerosCoreSnapshotProxy: { name: "KlerosCoreSnapshotProxy" }, +}; + +describe("getContracts", () => { + // Create Viem clients for testing + const arbitrumSepoliaClient = createPublicClient({ + chain: arbitrumSepolia, + transport: http("https://sepolia-rollup.arbitrum.io/rpc"), + }); + + const arbitrumClient = createPublicClient({ + chain: arbitrum, + transport: http("https://arb1.arbitrum.io/rpc"), + }); + + // Helper to verify contract instance + function verifyContractInstance(contract: any) { + expect(contract).to.have.property("address"); + expect(contract).to.have.property("abi"); + expect(contract.address).to.match(/^0x[a-fA-F0-9]{40}$/); + expect(contract.address).to.not.equal("0x0000000000000000000000000000000000000000"); + } + + // Helper to verify all contract instances + function verifyAllContractInstances(contracts: ReturnType) { + verifyContractInstance(contracts.klerosCore); + verifyContractInstance(contracts.sortition); + verifyContractInstance(contracts.disputeKitClassic); + verifyContractInstance(contracts.disputeResolver); + verifyContractInstance(contracts.disputeTemplateRegistry); + verifyContractInstance(contracts.evidence); + verifyContractInstance(contracts.policyRegistry); + verifyContractInstance(contracts.transactionBatcher); + verifyContractInstance(contracts.blockHashRng); + verifyContractInstance(contracts.pnk); + verifyContractInstance(contracts.klerosCoreSnapshotProxy); + + if (contracts.chainlinkRng) { + verifyContractInstance(contracts.chainlinkRng); + } + if (contracts.randomizerRng) { + verifyContractInstance(contracts.randomizerRng); + } + } + + // Helper to verify deployed addresses + async function verifyDeployedAddresses( + contracts: ReturnType, + network: NetworkType, + contractMapping: ContractMapping + ) { + for (const [key, { name, optional }] of Object.entries(contractMapping)) { + const contract = contracts[key as keyof typeof contracts]; + if (!contract) { + if (!optional) { + throw new Error(`Required contract ${name} is null`); + } + continue; + } + expect(contract.address).to.equal(await getActualAddress(network, name)); + } + } + + it("should return correct contract instances for devnet", async () => { + const contracts = getContracts({ + publicClient: arbitrumSepoliaClient, + deployment: "devnet", + }); + + // Verify chain ID + expect(arbitrumSepoliaClient.chain.id).to.equal(arbitrumSepolia.id); + + // Verify all contract instances + verifyAllContractInstances(contracts); + + // Verify specific RNG instances + expect(contracts.chainlinkRng).to.not.be.undefined; + expect(contracts.randomizerRng).to.be.undefined; + + // Verify deployed addresses + await verifyDeployedAddresses(contracts, NETWORKS.DEVNET, baseContractMapping); + }); + + it("should return correct contract instances for university", async () => { + const contracts = getContracts({ + publicClient: arbitrumSepoliaClient, + deployment: "university", + }); + + // Verify chain ID + expect(arbitrumSepoliaClient.chain.id).to.equal(arbitrumSepolia.id); + + // Verify all contract instances + verifyAllContractInstances(contracts); + + // Verify specific RNG instances + expect(contracts.chainlinkRng).to.be.undefined; + expect(contracts.randomizerRng).to.be.undefined; + + // Verify deployed addresses + await verifyDeployedAddresses(contracts, NETWORKS.DEVNET, universityContractMapping); + }); + + it("should return correct contract instances for testnet", async () => { + const contracts = getContracts({ + publicClient: arbitrumSepoliaClient, + deployment: "testnet", + }); + + // Verify chain ID + expect(arbitrumSepoliaClient.chain.id).to.equal(arbitrumSepolia.id); + + // Verify all contract instances + verifyAllContractInstances(contracts); + + // Verify specific RNG instances + expect(contracts.chainlinkRng).to.not.be.undefined; + expect(contracts.randomizerRng).to.be.undefined; + + // Verify deployed addresses + await verifyDeployedAddresses(contracts, NETWORKS.TESTNET, baseContractMapping); + }); + + it("should return correct contract instances for mainnetNeo", async () => { + const contracts = getContracts({ + publicClient: arbitrumClient, + deployment: "mainnetNeo", + }); + + // Verify chain ID + expect(arbitrumClient.chain.id).to.equal(arbitrum.id); + + // Verify all contract instances + verifyAllContractInstances(contracts); + + // Verify specific RNG instances + expect(contracts.chainlinkRng).to.not.be.undefined; + expect(contracts.randomizerRng).to.not.be.undefined; + + // Verify deployed addresses + await verifyDeployedAddresses(contracts, NETWORKS.MAINNET, neoContractMapping); + }); + + it("should throw error for unsupported deployment", () => { + expect(() => + getContracts({ + publicClient: arbitrumSepoliaClient, + // @ts-expect-error Testing invalid deployment + deployment: "invalid", + }) + ).to.throw(/Cannot destructure property 'chainId'/); + }); +}); From 01d94309b809cbd6f73e41f33317536f7339d307 Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Thu, 17 Apr 2025 19:53:01 +0100 Subject: [PATCH 4/5] fix: package entry points and other small improvements --- contracts/deployments/contractsEthers.ts | 30 +----------- contracts/deployments/contractsViem.ts | 46 ++++--------------- contracts/deployments/index.ts | 3 ++ contracts/deployments/utils.ts | 29 ++++++++++++ contracts/package.json | 16 +++---- contracts/scripts/publish.sh | 16 +++---- .../integration/getContractsEthers.test.ts | 2 +- .../test/integration/getContractsViem.test.ts | 4 +- 8 files changed, 61 insertions(+), 85 deletions(-) create mode 100644 contracts/deployments/utils.ts diff --git a/contracts/deployments/contractsEthers.ts b/contracts/deployments/contractsEthers.ts index 7653fc7a5..46def6375 100644 --- a/contracts/deployments/contractsEthers.ts +++ b/contracts/deployments/contractsEthers.ts @@ -1,5 +1,4 @@ import { ethers } from "ethers"; -import { arbitrum, arbitrumSepolia } from "viem/chains"; import { klerosCoreConfig as devnetCoreConfig, sortitionModuleConfig as devnetSortitionConfig, @@ -83,34 +82,7 @@ import { SortitionModuleNeo, SortitionModuleNeo__factory, } from "../typechain-types"; - -const deployments = { - devnet: { - chainId: arbitrumSepolia.id, - }, - university: { - chainId: arbitrumSepolia.id, - }, - testnet: { - chainId: arbitrumSepolia.id, - }, - mainnetNeo: { - chainId: arbitrum.id, - }, -} as const; - -type DeploymentName = keyof typeof deployments; - -type ContractConfig = { - address: Record; - abi: readonly any[]; -}; - -function getAddress(config: ContractConfig, chainId: number): `0x${string}` { - const address = config.address[chainId]; - if (!address) throw new Error(`No address found for chainId ${chainId}`); - return address; -} +import { type ContractConfig, type DeploymentName, deployments, getAddress } from "./utils"; type CommonFactoriesConfigs = { dkcConfig: ContractConfig; diff --git a/contracts/deployments/contractsViem.ts b/contracts/deployments/contractsViem.ts index fc181dc83..bbf3b9748 100644 --- a/contracts/deployments/contractsViem.ts +++ b/contracts/deployments/contractsViem.ts @@ -1,4 +1,5 @@ -import { arbitrum, arbitrumSepolia } from "viem/chains"; +import { type PublicClient, type WalletClient, getContract } from "viem"; +import { type ContractConfig, type DeploymentName, deployments, getAddress } from "./utils"; import { klerosCoreConfig as devnetCoreConfig, sortitionModuleConfig as devnetSortitionConfig, @@ -46,35 +47,19 @@ import { pnkConfig as mainnetPnkConfig, klerosCoreSnapshotProxyConfig as mainnetSnapshotProxyConfig, } from "./mainnet.viem"; -import { type PublicClient, type WalletClient, getContract } from "viem"; - -const deployments = { - devnet: { - chainId: arbitrumSepolia.id, - }, - university: { - chainId: arbitrumSepolia.id, - }, - testnet: { - chainId: arbitrumSepolia.id, - }, - mainnetNeo: { - chainId: arbitrum.id, - }, -} as const; - -type DeploymentName = keyof typeof deployments; - -type ContractConfig = { - address: Record; - abi: readonly any[]; -}; type ContractInstance = { address: `0x${string}`; abi: readonly any[]; }; +function getContractConfig({ config, chainId }: { config: ContractConfig; chainId: number }): ContractInstance { + return { + address: getAddress(config, chainId), + abi: config.abi, + }; +} + type ContractInstances = { klerosCore: ContractInstance; sortition: ContractInstance; @@ -91,19 +76,6 @@ type ContractInstances = { klerosCoreSnapshotProxy: ContractInstance; }; -function getAddress(config: ContractConfig, chainId: number): `0x${string}` { - const address = config.address[chainId]; - if (!address) throw new Error(`No address found for chainId ${chainId}`); - return address; -} - -function getContractConfig({ config, chainId }: { config: ContractConfig; chainId: number }): ContractInstance { - return { - address: getAddress(config, chainId), - abi: config.abi, - }; -} - function getCommonConfigs({ chainId, configs, diff --git a/contracts/deployments/index.ts b/contracts/deployments/index.ts index 3b46603f0..3479c5edf 100644 --- a/contracts/deployments/index.ts +++ b/contracts/deployments/index.ts @@ -11,6 +11,9 @@ export * as testnetViem from "./testnet.viem"; // Typechain-types export * from "../typechain-types"; +// Common utils +export * from "./utils"; + // Contracts getters export { getContracts as getContractsEthers } from "./contractsEthers"; export { getContracts as getContractsViem } from "./contractsViem"; diff --git a/contracts/deployments/utils.ts b/contracts/deployments/utils.ts new file mode 100644 index 000000000..e2711a377 --- /dev/null +++ b/contracts/deployments/utils.ts @@ -0,0 +1,29 @@ +import { arbitrum, arbitrumSepolia } from "viem/chains"; + +export const deployments = { + devnet: { + chainId: arbitrumSepolia.id, + }, + university: { + chainId: arbitrumSepolia.id, + }, + testnet: { + chainId: arbitrumSepolia.id, + }, + mainnetNeo: { + chainId: arbitrum.id, + }, +} as const; + +export type DeploymentName = keyof typeof deployments; + +export type ContractConfig = { + address: Record; + abi: readonly any[]; +}; + +export function getAddress(config: ContractConfig, chainId: number): `0x${string}` { + const address = config.address[chainId]; + if (!address) throw new Error(`No address found for chainId ${chainId}`); + return address; +} diff --git a/contracts/package.json b/contracts/package.json index 6a69e2bca..2e59987ec 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,16 +1,16 @@ { "name": "@kleros/kleros-v2-contracts", - "version": "0.8.1", + "version": "0.9.2", "description": "Smart contracts for Kleros version 2", - "main": "./dist/cjs/index.js", - "module": "./dist/esm/index.js", - "types": "./dist/types/index.d.ts", + "main": "./cjs/deployments/index.js", + "module": "./esm/deployments/index.js", + "types": "./types/deployments/index.d.ts", "exports": { ".": { - "types": "./dist/types/index.d.ts", - "import": "./dist/esm/index.js", - "require": "./dist/cjs/index.js", - "default": "./dist/esm/index.js" + "types": "./types/deployments/index.d.ts", + "import": "./esm/deployments/index.js", + "require": "./cjs/deployments/index.js", + "default": "./esm/deployments/index.js" } }, "files": [ diff --git a/contracts/scripts/publish.sh b/contracts/scripts/publish.sh index b1bd85ad7..119b003be 100755 --- a/contracts/scripts/publish.sh +++ b/contracts/scripts/publish.sh @@ -2,7 +2,7 @@ shopt -s extglob -SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" #-------------------------------------- # Error handling @@ -11,7 +11,7 @@ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" set -Ee function _catch { # Don't propagate to outer shell - exit 0 + exit 0 } function _finally { # TODO: rollback version bump @@ -27,6 +27,9 @@ if [[ "$PWD" != */contracts ]]; then exit 1 fi +# Bump the version +yarn version $1 + # Recompile the contracts yarn clean yarn build @@ -53,7 +56,7 @@ mkdir dist yarn build:all # Copy the README and contracts -cp -pr README.md src/ dist/ +cp -pr README.md src/ dist/ # Remove unwanted files rm -rf dist/config @@ -67,12 +70,9 @@ rm -rf dist/deployments/**/solcInputs rm -rf dist/deployments/localhost rm -rf dist/deployments/hardhat rm -rf dist/deployments/hardhat.viem.ts -jq 'del(.scripts.prepare)' package.json > dist/package.json - -# Bump the version -yarn version $1 +jq 'del(.scripts.prepare)' package.json >dist/package.json # Publish the package cd dist -npm publish --dry-run +npm publish cd - diff --git a/contracts/test/integration/getContractsEthers.test.ts b/contracts/test/integration/getContractsEthers.test.ts index 84a265a5d..7c46f33c7 100644 --- a/contracts/test/integration/getContractsEthers.test.ts +++ b/contracts/test/integration/getContractsEthers.test.ts @@ -87,7 +87,7 @@ const neoContractMapping: ContractMapping = { klerosCoreSnapshotProxy: { name: "KlerosCoreSnapshotProxy" }, }; -describe("getContracts", () => { +describe("getContractsEthers", () => { // Use real providers for each network const arbitrumSepoliaProvider = new ethers.JsonRpcProvider("https://sepolia-rollup.arbitrum.io/rpc"); const arbitrumProvider = new ethers.JsonRpcProvider("https://arb1.arbitrum.io/rpc"); diff --git a/contracts/test/integration/getContractsViem.test.ts b/contracts/test/integration/getContractsViem.test.ts index 0e4d3e801..58fdfd0c2 100644 --- a/contracts/test/integration/getContractsViem.test.ts +++ b/contracts/test/integration/getContractsViem.test.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { createPublicClient, createWalletClient, http, Address } from "viem"; +import { createPublicClient, http } from "viem"; import { arbitrum, arbitrumSepolia } from "viem/chains"; import { getContracts } from "../../deployments/contractsViem"; import { getActualAddress } from "../utils/getActualAddress"; @@ -68,7 +68,7 @@ const neoContractMapping: ContractMapping = { klerosCoreSnapshotProxy: { name: "KlerosCoreSnapshotProxy" }, }; -describe("getContracts", () => { +describe("getContractsViem", () => { // Create Viem clients for testing const arbitrumSepoliaClient = createPublicClient({ chain: arbitrumSepolia, From e832cc7d32382e860eccad34e3e2c5e913590fcb Mon Sep 17 00:00:00 2001 From: jaybuidl Date: Fri, 18 Apr 2025 15:26:56 +0100 Subject: [PATCH 5/5] fix: exported also the CJS/ESM entrypoint explicitely --- contracts/package.json | 12 +++++++++++- contracts/scripts/publish.sh | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/contracts/package.json b/contracts/package.json index 2e59987ec..0b4d3dd75 100644 --- a/contracts/package.json +++ b/contracts/package.json @@ -1,6 +1,6 @@ { "name": "@kleros/kleros-v2-contracts", - "version": "0.9.2", + "version": "0.9.3", "description": "Smart contracts for Kleros version 2", "main": "./cjs/deployments/index.js", "module": "./esm/deployments/index.js", @@ -11,6 +11,16 @@ "import": "./esm/deployments/index.js", "require": "./cjs/deployments/index.js", "default": "./esm/deployments/index.js" + }, + "./cjs/deployments": { + "types": "./types/deployments/index.d.ts", + "require": "./cjs/deployments/index.js", + "default": "./cjs/deployments/index.js" + }, + "./esm/deployments": { + "types": "./types/deployments/index.d.ts", + "import": "./esm/deployments/index.js", + "default": "./esm/deployments/index.js" } }, "files": [ diff --git a/contracts/scripts/publish.sh b/contracts/scripts/publish.sh index 119b003be..cfc441064 100755 --- a/contracts/scripts/publish.sh +++ b/contracts/scripts/publish.sh @@ -38,7 +38,7 @@ yarn build rm -rf artifacts/src/**/*[mM]ock* find artifacts/src -name "*.dbg.json" -type f -delete rm -rf typechain-types -yarn typechain --out-dir typechain-types --glob 'artifacts/src/**/*.json' --target ethers-v6 +yarn typechain --out-dir typechain-types --glob 'artifacts/src/**/*.json' --target ethers-v6 --node16-modules # Generate the viem artifacts yarn viem:generate-devnet