1. Saving new artifacts and updating existing

Users and tooling must be able to save new and update existing Artifact files.

As a core behavior, tooling must facilitate the internal record-keeping of all known Contract Instances, updating them appropriately when they change.

Beyond that, developers may also need to manually generate new artifacts for previously unknown contracts.

Since a smart contract blockchain network is effectively a distributed global database, developers may write their applications for the express or implicit purpose of accessing other contract instances on that network.

Developers should be able to leverage tooling to interface with these external contracts, including public libraries and contract instances for other applications. Tooling should integrate well with code written outside a given project.

Additionally, some applications may not use tooling’s built-in deployment systems, and some Contract Instances may deploy others. Tooling should be able to account for the record-keeping in all these cases.

left to right direction

:Smart Contract Developer: as :SmartContractDev:

rectangle Saving {
  (Save contract) as (SaveContract)
  (Save contract instance) as (SaveInstance)
  (Save library instance) as (SaveLibrary)
  (Save interface instance) as (SaveInterface)
}

SmartContractDev -- SaveContract
SmartContractDev -- SaveInstance
SmartContractDev -- SaveLibrary
SmartContractDev -- SaveInterface

1.1. Save contract

Users must be able to save a new Contract, whether or not it has any corresponding Contract Instances.

1.2. Save contract instance

Users must be able to save new Contract Instances on a given network, identifying them as a known Contract.

1.3. Save library instance

Similarly, users must be able to save records of externally-deployed Libraries. This is particularly useful, as many commonly-used libraries are already deployed on a majority of Networks, and users should not have to pay the extra gas just to accommodate tooling.

1.4. Save interface instance

In cases where source is not known for an external contract, users may need to reference that Contract Instance by its interface (i.e. via Solidity’s interface mechanism).

2. Compiling smart contracts

At the core of smart contract development and deployment is the compilation of high-level languages to the underlying EVM Bytecode.

Smart contract developers compile/recompile Sources many times throughout the lifecycle of development.

scale 0.80
left to right direction

:Smart Contract Developer: as :Developer:

rectangle Saving << external >> {
  (Save contract) as (SaveContract)
}

rectangle Compilation {
  (Compile all project sources) as (CompileAll)

  (Determine modified sources) as (DetermineModified)
  (Compile modified project sources) as (CompileModified)
  CompileModified -> DetermineModified

  (Compile source file) as (Compile)
  Compile -> SaveContract

  (Compile primary and related source files) as (CompileMultiple)
  CompileMultiple .|> Compile

  CompileAll --> Compile
  CompileModified --> Compile
}

Developer -- CompileAll
Developer -- CompileModified

2.1. Compile all project sources

Either as part of the first development iteration, or to reset, developers must be able to compile all Source files in a Project and obtain saved Artifacts.

2.2. Compile modified project sources

For most of development, it’s more commonly useful to compile only Source files that have changed. Users should be able to perform this minimal compilation.

2.3. Determine modified sources

To support compilation of changed Sources, tooling should provide a means to identify which files have changed since their last compilation.

2.4. Compile source file

Tooling must provide a means to compile a single, standalone Source file (no imports), saving a corresponding Artifact for the Contract.

3. Reading contract metadata

Developers, auditors, business stakeholders, et al., all often need to access metadata information about both Contracts and Contract Instances.

For example, stakeholders often need to view the JSON ABI, auditors need source and compiler information, developers need to maintain records of source file relations and deployment statuses, etc.

Beyond direct access of contract metadata, many other use cases rely on this metadata heavily.

Metadata should be accessible both for current versions and for all historical versions that may be in use.

scale 0.60
left to right direction

:Smart Contract Developer: as :SmartContractDev:

rectangle Metadata {
  (Read contract instance info) as (ReadContractInstance)
  (Read contract info) as (ReadContract)
  (Read historical contract instance contract info) as (ReadHistoricalInstanceContract)

  (Read instance) as (ReadInstanceData)
  (Read contract) as (ReadContractData)

  ReadInstanceData <|.up. ReadContractInstance
  ReadContractData <|.up.. ReadContract
  ReadContractData <|.up.. ReadHistoricalInstanceContract

  ' both
  (Read name) as (ReadName)
  (Read ABI) as (ReadABI)
  (Read bytecodes) as (ReadBytecodes)
  (Read source mappings) as (ReadSourceMaps)
  (Read link values) as (ReadLinkVals)
  (Read sources) as (ReadSources)
  (Read ASTs) as (ReadASTs)

  ' instance
  (Read link references) as (ReadLinkRefs)
  (Read address) as (ReadAddress)

  ReadContractData --> ReadName
  ReadContractData --> ReadABI
  ReadContractData --> ReadBytecodes
  ReadContractData --> ReadSourceMaps
  ReadContractData --> ReadLinkRefs
  ReadContractData --> ReadSources
  ReadContractData --> ReadASTs

  ReadInstanceData --> ReadContractData
  ReadInstanceData --> ReadLinkVals
  ReadInstanceData --> ReadAddress
}

SmartContractDev -- ReadContractInstance
SmartContractDev -- ReadContract
SmartContractDev -- ReadHistoricalInstanceContract

3.1. Read contract instance information

Users must be able to read Contract Instance metadata, including ABI, any link values, and all contract-level metadata for that instance.

3.2. Read contract info

Users must be able to read metadata about a Contract that has not been deployed, as well as metadata for a given Contract Instance.

3.3. Read contract instance historical contract info

If the Contract Instance has a contract that is no longer the current version in source, users should still be able to read that information, for the contract at deploy-time. This may differ from the current contract version arbitrarily: different source, different ABI, etc.

4. Querying for specific contracts

Developers must be able to view all known Contract Instances on a given network, or a list of networks with known instances of a given Contract.

left to right direction
:Smart Contract Developer: as :Developer:

rectangle Querying {
  (Query known instances on a given network) as (QueryNetwork)
  (Query all networks for a given contract) as (QueryContract)
  (Query all networks for instances with a given role) as (QueryInstance)
  (Query for instance by network and address) as (QueryAddress)
  (Query all contracts with a given bytecode) as (QueryBytecode)
  (Query for bytecode ignoring link references) as (QueryBytecodeUnlinked)

  QueryInstance .|> QueryContract
  QueryBytecodeUnlinked .|> QueryBytecode
}

Developer -- QueryNetwork
Developer -- QueryContract
Developer -- QueryInstance
Developer -- QueryAddress
Developer -- QueryBytecode

4.1. Query for instance by network and address

Users must be able to look up known Contract Instances given a Network and an Address.

4.2. Query all contracts with a given bytecode

Because it is immutable, contract Bytecode serves as a reliable secondary index for both Contract Instances and Contracts. Users should be able to find all known Contracts based on the raw hexadecimal representation of EVM machine code.

4.4. Query known instances on a given network

Users should be able to see, at a glance, all Contract Instances on a given Network. This can be useful for validating migration state, or for easy listing of address / ABI information, to present/share externally.

4.5. Query all networks for a given contract

Developers often write applications so that each Contract has a singleton deployed Contract Instance. Tooling should enable this first-class nature of contracts, and users should be able to track the various instances across all known Networks.

This is particularly useful when creating front-end applications, referencing instances across networks with a single contract description.

4.6. Query all networks for instances with a given role

In cases where a particular Contract is not deployed as a singleton, tooling should provide a mechanism by which users can deploy multiple Contract Instances.

As a result, users should be able to use this distinct identifier to query for analogous instances across Networks, instead of relying solely on the contract.

5. Interacting with deployed instances

Besides metadata information, users must be able to retrieve runtime state information about Contract Instances and to be able to write to those instances over the network.

left to right direction

:User: as :User:

rectangle Metadata << external >> {
  (Read ABI) as (ReadABI)
  (Read instance) as (ReadInstance)
  (ReadInstance) -> ReadABI
}

rectangle Interaction {
  (Call read-only method) as (Call)
  (Invoke method via a transaction) as (SendTransaction)
  (Send ETH to a contract instance) as (Send)

  Call --> ReadInstance
  SendTransaction --> ReadInstance
}

User -- Call
User -- SendTransaction
User -- Send

5.1. Call read-only method

Almost every smart contract provides mechanisms for viewing information about a Contract Instance’s current state, including public storage variables and computed data views. These interfaces are specified for each instance’s Contract, and described in its ABI.

Users must be able to call these read-only methods and obtain their results.

5.2. Invoke method via a transaction

Similarly to calls, most contracts provide interface methods for modifying Contract Instance state. Users must be able to execute the allowable “write” operations for a contract instance.

5.3. Send ETH to a contract instance

Many smart contracts expose methods for receiving payment in the form of ether or other tokens.

Developers must be able to test the receipt of ether; business stakeholders may have to provide initial funds for applications that require it.

6. Linking contracts to libraries

Smart contract development best practices encourage the separation of code into multiple discrete components. As such, developers may write contracts that leverage the use of Libraries to provide composed behavior.

Contracts using libraries are compiled with unresolved Link References. Libraries are deployed as their own instances and later filled in for the Contract as a corresponding Link Value.

left to right direction

:Smart Contract Developer: as :Developer:

rectangle Saving << external >> {
  (Save contract) as (SaveContract)
  (Save contract instance) as (SaveInstance)
}

rectangle Linking {
  (Link contract to library) as (LinkContract)
  (Link contract instance to library) as (LinkInstance)
  LinkInstance .left.|> LinkType

  LinkContract --> SaveContract
  LinkInstance --> SaveInstance
}

Developer -- LinkContract
Developer -- LinkInstance

7. Deploying new instances

During and after the process of creating smart contracts, smart contract developers need to deploy Contracts on one or more Networks, creating one or more Contract Instances.

