From 76c06692df63ae9625014bf8348927c2fe390dff Mon Sep 17 00:00:00 2001 From: David Drazic Date: Thu, 21 Jul 2022 19:08:01 +0200 Subject: [PATCH] [e2e] Deploy contracts directly seeding Ganache (#14631) * Add some experimental Ganache seeder WIP Refactor GanacheSeeder * Added all smart contract bytecodes and abis * Deploy smart contract by contract name * Clean up and fix tx hash * Removed console logs * Remove last console log * Use node module path for grabbing contract constants * Remove contract deployment example on e2e test * Small refactor for removing extra line Co-authored-by: seaona --- test/e2e/helpers.js | 11 +++ .../ganache-contract-address-registry.js | 28 ++++++ test/e2e/seeder/ganache-seeder.js | 88 +++++++++++++++++++ test/e2e/seeder/smart-contracts.js | 43 +++++++++ 4 files changed, 170 insertions(+) create mode 100644 test/e2e/seeder/ganache-contract-address-registry.js create mode 100644 test/e2e/seeder/ganache-seeder.js create mode 100644 test/e2e/seeder/smart-contracts.js diff --git a/test/e2e/helpers.js b/test/e2e/helpers.js index cd05e38e0..72a82acc8 100644 --- a/test/e2e/helpers.js +++ b/test/e2e/helpers.js @@ -9,6 +9,7 @@ const FixtureServer = require('./fixture-server'); const PhishingWarningPageServer = require('./phishing-warning-page-server'); const { buildWebDriver } = require('./webdriver'); const { ensureXServerIsRunning } = require('./x-server'); +const GanacheSeeder = require('./seeder/ganache-seeder'); const tinyDelayMs = 200; const regularDelayMs = tinyDelayMs * 2; @@ -23,6 +24,7 @@ async function withFixtures(options, testSuite) { dapp, fixtures, ganacheOptions, + smartContract, driverOptions, dappOptions, title, @@ -46,6 +48,14 @@ async function withFixtures(options, testSuite) { let failed = false; try { await ganacheServer.start(ganacheOptions); + let contractRegistry; + + if (smartContract) { + const ganacheSeeder = new GanacheSeeder(true); + await ganacheSeeder.deploySmartContract(smartContract); + contractRegistry = ganacheSeeder.getContractRegistry(); + } + if (ganacheOptions?.concurrent) { const { port, chainId } = ganacheOptions.concurrent; secondaryGanacheServer = new Ganache(); @@ -100,6 +110,7 @@ async function withFixtures(options, testSuite) { await testSuite({ driver, mockServer, + contractRegistry, }); if (process.env.SELENIUM_BROWSER === 'chrome') { diff --git a/test/e2e/seeder/ganache-contract-address-registry.js b/test/e2e/seeder/ganache-contract-address-registry.js new file mode 100644 index 000000000..bfad708d3 --- /dev/null +++ b/test/e2e/seeder/ganache-contract-address-registry.js @@ -0,0 +1,28 @@ +/* + * Use this class to store pre-deployed smart contract addresses of the contracts deployed to + * a local blockchain instance ran by Ganache. + */ +class GanacheContractAddressRegistry { + #addresses = {}; + + /** + * Store new contract address in key:value pair. + * + * @param contractName + * @param contractAddress + */ + storeNewContractAddress(contractName, contractAddress) { + this.#addresses[contractName] = contractAddress; + } + + /** + * Get deployed contract address by its name (key). + * + * @param contractName + */ + getContractAddress(contractName) { + return this.#addresses[contractName]; + } +} + +module.exports = GanacheContractAddressRegistry; diff --git a/test/e2e/seeder/ganache-seeder.js b/test/e2e/seeder/ganache-seeder.js new file mode 100644 index 000000000..85cc199bc --- /dev/null +++ b/test/e2e/seeder/ganache-seeder.js @@ -0,0 +1,88 @@ +const { ethers } = require('ethers'); +const ganache = require('ganache'); +const { contractConfiguration } = require('./smart-contracts'); +const GanacheContractAddressRegistry = require('./ganache-contract-address-registry'); + +/* + * Ganache seeder is used to seed initial smart contract or set initial blockchain state. + */ +class GanacheSeeder { + constructor(debug = false) { + this.debug = debug; + this.smartContractRegistry = new GanacheContractAddressRegistry(); + } + + /** + * Deploy initial smart contracts that can be used later within the e2e tests. + * + * @param contractName + */ + + async deploySmartContract(contractName) { + if (this.debug) { + console.log('Deploying smart contracts using GanacheSeeder'); + } + + const ethersProvider = new ethers.providers.Web3Provider( + ganache.provider(), + 'any', + ); + const contractFactory = new ethers.ContractFactory( + contractConfiguration[contractName].abi, + contractConfiguration[contractName].bytecode, + ethersProvider.getSigner(), + ); + + let contract; + + if (contractName === 'hst') { + contract = await contractFactory.deploy( + contractConfiguration.hst.initialAmount, + contractConfiguration.hst.tokenName, + contractConfiguration.hst.decimalUnits, + contractConfiguration.hst.tokenSymbol, + ); + } else { + contract = await contractFactory.deploy(); + } + + await contract.deployTransaction.wait(); + + if (this.debug) { + console.log( + `Contract mined! address: ${contract.address} transactionHash: ${contract.deployTransaction.hash}`, + ); + } + this.storeSmartContractAddress(contractName, contract.address); + } + + /** + * Store deployed smart contract address within the environment variables + * to make it available everywhere. + * + * @param contractName + * @param contractAddress + */ + storeSmartContractAddress(contractName, contractAddress) { + if (this.debug) { + console.log( + `Storing smart contract address: [${contractName}] => ${contractAddress}`, + ); + } + this.smartContractRegistry.storeNewContractAddress( + contractName, + contractAddress, + ); + } + + /** + * Return an instance of the currently used smart contract registry. + * + * @returns GanacheContractAddressRegistry + */ + getContractRegistry() { + return this.smartContractRegistry; + } +} + +module.exports = GanacheSeeder; diff --git a/test/e2e/seeder/smart-contracts.js b/test/e2e/seeder/smart-contracts.js new file mode 100644 index 000000000..c2394bb9a --- /dev/null +++ b/test/e2e/seeder/smart-contracts.js @@ -0,0 +1,43 @@ +const { + hstBytecode, + hstAbi, + piggybankBytecode, + piggybankAbi, + collectiblesAbi, + collectiblesBytecode, + failingContractAbi, + failingContractBytecode, +} = require('../../../node_modules/@metamask/test-dapp/dist/constants.json'); + +const hstFactory = { + initialAmount: 100, + tokenName: 'TST', + decimalUnits: 4, + tokenSymbol: 'TST', + bytecode: hstBytecode, + abi: hstAbi, +}; + +const collectiblesFactory = { + bytecode: collectiblesBytecode, + abi: collectiblesAbi, +}; + +const piggybankFactory = { + bytecode: piggybankBytecode, + abi: piggybankAbi, +}; + +const failingContract = { + bytecode: failingContractBytecode, + abi: failingContractAbi, +}; + +const contractConfiguration = { + hst: hstFactory, + collectibles: collectiblesFactory, + piggybank: piggybankFactory, + failing: failingContract, +}; + +module.exports = { contractConfiguration };