Smart Contracts & Solidity

Chapter 9
Development Frameworks and Professional Workflow

Advance Diploma in Blockchain
Smart Contracts & Solidity   •   Advance Diploma

Development Frameworks and Professional Workflow

Introduction

Smart-contract development is not limited to writing Solidity code. A professional system requires a controlled workflow for compiling contracts, managing dependencies, deploying to local and public networks, testing expected and adversarial behaviour, reviewing changes, and producing evidence that a particular software version can be rebuilt. Development frameworks provide the tools and conventions for this work.

Remix is highly accessible and valuable for learning, rapid experimentation, and isolated contract interactions. Foundry and Hardhat provide more extensive project automation, testing, scripting, dependency management, and integration with professional software-engineering practices. These tools are complementary rather than mutually exclusive. A developer may prototype in Remix, implement and test in Foundry or Hardhat, and use a public testnet before production deployment.

The central principle of this chapter is reproducibility: another developer, auditor, or future maintainer should be able to determine which source code, compiler, dependencies, configuration, test commands, and network settings produced a given contract artifact and deployment.

Learning Objectives

By the end of this chapter, students should be able to:

  1. Explain the roles of Remix, Foundry, and Hardhat in Solidity development.
  2. Structure a maintainable smart-contract project and manage package dependencies.
  3. Configure Solidity compilers and understand the importance of compiler version and optimizer settings.
  4. Use local blockchain networks, automated tests, scripts, and testnets safely.
  5. Apply Git-based version control and continuous integration practices.
  6. Design a reproducible workflow that supports review, auditing, deployment, and maintenance.
  7. Identify operational, security, governance, and regulatory risks associated with development tooling.

1. The Development Lifecycle

A professional smart-contract lifecycle normally contains the following stages:

  1. Specification: define actors, assets, permissions, invariants, failure conditions, and upgrade assumptions.
  2. Design: select contract boundaries, data structures, access-control mechanisms, events, and interaction patterns.
  3. Implementation: write Solidity code using an explicit compiler version and established libraries.
  4. Local testing: execute unit, integration, property-based, and failure-path tests on a local chain.
  5. Static and security analysis: inspect compiler warnings, lint results, dependency versions, and known vulnerability patterns.
  6. Review: use peer review and, for high-value systems, independent audit.
  7. Testnet deployment: deploy using controlled accounts and verify behaviour in an environment resembling production.
  8. Release: create an identifiable version, record artifacts and configuration, and obtain deployment approval.
  9. Monitoring and maintenance: observe events, balances, permissions, incidents, and dependency changes.

A framework does not replace specification or security judgment. It automates repeatable activities and makes assumptions visible.

2. Remix: Accessible Browser-Based Development

Remix is an integrated browser-based environment for Solidity. It includes a source editor, compiler interface, deployment panel, transaction interaction tools, and commonly used plugins. It is particularly appropriate for:

  • learning Solidity syntax and the transaction model;
  • compiling a small contract quickly;
  • interacting with a deployed contract;
  • reproducing a minimal bug;
  • demonstrating ABI functions and events; and
  • testing a short example without creating a full local project.

A typical Remix workflow is:

  1. Create or import a .sol file.
  2. Select the Solidity compiler version and optimizer settings.
  3. Compile the contract and inspect warnings.
  4. Select an execution environment, such as a simulated VM, an injected browser wallet, or a custom provider.
  5. Deploy the contract with constructor arguments and, where necessary, deployment value.
  6. Call read functions and submit transactions to state-changing functions.
  7. Record the address, network, transaction hash, compiler settings, and source version.

Remix's simulated VM is useful because it provides deterministic accounts and immediate feedback. However, it should not be confused with a public testnet. Its state is temporary, its accounts are artificial, and its execution environment may not reproduce every feature of a production network.

Remix also has limitations for large projects. Repeated manual deployment is error-prone, test suites are less structured than framework-based suites, and project-wide dependency and configuration management is weaker. Consequently, Remix is best treated as a learning, prototyping, and interaction environment rather than the sole basis for a production release.

3. Foundry and Hardhat

3.1 Foundry

Foundry is a toolchain centred on fast command-line development. Its principal tools include:

  • Forge: compilation, testing, scripting, and deployment;
  • Anvil: a local Ethereum-compatible node with funded development accounts and useful testing features;
  • Cast: command-line calls, transaction submission, account and data inspection; and
  • Chisel: an interactive Solidity-oriented environment for experimentation.

Foundry tests are commonly written in Solidity. This permits direct use of contracts, cheatcodes, and blockchain-state manipulation in the same language used for production contracts. Foundry is especially effective for fast unit tests, fuzz testing, invariant testing, gas measurements, and fork-based testing.

3.2 Hardhat

Hardhat is a JavaScript or TypeScript-oriented development environment. It provides a local network, compilation, testing, scripting, and an extensible plugin ecosystem. Tests may be written with JavaScript or TypeScript testing libraries, allowing convenient integration with web applications, APIs, signing systems, and external services.

Hardhat is useful when a project requires:

  • TypeScript-based deployment and operational scripts;
  • integration tests involving application code;
  • custom plugins and task automation;
  • structured network configuration; and
  • integration with broader JavaScript tooling.

3.3 Selecting a framework

RequirementSuitable primary tool
First Solidity experimentRemix
Small interactive demonstrationRemix
Solidity-native unit and fuzz testingFoundry
High-speed local testing and invariantsFoundry
TypeScript application integrationHardhat
Complex deployment tasks and pluginsHardhat
Production projectFoundry or Hardhat, often with additional independent tools

The choice should be based on team skills, existing infrastructure, testing needs, audit expectations, and maintenance costs. A mixed workflow is acceptable, but it must avoid having two conflicting sources of truth for compiler settings, deployments, and artifacts.

4. Project Structure and Package Libraries

A maintainable project separates production code, tests, scripts, configuration, and documentation. A representative structure is:

project/
├── src/ or contracts/       # Solidity source files
├── test/                    # unit, integration, fuzz, and invariant tests
├── script/ or scripts/      # deployment and operational scripts
├── lib/ or node_modules/    # pinned external libraries
├── deployments/             # recorded addresses and deployment metadata
├── artifacts/               # generated ABI and bytecode; often ignored by Git
├── foundry.toml or hardhat.config.ts
├── remappings.txt           # Foundry import mappings, if used
├── package.json             # JavaScript dependencies, if used
├── README.md
└── .env.example

Libraries such as OpenZeppelin Contracts provide reviewed implementations of common components, including access control, token standards, cryptographic utilities, and security patterns. Reusing a reputable library is generally safer than reimplementing standard functionality, but it does not eliminate the need to understand the code or check compatibility.

Dependencies should be pinned or constrained deliberately. A floating dependency that silently changes between builds undermines reproducibility. A lockfile records the resolved dependency graph for package managers. Git submodules or versioned library directories can also be used, provided their commit references are recorded.

A package library should be evaluated for:

  • licence compatibility;
  • maintenance activity and release history;
  • compiler compatibility;
  • transitive dependencies;
  • known vulnerabilities;
  • upgrade or storage-layout implications; and
  • suitability for the intended chain and protocol version.

Never import a package merely because it is popular. Review the exact version and the code paths used by the contract.

5. Compiler Configuration

The Solidity compiler is part of the trusted build process. A deployment record should identify at least:

  • compiler version;
  • optimizer enabled or disabled;
  • optimizer run target;
  • EVM version, where explicitly configured;
  • source files and dependency versions;
  • contract name and source path; and
  • metadata or verification settings.

A version pragma such as pragma solidity ^0.8.20; expresses a permitted range, not necessarily the exact compiler used. For reproducible releases, the project configuration should select an exact compiler version or an explicitly controlled range, and continuous integration should verify that choice.

The optimizer trades compilation time and bytecode characteristics against runtime gas behaviour. The optimizer's run target is an estimate of whether the deployed contract will be executed relatively few times or many times. It is not a guarantee of a particular gas result. Gas measurements must be made using the actual compiler, EVM target, input data, and execution path.

Compiler warnings deserve attention. A warning may identify unreachable code, shadowing, implicit conversions, or an uninitialised variable. Warnings are not automatically vulnerabilities, but suppressing them without justification weakens review quality.

The deployed bytecode must be distinguished from creation bytecode. Creation bytecode runs during deployment and returns the runtime bytecode that remains at the contract address. Constructor arguments are encoded into the deployment transaction and can affect verification procedures even though they are not part of the runtime bytecode.

6. Local Chains and Deterministic Testing

A local chain is an Ethereum-compatible blockchain process running on a developer's machine or in CI. It provides rapid execution, test accounts, controllable balances, and resettable state. Anvil and Hardhat Network are common examples.

Local testing can include:

  • ordinary unit tests;
  • reverts and custom-error assertions;
  • event and state-transition checks;
  • access-control tests;
  • fuzz testing with many generated inputs;
  • invariant testing, where a property must remain true across sequences of actions;
  • time and block manipulation; and
  • fork testing against a copy of a public network state.

A local chain is not identical to a public chain. Differences may include RPC behaviour, gas pricing, precompiled contracts, chain configuration, transaction ordering, external services, oracle updates, and liquidity. Fork tests improve realism but inherit the selected fork block and external-state assumptions.

A strong test suite checks both successful and unsuccessful behaviour. For example, a token withdrawal test should verify correct balance changes, emitted events, authorization, insufficient-balance failure, repeated-call behaviour, and reentrancy assumptions where relevant.

Tests should express properties, not merely implementation details. An invariant might be: “The sum of all user balances equals the recorded total supply,” subject to explicitly defined minting and burning operations. Tests must also consider integer boundaries, zero addresses, duplicate operations, stale signatures, paused states, and unexpected token behaviour.

7. Testnets and Deployment Discipline

A public testnet is a shared blockchain used to approximate production interaction. It is useful for testing wallet flows, RPC providers, block explorers, contract verification, front-end integration, and multi-party operations. Testnet tokens generally have no intended monetary value, but private keys and signing approvals must still be managed professionally.

A deployment plan should specify:

  • target chain and chain identifier;
  • deployer account and approval authority;
  • contract constructor parameters;
  • library addresses, if linked;
  • post-deployment configuration;
  • ownership transfer or multisignature procedure;
  • verification method;
  • smoke tests; and
  • rollback or pause strategy.

Deployment scripts should be idempotent where practical: rerunning a script should not accidentally create an inconsistent set of contracts. If a script cannot be idempotent, it should detect prior deployments and stop safely. Record transaction hashes, addresses, block numbers, artifact hashes, and configuration values.

Never commit private keys, seed phrases, RPC credentials, or unencrypted production secrets. Use environment variables for local development and a managed secret store in CI/CD. A .env.example file can document required variable names without containing real values.

8. Git, Code Review, and Release Management

Git provides history, branching, review, and traceability. Commits should be small enough to understand and should describe the change accurately. A useful workflow may include feature branches, pull requests, required review, and protected release branches.

A pull request for a smart contract should include:

  • purpose and scope of the change;
  • affected contracts and storage layout;
  • test results;
  • compiler and dependency changes;
  • gas or performance effects where relevant;
  • security implications; and
  • deployment or migration requirements.

Generated artifacts may be excluded from version control when they can be deterministically regenerated. Deployment manifests and verified release metadata, however, should be retained in a controlled and auditable form. Tags should identify releases, and a release should reference the exact commit used for deployment.

Storage layout requires special attention in upgradeable systems. Adding, removing, or reordering state variables can corrupt proxy storage. Upgrade reviews should compare storage layouts, initializer behaviour, access controls, and rollback assumptions rather than treating an upgrade as an ordinary code change.

9. Continuous Integration and Delivery

Continuous integration (CI) automatically builds and tests proposed changes. A suitable pipeline may perform the following sequence:

  1. Check out the exact commit.
  2. Install dependencies using a lockfile or pinned references.
  3. Compile with the specified Solidity version.
  4. Run formatting and lint checks.
  5. Run unit, fuzz, and invariant tests.
  6. Generate a gas report where useful.
  7. Run static-analysis and dependency-security checks.
  8. Compare generated artifacts or storage layouts where required.
  9. Publish test evidence.

Continuous delivery (CD) extends CI by preparing or executing deployments. Automatic production deployment is not always appropriate. A safer arrangement often requires manual approval, multisignature authorization, environment-specific secrets, and an explicit release tag.

A pipeline should fail closed when critical checks fail. It should also prevent accidental deployment from unreviewed branches. Network identifiers must be checked in deployment scripts so that a testnet configuration cannot silently send transactions to production.

Reproducibility means that the same source commit and controlled inputs produce equivalent outputs. Exact byte-for-byte equivalence can be affected by metadata settings and tool versions, so teams should define what must be identical: for example, runtime bytecode, ABI, storage layout, and deployment parameters. Build reports should record tool versions, dependency hashes, compiler settings, and environment information.

10. An End-to-End Professional Workflow

The following workflow combines the tools and controls discussed above.

Step 1: Define the contract specification

Write the state model, roles, permitted transitions, events, invariants, threat model, and non-goals. Identify whether the contract is immutable or upgradeable and who controls administrative functions.

Step 2: Prototype

Use Remix or a small framework project to validate syntax, interfaces, and basic interactions. Do not treat a successful prototype transaction as evidence of security.

Step 3: Create a versioned project

Select Foundry or Hardhat, define the compiler version, add a reputable library at a controlled version, and commit the initial configuration. Add formatting, testing, and documentation conventions.

Step 4: Implement incrementally

Use small commits and clear interfaces. Emit events for important state changes. Avoid unnecessary complexity and minimise privileged operations.

Step 5: Test locally

Run unit tests and negative tests first, then fuzz and invariant tests. Use fork testing when integration with existing contracts or realistic state matters. Record known limitations.

Step 6: Review and analyse

Conduct peer review, inspect compiler warnings, run static analysis, and check dependencies. For valuable systems, commission an independent audit and resolve findings through tracked issues.

Step 7: Deploy to a testnet

Use a scripted deployment, verify source code, execute smoke tests, and test the front end and operational procedures. Confirm ownership, roles, pause controls, and event monitoring.

Step 8: Release with evidence

Tag the approved commit. Archive build information, deployment transactions, addresses, configuration, verification results, and approvals. Use controlled secrets and a production-specific checklist.

Step 9: Monitor and maintain

Monitor events, failed transactions, administrative actions, balances, oracle behaviour, and unusual activity. Document incident response and update procedures.

11. Illustrative Case: A Community Grant Escrow

Background

A university innovation programme wants an escrow contract that holds grant funds and releases them to approved recipients when milestone evidence is accepted. The illustrative contract has a grant administrator, recipients, and an oversight multisignature account.

Problem

An initial prototype was deployed from Remix. The team later discovered that constructor parameters had not been recorded, the compiler setting differed between developers, and the administrator role remained with an individual wallet. Reproducing the deployment was difficult, and a test did not cover a rejected milestone.

Analysis

The main risks were not only Solidity defects. They included weak configuration control, incomplete testing, excessive privilege concentration, and inadequate deployment records. The contract specification was revised to define milestone states, permitted transitions, cancellation rules, withdrawal conditions, and event requirements.

Approach and solution

The team moved the project into a Foundry repository. The Solidity compiler version and optimizer settings were committed in configuration. A versioned OpenZeppelin access-control library was added. Tests covered successful release, unauthorized approval, duplicate release, cancellation, zero-value cases, and invalid state transitions. Fuzz tests generated arbitrary grant amounts and recipient addresses subject to test constraints.

A deployment script required the expected chain identifier and accepted constructor parameters from a reviewed configuration file. It recorded the resulting address, transaction hash, block number, artifact hash, and administrator. After deployment to a testnet, the team verified source code, transferred administration to the oversight multisignature account, and executed a scripted smoke test.

CI compiled the project, ran tests, checked formatting, and rejected unreviewed deployment attempts. Production deployment required a tagged commit and manual approval.

Outcome

The project gained reproducible builds, clearer authority boundaries, stronger failure-path coverage, and an auditable release record. The workflow did not prove that the contract was free of vulnerabilities, but it substantially improved the team's ability to detect, review, and manage defects.

