From 3bd76386cc6a7be83579f59603573514137cfa76 Mon Sep 17 00:00:00 2001 From: Shehu-Fatiudeen Lawal Date: Wed, 28 May 2025 05:12:14 +0100 Subject: [PATCH 1/5] feat: enhance package generation script to include TypeScript declaration file and update package.json for type definitions --- scripts/generate-package.ts | 53 +++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 5 deletions(-) diff --git a/scripts/generate-package.ts b/scripts/generate-package.ts index 0a52127..a0a2a7d 100644 --- a/scripts/generate-package.ts +++ b/scripts/generate-package.ts @@ -1,6 +1,6 @@ import * as fs from "fs"; import * as path from "path"; -import { ethers } from "ethers"; +// import { ethers } from "ethers"; async function main() { // Read deployment addresses @@ -86,6 +86,7 @@ async function main() { // Generate index.js content let indexJs = ""; + let indexDts = ""; // TypeScript declaration file content for (const contract of contracts) { const artifactPath = path.join( @@ -115,26 +116,68 @@ async function main() { ); // Add to index.js - indexJs += `const ${contract.name}Type = require('./typechain/types/${contract.name}').${contract.name};\n`; + indexJs += `const ${ + contract.name + }Factory = require('./typechain/factories/contracts/${contract.path.replace( + ".sol", + "" + )}__factory').${contract.name}__factory;\n`; indexJs += `exports.${contract.name} = {\n`; indexJs += ` address: '${deployments[contract.name]}',\n`; indexJs += ` abi: require('./artifacts/${contract.name}/abi.json'),\n`; indexJs += ` bytecode: require('./artifacts/${contract.name}/bytecode.json').bytecode,\n`; indexJs += ` network: '${deployments.network}',\n`; indexJs += ` chainId: ${deployments.chainId},\n`; - indexJs += ` contract: ${contract.name}Type\n`; + indexJs += ` factory: ${contract.name}Factory,\n`; + indexJs += ` connect: (signer) => ${contract.name}Factory.connect('${ + deployments[contract.name] + }', signer)\n`; indexJs += `};\n\n`; + + // Add to index.d.ts + indexDts += `import { ${ + contract.name + }__factory } from './typechain/factories/contracts/${contract.path.replace( + ".sol", + "" + )}__factory';\n`; + indexDts += `import { ${ + contract.name + } } from './typechain/contracts/${contract.path.replace(".sol", "")}';\n`; + indexDts += `export const ${contract.name}: {\n`; + indexDts += ` address: string;\n`; + indexDts += ` abi: any[];\n`; + indexDts += ` bytecode: string;\n`; + indexDts += ` network: string;\n`; + indexDts += ` chainId: number;\n`; + indexDts += ` factory: typeof ${contract.name}__factory;\n`; + indexDts += ` connect: (signer: ethers.Signer) => ${contract.name};\n`; + indexDts += `};\n\n`; } - // Add network info + // Add network info to both files indexJs += `exports.network = {\n`; indexJs += ` name: '${deployments.network}',\n`; indexJs += ` chainId: ${deployments.chainId},\n`; indexJs += ` deployedAt: '${deployments.deployedAt}'\n`; indexJs += `};\n`; - // Write index.js + indexDts += `export const network: {\n`; + indexDts += ` name: string;\n`; + indexDts += ` chainId: number;\n`; + indexDts += ` deployedAt: string;\n`; + indexDts += `};\n`; + + // Write both files fs.writeFileSync(path.join(packageDir, "index.js"), indexJs); + fs.writeFileSync(path.join(packageDir, "index.d.ts"), indexDts); + + // Update package.json to include types + packageJsonForBuild.types = "index.d.ts"; + fs.writeFileSync( + path.join(packageDir, "package.json"), + JSON.stringify(packageJsonForBuild, null, 2) + ); console.log("Package generated successfully!"); } From 96e049acf97f3ecf71994e12e2f2c70ac28f5b64 Mon Sep 17 00:00:00 2001 From: Shehu-Fatiudeen Lawal Date: Wed, 28 May 2025 11:01:45 +0100 Subject: [PATCH 2/5] refactor: update deployment workflow and scripts for Arbitrum networks, consolidate deployed addresses, and enhance package generation --- .github/workflows/deploy-contracts.yml | 35 ++- scripts/deploy-arbitrum.ts | 10 +- scripts/deployed_addresses.json | 16 +- scripts/deployed_addresses_arbitrum.json | 11 - scripts/generate-package.ts | 355 +++++++++++++++++------ scripts/update-package.sh | 52 ++++ scripts/verify-arbitrum.ts | 5 +- 7 files changed, 349 insertions(+), 135 deletions(-) delete mode 100644 scripts/deployed_addresses_arbitrum.json create mode 100755 scripts/update-package.sh diff --git a/.github/workflows/deploy-contracts.yml b/.github/workflows/deploy-contracts.yml index e7a40e4..16c874d 100644 --- a/.github/workflows/deploy-contracts.yml +++ b/.github/workflows/deploy-contracts.yml @@ -10,13 +10,13 @@ on: workflow_dispatch: inputs: network: - description: 'Network to deploy to (testnet/mainnet)' + description: 'Network to deploy to (arbitrum-sepolia/arbitrum)' required: true - default: 'testnet' + default: 'arbitrum-sepolia' type: choice options: - - testnet - - mainnet + - arbitrum-sepolia + - arbitrum version: description: 'Custom version to use (e.g., 1.0.0). Leave empty for auto-increment.' required: false @@ -46,34 +46,39 @@ jobs: - name: Compile contracts run: npm run compile + - name: Update local package + run: | + chmod +x scripts/update-package.sh + ./scripts/update-package.sh + - name: Generate TypeChain types run: npm run typechain - - name: Deploy to Testnet - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.network == 'testnet') + - name: Deploy to Arbitrum Sepolia + if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.network == 'arbitrum-sepolia') run: npm run deploy:arbitrum-testnet env: ARBITRUM_SEPOLIA_RPC_URL: ${{ secrets.ARBITRUM_SEPOLIA_RPC_URL }} PRIVATE_KEY: ${{ secrets.DEPLOYER_PRIVATE_KEY }} ARBISCAN_API_KEY: ${{ secrets.ARBISCAN_API_KEY }} - - name: Deploy to Mainnet - if: github.event_name == 'workflow_dispatch' && github.event.inputs.network == 'mainnet' + - name: Deploy to Arbitrum + if: github.event_name == 'workflow_dispatch' && github.event.inputs.network == 'arbitrum' run: npm run deploy:arbitrum env: ARBITRUM_ONE_RPC_URL: ${{ secrets.ARBITRUM_ONE_RPC_URL }} PRIVATE_KEY: ${{ secrets.DEPLOYER_PRIVATE_KEY }} ARBISCAN_API_KEY: ${{ secrets.ARBISCAN_API_KEY }} - - name: Verify Contracts on Testnet - if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.network == 'testnet') + - name: Verify Contracts on Arbitrum Sepolia + if: github.event_name == 'push' || (github.event_name == 'workflow_dispatch' && github.event.inputs.network == 'arbitrum-sepolia') run: npm run verify:all-arbitrum-testnet env: ARBITRUM_SEPOLIA_RPC_URL: ${{ secrets.ARBITRUM_SEPOLIA_RPC_URL }} ARBISCAN_API_KEY: ${{ secrets.ARBISCAN_API_KEY }} - - name: Verify Contracts on Mainnet - if: github.event_name == 'workflow_dispatch' && github.event.inputs.network == 'mainnet' + - name: Verify Contracts on Arbitrum + if: github.event_name == 'workflow_dispatch' && github.event.inputs.network == 'arbitrum' run: npm run verify:all-arbitrum env: ARBITRUM_ONE_RPC_URL: ${{ secrets.ARBITRUM_ONE_RPC_URL }} @@ -84,7 +89,7 @@ jobs: with: name: deployment-artifacts path: | - scripts/deployed_addresses_arbitrum.json + scripts/deployed_addresses.json typechain-types/ if-no-files-found: error @@ -140,7 +145,7 @@ jobs: git push origin HEAD - name: Generate Package - run: npx ts-node scripts/generate-package.ts + run: npx ts-node scripts/generate-package.ts ${{ github.event_name == 'push' && 'arbitrum-sepolia' || github.event.inputs.network }} - name: Publish Package run: | @@ -157,7 +162,7 @@ jobs: # name: Contracts v${{ env.NEW_VERSION }} # body: | # DeCleanup Network Smart Contracts Release - # - Deployed to ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.network == 'mainnet' && 'Arbitrum Mainnet' || 'Arbitrum Testnet' }} + # - Deployed to ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.network == 'arbitrum' && 'Arbitrum' || 'Arbitrum Sepolia' }} # - Published to npm as @decleanup/contracts@${{ env.NEW_VERSION }} # env: # GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/scripts/deploy-arbitrum.ts b/scripts/deploy-arbitrum.ts index c449f21..dab3e91 100644 --- a/scripts/deploy-arbitrum.ts +++ b/scripts/deploy-arbitrum.ts @@ -95,14 +95,8 @@ async function main() { deployedAt: new Date().toISOString(), }; - const deploymentsPath = path.join( - __dirname, - "deployed_addresses_arbitrum.json" - ); - fs.writeFileSync( - deploymentsPath, - JSON.stringify(deployedAddresses, null, 2) - ); + const deploymentsPath = path.join(__dirname, "deployed_addresses.json"); + fs.writeFileSync(deploymentsPath, JSON.stringify(deployedAddresses, null, 2)); console.log("Deployed addresses saved to:", deploymentsPath); } diff --git a/scripts/deployed_addresses.json b/scripts/deployed_addresses.json index 530ba08..82bb51f 100644 --- a/scripts/deployed_addresses.json +++ b/scripts/deployed_addresses.json @@ -1,7 +1,11 @@ { - "DCUContracts#DCUStorage": "0x6B0b410c922713d359f0fe34F710c4D77351DEC5", - "DCUContracts#NFTCollection": "0xf3B81c0Bb5089FA4244f6eE633E1205453C65b37", - "DCUContracts#DCUAccounting": "0xF2c3add9b1a4075086e3CE693DCD9Efee81918Ff", - "DCUContracts#DCURewardManager": "0x589679A4aB985E7e50f469507397b2d7a5279c41", - "DCUContracts#RewardLogic": "0xE23e8f18b7CF16201F7D4F50fBf28A654433CE7A" -} + "DCUToken": "0x3d5b9A91c4dA09097E565202FCaDfF6A006eA9Fd", + "RewardLogic": "0x8e6E38DA075afe91E46d99D64fc8347D9C8826fB", + "DCUAccounting": "0xCC8cB764fc3084719468e314BaeeC12bb2689EC6", + "DCUStorage": "0x026d43f024a271Fa82E10F0F83a8D80189712F89", + "DCURewardManager": "0xE3A86A05a489f832bC584744DDf102cAE7ACD5dF", + "DipNft": "0xF954E197Dc3a8F950b440f940c871873453A6E72", + "network": "arbitrum-sepolia", + "chainId": 421614, + "deployedAt": "2025-04-22T01:39:41.362Z" +} \ No newline at end of file diff --git a/scripts/deployed_addresses_arbitrum.json b/scripts/deployed_addresses_arbitrum.json deleted file mode 100644 index 82bb51f..0000000 --- a/scripts/deployed_addresses_arbitrum.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "DCUToken": "0x3d5b9A91c4dA09097E565202FCaDfF6A006eA9Fd", - "RewardLogic": "0x8e6E38DA075afe91E46d99D64fc8347D9C8826fB", - "DCUAccounting": "0xCC8cB764fc3084719468e314BaeeC12bb2689EC6", - "DCUStorage": "0x026d43f024a271Fa82E10F0F83a8D80189712F89", - "DCURewardManager": "0xE3A86A05a489f832bC584744DDf102cAE7ACD5dF", - "DipNft": "0xF954E197Dc3a8F950b440f940c871873453A6E72", - "network": "arbitrum-sepolia", - "chainId": 421614, - "deployedAt": "2025-04-22T01:39:41.362Z" -} \ No newline at end of file diff --git a/scripts/generate-package.ts b/scripts/generate-package.ts index a0a2a7d..64a587f 100644 --- a/scripts/generate-package.ts +++ b/scripts/generate-package.ts @@ -2,12 +2,84 @@ import * as fs from "fs"; import * as path from "path"; // import { ethers } from "ethers"; +// Utility function to convert network name to camel case +function toCamelCase(str: string): string { + return str + .split("-") + .map((part, index) => { + if (index === 0) return part; + return part.charAt(0).toUpperCase() + part.slice(1); + }) + .join(""); +} + +// Utility function to convert network name to Pascal case +function toPascalCase(str: string): string { + return str + .split("-") + .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) + .join(""); +} + +interface NetworkConfig { + chainId: number; + path: string; +} + +interface ContractInfo { + name: string; + path: string; +} + +interface Networks { + [key: string]: NetworkConfig; +} + async function main() { + // Get network name from command line argument + const networkName = process.argv[2]; + if (!networkName) { + console.error("Please provide a network name as an argument"); + process.exit(1); + } + + // Define network configuration + const networks: Networks = { + "arbitrum-sepolia": { + chainId: 421614, + path: "arbitrum-sepolia", + }, + arbitrum: { + chainId: 42161, + path: "arbitrum", + }, + }; + + if (!networks[networkName]) { + console.error(`Network ${networkName} is not supported`); + process.exit(1); + } + + const networkConfig = networks[networkName]; + const networkClassName = toPascalCase(networkName) + "Contracts"; + + const contracts: ContractInfo[] = [ + { name: "DCUToken", path: "tokens/DCUToken.sol" }, + { name: "RewardLogic", path: "RewardLogic.sol" }, + { name: "DCUAccounting", path: "DCUAccounting.sol" }, + { name: "DCUStorage", path: "DCUStorage.sol" }, + { name: "DCURewardManager", path: "DCURewardManager.sol" }, + { name: "DipNft", path: "tokens/DipNft.sol" }, + ]; + // Read deployment addresses - const deploymentsPath = path.join( - __dirname, - "deployed_addresses_arbitrum.json" - ); + const deploymentsPath = path.join(__dirname, "deployed_addresses.json"); + if (!fs.existsSync(deploymentsPath)) { + console.error( + `Deployment addresses file not found for network: ${networkName}` + ); + process.exit(1); + } const deployments = JSON.parse(fs.readFileSync(deploymentsPath, "utf8")); // Create package directory @@ -23,7 +95,7 @@ async function main() { description: "DeCleanup Network Smart Contracts", main: "index.js", types: "index.d.ts", - files: ["*.js", "*.d.ts", "artifacts/**/*.json", "typechain/**/*"], + files: ["*.js", "*.d.ts", `${networkName}/**/*`], repository: { type: "git", url: "git+https://github.com/decleanup/DeCleanup_Network.git", @@ -45,11 +117,103 @@ async function main() { JSON.stringify(packageJsonForBuild, null, 2) ); + // Create network directory + const networkDir = path.join(packageDir, networkConfig.path); + if (!fs.existsSync(networkDir)) { + fs.mkdirSync(networkDir, { recursive: true }); + } + + // Create network config file + const networkConfigFile = { + chainId: networkConfig.chainId, + ...Object.fromEntries( + contracts.map((contract) => [contract.name, deployments[contract.name]]) + ), + }; + fs.writeFileSync( + path.join(networkDir, "config.json"), + JSON.stringify(networkConfigFile, null, 2) + ); + + // Generate network-specific index.js + let networkIndexJs = ""; + let networkIndexDts = ""; + + // Generate contract factory imports for this network + for (const contract of contracts) { + networkIndexJs += `const ${ + contract.name + }Factory = require('./typechain/factories/contracts/${contract.path.replace( + ".sol", + "" + )}__factory').${contract.name}__factory;\n`; + } + networkIndexJs += `\n`; + + // Generate network-specific class + networkIndexJs += `class ${networkClassName} {\n`; + networkIndexJs += ` constructor() {\n`; + networkIndexJs += ` this.config = require('./config.json');\n`; + networkIndexJs += ` }\n\n`; + + // Add contract getters + for (const contract of contracts) { + networkIndexJs += ` get ${contract.name}() {\n`; + networkIndexJs += ` return {\n`; + networkIndexJs += ` address: this.config.${contract.name},\n`; + networkIndexJs += ` abi: require('./artifacts/${contract.name}/abi.json'),\n`; + networkIndexJs += ` bytecode: require('./artifacts/${contract.name}/bytecode.json').bytecode,\n`; + networkIndexJs += ` network: '${networkName}',\n`; + networkIndexJs += ` chainId: this.config.chainId,\n`; + networkIndexJs += ` factory: ${contract.name}Factory,\n`; + networkIndexJs += ` connect: (signer) => ${contract.name}Factory.connect(this.config.${contract.name}, signer)\n`; + networkIndexJs += ` };\n`; + networkIndexJs += ` }\n\n`; + } + + networkIndexJs += `}\n\n`; + networkIndexJs += `module.exports = ${networkClassName};\n`; + + // Generate network-specific TypeScript declarations + networkIndexDts += `import { ethers } from 'ethers';\n\n`; + for (const contract of contracts) { + networkIndexDts += `import { ${ + contract.name + }__factory } from './typechain/factories/contracts/${contract.path.replace( + ".sol", + "" + )}__factory';\n`; + networkIndexDts += `import { ${ + contract.name + } } from './typechain/contracts/${contract.path.replace(".sol", "")}';\n`; + } + networkIndexDts += `\n`; + + networkIndexDts += `export interface ContractConfig {\n`; + networkIndexDts += ` address: string;\n`; + networkIndexDts += ` abi: any[];\n`; + networkIndexDts += ` bytecode: string;\n`; + networkIndexDts += ` network: '${networkName}';\n`; + networkIndexDts += ` chainId: number;\n`; + networkIndexDts += ` factory: T;\n`; + networkIndexDts += ` connect: (signer: ethers.Signer) => T;\n`; + networkIndexDts += `}\n\n`; + + networkIndexDts += `export class ${networkClassName} {\n`; + for (const contract of contracts) { + networkIndexDts += ` get ${contract.name}(): ContractConfig<${contract.name}>;\n`; + } + networkIndexDts += `}\n`; + + // Write network-specific files + fs.writeFileSync(path.join(networkDir, "index.js"), networkIndexJs); + fs.writeFileSync(path.join(networkDir, "index.d.ts"), networkIndexDts); + // Copy TypeChain types const typechainDir = path.join(__dirname, "../typechain-types"); - const packageTypechainDir = path.join(packageDir, "typechain"); - if (!fs.existsSync(packageTypechainDir)) { - fs.mkdirSync(packageTypechainDir, { recursive: true }); + const networkTypechainDir = path.join(networkDir, "typechain"); + if (!fs.existsSync(networkTypechainDir)) { + fs.mkdirSync(networkTypechainDir, { recursive: true }); } // Copy all TypeChain files @@ -67,28 +231,21 @@ async function main() { } }; - copyTypechainFiles(typechainDir, packageTypechainDir); - - const contracts = [ - { name: "DCUToken", path: "tokens/DCUToken.sol" }, - { name: "RewardLogic", path: "RewardLogic.sol" }, - { name: "DCUAccounting", path: "DCUAccounting.sol" }, - { name: "DCUStorage", path: "DCUStorage.sol" }, - { name: "DCURewardManager", path: "DCURewardManager.sol" }, - { name: "DipNft", path: "tokens/DipNft.sol" }, - ]; + copyTypechainFiles(typechainDir, networkTypechainDir); - // Create artifacts directory - const artifactsDir = path.join(packageDir, "artifacts"); + // Create artifacts directory for network + const artifactsDir = path.join(networkDir, "artifacts"); if (!fs.existsSync(artifactsDir)) { - fs.mkdirSync(artifactsDir); + fs.mkdirSync(artifactsDir, { recursive: true }); } - // Generate index.js content - let indexJs = ""; - let indexDts = ""; // TypeScript declaration file content - + // Copy contract artifacts for (const contract of contracts) { + const contractDir = path.join(artifactsDir, contract.name); + if (!fs.existsSync(contractDir)) { + fs.mkdirSync(contractDir, { recursive: true }); + } + const artifactPath = path.join( __dirname, "../artifacts/contracts", @@ -97,12 +254,6 @@ async function main() { ); const artifact = JSON.parse(fs.readFileSync(artifactPath, "utf8")); - // Create contract-specific files - const contractDir = path.join(packageDir, "artifacts", contract.name); - if (!fs.existsSync(contractDir)) { - fs.mkdirSync(contractDir, { recursive: true }); - } - // Save ABI fs.writeFileSync( path.join(contractDir, "abi.json"), @@ -114,72 +265,94 @@ async function main() { path.join(contractDir, "bytecode.json"), JSON.stringify({ bytecode: artifact.bytecode }, null, 2) ); + } - // Add to index.js - indexJs += `const ${ - contract.name - }Factory = require('./typechain/factories/contracts/${contract.path.replace( - ".sol", - "" - )}__factory').${contract.name}__factory;\n`; - indexJs += `exports.${contract.name} = {\n`; - indexJs += ` address: '${deployments[contract.name]}',\n`; - indexJs += ` abi: require('./artifacts/${contract.name}/abi.json'),\n`; - indexJs += ` bytecode: require('./artifacts/${contract.name}/bytecode.json').bytecode,\n`; - indexJs += ` network: '${deployments.network}',\n`; - indexJs += ` chainId: ${deployments.chainId},\n`; - indexJs += ` factory: ${contract.name}Factory,\n`; - indexJs += ` connect: (signer) => ${contract.name}Factory.connect('${ - deployments[contract.name] - }', signer)\n`; - indexJs += `};\n\n`; - - // Add to index.d.ts - indexDts += `import { ${ - contract.name - }__factory } from './typechain/factories/contracts/${contract.path.replace( - ".sol", - "" - )}__factory';\n`; - indexDts += `import { ${ - contract.name - } } from './typechain/contracts/${contract.path.replace(".sol", "")}';\n`; - indexDts += `export const ${contract.name}: {\n`; - indexDts += ` address: string;\n`; - indexDts += ` abi: any[];\n`; - indexDts += ` bytecode: string;\n`; - indexDts += ` network: string;\n`; - indexDts += ` chainId: number;\n`; - indexDts += ` factory: typeof ${contract.name}__factory;\n`; - indexDts += ` connect: (signer: ethers.Signer) => ${contract.name};\n`; - indexDts += `};\n\n`; - } - - // Add network info to both files - indexJs += `exports.network = {\n`; - indexJs += ` name: '${deployments.network}',\n`; - indexJs += ` chainId: ${deployments.chainId},\n`; - indexJs += ` deployedAt: '${deployments.deployedAt}'\n`; - indexJs += `};\n`; - - indexDts += `export const network: {\n`; - indexDts += ` name: string;\n`; + // Generate main index.js content + let indexJs = ""; + let indexDts = ""; + + // Add networks enum + indexJs += `const Networks = {\n`; + indexJs += ` ARBITRUM_SEPOLIA: 'arbitrum-sepolia',\n`; + indexJs += ` ARBITRUM: 'arbitrum'\n`; + indexJs += `};\n\n`; + indexJs += `exports.Networks = Networks;\n\n`; + + // Import all network-specific classes + for (const [netName, netConfig] of Object.entries(networks)) { + const netClassName = toPascalCase(netName) + "Contracts"; + indexJs += `const ${netClassName} = require('./${netConfig.path}');\n`; + } + indexJs += `\n`; + + // Generate main class + indexJs += `class DCUContracts {\n`; + indexJs += ` constructor(network) {\n`; + indexJs += ` if (!Object.values(Networks).includes(network)) {\n`; + indexJs += ` throw new Error(\`Network \${network} not supported\`);\n`; + indexJs += ` }\n`; + indexJs += ` this.network = network;\n`; + indexJs += ` switch(network) {\n`; + for (const [netName, netConfig] of Object.entries(networks)) { + const netClassName = toPascalCase(netName) + "Contracts"; + indexJs += ` case Networks.${netName + .toUpperCase() + .replace("-", "_")}:\n`; + indexJs += ` this.networkInstance = new ${netClassName}();\n`; + indexJs += ` break;\n`; + } + indexJs += ` default:\n`; + indexJs += ` throw new Error(\`Network \${network} not supported\`);\n`; + indexJs += ` }\n`; + indexJs += ` }\n\n`; + + // Add contract getters + for (const contract of contracts) { + indexJs += ` get ${contract.name}() {\n`; + indexJs += ` return this.networkInstance.${contract.name};\n`; + indexJs += ` }\n\n`; + } + + indexJs += `}\n\n`; + indexJs += `exports.DCUContracts = DCUContracts;\n`; + + // Generate main TypeScript declarations + indexDts += `import { ethers } from 'ethers';\n\n`; + + // Import all network-specific classes in TypeScript declarations + for (const [netName, netConfig] of Object.entries(networks)) { + const netClassName = toPascalCase(netName) + "Contracts"; + indexDts += `import { ${netClassName} } from './${netConfig.path}';\n`; + } + indexDts += `\n`; + + indexDts += `export enum Networks {\n`; + indexDts += ` ARBITRUM_SEPOLIA = 'arbitrum-sepolia',\n`; + indexDts += ` ARBITRUM = 'arbitrum'\n`; + indexDts += `}\n\n`; + + indexDts += `export interface ContractConfig {\n`; + indexDts += ` address: string;\n`; + indexDts += ` abi: any[];\n`; + indexDts += ` bytecode: string;\n`; + indexDts += ` network: Networks;\n`; indexDts += ` chainId: number;\n`; - indexDts += ` deployedAt: string;\n`; - indexDts += `};\n`; + indexDts += ` factory: T;\n`; + indexDts += ` connect: (signer: ethers.Signer) => T;\n`; + indexDts += `}\n\n`; - // Write both files + indexDts += `export class DCUContracts {\n`; + indexDts += ` constructor(network: Networks);\n`; + for (const contract of contracts) { + indexDts += ` get ${contract.name}(): ContractConfig<${contract.name}>;\n`; + } + indexDts += `}\n`; + + // Write main files fs.writeFileSync(path.join(packageDir, "index.js"), indexJs); fs.writeFileSync(path.join(packageDir, "index.d.ts"), indexDts); - // Update package.json to include types - packageJsonForBuild.types = "index.d.ts"; - fs.writeFileSync( - path.join(packageDir, "package.json"), - JSON.stringify(packageJsonForBuild, null, 2) - ); - - console.log("Package generated successfully!"); + console.log(`Package generated successfully for network: ${networkName}!`); } main() diff --git a/scripts/update-package.sh b/scripts/update-package.sh new file mode 100755 index 0000000..bb5f273 --- /dev/null +++ b/scripts/update-package.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +# Create a temporary directory +BASE_DIR=$(pwd) +TEMP_DIR=$(mktemp -d) +echo "Created temporary directory: $TEMP_DIR" + +# Remove package directory if it exists and create a new one +if [ -d "$BASE_DIR/package" ]; then + echo "Removing existing package directory..." + rm -rf "$BASE_DIR/package" +fi +echo "Creating package directory..." +mkdir -p "$BASE_DIR/package" + +# Navigate to the temp directory +cd $TEMP_DIR + +# Download and pack the package +echo "Downloading @decleanup/contracts..." +npm pack "@decleanup/contracts" + +# Get the package filename (it will be the only .tgz file in the directory) +PACKAGE_FILE=$(ls *.tgz) +echo "Downloaded package: $PACKAGE_FILE" + +# Create a directory to extract the package +mkdir package +cd package + +# Extract the package +echo "Extracting package..." +tar -xzf ../$PACKAGE_FILE + +# Move the package contents to the correct location +echo "Moving package contents..." +cp -r package/* $BASE_DIR/package/ + +# Clean up temp directory +cd ../.. +rm -rf $TEMP_DIR + +# Clean up artifacts and typechain folders +echo "Cleaning up artifacts and typechain folders..." +if [ -d "$BASE_DIR/package/artifacts" ]; then + rm -rf "$BASE_DIR/package/artifacts" +fi +if [ -d "$BASE_DIR/package/typechain" ]; then + rm -rf "$BASE_DIR/package/typechain" +fi + +echo "Package update complete!" \ No newline at end of file diff --git a/scripts/verify-arbitrum.ts b/scripts/verify-arbitrum.ts index a227041..4e9e083 100644 --- a/scripts/verify-arbitrum.ts +++ b/scripts/verify-arbitrum.ts @@ -3,10 +3,7 @@ import * as fs from "fs"; import * as path from "path"; // Read deployed addresses from deployment file -const deployedAddressesPath = path.join( - __dirname, - "deployed_addresses_arbitrum.json" -); +const deployedAddressesPath = path.join(__dirname, "deployed_addresses.json"); async function main() { console.log("Starting contract verification on Arbitrum..."); From 7ac9fbdd5c8185449b6d299188ee59eb526a8a38 Mon Sep 17 00:00:00 2001 From: Shehu-Fatiudeen Lawal Date: Wed, 28 May 2025 11:01:57 +0100 Subject: [PATCH 3/5] docs: update CONTRACT_PACKAGE.md for new DCUContracts usage and add ONBOARDING_NEW_NETWORK.md guide for adding new networks --- docs/CONTRACT_PACKAGE.md | 92 ++++++++--------- docs/ONBOARDING_NEW_NETWORK.md | 176 +++++++++++++++++++++++++++++++++ 2 files changed, 224 insertions(+), 44 deletions(-) create mode 100644 docs/ONBOARDING_NEW_NETWORK.md diff --git a/docs/CONTRACT_PACKAGE.md b/docs/CONTRACT_PACKAGE.md index 23f6a16..a9f75f0 100644 --- a/docs/CONTRACT_PACKAGE.md +++ b/docs/CONTRACT_PACKAGE.md @@ -14,59 +14,73 @@ npm install @decleanup/contracts ```typescript import { ethers } from 'ethers'; -import { DCUToken, RewardLogic, DCUAccounting, DCUStorage, DCURewardManager, DipNft } from '@decleanup/contracts'; +import { DCUContracts, Networks } from '@decleanup/contracts'; + +// Initialize with a specific network +const contracts = new DCUContracts(Networks.ARBITRUM_SEPOLIA); // Initialize provider and signer const provider = new ethers.JsonRpcProvider('YOUR_RPC_URL'); const signer = new ethers.Wallet('YOUR_PRIVATE_KEY', provider); // Get contract instances with full type inference -const token = DCUToken.contract.connect(signer); -const rewardLogic = RewardLogic.contract.connect(signer); -const accounting = DCUAccounting.contract.connect(signer); -const storage = DCUStorage.contract.connect(signer); -const rewardManager = DCURewardManager.contract.connect(signer); -const dipNft = DipNft.contract.connect(signer); +const token = contracts.DCUToken.connect(signer); +const rewardLogic = contracts.RewardLogic.connect(signer); +const accounting = contracts.DCUAccounting.connect(signer); +const storage = contracts.DCUStorage.connect(signer); +const rewardManager = contracts.DCURewardManager.connect(signer); +const dipNft = contracts.DipNft.connect(signer); ``` ### Contract Information -Each contract export contains the following information: +Each contract provides the following information: ```typescript -import { DCUToken } from '@decleanup/contracts'; +import { DCUContracts, Networks } from '@decleanup/contracts'; + +const contracts = new DCUContracts(Networks.ARBITRUM_SEPOLIA); // Contract address -console.log(DCUToken.address); +console.log(contracts.DCUToken.address); // Contract ABI -console.log(DCUToken.abi); +console.log(contracts.DCUToken.abi); // Network information -console.log(DCUToken.network); // 'arbitrum' or 'arbitrum-testnet' -console.log(DCUToken.chainId); // Chain ID +console.log(contracts.DCUToken.network); // 'arbitrum-sepolia' or 'arbitrum' +console.log(contracts.DCUToken.chainId); // Chain ID // Type-safe contract instance -const token = DCUToken.contract.connect(signer); +const token = contracts.DCUToken.connect(signer); ``` -### Network Information +### Network Support + +The package supports multiple networks through the `Networks` enum: ```typescript -import { network } from '@decleanup/contracts'; +import { DCUContracts, Networks } from '@decleanup/contracts'; -console.log(network.name); // Network name -console.log(network.chainId); // Chain ID -console.log(network.deployedAt); // Deployment timestamp +// Initialize for Arbitrum Sepolia +const sepoliaContracts = new DCUContracts(Networks.ARBITRUM_SEPOLIA); + +// Initialize for Arbitrum Mainnet +const mainnetContracts = new DCUContracts(Networks.ARBITRUM); + +// Check network information +console.log(sepoliaContracts.DCUToken.chainId); // 421614 +console.log(mainnetContracts.DCUToken.chainId); // 42161 ``` ### Example: Token Operations ```typescript -import { DCUToken } from '@decleanup/contracts'; +import { DCUContracts, Networks } from '@decleanup/contracts'; async function transferTokens(signer: ethers.Signer, recipient: string, amount: bigint) { - const token = DCUToken.contract.connect(signer); + const contracts = new DCUContracts(Networks.ARBITRUM_SEPOLIA); + const token = contracts.DCUToken.connect(signer); // Type-safe transfer const tx = await token.transfer(recipient, amount); @@ -81,11 +95,12 @@ async function transferTokens(signer: ethers.Signer, recipient: string, amount: ### Example: Reward Operations ```typescript -import { RewardLogic, DCURewardManager } from '@decleanup/contracts'; +import { DCUContracts, Networks } from '@decleanup/contracts'; async function claimRewards(signer: ethers.Signer, submissionId: bigint) { - const rewardManager = DCURewardManager.contract.connect(signer); - const rewardLogic = RewardLogic.contract.connect(signer); + const contracts = new DCUContracts(Networks.ARBITRUM_SEPOLIA); + const rewardManager = contracts.DCURewardManager.connect(signer); + const rewardLogic = contracts.RewardLogic.connect(signer); // Check if rewards are available const isAvailable = await rewardLogic.isRewardAvailable(submissionId); @@ -102,10 +117,11 @@ async function claimRewards(signer: ethers.Signer, submissionId: bigint) { ### Example: NFT Operations ```typescript -import { DipNft } from '@decleanup/contracts'; +import { DCUContracts, Networks } from '@decleanup/contracts'; async function mintNFT(signer: ethers.Signer, to: string, tokenURI: string) { - const nft = DipNft.contract.connect(signer); + const contracts = new DCUContracts(Networks.ARBITRUM_SEPOLIA); + const nft = contracts.DipNft.connect(signer); // Mint new NFT const tx = await nft.mint(to, tokenURI); @@ -130,39 +146,27 @@ The package includes TypeChain-generated type definitions, providing: - Compile-time error checking ```typescript -import { DCUToken } from '@decleanup/contracts'; +import { DCUContracts, Networks } from '@decleanup/contracts'; -const token = DCUToken.contract.connect(signer); +const contracts = new DCUContracts(Networks.ARBITRUM_SEPOLIA); +const token = contracts.DCUToken.connect(signer); // Type-safe method calls await token.transfer(recipient, amount); // ✅ Correct await token.transfer(recipient, "100"); // ❌ Error: amount must be bigint ``` -## Network Support - -The package supports both Arbitrum mainnet and testnet deployments. The correct contract addresses and ABIs are automatically selected based on the network you're connecting to. - -```typescript -import { network } from '@co/contracts'; - -if (network.chainId === 42161) { - console.log('Connected to Arbitrum mainnet'); -} else if (network.chainId === 421614) { - console.log('Connected to Arbitrum testnet'); -} -``` - ## Error Handling All contract interactions include proper error handling: ```typescript -import { DCUToken } from '@decleanup/contracts'; +import { DCUContracts, Networks } from '@decleanup/contracts'; async function safeTransfer(signer: ethers.Signer, recipient: string, amount: bigint) { try { - const token = DCUToken.contract.connect(signer); + const contracts = new DCUContracts(Networks.ARBITRUM_SEPOLIA); + const token = contracts.DCUToken.connect(signer); const tx = await token.transfer(recipient, amount); await tx.wait(); return true; diff --git a/docs/ONBOARDING_NEW_NETWORK.md b/docs/ONBOARDING_NEW_NETWORK.md new file mode 100644 index 0000000..6465bae --- /dev/null +++ b/docs/ONBOARDING_NEW_NETWORK.md @@ -0,0 +1,176 @@ +# Onboarding a New Network + +This guide explains how to add a new network to the DeCleanup Network contracts system. + +## Prerequisites + +1. Network details: + - Chain ID + - RPC URL + - Network name (in kebab-case, e.g., `new-network`) + - Explorer URL (if available) + +2. Access to: + - GitHub repository + - NPM registry + - Network's block explorer (if verification is supported) + +## Steps to Add a New Network + +### 1. Update Network Configuration + +Add the new network to the `networks` object in `contracts/scripts/generate-package.ts`: + +```typescript +const networks: Networks = { + "arbitrum-sepolia": { + chainId: 421614, + path: "arbitrum-sepolia", + }, + arbitrum: { + chainId: 42161, + path: "arbitrum", + }, + "new-network": { // Add your network here + chainId: YOUR_CHAIN_ID, + path: "new-network", + }, +}; +``` + +### 2. Add Network to GitHub Workflow + +Update `contracts/.github/workflows/deploy-contracts.yml`: + +1. Add the network to the workflow inputs: +```yaml +workflow_dispatch: + inputs: + network: + description: 'Network to deploy to (arbitrum-sepolia/arbitrum/new-network)' + required: true + default: 'arbitrum-sepolia' + type: choice + options: + - arbitrum-sepolia + - arbitrum + - new-network +``` + +2. Add deployment step: +```yaml +- name: Deploy to New Network + if: github.event_name == 'workflow_dispatch' && github.event.inputs.network == 'new-network' + run: npm run deploy:new-network + env: + NEW_NETWORK_RPC_URL: ${{ secrets.NEW_NETWORK_RPC_URL }} + PRIVATE_KEY: ${{ secrets.DEPLOYER_PRIVATE_KEY }} + EXPLORER_API_KEY: ${{ secrets.EXPLORER_API_KEY }} +``` + +3. Add verification step: +```yaml +- name: Verify Contracts on New Network + if: github.event_name == 'workflow_dispatch' && github.event.inputs.network == 'new-network' + run: npm run verify:all-new-network + env: + NEW_NETWORK_RPC_URL: ${{ secrets.NEW_NETWORK_RPC_URL }} + EXPLORER_API_KEY: ${{ secrets.EXPLORER_API_KEY }} +``` + +### 3. Add Network-Specific Scripts + +1. Add deployment script to `package.json`: +```json +{ + "scripts": { + "deploy:new-network": "hardhat run scripts/deploy.ts --network new-network", + "verify:all-new-network": "hardhat run scripts/verify.ts --network new-network" + } +} +``` + +2. Add network configuration to `hardhat.config.ts`: +```typescript +const config: HardhatUserConfig = { + networks: { + "new-network": { + url: process.env.NEW_NETWORK_RPC_URL || "", + accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : [], + chainId: YOUR_CHAIN_ID, + }, + }, +}; +``` + +### 4. Add GitHub Secrets + +Add the following secrets to your GitHub repository: +- `NEW_NETWORK_RPC_URL`: RPC URL for the new network +- `EXPLORER_API_KEY`: API key for the network's block explorer (if applicable) + +### 5. Test the Integration + +1. Deploy contracts to the new network: + - Go to GitHub Actions + - Select "Deploy Smart Contracts" + - Choose "new-network" from the network dropdown + - Run the workflow + +2. Verify the deployment: + - Check the deployment artifacts + - Verify contracts on the block explorer + - Test the generated package with the new network + +### 6. Update Documentation + +1. Update `contracts/docs/CONTRACT_PACKAGE.md` to include the new network +2. Add any network-specific considerations or requirements +3. Update example code to show usage with the new network + +## Package Generation + +The package generation script (`generate-package.ts`) will automatically: +1. Create a network-specific folder +2. Generate network-specific index files +3. Include the network in the base index files +4. Support the network in the `DCUContracts` class + +## Testing + +After adding a new network: +1. Deploy contracts to the network +2. Generate the package +3. Test the package with the new network: +```typescript +import { DCUContracts, Networks } from "@decleanup/contracts"; + +const contracts = new DCUContracts(Networks.NEW_NETWORK); +// Use contracts as needed +``` + +## Troubleshooting + +Common issues and solutions: + +1. **Deployment Fails** + - Check RPC URL and private key + - Verify network configuration in Hardhat + - Check gas settings if needed + +2. **Verification Fails** + - Verify explorer API key + - Check contract constructor arguments + - Ensure network is supported by the explorer + +3. **Package Generation Issues** + - Check network name format (kebab-case) + - Verify deployment addresses file + - Check TypeChain types generation + +## Support + +If you encounter any issues: +1. Check the existing documentation +2. Review GitHub issues +3. Contact the development team \ No newline at end of file From 57b9cadfea0af8e86ecba357a55e1e39c7bf9962 Mon Sep 17 00:00:00 2001 From: Shehu-Fatiudeen Lawal Date: Fri, 4 Jul 2025 11:54:04 +0100 Subject: [PATCH 4/5] feat: add NFTCollection and Submission contracts to deployment scripts and update package.json with new generation commands --- ignition/modules/DCUContracts.ts | 17 ++++++++++++++++- package.json | 5 ++++- scripts/deploy-arbitrum.ts | 22 ++++++++++++++++++++++ scripts/generate-package.ts | 2 ++ 4 files changed, 44 insertions(+), 2 deletions(-) diff --git a/ignition/modules/DCUContracts.ts b/ignition/modules/DCUContracts.ts index 9cd6afb..a58a590 100644 --- a/ignition/modules/DCUContracts.ts +++ b/ignition/modules/DCUContracts.ts @@ -20,7 +20,20 @@ export default buildModule("DCUContracts", (m) => { const dcuToken = m.contract("DCUToken", [rewardLogic]); // Deploy the DCURewardManager contract with DCUToken address - const dcuRewardManager = m.contract("DCURewardManager", [dcuToken]); + const dcuRewardManager = m.contract("DCURewardManager", [ + dcuToken, + nftCollection, + ]); + + // Deploy the DipNft contract with DCURewardManager address + const dipNft = m.contract("DipNft", [dcuRewardManager]); + + // Deploy the Submission contract + const submission = m.contract("Submission", [ + dcuToken, + rewardLogic, + "10000000000000000000", + ]); // 10 DCU default reward // Return all deployed contracts return { @@ -30,5 +43,7 @@ export default buildModule("DCUContracts", (m) => { rewardLogic, dcuToken, dcuRewardManager, + dipNft, + submission, }; }); diff --git a/package.json b/package.json index abedf15..6cc98d2 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,10 @@ "verify:all-arbitrum-testnet": "hardhat run scripts/verify-arbitrum.ts --network arbitrumSepolia", "gas:compare": "hardhat run scripts/gas-comparison.ts", "typechain": "hardhat typechain", - "clean": "hardhat clean" + "clean": "hardhat clean", + "generate-package": "ts-node scripts/generate-package.ts", + "generate-package:arbitrum": "ts-node scripts/generate-package.ts arbitrum", + "generate-package:arbitrum-sepolia": "ts-node scripts/generate-package.ts arbitrum-sepolia" }, "dependencies": { "@openzeppelin/contracts": "^5.2.0", diff --git a/scripts/deploy-arbitrum.ts b/scripts/deploy-arbitrum.ts index dab3e91..9de0be1 100644 --- a/scripts/deploy-arbitrum.ts +++ b/scripts/deploy-arbitrum.ts @@ -65,6 +65,14 @@ async function main() { const dcuRewardManagerAddress = dcuRewardManager.address; console.log("DCURewardManager deployed to:", dcuRewardManagerAddress); + // Deploy NFTCollection + console.log("Deploying NFTCollection..."); + const NFTCollection = await ethers.getContractFactory("NFTCollection"); + const nftCollection = await NFTCollection.deploy(); + await nftCollection.deployed(); + const nftCollectionAddress = nftCollection.address; + console.log("NFTCollection deployed to:", nftCollectionAddress); + // Deploy DipNft with the reward manager address console.log("Deploying DipNft..."); const DipNft = await ethers.getContractFactory("DipNft"); @@ -73,6 +81,18 @@ async function main() { const dipNftAddress = dipNft.address; console.log("DipNft deployed to:", dipNftAddress); + // Deploy Submission contract + console.log("Deploying Submission..."); + const Submission = await ethers.getContractFactory("Submission"); + const submission = await Submission.deploy( + dcuTokenAddress, + rewardLogicAddress, + ethers.utils.parseEther("10") // Default reward amount: 10 DCU + ); + await submission.deployed(); + const submissionAddress = submission.address; + console.log("Submission deployed to:", submissionAddress); + // Update NFT collection references in both contracts console.log("Updating NFT collection in RewardLogic..."); await rewardLogic.setNFTCollection(dipNftAddress); @@ -90,6 +110,8 @@ async function main() { DCUStorage: dcuStorageAddress, DCURewardManager: dcuRewardManagerAddress, DipNft: dipNftAddress, + NFTCollection: nftCollectionAddress, + Submission: submissionAddress, network: (await ethers.provider.getNetwork()).name, chainId: Number((await ethers.provider.getNetwork()).chainId), deployedAt: new Date().toISOString(), diff --git a/scripts/generate-package.ts b/scripts/generate-package.ts index 64a587f..bcbbf3f 100644 --- a/scripts/generate-package.ts +++ b/scripts/generate-package.ts @@ -70,6 +70,8 @@ async function main() { { name: "DCUStorage", path: "DCUStorage.sol" }, { name: "DCURewardManager", path: "DCURewardManager.sol" }, { name: "DipNft", path: "tokens/DipNft.sol" }, + { name: "NFTCollection", path: "tokens/NFTCollection.sol" }, + { name: "Submission", path: "Submission.sol" }, ]; // Read deployment addresses From 6ad4ec927071941ae348e1c8a6e1a7bec66362c8 Mon Sep 17 00:00:00 2001 From: Shehu-Fatiudeen Lawal Date: Fri, 4 Jul 2025 11:54:13 +0100 Subject: [PATCH 5/5] docs: expand CONTRACT_PACKAGE.md with detailed contract overview, package generation instructions, and troubleshooting tips --- docs/CONTRACT_PACKAGE.md | 202 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 189 insertions(+), 13 deletions(-) diff --git a/docs/CONTRACT_PACKAGE.md b/docs/CONTRACT_PACKAGE.md index a9f75f0..daf7fa3 100644 --- a/docs/CONTRACT_PACKAGE.md +++ b/docs/CONTRACT_PACKAGE.md @@ -1,4 +1,4 @@ -# DeCleanup Network Smart Contracts +# DeCleanup Network Smart Contracts Package This package provides the smart contracts for the DeCleanup Network, including TypeChain-generated type definitions for type-safe contract interactions. @@ -8,6 +8,91 @@ This package provides the smart contracts for the DeCleanup Network, including T npm install @decleanup/contracts ``` +## Overview + +The DeCleanup Network smart contracts package includes all core contracts for the environmental cleanup platform: + +### Core Token Contracts +- **DCUToken** - Main ERC-20 utility token with dynamic supply +- **DCUStorage** - Token storage with TGE restrictions and staking +- **DCUAccounting** - Token deposit/withdrawal management + +### NFT & Reward Contracts +- **DipNft** - Soulbound NFTs representing Impact Products +- **NFTCollection** - Basic NFT collection for testing +- **DCURewardManager** - Manages DCU rewards for various activities +- **RewardLogic** - Handles reward distribution logic + +### Additional Contracts +- **Submission** - Handles form submissions from the DeCleanup dapp + +## Package Generation + +### Prerequisites + +1. **Compile Contracts:** + ```bash + npm run compile + ``` + +2. **Generate TypeChain Types:** + ```bash + npm run typechain + ``` + +3. **Deploy Contracts:** + ```bash + # For Arbitrum Mainnet + npm run deploy:arbitrum + + # For Arbitrum Sepolia Testnet + npm run deploy:arbitrum-testnet + ``` + +### Generating the Package + +#### For Arbitrum Mainnet +```bash +npm run generate-package:arbitrum +``` + +#### For Arbitrum Sepolia Testnet +```bash +npm run generate-package:arbitrum-sepolia +``` + +#### For Custom Network +```bash +npm run generate-package +``` + +### Package Structure + +The generated package will have the following structure: + +``` +package/ +├── package.json +├── index.js +├── index.d.ts +└── / + ├── config.json + ├── index.js + ├── index.d.ts + ├── artifacts/ + │ ├── DCUToken/ + │ │ ├── abi.json + │ │ └── bytecode.json + │ ├── DipNft/ + │ │ ├── abi.json + │ │ └── bytecode.json + │ └── ... (other contracts) + └── typechain/ + ├── contracts/ + ├── factories/ + └── ... (TypeChain files) +``` + ## Usage ### Basic Setup @@ -30,6 +115,8 @@ const accounting = contracts.DCUAccounting.connect(signer); const storage = contracts.DCUStorage.connect(signer); const rewardManager = contracts.DCURewardManager.connect(signer); const dipNft = contracts.DipNft.connect(signer); +const submission = contracts.Submission.connect(signer); +const nftCollection = contracts.NFTCollection.connect(signer); ``` ### Contract Information @@ -97,20 +184,15 @@ async function transferTokens(signer: ethers.Signer, recipient: string, amount: ```typescript import { DCUContracts, Networks } from '@decleanup/contracts'; -async function claimRewards(signer: ethers.Signer, submissionId: bigint) { +async function claimRewards(signer: ethers.Signer, amount: bigint) { const contracts = new DCUContracts(Networks.ARBITRUM_SEPOLIA); const rewardManager = contracts.DCURewardManager.connect(signer); - const rewardLogic = contracts.RewardLogic.connect(signer); - - // Check if rewards are available - const isAvailable = await rewardLogic.isRewardAvailable(submissionId); - if (!isAvailable) { - throw new Error('Rewards not available for this submission'); - } // Claim rewards - const tx = await rewardManager.claimRewards(submissionId); + const tx = await rewardManager.claimRewards(amount); await tx.wait(); + + return tx; } ``` @@ -119,12 +201,12 @@ async function claimRewards(signer: ethers.Signer, submissionId: bigint) { ```typescript import { DCUContracts, Networks } from '@decleanup/contracts'; -async function mintNFT(signer: ethers.Signer, to: string, tokenURI: string) { +async function mintNFT(signer: ethers.Signer) { const contracts = new DCUContracts(Networks.ARBITRUM_SEPOLIA); const nft = contracts.DipNft.connect(signer); - // Mint new NFT - const tx = await nft.mint(to, tokenURI); + // Mint new NFT (requires PoI verification) + const tx = await nft.safeMint(); await tx.wait(); // Get token ID @@ -136,6 +218,28 @@ async function mintNFT(signer: ethers.Signer, to: string, tokenURI: string) { } ``` +### Example: Submission Operations + +```typescript +import { DCUContracts, Networks } from '@decleanup/contracts'; + +async function createSubmission(signer: ethers.Signer, dataURI: string) { + const contracts = new DCUContracts(Networks.ARBITRUM_SEPOLIA); + const submission = contracts.Submission.connect(signer); + + // Create a new submission + const tx = await submission.createSubmission(dataURI); + await tx.wait(); + + // Get submission ID from event + const receipt = await tx.wait(); + const event = receipt.logs.find(log => log.fragment.name === 'SubmissionCreated'); + const submissionId = event?.args[0]; + + return submissionId; +} +``` + ## Type Safety The package includes TypeChain-generated type definitions, providing: @@ -177,6 +281,78 @@ async function safeTransfer(signer: ethers.Signer, recipient: string, amount: bi } ``` +## Adding New Contracts + +To add a new contract to the package: + +1. **Add to Contract List**: Update the `contracts` array in `scripts/generate-package.ts` + ```typescript + const contracts: ContractInfo[] = [ + // ... existing contracts + { name: "NewContract", path: "NewContract.sol" }, + ]; + ``` + +2. **Update Deployment Script**: Add deployment logic to `scripts/deploy-arbitrum.ts` + ```typescript + // Deploy NewContract + const NewContract = await ethers.getContractFactory("NewContract"); + const newContract = await NewContract.deploy(/* constructor args */); + await newContract.deployed(); + const newContractAddress = newContract.address; + ``` + +3. **Update Ignition Module**: Add to `ignition/modules/DCUContracts.ts` + ```typescript + const newContract = m.contract("NewContract", [/* constructor args */]); + ``` + +4. **Update Deployed Addresses**: Add to the `deployedAddresses` object + ```typescript + const deployedAddresses = { + // ... existing addresses + NewContract: newContractAddress, + }; + ``` + +## Troubleshooting + +### Common Issues + +1. **Missing Contract Artifacts** + - Ensure contracts are compiled: `npm run compile` + - Check that the contract path in `generate-package.ts` is correct + +2. **Missing Deployment Addresses** + - Deploy contracts first: `npm run deploy:arbitrum` + - Verify `deployed_addresses.json` exists and contains all contracts + +3. **TypeChain Types Missing** + - Generate types: `npm run typechain` + - Check that `typechain-types/` directory exists + +4. **Network Not Supported** + - Add network configuration to `generate-package.ts` + - Update deployment scripts for the new network + +### Verification + +After generating the package: + +1. **Check Package Structure**: Verify all contracts are included +2. **Test Integration**: Use the package in a test project +3. **Verify Addresses**: Confirm contract addresses match deployments +4. **Test TypeScript**: Ensure TypeScript types work correctly + +## Publishing + +To publish the package to npm: + +1. **Build Package**: Generate the package for all networks +2. **Test Package**: Verify functionality in a test environment +3. **Update Version**: Increment version in `package.json` +4. **Publish**: Use `npm publish` in the package directory + ## Contributing To contribute to this package: