BaseDAO is implemented using the LIGO programming language, in particular its CameLIGO dialect.
To generate the contract code, or the storage for one of the provided examples, you will need the ligo executable installed.
The latest working version tested is 0.46.0.
Since this repo contain multiple variants, a slightly different command is required to build
the contract, depending on the variant that is required. For example, you can build the most simple
variant, the TrivialDAO LIGO contract using the Makefile in following command
make out/trivialDAO.tzwhich will use ligo compile contract and save the result in out/trivialDAO.tz.
If you prefer to build it manually, the contract's main entrypoint is
base_DAO_contract, located in src/base_DAO.mligo.
Imagine you want to implement a DAO called 'acme'. Variants can differ among each other in a couple of ways.
- Different type for the contract-extra field, and hence completely different storage type.
- Different type for the custom entrypoint parameter, and hence completely different parameter type.
- Different logic for various variant specific callbacks, like decision callback and proposal check.
To accomodate this, we need to create two ligo source files that wraps these differences. Specifically these are the steps one should follow to accomplish this.
- Create a new folder
src/variants/acme. - Copy
src/variants/template/implementation.mligotosrc/variants/acme/implementation.mligoandsrc/variants/template/storage.mligotosrc/variants/acme/storage.mligo.
Now you can follow the instructions in the comments for these files, to define the contract extra field implement your logic for proposal check, decision callback and proposal check as well as the handler for the custom entrypoint.
For your custom variant, If you need to have two custom custom entrypoints of'my_custom_ep1' of type (nat, nat) and
'my_custom_ep2' of type (int, int) change the line:
type custom_ep_param = unit
to
type custom_ep_param =
| My_custom_ep1 of (nat, nat)
| My_custom_ep2 of (int, int)
if your custom entrypoints only contain a single entrypoint, you might have to pair it with a dummy entrypoint so that the entrypoint annotation will show up in the final contract.
type custom_ep_param =
| MyCustomEp1 of (nat, nat)
| MyCustomEpDummy of unit
After defining implementation.mligo and storage.mligo files for your variant, you can build it by using the make command:
make out/acmeDAO.tz
BaseDAO metadata should be compliant with TZIP-16.
You can generate the known metadata, but you'll need the stack tool installed, see
The Haskell Tool Stack tutorial for instructions on how to obtain it.
make metadata \
frozen_token_symbol=frozen_token \
frozen_token_name="BaseDAO Frozen Token" \
frozen_token_thumbnail_uri="ipfs://QmV3a1TAdCncfs84Gi9msDsDJVQBDt6Wb5gJRVuFRfrgtG" \
output=metadata.jsonYou can then modify the produced metadata.json if you want to add more information.
This metadata should be stored either:
- On IPFS: Get started with IPFS
- As a separate contract: The
tezos-storageURI Scheme
To use this metadata, follow TZIP-16 Contract storage
Specifically:
The encoding of the values must be the direct stream of bytes of the data being stored. For instance, an URI starting with
http:will start with the 5 bytes0x687474703a(his0x68,tis0x74, etc.). There is no implicit conversion to Michelson's binary format (PACK) nor quoting mechanism.
After the conversion, the bytes can be use in the argument metadata_map of the make <storage> command
(See the next section for more detail.)
make <storage.tz> \
... \
metadata_map=(Big_map.literal [("", <metadata-bytes>)])
You can use ligo to also generate the contract initial storage, using the
ligo compile-storage command.
Following are the intructions to generate the initial storages of the DAO configuration provided in this codebase, see the README.
This storage is the one based on the default storage values.
make out/trivialDAO_storage.tz \
admin_address=tz1QozfhaUW4wLnohDo6yiBUmh7cPCSXE9Af \
guardian_address=KT1QbdJ7M7uAQZwLpvzerUyk7LYkJWDL7eDh \
governance_token_address=KT1RdwP8XJPjFyGoUsXFQnQo1yNm6gUqVdp5 \
governance_token_id=0n \
start_level=100n \
metadata_map=Big_map.empty \
freeze_history=[] \
fixed_proposal_fee_in_token=0n \
quorum_threshold=10n \
min_quorum=1n \
max_quorum=99n \
period=15840n \
quorum_change=5n \
max_quorum_change=19n \
governance_total_supply=1000n \
proposal_flush_level=36000n \
proposal_expired_level=47520n \The admin_address, guardian_address, governance_token_address,
start_level and governance_token_id are required values. The rest of the
arguments are optional and will be equal to the values above if not specified.
You can see the specification for more info about these values.
This storage is defined in src/variants/registry/implementation.mligo, can be
compiled with make, as usual:
make out/registryDAO_storage.tz \
admin_address=tz1QozfhaUW4wLnohDo6yiBUmh7cPCSXE9Af \
guardian_address=KT1QbdJ7M7uAQZwLpvzerUyk7LYkJWDL7eDh \
governance_token_address=KT1RdwP8XJPjFyGoUsXFQnQo1yNm6gUqVdp5 \
governance_token_id=0n \
frozen_scale_value=1n \
frozen_extra_value=0n \
max_proposal_size=100n \
slash_scale_value=1n \
slash_division_value=1n \
min_xtz_amount=0mutez \
max_xtz_amount=100mutez \
start_level=100n \
metadata_map=Big_map.empty \
freeze_history=[] \
fixed_proposal_fee_in_token=0n \
quorum_threshold=10n \
min_quorum=1n \
max_quorum=99n \
period=15840n \
quorum_change=5n \
max_quorum_change=19n \
proposal_flush_level=36000n \
proposal_expired_level=47520n \
governance_total_supply=1000nThe admin_address, guardian_address, governance_token_address,
start_level, and governance_token_id are required values. The rest of the
arguments are optional and will be equal to the values above if not specified.
This storage is defined in src/variants/treasury/implementation.mligo and
can be compiled once again with make:
make out/treasuryDAO_storage.tz \
admin_address=tz1QozfhaUW4wLnohDo6yiBUmh7cPCSXE9Af \
guardian_address=KT1QbdJ7M7uAQZwLpvzerUyk7LYkJWDL7eDh \
governance_token_address=KT1RdwP8XJPjFyGoUsXFQnQo1yNm6gUqVdp5 \
governance_token_id=0n \
frozen_scale_value=1n \
frozen_extra_value=0n \
max_proposal_size=100n \
slash_scale_value=1n \
slash_division_value=1n \
min_xtz_amount=0mutez \
max_xtz_amount=100mutez \
start_level=100n \
metadata_map=Big_map.empty \
fixed_proposal_fee_in_token=0n \
freeze_history=[] \
quorum_threshold=10n \
min_quorum=1n \
max_quorum=99n \
period=15840n \
quorum_change=5n \
max_quorum_change=19n \
proposal_flush_level=36000n \
proposal_expired_level=47520n \
governance_total_supply=1000nThe admin_address, guardian_address, governance_token_address,
start_level and governance_token_id are required values. The rest of the
arguments are optional and will be equal to the values above if not specified.
This storage is defined in src/variants/lambda/implementation.mligo and
can be compiled with make:
make out/lambdaDAO_storage.tz \
admin_address=tz1QozfhaUW4wLnohDo6yiBUmh7cPCSXE9Af \
guardian_address=KT1QbdJ7M7uAQZwLpvzerUyk7LYkJWDL7eDh \
governance_token_address=KT1RdwP8XJPjFyGoUsXFQnQo1yNm6gUqVdp5 \
governance_token_id=0n \
start_level=100n \
metadata_map=Big_map.empty \
freeze_history=[] \
fixed_proposal_fee_in_token=0n \
quorum_threshold=10n \
min_quorum=1n \
max_quorum=99n \
period=15840n \
quorum_change=5n \
max_quorum_change=19n \
proposal_flush_level=36000n \
proposal_expired_level=47520n \
governance_total_supply=1000nThis storage is defined in src/variants/lambdaregistry/implementation.mligo and
can be compiled with make:
make out/lambdaregistryDAO_storage.tz \
admin_address=tz1QozfhaUW4wLnohDo6yiBUmh7cPCSXE9Af \
guardian_address=KT1QbdJ7M7uAQZwLpvzerUyk7LYkJWDL7eDh \
governance_token_address=KT1RdwP8XJPjFyGoUsXFQnQo1yNm6gUqVdp5 \
governance_token_id=0n \
frozen_scale_value=1n \
frozen_extra_value=0n \
max_proposal_size=100n \
slash_scale_value=1n \
slash_division_value=1n \
min_xtz_amount=0mutez \
max_xtz_amount=100mutez \
start_level=100n \
metadata_map=Big_map.empty \
freeze_history=[] \
fixed_proposal_fee_in_token=0n \
quorum_threshold=10n \
min_quorum=1n \
max_quorum=99n \
period=15840n \
quorum_change=5n \
max_quorum_change=19n \
proposal_flush_level=36000n \
proposal_expired_level=47520n \
governance_total_supply=1000nThis storage is defined in src/variants/lambdatreasury/implementation.mligo and
can be compiled with make:
make out/lambdatreasuryDAO_storage.tz \
admin_address=tz1QozfhaUW4wLnohDo6yiBUmh7cPCSXE9Af \
guardian_address=KT1QbdJ7M7uAQZwLpvzerUyk7LYkJWDL7eDh \
governance_token_address=KT1RdwP8XJPjFyGoUsXFQnQo1yNm6gUqVdp5 \
governance_token_id=0n \
frozen_scale_value=1n \
frozen_extra_value=0n \
max_proposal_size=100n \
slash_scale_value=1n \
slash_division_value=1n \
min_xtz_amount=0mutez \
max_xtz_amount=100mutez \
start_level=100n \
metadata_map=Big_map.empty \
freeze_history=[] \
fixed_proposal_fee_in_token=0n \
quorum_threshold=10n \
min_quorum=1n \
max_quorum=99n \
period=15840n \
quorum_change=5n \
max_quorum_change=19n \
proposal_flush_level=36000n \
proposal_expired_level=47520n \
governance_total_supply=1000nThe LIGO functions used by the Makefile targets above perform some automatic check, specifically:
- if
freeze_historyis specified, thentotal_supplywill be calculated, depending on its value proposal_expired_timewill be rejected if it's not bigger thanproposal_flush_timeproposal_flush_timewill be rejected if it's not bigger than twice theperiodlengthquorum_thresholdwill be rejected if bigger thanmax_quorumor smaller thanmin_quorum