left to right direction

:Smart Contract Developer: as :Developer:

rectangle Saving << external >> {
  (Save contract instance) as (SaveInstance)
}

rectangle Deployment {
  (Deploy instance of a contract) as (DeployInstance)
  (Deploy multiple instances) as (MultipleInstances)
  MultipleInstances .|> DeployInstance
  DeployInstance --> SaveInstance
}

Developer -- DeployInstance
Developer -- MultipleInstances

7.1. Deploy instance of a contract

The base case for deployment: given a Contract, users must be able to deploy a new Contract Instance on a particular Network.

7.2. Deploy multiple instances

For applications that require multiple Contract Instances per Contract, users should be able to deploy instances by role or other identifier.

8. Migrations

Smart contract developers should be able to track the states of different Networks separately. Contract Instances on different networks can be of different versions of the same Contract. This difference should be understood by the underlying tooling, and easy to reason about for the user of the tool.

scale 0.55
left to right direction

:Smart Contract Developer: as :Developer:

rectangle Querying << external >> {
  (Query for a given contract) as (QueryContract)
}

rectangle Interacting << external >> {
  (Call read-only method) as (Call)
}

rectangle Linking << external >> {
  (Link contract to library) as (LinkContract)
}

rectangle Deployment << external >> {
  (Deploy instance of a contract) as (DeployInstance)
}

rectangle Migrations {
  (Run all migrations) as (RunMigrations)
  (Run specific migration) as (RunSpecific)
  (Run as historical) as (RunHistorical)
  (Determine last completed migration) as (DetermineLastCompleted)


  RunHistorical ..|> RunMigrations

  RunMigrations -right-> RunSpecific
  RunMigrations -left-> DetermineLastCompleted

  DetermineLastCompleted -down-> QueryContract : find deployed Migrations
  DetermineLastCompleted -down-> Call : read instance state
  RunSpecific -down-> DeployInstance
  RunSpecific -down-> LinkContract


}

Developer -- RunMigrations
Developer -- RunSpecific
Developer -- RunHistorical
Developer -- DetermineLastCompleted

8.1. Determine last completed migration

Querying and reading the on-chain Contract Instance for the Migrations contract, users should be able to determine the most recently completed migration on that Network.

8.2. Run all migrations

Either starting at the last completed migration, or resetting from the beginning, users should be able to run all migrations for a Project on a particular Network.

8.3. Run specific migration

As part of the larger use case of running all migrations, or as a standalone operation. Users and tooling must support running a single migration.

8.4. Run with historical contracts

Specifying parent network, users should be able to mimic the state of a given network, determining Contracts for all known Contract Instances, and deploy new instances matching those contracts instead of current.

9. Testing smart contracts

Being able to validate that smart contracts behave as expected is of paramount importance.

Development workflow best practices involve writing/running automated tests early and often in the process. Developers should be able to test their contracts locally before deployment, and once deployed, be able to test their contract instances locally, matching expected behavior on the network itself.

scale 0.65
left to right direction

:Smart Contract Developer: as :Developer:


rectangle Compilation << external >> {
  (Compile all project sources) as (CompileAll)
}

rectangle Migrations << external >> {
  (Run all migrations) as (RunMigrations)
  (Run as historical) as (RunHistorical)
  RunHistorical .|> RunMigrations
}

rectangle Testing {
  (Run automated tests for contract) as (TestContract)
  (Run automated tests for contract instance) as (TestInstance)
  (Run automated tests for library type) as (TestLibrary)
  (Run automated tests for library instance) as (TestLibraryInstance)

  TestInstance --> RunHistorical
  TestLibraryInstance --> RunHistorical

  (Run test written in Solidity) as (RunSolidity)
  RunSolidity --> CompileAll
  RunSolidity --> RunMigrations

  (Run test written in Javascript) as (RunJavascript)
  RunJavascript --> CompileAll
  RunJavascript --> RunMigrations

  TestContract --> RunSolidity
  TestContract --> RunJavascript

  TestInstance --> RunSolidity
  TestInstance --> RunJavascript

  TestLibrary --> RunSolidity
  TestLibraryInstance --> RunSolidity
}

Developer -- TestContract
Developer -- TestInstance
Developer -- TestLibrary
Developer -- TestLibraryInstance

9.1. Run automated tests for contract

Users must be able to run the full test suite for a given Contract, written in either Solidity or Javascript, starting fresh on a given Network and running all compilation/migration steps in the process.

9.2. Run automated tests for contract instance

Some tests may be written for a specific Contract Instance to account for historical differences between that instance and the current Source for the corresponding Contract.

Users should be able run tests on a fresh local Network, reflecting that historical version.

9.3. Run automated tests for library type

In order to validate a particular Library, users should be able to run tests in a fresh local Network environment, with given library deployed.

9.4. Run automated tests for library instance

Much like running automated tests for a Contract Instance, users should be able to validate behavior for outdated instances of a given Library.