Contract rules overview
Both contract types (Protocol and Dynamic) derive from the same base classes in our source code (
BaseContract
in src/contract/contract.h
, and DynamicContract
in src/contract/dynamiccontract.h
), though each type adheres to specific rules that must be followed to ensure they will work as intended.For both contracts, you must:
- Inherit from their base class (Protocol Contracts should inherit
BaseContract
directly, and Dynamic Contracts should inheritDynamicContract
) and make sure you're passing the right arguments for them - Manage variables within the state and database during the contract's construction (loading) and destruction (saving)
- Register callbacks for the contract's functions with the proper function signature (if functions are called by RPC,
eth_call
, or a transaction) - Ensure that, when building the contract, its name must match the contract's class name
BaseContract
andDynamicContract
take acontractName
string as an argument - for example, if your contract is called "TestContract", your class would beclass TestContract : public DynamicContract { ... }
and your constructor call would beDynamicContract(interface, "TestContract", ...)
- Declare view functions (functions that do not change state) as
const
and return astd::string
with the encoded ABI (e.g.std::string getBalance(Address add) const { ... }
). - Declare callable functions (functions that do change state and are callable by transactions within ContractManager) as non-
const
and returnvoid
(e.g.void transfer(Address to, uint256_t value) { ... }
).
For Protocol Contracts specifically, you must:
- Override the BaseContract's
ethCall()
functions to parse transaction arguments and commit/revert them if necessary - Manage state changes during
ethCall()
functions (depending on whether the call is committing or not)
For Dynamic Contracts specifically, you must:
- Override
registerContractFunctions()
and call it inside the contract's constructor - Provide two constructors: one for contract creation within ContractManager, and one for loading the contract from the database
- Allow the contract creation only through a transaction call to the ContractManager contract
- Develop functions for handling your new contract creation, and modify
ethCall()
functions to register and access them - Set all contract variables as
private
and inherit them from one of the many Safe Variable classes available - Allow loops using containers such as SafeUnorderedMap, but keep in mind how Safe Containers work
- e.g. when you access a key from a SafeUnorderedMap, it'll check if it exists and copy only the key, not the entire map or its value - thus when iterating a loop, you can't assume the "temporary" value is the original one
- It's recommended that you only loop inside view functions to ensure value safety, but you can do it on non-view functions as well, just be careful when doing so
- Trigger state changes only via transaction calls to the contract's functions
- Call
updateState(true)
at the end of the contract's constructor - Use
this
with all SafeVariables
There are a few global functions that can be used by your contract during an
ethCall()
:Global Function | Description | return type |
---|---|---|
getContractAddress() | Returns the contract's address | Address |
getContractOwner() | Returns the contract's owner | Address |
getContractChainId() | Returns the contract's chainId | uint64_t |
getContractName() | Returns the contract's name | string |
getOrigin() | Returns the transaction's origin | Address |
getCaller() | Returns the transaction's caller | Address |
getValue() | Returns the transaction's value | uint256_t |
getCommit() | Returns if the call is committing to state | bool |
Dynamic Contracts also have access to the following global functions:
Global Function | Description | return type |
---|---|---|
const getContract(address) | Returns a contract of type T | const T |
callContract(address, ABI, callValue) | Calls contract function | void |
getBalance(address) | Get the current balance of an address | uint256_t |
sendTokens(address, value) | Send tokens to an address | void |
Last modified 1mo ago