Learning points

  1. Tool selection must support the whole lifecycle, not merely initial coding.
  2. Deployment parameters and administrative roles are part of the security boundary.
  3. Testnet success is evidence of integration behaviour, not a security guarantee.
  4. Reproducibility depends on source, compiler, dependencies, configuration, and secrets discipline.
  5. Governance controls, including multisignature approval, may be as important as contract logic.

12. Limitations, Governance, and Contemporary Practice

Frameworks can create false confidence. Passing tests cover only the inputs and sequences exercised. Fuzzing depends on meaningful properties and adequate exploration. Static analysis produces findings that require expert interpretation. Audits are point-in-time assessments and cannot replace secure governance.

Dependency maintainers may publish compromised packages, repositories may be taken over, and CI credentials may be abused. Organisations should use least-privilege tokens, protected branches, dependency review, isolated build runners where appropriate, and independent verification of release artifacts.

Smart-contract projects also raise governance and regulatory questions. A deployment may be technically irreversible but subject to legal obligations concerning consumer protection, financial services, data privacy, sanctions, or reporting. On-chain data is generally persistent; sensitive personal information should not be placed directly on a public ledger without careful legal and architectural analysis.

AI-assisted coding can accelerate boilerplate generation and test drafting, but generated code may contain subtle access-control, arithmetic, authorization, or dependency errors. Every AI-produced change requires compilation, testing, review, and security analysis by accountable developers. AI tools should not receive private keys, confidential audit material, or proprietary source without appropriate controls.

Best-Practice Checklist

  • Define behaviour and threat assumptions before implementation.
  • Pin compiler and dependency versions.
  • Use reputable libraries and inspect the exact imported version.
  • Keep secrets outside source control.
  • Test success, failure, boundaries, and adversarial sequences.
  • Use local chains for speed and testnets for integration realism.
  • Record chain identifiers, addresses, transaction hashes, and artifacts.
  • Require review for contract, configuration, and deployment changes.
  • Protect CI/CD credentials and production branches.
  • Verify deployed source and runtime configuration.
  • Treat upgrades, privileges, and emergency controls as governance decisions.
  • Maintain monitoring, incident response, and release documentation.

Chapter Summary

Professional Solidity development is a controlled engineering process rather than a sequence of manual deployments. Remix supports accessible experimentation and interaction, while Foundry and Hardhat provide structured compilation, testing, scripting, and automation. Local chains enable fast and repeatable tests; public testnets expose integration and operational issues but do not guarantee production security.

Compiler versions, optimizer settings, dependencies, deployment parameters, and environment details must be controlled to achieve reproducibility. Git, peer review, CI, protected secrets, release tags, and deployment records provide traceability. Strong workflows test both successful and failing behaviour, use fuzzing and invariants where appropriate, and treat access control, upgradeability, monitoring, and governance as part of the system's security boundary.

Key Terms

Remix: Browser-based Solidity development and interaction environment.

Foundry: Command-line Ethereum development toolkit including Forge and Anvil.

Hardhat: Extensible JavaScript/TypeScript-oriented Ethereum development framework.

Forge: Foundry tool for compiling, testing, and scripting.

Anvil: Foundry local Ethereum-compatible node.

ABI: Application Binary Interface describing how applications encode contract calls and decode results.

Artifact: Generated build output such as ABI, bytecode, and metadata.

Optimizer: Compiler component that transforms code to improve selected deployment or execution characteristics.

Compiler pragma: Solidity statement specifying permitted compiler versions.

Dependency pinning: Controlling a project to a known library or package version.

Local chain: Developer-controlled blockchain used for rapid testing.

Fork test: Test executed against a locally simulated copy of a public-chain state.

Fuzz testing: Automated testing with generated inputs.

Invariant testing: Testing that specified properties remain true across action sequences.

Testnet: Public blockchain network used for development and integration testing.

Continuous integration: Automated building and testing of proposed changes.

Continuous delivery: Controlled preparation or deployment of approved software releases.

Reproducible build: Build whose relevant outputs can be regenerated from controlled inputs.

