Smart Contracts Catalogue - Neufund/platform-contracts GitHub Wiki
Access Control contracts group
Contracts in this group provide a mixin AccessControlled
that adds ability to limit execution of public functions via only
modifier. AccessControlled
implements IAccessControlled
which allows to change the controller if such controller allows (see setAccessPolicy
which itself uses only
modifier). Primary use case for changing the controller is to totally separate access control in given contract by providing its private controller instance.
Controlled contracts are controlled by a controller contract implementing IAccessPolicy
interface (see code, it's documented).
Platform used role-based access policy (RoleBasedAccessPolicy
) where execution access is allowed per role which is required in only
modifier. ACLs (which msg.sender addresses belong to which role in which contract) are defined RoleBasedAccessPolicy. For more flexibility, special msg.sender and contract contexts were defined: GLOBAL
contract context that applies to all controlled contracts and EVERYONE
msg.sender which allows to execute given role to everyone (see burn
function of Neumark
contract).
Code and behaviors are documented with 100% test coverage. Read code and tests for details.
RoleBasedAccessPolicy
was deployed during ICBM and we intend to use it forever.
Roles are defined in StandardRoles
(just a role to change ACLs and access policy) and AccessRoles
which defines all roles recognized by platform contracts.
Note that we still use other access control schemes, for example in ETOCommitment contract there are functions that only Company (Issuer) or Nominee can execute and this is done via locally implemented modifiers
Universe
Universe contract server as a root of trust of Platform. Add that Universe points to are supported and official "Neufund Platform" contracts. Supported contracts are classified by KnownInterfaces
which are unique identifiers of services provided by those contracts (can be implemented differently by particular contract with different ABI). There are two types of services:
- Singletons - there just one instance of service present in Universe. Setting new instance, replaces old one. Example: Neumark token, RoleBasedAccessPolicy
- Collections - there may be many such instances present in Universe. You need to add and remove instances explicitely. Example: ETO Commitment contracts, Equity Token contracts
There is test coverage for essential functions, missing tests are declared
we plan to add implementation identifier on top of known service id for easier versioning and UI generation
Agreement
Concept is well explained in IAgreement
and Agreement
mixin with corresponding tests. Best Agreement usage is Neumark.sol.
Also we have a nice diagram: https://github.com/Neufund/platform-contracts#smart-contract-and-legal-contract-parity
IdentityRegistry
We use it to store claims from a single issuer. Otherwise we have identical mechanics to uPort implementation (which is simple and cheap as well ;> https://github.com/uport-project/ethereum-claims-registry/blob/master/contracts/EthereumClaimsRegistry.sol
For more details please read IIdentityRegistry
and IdentityRegistry
implementation. Please take a look at IdentityRecord
, there is a little bit of assembly trickery and homework is to explain how it works
Token Rate Oracle and Gas Exchange
Token rate oracle provides price feeds of token pairs. Oracle is centralized and only a special platform role may provide rates. ITokenExchangeRateOracle.sol
defines how rates are read (please note that we provide timestamps where rate was obtained). Writing is not part of oracle spec. Look at implementation of reading and writing in SimpleExchange.sol
where public methods are sufficiently documented.
Gas Exchange lets users without any Ether to exchange "other token" (ie. Euro Token) to ether. It happens via backend service that has permission to use exchange functionality and will throttle such operations so exchanged amounts are very small. Exchange process also requires ERC20 allowance in the "other token" for the exchange contract to do the transferFrom
of "other token" to itself (as a part of "ether purchase" transaction).
We intend to use it with Euro Token to onboard new users with gas. If user has no gas then how he makes allowance to exchange contract which is Ethereum transaction? In case of Euro Token he does not. There is always small constant allowance to simple exchange embedded in Euro Token Controller. That's explained separately.
Gas Exchange interface is defined in IGasExchange.sol
and implemented by SimpleExchange.sol
. This interface is not perfect and possibly we should split it into IGas and IExchange parts so IExchange can handle token rate writes. Tbd.
There is a test coverage for happy path in SimpleExchange.js
EtherToken and ICBMEtherToken
ICBMEtherToken is ERC223 compatible token that is used to keep ether of ICBM investors. Currently all ether is kept as ICBMLockedAccount balance (so this contract is the only token holder). We didn't intend to migrate from this contract however a few things changed
- Ethereum community settled on different callback function name for ERC223 (compare
IERC223LegacyCallback
vsIERC223Callback
) - We need a few convenience methods that will make our UX better.
So we offer new EtherToken. It's based on zeppelin/StandardToken (which does not have much in common with Zeppelin now). It's not a snapshot token. Code base is simple read it!
Now we have two convenience methods
- depositAndTransfer
- withdrawAndSend
Homework is to explain what they do. The problem solved is as follows: as investor you have ether on your wallet but you can also have ether in ether token. Now you want to invest whole amount. or withdraw to other wallet (whole amount). we do not want to bother investor with such complexity...
look at the tests of ICBMEtherToken and EtherToken - how they are standardized. tests for convenience methods are missing. this code was never run
EuroToken and EuroTokenController
Token controller is a pattern that let's you abstract away checking who can do transfer to whom (also deposit, withdraw, allowance). We use it to limit transfers in Euro Token but also in Equity Token (where you need voting to enable transfers). Token controller is a powerful pattern. Euro Token is even simpler than Ether Token, derives from the same code base.
- look how token controller is attached via modifiers
- look how permanent allowance for gas exchange works (in
transferFrom
overrid). super ugly trick, I know Now the token controller. The only complicated thing is logic to allow transfer to happen inisTransferAllowedPrivate
- you can explicitely enable an address to send or receive transfers (
setAllowedTransfer...
) - it's an mechanism from old ICBMEuroToken. It overrides other logic - you can receive transfer if you are ETO contract and sender has KYC
- you can send when you are ETO contract and sender has KYC (refund, payout of fund-raising amount to Nominee)
Also look how euro token controller settings are changed and what special transfer permissions are set
There is interesting quirk for transfer with a broker. I'll explain that later.
EuroTokenController has separate tests, we still are missing several tests they must be implemented
PlatformTerms, ETOTerms, DurationTerms and ShareholderRights
Required read: https://github.com/Neufund/platform-backend/wiki/5.7.-Use-Case-ETO-Listing - the role of each contract is explained.
ETOTerms contains several function to manage whitelist and also abstracts away computations of min - max tickets, token prices etc. for particular investor. This (in theory) allows to change discount and pricing structure (for example: introduce price curves) without changing ETO Commitment.
For each contract there is unfinished test suite. Objective is to finish them. Also PlatformTerms are not fully tested. See todo in deploymentContracts.js helper.
ETOTerms will get upgrade for whitelist discounts: https://github.com/Neufund/platform-contracts/issues/130
ICommitment, IETOCommitment and ETO State Machine implementation
ICommitment
- describes basic interface of all commitments (ETOs, ICBM, other ICOs) that may happen on the platform. It requires proper tracking of investments (via events), investment happening only via ERC223 compatible payment tokens (fallback) and basic state: like commitment is finished and if it succeeded.
IETOCommitment
- describes ETO Commitment process, defines states (IETOCommitmentStates), and method to check ETO progress. It also introduces concept of ETOCommitment observer. Observer is a callback interface that is called on every state transition of IETOCommitment. Currently the observer is Equity Token Controller. This will be explained later.
ETOTimedStateMachine
- implements time induced state machine. It develops concepts from ICBM/Commitment/ICBMStateMachine. All methods that participate in state (require to be run in certain state and be able to modify state) must use withStateTransition
modifier. Please its code. It basically explains how state machine works.
- before body is executed, we check time. if we see that state transition is required we do it (one by one - if states must be skipped)
- after body is executed, we check if we should change state due to business logic (for example max cap was reached and we should finalize ETO)
- derived contract has no access to state or timestamps. it is wired to state changes and can also influence state by implementing internal observer interface (will be extracted for clarity)
- by implementing that interface, derived contract is aware of time induced transitions and can override those (example: next default state is claim which happens after 30 days but derived class overrides with refund because max cap was not reached)
- it can also induce state transition due to business logic.
happy path was tested, the task is to provide full tests for state machine by implementing placeholding eto commitment contract and checking all possible state changes (with overrides and mocked logic) - see tests for ICBM/Commitment where state machine is tested extensively
ETO Commitment - investment via payment token
Investment happens via supported payment token via ERC223 callback. We'll get through the whole process from the investor's side
- reserve (commit)
- claim
- refund
- payout
we mention ICBM wallets but this is another lesson
Equity Token and Equit Token Controller
Equity Token is based on Snapshot Token which is based on MiniMe token from Giveth. Snapshots are necessary to fulfill "earned" rights of token holders. Those rights come with having the balance and include voting power, dividends, information and anything you can imagine. Those rights you do not lose with selling the token: you should be able to take past dividends and your past votes should still count.
Here is more on snaphotting: https://github.com/Neufund/EIPs/blob/token-with-snapshots/eip-token-with-snapshots.md
Here is more on snapshot token derivation: https://github.com/Neufund/platform-contracts/tree/master/contracts/SnapshotToken
Other important property of Equity Token is that is it legally binding: it has corresponding legal agreement attached via IAgreement
interface
EquityToken and EquityTokenController
Equity token is very simple and deals just with ownership and transfer rights. It also preserves past balances. It's ERC223 token. Equity Token Controller is the same interface we had for euro token but in this case it represents a company (issuer) of the token and is responsible for executing rights of the token holders. In principle it says
- how tokens are issued and destroyed
- who can transfer to whom
- how you can take dividend
- what is the voting procedure (governance)
basically Equity Token Controller can be any governance mechanism even a DAO. In case of Equity Token it is a contract representing off-chain company and company governance (which is very democratic).
We'll implement many versions of the controller. For the launch we have PlaceholderEquityTokenController which provides only information and token issuance rights. It's intended to be upgraded with full governance version.
Exercises: finish EquityToken and Placeholder tests. most of those are already declared