SparqNet
Search
⌃K

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 inherit DynamicContract) 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 and DynamicContract take a contractName string as an argument - for example, if your contract is called "TestContract", your class would be class TestContract : public DynamicContract { ... } and your constructor call would be DynamicContract(interface, "TestContract", ...)
  • Declare view functions (functions that do not change state) as const and return a std::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 return void (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

Global Contract Variables

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