Multisignature wallet: Account requiring approval from multiple authorized signers.

Storage layout: Arrangement of state variables in contract storage slots.

Review Questions

Short Answer Questions

  1. What are the principal uses and limitations of Remix?
  2. Distinguish Foundry from Hardhat in terms of language and workflow orientation.
  3. Why should compiler and dependency versions be controlled?
  4. What is the difference between a local chain and a public testnet?
  5. Identify three items that should be recorded after deployment.

Long Answer Questions

  1. Explain an end-to-end professional smart-contract development lifecycle and the purpose of each stage.
  2. Compare Remix, Foundry, and Hardhat, and recommend a toolchain for a TypeScript-integrated decentralised application.
  3. Discuss how compiler configuration, package management, and deployment metadata contribute to reproducibility.
  4. Explain how Git, code review, CI, and CD reduce technical and operational risk.
  5. Analyse why passing automated tests and completing an audit do not guarantee smart-contract security.

Analytical / Application Questions

  1. A team deploys successfully to a local chain but receives different runtime behaviour on a testnet. Identify at least five possible causes and propose an investigation sequence.
  2. Design a CI/CD policy for a contract controlling valuable funds. Include testing, secret management, approval gates, artifact handling, and production deployment controls.
  3. A proposed upgrade changes the order of state variables in a proxy implementation. Assess the risk and specify the checks required before approval.

Multiple Choice Questions with Answers

Question 1

Which tool is primarily designed as a browser-based environment for Solidity experimentation and interaction?

A. Remix

B. Anvil

C. Cast

D. Forge

Correct Answer: A. Remix

Question 2

What is the principal purpose of a lockfile or pinned dependency reference?

A. To increase the contract's token supply

B. To control the exact dependency versions used in a build

C. To eliminate the need for testing

D. To transfer contract ownership

Correct Answer: B. To control the exact dependency versions used in a build

Question 3

Which Foundry tool provides a local Ethereum-compatible development node?

A. Forge

B. Cast

C. Anvil

D. Chisel

Correct Answer: C. Anvil

Question 4

Why is a public testnet useful?

A. It guarantees that production contains no vulnerabilities

B. It provides free production-value assets

C. It tests integration with shared network, wallet, and RPC conditions

D. It removes the need for deployment scripts

Correct Answer: C. It tests integration with shared network, wallet, and RPC conditions

Question 5

Which practice best protects production deployment credentials?

A. Commit private keys to a private Git branch

B. Place seed phrases in deployment comments

C. Use managed secrets and least-privilege access

D. Reuse a developer's personal wallet

Correct Answer: C. Use managed secrets and least-privilege access

Question 6

What does invariant testing examine?

A. Whether a property remains true across sequences of actions

B. Whether a user interface has a particular colour

C. Whether a package has a permissive licence

D. Whether a compiler can be installed

Correct Answer: A. Whether a property remains true across sequences of actions

Question 7

Which item is most important for reproducing a deployed contract artifact?

A. The developer's screen resolution

B. The exact source, compiler, dependency, and configuration inputs

C. The number of comments in the source code

D. The name of the project folder

Correct Answer: B. The exact source, compiler, dependency, and configuration inputs

Question 8

Why should a deployment script check the target chain identifier?

A. To make Solidity compile faster

B. To prevent a configuration intended for one network being used on another

C. To increase block size

D. To replace source-code verification

Correct Answer: B. To prevent a configuration intended for one network being used on another

Question 9

What is a major risk of changing the order of state variables in an upgradeable contract?

A. The user interface becomes multilingual

B. Proxy storage may be misinterpreted or corrupted

C. The compiler automatically deletes all events

D. The testnet receives more tokens

Correct Answer: B. Proxy storage may be misinterpreted or corrupted

Question 10

Which statement about AI-generated Solidity code is most accurate?

A. It is secure if it compiles once

B. It eliminates the need for peer review

C. It should undergo testing, review, and security analysis

D. It can safely receive production private keys

Correct Answer: C. It should undergo testing, review, and security analysis

Smart Contracts & Solidity
Chapter 9 of 15