diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5a8b375 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "external/libeosdac"] + path = external/libeosdac + url = https://github.com/eosdac/libeosdac.git diff --git a/CMakeLists.txt b/CMakeLists.txt index 2f0b5a5..3534fd6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,7 +17,7 @@ include(ExternalProject) find_package(eosio.cdt) -message(STATUS "Building eosio.contracts v${VERSION_FULL}") +message(STATUS "Building eosdao.contracts v${VERSION_FULL}") set(EOSIO_CDT_VERSION_MIN "1.6") set(EOSIO_CDT_VERSION_SOFT_MAX "1.6") diff --git a/Dockerfile b/Dockerfile index 34369d7..823fac4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,5 +5,7 @@ COPY /docker/run_compile.sh / RUN mkdir /src COPY . /src -RUN /run_compile.sh +CMD ["/run_compile.sh"] + +ENTRYPOINT ["/bin/bash", "-l", "-c"] diff --git a/contracts/custodian/CMakeLists.txt b/contracts/custodian/CMakeLists.txt deleted file mode 100644 index 021f4ff..0000000 --- a/contracts/custodian/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ - -cmake_minimum_required(VERSION 3.5) -project(daccustodian VERSION 1.0.0) - -find_package(eosio.cdt) - -### Generate the wasm and abi -add_contract( daccustodian daccustodian - daccustodian.cpp - external_observable_actions.cpp - newperiod_components.cpp - pay_handling.cpp - privatehelpers.cpp - registering.cpp - update_member_details.cpp - ) - -set_target_properties(daccustodian - PROPERTIES - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") - -### add the path to where the ricardian contracts/clauses are found -target_compile_options( daccustodian PUBLIC -R${CMAKE_CURRENT_SOURCE_DIR} ) diff --git a/contracts/custodian/README.md b/contracts/custodian/README.md deleted file mode 100644 index dd9d6d9..0000000 --- a/contracts/custodian/README.md +++ /dev/null @@ -1,365 +0,0 @@ -# `daccustodian` - Custodian Elections Contract - -This contract will be in charge of custodian registration and voting for candidates. It will also contain a function which could be called periodically to update the custodian set, and allocate payments. - -When a candidate registers, they need to provide a set of configuration variables which will include things like their requested pay. The system will select the median requested pay when choosing the actual pay. -The median pay is to be paid to elected custodians at the end of each period. If an elected custodian resigns via the `withdrawcand` during a period a new candidate will be chosen to fill the gap on the custodian board from the votes ranking in the candidates at that moment. - -Eg. 12 custodians are elected and their median `requestedpay` is 100 EOSDAC If one of the custodians resigns partially through a period they will not be paid for that partial period. The median pay amount will be calculated based on the current elected custodians `requestedpay` value. If a candidate changes their requested pay it will not be included in the pay calculation until the next period if they are re-elected. - -## Tables - -### candidates - -- candidate_name (name) - Account name of the candidate (INDEX) -- isactive (int8) - Boolean indicating if the candidate is currently available for election. (INDEX) -- locked_tokens (asset) - An asset object representing the number of tokens locked when registering -- requestedpay (asset) - The amount of pay requested by the candidate to be paid if they were elected for the following period. -- total_votes (uint64) - Updated tally of the number of votes cast to a candidate. This is updated and used as part of the `newperiod` calculations. It is updated every time there is a vote change or a change of token balance for a voter for this candidate to facilitate live voting stats. - -### custodians - -- cust_name (name) - Account name of the custodian (INDEX) -- requestedpay - The amount of pay requested by the candidate to be paid as an elected custodian for the current period. -- total_votes - Tally of the number of votes cast to a custodian when they were elected in. This is updated as part of the `newperiod` action. - -### votes - -- voter (account_name) - The account name of the voter (INDEX) -- proxy (account_name) - Name of another voter used to proxy votes through. This should not have a value in both the proxy and candidates at the same time. -- candidates (account_name[]) - The candidates voted for, can supply up to the maximum number of votes (currently 5) - Can be configured via `updateconfig` - -### pendingpay - -- key (uint64) - auto incrementing id to identify a payment due to a custodian -- receiver (account_name) - The account name of the intended receiver. -- quantity (asset) - The amount for the payment. -- memo (string) - A string used in the memo to help the receiver identify it in logs. - -### config - -- lockupasset (asset) - The amount of assets that are locked up by each candidate applying for election. -- maxvotes (int default=5) - The maximum number of votes that each member can make for a candidate. -- numelected (int) - Number of custodians to be elected for each election count. -- periodlength (uint32 = 7 * 24 * 60 * 60) - Length of a period in seconds. Used for pay calculations if an early election is called and to trigger deferred `newperiod` calls. -- authaccount ( account= "dacauthority") - account to have active auth set with all custodians on the newperiod. -- tokenholder (account = "eosdacthedac") - The contract that holds the fund for the DAC. This is used as the source for custodian pay. -- initial_vote_quorum_percent (uint32) - Amount of token value in votes required to trigger the initial set of custodians -- vote_quorum_percent (uint32) - Amount of token value in votes required to trigger the allow a new set of custodians to be set after the initial threshold has been achieved. -- auth_threshold_high (uint8) - Number of custodians required to approve highest level actions. -- auth_threshold_mid (uint8) - Number of custodians required to approve highest level actions. -- auth_threshold_low (uint8) - Number of custodians required to approve highest level actions. -- lockup_release_time_delay (date) - The time before locked up stake can be released back to the candidate using the unstake action -- requested_pay_max (asset) - The max amount a custodian can requested as a candidate. - -## Actions - ---- -### nominatecand - -This action is used to nominate a candidate for custodian elections. It must be authorised by the candidate and the candidate must be an active member of the DAC, having agreed to the latest constitution. The candidate must have transferred a number of tokens (determined by a config setting - `lockupasset`) to the contract for staking before this action is executed. This could have been from a recent transfer with the contract name in the memo or from a previous time when this account had nominated, as long as the candidate had never `unstake`d those tokens. - -##### Assertions: - -- The account performing the action is authorised. -- The candidate is not already a nominated candidate. -- The requested pay amount is not more than the config max amount. -- The requested pay symbol type is the same from config max amount ( The contract supports only one token symbol for payment). -- The candidate is currently a member or has agreed to the latest constitution. -- The candidate has transferred sufficient funds for staking if they are a new candidate. -- The candidate has enough staked if they are re-nominating as a candidate and the required stake has changed since they last nominated. - -##### Parameters: - - cand - The account id for the candidate nominating. - requestedpay - The amount of pay the candidate would like to receive if they are elected as a custodian. This amount must not exceed the maximum allowed amount of the contract config parameter (`requested_pay_max`) and the symbol must also match. - -##### Post Condition: - -The candidate should be present in the candidates table and be set to active. If they are a returning candidate they should be set to active again. The `locked_tokens` value should reflect the total of the tokens they have transferred to the contract for staking. The number of active candidates in the contract will be incremented. - ---- -### withdrawcand - -This action is used to withdraw a candidate from being active for custodian elections. - -#### Assertions: - -- The account performing the action is authorised. -- The candidate is already a nominated candidate. - -##### Parameters: - - cand - The account id for the candidate nominating. - -##### Post Condition: - -The candidate should still be present in the candidates table and be set to inactive. If the were recently an elected custodian there may be a time delay on when they can unstake their tokens from the contract. If not they will be able to unstake their tokens immediately using the unstake action. - ---- -### resigncust - -This action is used to resign as a custodian. - -##### Assertions: - -- The `cust` account performing the action is authorised to do so. -- The `cust` account is currently an elected custodian. - -##### Parameters: - - cust - The account id for the candidate nominating. - -##### Post Condition: - -The custodian will be removed from the active custodians and should still be present in the candidates table but will be set to inactive. Their staked tokens will be locked up for the time delay added from the moment this action was called so they will not able to unstake until that time has passed. A replacement custodian will be selected from the candidates to fill the missing place (based on vote ranking) then the auths for the controlling DAC auth account will be set for the custodian board. - ---- - -### updatebio - -Update the bio for this candidate / custodian. This will be available on the account immediately in preparation for the next election cycle. - -##### Assertions: - - - the message has the permission of the account registering. - - the account has agreed to the current terms. - - the `cand` account is currently registered. - - the length of the bio must be less than 256 characters. - - ##### Parameters - - cand - The account id for updating profile - bio - Bio content - ---- - -### updatereqpay - -This action is used to update the requested pay for a candidate. - -##### Assertions: - -- The `cand` account performing the action is authorised to do so. -- The candidate is currently registered as a candidate. -- The requestedpay is not more than the requested pay amount. - -##### Parameters: - - cand - The account id for the candidate nominating. - requestedpay - A string representing the asset they would like to be paid as custodian. - -##### Post Condition: - -The requested pay for the candidate should be updated to the new asset. - ---- - -### votecust - -This action is to facilitate voting for candidates to become custodians of the DAC. Each member will be able to vote a configurable number of custodians set by the contract configuration. When a voter calls this action either a new vote will be recorded or the existing vote for that voter will be modified. If an empty array of candidates is passed to the action an existing vote for that voter will be removed. - -##### Assertions: - -- The voter account performing the action is authorised to do so. -- The voter account performing has agreed to the latest member terms for the DAC. -- The number of candidates in the newvotes vector is not greater than the number of allowed votes per voter as set by the contract config. -- Ensure there are no duplicate candidates in the voting vector. -- Ensure all the candidates in the vector are registered and active candidates. - -#### Parameters: - - voter - The account id for the voter account. - newvotes - A vector of account ids for the candidate that the voter is voting for. - -##### Post Condition: - -An active vote record for the voter will have been created or modified to reflect the newvotes. Each of the candidates will have their total_votes amount updated to reflect the delta in voter's token balance. Eg. If a voter has 1000 tokens and votes for 5 candidates, each of those candidates will have their total_votes value increased by 1000. Then if they change their votes to now vote 2 different candidates while keeping the other 3 the same there would be a change of -1000 for 2 old candidates +1000 for 2 new candidates and the other 3 will remain unchanged. - ---- - -### voteproxy ( inactive development at the moment to reduce scope for the initial release) - -Create/update the active vote to proxy through another voter. This vote will overwrite any existing vote for either a custodian vote or proxy vote. - -#### Message - -```c -account (account_name) -proxy (account_name) -```` - -This action asserts: - - - the message has the permission of the account registering. - - the `cand` account is currently registered. - - the account has agreed to the current terms. - - the vote is not proxying to themselves as a proxy. - - the vote is not proxying to another proxy. - -Save the votes in the `votes` table, update if the voting account already has a record. - ---- -### updateconfig - -Updates the contract configuration parameters to allow changes without needing to redeploy the source code. - -#### Message - -updateconfig() - -This action asserts: - - - the message has the permission of the contract account. - - the supplied asset symbol matches the current lockup symbol if it has been previously set or that there have been no . - -The parameters are: - -- lockupasset(uint8_t) : defines the asset and amount required for a user to register as a candidate. This is the amount that will be locked up until the user calls `withdrawcand` in order to get the asset returned to them. If there are currently already registered candidates in the contract this cannot be changed to a different asset type because of introduced complexity of handling the staked amounts. -- maxvotes(asset) : Defines the maximum number of candidates a user can vote for at any given time. -- numelected(uint16_t) : The number of candidates to elect for custodians. This is used for the payment amount to custodians for median amount. -- periodlength(uint32_t) : The length of a period in seconds. This is used for the scheduling of the deferred `newperiod` actions at the end of processing the current one. Also is used as part of the partial payment to custodians in the case of an elected custodian resigning which would also trigger a `newperiod` action. -- tokcontr(name) : The token contract used to manage the tokens for the DAC. -- authaccount(name) : The managing account that controls the whole DAC. -- tokenholder(name) : The account that controls the funds for the DAC. -- initial_vote_quorum_percent (uint32) : The percent of voters required to activate the DAC for the first election period. -- vote_quorum_percent (uint32) : The percent of voters required to continue the DAC for the following election periods after the first one has activated the DAC. -- auth_threshold_high (uint8) : The number of custodians required to approve an action in the high permission category (exceptional change). -- auth_threshold_mid (uint8) : The number of custodians required to approve an action in the mid permission category ( extraordinary change). -- auth_threshold_low (uint8) : The number of custodians required to approve an action in the low permission category ( ordinary action such as a worker proposal). - ---- - -### newperiod - -This action is to be run to end and begin each period in the DAC life cycle. It performs multiple tasks for the DAC including: - -- Allocate custodians from the candidates tables based on those with most votes at the moment this action is run. -- This action removes and selects a full set of custodians each time it is successfully run selected from the candidates with the most votes weight. If there are not enough eligible candidates to satisfy the DAC config numbers the action adds the highest voted candidates as custodians as long their votes weight is greater than 0. At this time the held stake for the departing custodians is set to have a time delayed lockup to prevent the funds from releasing too soon after each custodian has been in office. -- Distribute pay for the existing custodians based on the configs into the pending pay table so it can be claimed by individual candidates. -- The pay is distributed as determined by the median pay of the currently elected custodians. Therefore all elected custodians receive the same pay amount. -- Set the DAC auths for the intended controlling accounts based on the configs thresholds with the newly elected custodians. This action asserts unless the following conditions have been met: -- The action cannot be called multiple times within the period since the last time it was previously run successfully. This minimum time between allowed calls is configured by the period length parameter in contract configs. -- To run for the first time a minimum threshold of voter engagement must be satisfied. This is configured by the `initial_vote_quorum_percent` field in the contract config with the percentage calculated from the amount of registered votes cast by voters against the max supply of tokens for DAC's primary currency. -- After the initial vote quorum percent has been reached subsequent calls to this action will require a minimum of `vote_quorum_percent` to vote for the votes to be considered sufficient to trigger a new period with new custodians. - - ##### Parameters: - - message - a string that is used to log a message in the chain history logs. It serves no function in the contract logic. - ---- - -### claimpay - -This action is to claim pay as a custodian. - -##### Assertions: - -- The caller to the action account performing the action is authorised to do so. -- The payid is for a valid pay record in the pending pay table. -- The caller account is the same as the intended destination account for the pay record. - -##### Parameters: - - payid - The id for the pay record to claim from the pending pay table. - - Post Condition: - -The quantity owed to the custodian as referred to by the pay record is transferred to the claimer and then the pay record is removed from the pending pay table. - ---- -### unstake - -This action is used to unstake a candidates tokens and have them transferred to their account. - -##### Assertions: - -- The candidate was a nominated candidate at some point in the past. -- The candidate is not already a nominated candidate. -- The tokens held under candidate's account are not currently locked in a time delay. - -##### Parameters: - - cand - The account id for the candidate nominating. - -### Post Condition: - -The candidate should still be present in the candidates table and should be still set to inactive. The candidates tokens will be transferred back to their account and their `locked_tokens` value will be reduced to 0. - ---- -### firecand - -This action is used to remove a candidate from being a candidate for custodian elections. - -## Assertions: - -- The action is authorised by the mid level permission the auth account for the contract. -- The candidate is already a nominated candidate. - -##### Parameters - - cand - The account id for the candidate nominating. - lockupStake - if true the stake will be locked up for a time period as set by the contract config `lockup_release_time_delay` - -##### Post Condition: - -The candidate should still be present in the candidates table and be set to inactive. If the `lockupstake` parameter is true the stake will be locked until the time delay has passed. If not the candidate will be able to unstake their tokens immediately using the unstake action to have them returned. - ---- -### firecust - -This action is used to remove a custodian. - -##### Assertions: - -- The action is authorised by the mid level of the auth account (currently elected custodian board). -- The `cust` account is currently an elected custodian. - -##### Parameters: - - cand - The account id for the candidate nominating. - -##### Post Condition: - -The custodian will be removed from the active custodians and should still be present in the candidates table but will be set to inactive. Their staked tokens will be locked up for the time delay added from the moment this action was called so they will not able to unstake until that time has passed. A replacement custodian will be selected from the candidates to fill the missing place (based on vote ranking) then the auths for the controlling dac auth account will be set for the custodian board. - ---- - -# Compile - - -The contract code has some compile time constants used for configuration. As a compile time constant the code has more flexibility for reuse on other DACs, and an extra layer of safety over exposing another configuration variable which could be changed after the code has been set and the ability to unit test the code without needing to modify the source just for testing. -The available compile time flags are: - -- TOKENCONTRACT (default = "eosdactokens") - This is to set the associated token contract to inter-operate with for tracking voting weights, registered members and staking. -- VOTING_DISABLED (default = false) - Setting this flag will disable the ability for anyone to vote for custodians by disabling the vote action. -- TRANSFER_DELAY (default = 60 * 60) - for configuring the time delay on token transfers from the contract - -When put all together a compile command with all the bells and whistles might look like: - -```bash -eosio-cpp -DTOKENCONTRACT=eosdactokens -DTRANSFER_DELAY=3600 -DVOTING_DISABLED -o daccustodian.wasm daccustodian.cpp -``` - -> **Note:** Since there are default values for the above flags they do not all need to be included to compile successfully. - ---- - -# Tests - -The repo includes automated tests to exercise the main action paths in the contract against a running local node. -The tests are included in the tests folder as `rspec` tests in Ruby. - -### Installation - -To run the tests you would first need: -- `ruby 2.4.1` or later installed. -- Ruby gems as specified in the `Gemfile`. - - These can be installed by running `bundle install` from the project root directory. -Eos installed locally that can be launched via `nodeos` - -There is one action that requires `ttab` which is a nodejs module but this can be easily avoided by a small modification to the rspec tests as detailed in the file. The purpose of using ttab is to start a second tab running nodeos to help diagnose bugs during the test run. - -### To run the tests: - -```bash -run `rspec tests/contract_spec.rb` from the project root. -``` diff --git a/contracts/custodian/compile.sh b/contracts/custodian/compile.sh deleted file mode 100755 index 82dde4e..0000000 --- a/contracts/custodian/compile.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -source ../_compiled_contracts/daccustodian/compile_all.sh \ No newline at end of file diff --git a/contracts/custodian/config.cpp b/contracts/custodian/config.cpp deleted file mode 100644 index bb58ca9..0000000 --- a/contracts/custodian/config.cpp +++ /dev/null @@ -1,26 +0,0 @@ -#include "../_contract-shared-headers/dacdirectory_shared.hpp" - -void daccustodian::updateconfig(contr_config new_config) { - updateconfige(new_config, get_self()); -} - -void daccustodian::updateconfige(contr_config new_config, name dac_id) { - - dacdir::dac dacForScope = dacdir::dac_for_id(dac_id); - auto auth_account = dacForScope.account_for_type(dacdir::AUTH); - require_auth(auth_account); - - check(new_config.auth_threshold_high < new_config.numelected, - "ERR::UPDATECONFIG_INVALID_AUTH_HIGH_TO_NUM_ELECTED::The auth threshold can never be satisfied with a value greater than the number of elected custodians"); - check(new_config.auth_threshold_mid <= new_config.auth_threshold_high, - "ERR::UPDATECONFIG_INVALID_AUTH_HIGH_TO_MID_AUTH::The mid auth threshold cannot be greater than the high auth threshold."); - check(new_config.auth_threshold_low <= new_config.auth_threshold_mid, - "ERR::UPDATECONFIG_INVALID_AUTH_MID_TO_LOW_AUTH::The low auth threshold cannot be greater than the mid auth threshold."); - - configscontainer config_singleton(_self, dac_id.value); - config_singleton.set(new_config, auth_account); - - contr_state currentState = contr_state::get_current_state(_self, dac_id); - currentState.save(_self, dac_id, auth_account); - print("Succesfully updated the daccustodian config for: ", dac_id); -} diff --git a/contracts/custodian/daccustodian.clauses.md b/contracts/custodian/daccustodian.clauses.md deleted file mode 100644 index 29bc48b..0000000 --- a/contracts/custodian/daccustodian.clauses.md +++ /dev/null @@ -1,5 +0,0 @@ -

ENTIRE AGREEMENT

-This contract contains the entire agreement of the parties, for all described actions, and there are no other promises or conditions in any other agreement whether oral or written concerning the subject matter of this Contract. This contract supersedes any prior written or oral agreements between the parties. - -

BINDING CONSTITUTION

-All the the action descibed in this contract are subject to the EOSDAC consitution as held at http://eosdac.io. This includes, but is not limited to membership terms and conditions, dispute resolution and severability. diff --git a/contracts/custodian/daccustodian.contracts.md b/contracts/custodian/daccustodian.contracts.md deleted file mode 100644 index b2c16a5..0000000 --- a/contracts/custodian/daccustodian.contracts.md +++ /dev/null @@ -1,427 +0,0 @@ -

-stprofile -

- -## ACTION: stprofile -**PARAMETERS:** -* __cand__ is an eosio account name. -* __profile__ is a string that provides a hash of the details of the candidate. - -**INTENT** The intent of stprofile is to record an update the user's profile. -#### Warning: This action will store the content on the chain in the history logs and the data cannot be deleted later so therefore should only store a unidentifiable hash of content rather than human readable content. - -

- stprofileuns -

- -## ACTION: stprofileuns -**PARAMETERS:** -* __cand__ is an eosio account name. -* __profile__ is a string that provides a hash of the details of the candidate. - -**INTENT:** -The intent of stprofileuns is to record an update the user's profile. ##Warning: This action will store the content on the chain in the history logs and the data cannot be deleted later. - -

-updatebio -

- -## ACTION: updatebio -**PARAMETERS:** -* __cand__ is an eosio account name. -* __profile__ is a string that provides a hash of the details of the candidate. - -**INTENT:** -The intent of updatebio is to record an update the user's bio. Unlike `stprofileuns` this action does not require auth of the cand to execute. - ####Warning: This action will store the content on the chain in the history logs and the data cannot be deleted later. - -

-updatebioe -

- -## ACTION: updatebioe -**PARAMETERS:** -* __cand__ is an eosio account name. -* __profile__ is a string that provides a hash of the details of the candidate. - -**INTENT:** -The intent of updatebio is to record an update the user's bio. Unlike `stprofileuns` this action does not require auth of the cand to execute. - ####Warning: This action will store the content on the chain in the history logs and the data cannot be deleted later. - - -

- firecust -

- -## ACTION: firecust -**PARAMETERS:** -* __cand__ is an eosio account name. - -**INTENT:** The intent of firecust is to allow elected custodians to (where quorum and configured majorities are met) to remove a fellow custodian and lock up their tokens until the configured delay period has passed. -**TERM:** This action lasts for the duration of the time taken to process the transaction. - -

- firecuste -

- -## ACTION: firecuste -**PARAMETERS:** -* __cand__ is an eosio account name. - -**INTENT:** The intent of firecust is to allow elected custodians to (where quorum and configured majorities are met) to remove a fellow custodian and lock up their tokens until the configured delay period has passed. -**TERM:** This action lasts for the duration of the time taken to process the transaction. - - -

- resigncust -

- ## ACTION: resigncuste -**PARAMETERS:** -* __cust__ is an eosio account name. - -**INTENT:** The intent of resigncust is to remove an elected custodian. This action must be run by the resigning custodian and the outcome should remove the elected custodian and lock up their tokens until the delay period has passed so the tokens can be claimed with the unstake action. -**TERM:** This action lasts for the duration of the time taken to process the transaction. - -

- resigncuste -

- ## ACTION: resigncuste -**PARAMETERS:** -* __cust__ is an eosio account name. - -**INTENT:** The intent of resigncust is to remove an elected custodian. This action must be run by the resigning custodian and the outcome should remove the elected custodian and lock up their tokens until the delay period has passed so the tokens can be claimed with the unstake action. -**TERM:** This action lasts for the duration of the time taken to process the transaction. - -

- firecand -

- -## ACTION: firecand -**PARAMETERS:** -* __cand__ is an eosio account name. -* __lockupStake__ is an indicator to show whether stake is being locked up or not - -**INTENT:** -The intent of forehand is to set a candidate to a state of inactive so they will be excluded from the next election round. This action may only be run by the by elected custodians (where quorum and configured majorities are met). There is an option to lock up the candidate's tokens until a delay period has passed based on the delay set in the config after which the tokens can be claimed with the unstake action. If the option passed is false and there is an existing lockup delay on the tokens then this lockup will continue to be active until the lock up time has passed. - -**TERM:** This action lasts for the duration of the time taken to process the transaction. - -

- firecande -

- -## ACTION: firecande -**PARAMETERS:** -* __cand__ is an eosio account name. -* __lockupStake__ is an indicator to show whether stake is being locked up or not - -**INTENT:** -The intent of forehand is to set a candidate to a state of inactive so they will be excluded from the next election round. This action may only be run by the by elected custodians (where quorum and configured majorities are met). There is an option to lock up the candidate's tokens until a delay period has passed based on the delay set in the config after which the tokens can be claimed with the unstake action. If the option passed is false and there is an existing lockup delay on the tokens then this lockup will continue to be active until the lock up time has passed. - -**TERM:** This action lasts for the duration of the time taken to process the transaction. - -

- unstake -

- -## ACTION: unstake -**PARAMETERS:** -* __cand__ is an eosio account name. - -**INTENT** The intent of unstake is to return staked tokens back to the candidate if the user is no longer an active candidate and there is no delay set on the candidate the staked tokens will be returned to the candidate. -**TERM:** This action lasts for the duration of the time taken to process the transaction. - -

- unstakee -

- -## ACTION: unstakee -**PARAMETERS:** -* __cand__ is an eosio account name. - -**INTENT** The intent of unstake is to return staked tokens back to the candidate if the user is no longer an active candidate and there is no delay set on the candidate the staked tokens will be returned to the candidate. -**TERM:** This action lasts for the duration of the time taken to process the transaction. - -

- updateconfig -

- - ## ACTION: updateconfig -**PARAMETERS:** -* __lockupasset__ is an asset to be locked up as part of the nominating process for a custodian passed to the action in the format: \"10.0000 EOSDAC\". default value: \"10.0000 EOSDAC\" -* __maxvotes__ is a integer to configure the maximum number of allowed votes for a nominated member in any single voting action. The default value is 5. -* __numelected__ is a integer to configure the number of candidates that will be elected as custodians of the DAC. default value is 12. -* __periodlength__ the length of office of a custodian vote (in seconds) before a new period . Default to 7 days. -* __authaccount__ The authorised account to change the contract which should be protected via a multisig of custodians, -* __tokenholder__ The account that controls the funds for the DAC. -* __initial_vote_quorum_percent__ The percent of voters required to activate the DAC for the first election period. -* __auth_threshold_high__ percentage of votes of custodians required to approve highest level actions. -* __auth_threshold_mid__ percentage of votes of custodians required to approve medium level actions. -* __auth_threshold_low__ percentage of votes of custodians required to approve lowest level actions. -* __lockup_release_time_delay__ The time before locked up stake can be released back to the candidate using the unstake action. -* __asset requested_pay_max__ - -**INTENT:** The intent of {{ updateconfig }} is update the configuration for the running contract of selected parameters without needing change the source code. This requires a privileged account. -**TERM:** The action sets the configuration until it is set by a subsequent updateconfig action. - -

- updateconfige -

- - ## ACTION: updateconfige -**PARAMETERS:** -* __lockupasset__ is an asset to be locked up as part of the nominating process for a custodian passed to the action in the format: \"10.0000 EOSDAC\". default value: \"10.0000 EOSDAC\" -* __maxvotes__ is a integer to configure the maximum number of allowed votes for a nominated member in any single voting action. The default value is 5. -* __numelected__ is a integer to configure the number of candidates that will be elected as custodians of the DAC. default value is 12. -* __periodlength__ the length of office of a custodian vote (in seconds) before a new period . Default to 7 days. -* __authaccount__ The authorised account to change the contract which should be protected via a multisig of custodians, -* __tokenholder__ The account that controls the funds for the DAC. -* __initial_vote_quorum_percent__ The percent of voters required to activate the DAC for the first election period. -* __auth_threshold_high__ percentage of votes of custodians required to approve highest level actions. -* __auth_threshold_mid__ percentage of votes of custodians required to approve medium level actions. -* __auth_threshold_low__ percentage of votes of custodians required to approve lowest level actions. -* __lockup_release_time_delay__ The time before locked up stake can be released back to the candidate using the unstake action. -* __asset requested_pay_max__ - -**INTENT:** The intent of {{ updateconfig }} is update the configuration for the running contract of selected parameters without needing change the source code. This requires a privileged account. -**TERM:** The action sets the configuration until it is set by a subsequent updateconfig action. - -

- nominatecand -

- -## ACTION: nominatecand -**PARAMETERS:** -* __cand__ is an account_name parameter for the nominating candidate. -* __requestedpay__ is an asset requested by the candidate as pay for being an elected custodian. It should be passed to the action in the format: \"10.0000 EOSDAC\". - -**INTENT:** The intent of {{ nominatecand }} is to nominates a candidate to custodian election, Accounts must nominate as a candidate before they can be voted for. The candidate must lock a configurable number of tokens before trying to nominate (configurable via {{ updateconfig }} in the parameter lockupasset which will be sent from the token contract as defined and set in the code of the contract. If a user previously been a candidate they may have enough staked tokens to not require further staking but will otherwise need to transfer the difference to meet the required stake. - -**TERM:** A candidate remains a candidate until they are removed from candidate status by a subsequent transaction. - -

- nominatecane -

- -## ACTION: nominatecane -**PARAMETERS:** -* __cand__ is an account_name parameter for the nominating candidate. -* __requestedpay__ is an asset requested by the candidate as pay for being an elected custodian. It should be passed to the action in the format: \"10.0000 EOSDAC\". - -**INTENT:** The intent of {{ nominatecand }} is to nominates a candidate to custodian election, Accounts must nominate as a candidate before they can be voted for. The candidate must lock a configurable number of tokens before trying to nominate (configurable via {{ updateconfig }} in the parameter lockupasset which will be sent from the token contract as defined and set in the code of the contract. If a user previously been a candidate they may have enough staked tokens to not require further staking but will otherwise need to transfer the difference to meet the required stake. - -**TERM:** A candidate remains a candidate until they are removed from candidate status by a subsequent transaction. - -

- withdrawcand -

- -## ACTION: withdrawcand -**PARAMETERS:** -* __cand__ is an account_name parameter for the nominating candidate. - -**INTENT:** The intent of withdrawcand is to withdraw a candidate for becoming an elected custodian. The action ensures the {{ cand }} account is currently nominated. On success the amount of tokens that was locked up via the {{ nominatecand }} action will be added to a list of pending transactions to transfer back to the {{ cand }} account. The actual transfer would be performed by a separate action due to the auth requirement for sending funds from the contract's account. - -**TERM:** The account will no longer be a candidate unless they it is nominated again. - -

- withdrawcane -

- -## ACTION: withdrawcane -**PARAMETERS:** -* __cand__ is an account_name parameter for the nominating candidate. - -**INTENT:** The intent of withdrawcane is to withdraw a candidate for becoming an elected custodian. The action ensures the {{ cand }} account is currently nominated. On success the amount of tokens that was locked up via the {{ nominatecand }} action will be added to a list of pending transactions to transfer back to the {{ cand }} account. The actual transfer would be performed by a separate action due to the auth requirement for sending funds from the contract's account. - -**TERM:** The account will no longer be a candidate unless they it is nominated again. - -## ACTION: updatebio -**PARAMETERS:** -* __cand__ is an account_name parameter for the nominating candidate. -* __bio__ is a string representing a bio for candidate. This should be a hash or a link where data is under the control of the individual. - -**INTENT:** The intent of updatebio is to allow a candidate update their bio information after they have nominated. The action ensures the user has agreed to the latest terms and conditions, has the correct authorization of the {{ cand }} to perform the action and is already nominated as a candidate. Then the bio information for the candidate will be updated leaving all other data of the candidate unchanged. - -**WARNING:** The action records information on the blockchain and hence should not include directly entered personally identifiable information. Instead hashes or links under the control of the individual should be used. - -

- updatereqpay -

- -## ACTION: updatereqpay -**PARAMETERS:** -* __cand__ is an account_name parameter for the nominating candidate. -* __requestedpay__ is an asset requested by the candidate as pay for being an elected custodian. It should be passed to the action in the format: \"10.0000 EOSDAC\". - -**INTENT:** The intent of updatereqpay is to allow a candidate update their requested pay after they have nominated. The action ensures the user has agreed to the latest terms and conditions, has the correct authorization of the {{ cand }} to perform the action and is already nominated as a candidate. All other data of the candidate will remain unchanged. If the custodian is elected, this requested pay is used along with other elected custodians requested pay to determine the level of pay for custodians - -**TERM:** The action changes the values until superseded by another action. - -

- updatereqpae -

- -## ACTION: updatereqpae -**PARAMETERS:** -* __cand__ is an account_name parameter for the nominating candidate. -* __requestedpay__ is an asset requested by the candidate as pay for being an elected custodian. It should be passed to the action in the format: \"10.0000 EOSDAC\". - -**INTENT:** The intent of updatereqpae is to allow a candidate update their requested pay after they have nominated. The action ensures the user has agreed to the latest terms and conditions, has the correct authorization of the {{ cand }} to perform the action and is already nominated as a candidate. All other data of the candidate will remain unchanged. If the custodian is elected, this requested pay is used along with other elected custodians requested pay to determine the level of pay for custodians - -**TERM:** The action changes the values until superseded by another action. - -

- votecust -

- -## ACTION: votecust -**PARAMETERS:** -* __voter__ is an eosio account_name parameter for the voting member. -* __newvotes__ is an array of nominated candidates account names that the voter intends to vote for with a maximum number of votes as configured by the contract. - -**INTENT:** The intent of votecust is to allow a member of the DAC to vote for candidates that are eligible become custodians after the next call to {{ newperiod }}. The action ensures the user has agreed to the latest terms and conditions and has the correct authorization of the account: {{ voter }} to place or change an active vote. Upon success this action will either update an existing vote with a new set of candidates or create a new active vote for the {{ voter }} for candidates eligible for election. This action will replace an existing vote for a proxy for {{ voter }} if one exists. - -**TERM:** The action changes the preferred custodians for an account until superseded by another action. - -

- votecuste -

- -## ACTION: votecuste -**PARAMETERS:** -* __voter__ is an eosio account_name parameter for the voting member. -* __newvotes__ is an array of nominated candidates account names that the voter intends to vote for with a maximum number of votes as configured by the contract. - -**INTENT:** The intent of votecuste is to allow a member of the DAC to vote for candidates that are eligible become custodians after the next call to {{ newperiod }}. The action ensures the user has agreed to the latest terms and conditions and has the correct authorization of the account: {{ voter }} to place or change an active vote. Upon success this action will either update an existing vote with a new set of candidates or create a new active vote for the {{ voter }} for candidates eligible for election. This action will replace an existing vote for a proxy for {{ voter }} if one exists. - -**TERM:** The action changes the preferred custodians for an account until superseded by another action. - -

- voteproxy -

- -## ACTION: voteproxy -**PARAMETERS:** -* __voter__ is an eosio account_name . -* __proxy__ is an account name that the voter intends to vote for with a maximum number of votes as configured by the contract. - -**INTENT:** The intent of voteproxy is to vote another single voter account that may vote with {{ voter }} weight for custodians as a proxy. The action ensures the {{ voter }} has agreed to the latest terms and conditions and has the correct authorization of the {{ voter }} to place or change an active vote. Upon success this action will either update an existing {{ proxy }} vote or create a new active vote for {{ proxy }}. This action will replace an existing vote for a custodians as created by the votecust action if one exists. This action will fail if {{ voter }} attempts to vote for a user who is already voting for a proxy or if they attempt to proxy vote for themselves. - -**TERM:** The action changes the proxy until superseded by another action. - -

- newperiod -

- -## ACTION: newperiod -**PARAMETERS:** -* __message__ is string used only for logging in the blockchain history and serves no purpose in the action contract logic. - -**INTENT:** The intent of {{ newperiod }} is to signal the end of one election period and commence the next. It performs several actions after the following conditions are met: - * The action is not called before the period should have ended - * Enough voter value has participated to trigger the initial running of the DAC - * After the Dac has started enough voter value has continued engagement with the dac voting process. -1. Calculate the mean `requestedpay` of all the currently elected custodians. -2. Distribute the median pay amount to all the currently elected custodians. This is achieved by adding a record to the `pendingpay` table with the custodian and the amount payable in preparation for an authorised action to `claimpay`. -3. Captures the highest voted candidates to set them as the custodians for the next period based on the accumulated vote weight. -4. Set the permissions for the elected custodians so they have sufficient permission to run the dac according to the constitution and technical permissions design. -5. Set the time for the beginning of the next period to mark the reset anniversary for the dac. - -**TERM:** The action changes the relevant contract data until a subsequent newperiod is called. - -

- newperiode -

- -## ACTION: newperiode -**PARAMETERS:** -* __message__ is string used only for logging in the blockchain history and serves no purpose in the action contract logic. - -**INTENT:** The intent of {{ newperiode }} is to signal the end of one election period and commence the next. It performs several actions after the following conditions are met: - * The action is not called before the period should have ended - * Enough voter value has participated to trigger the initial running of the DAC - * After the Dac has started enough voter value has continued engagement with the dac voting process. -1. Calculate the mean `requestedpay` of all the currently elected custodians. -2. Distribute the median pay amount to all the currently elected custodians. This is achieved by adding a record to the `pendingpay` table with the custodian and the amount payable in preparation for an authorised action to `claimpay`. -3. Captures the highest voted candidates to set them as the custodians for the next period based on the accumulated vote weight. -4. Set the permissions for the elected custodians so they have sufficient permission to run the dac according to the constitution and technical permissions design. -5. Set the time for the beginning of the next period to mark the reset anniversary for the dac. - -**TERM:** The action changes the relevant contract data until a subsequent newperiode is called. - -

- claimpay -

- -## ACTION: claimpay -**PARAMETERS:** -* __claimer__ account claiming the pay. This account must match the destination account for which the claim is for. - -**INTENT:** The intent of {{ claimpay }} is to allow an account to claim pending payment amounts due to the account. The pay claim they are claiming needs to be visible in the `pendingpay` table. transfers to the claimer via an inline transfer on the eosdactoken contract and then removes the pending payment record from the `pending_pay` table. The active auth of this claimer is required to complete this action. - -**TERM:** This action lasts for the duration of the time taken to process the transaction. - -

- claimpaye -

- -## ACTION: claimpaye -**PARAMETERS:** -* __claimer__ account claiming the pay. This account must match the destination account for which the claim is for. - -**INTENT:** The intent of {{ claimpaye }} is to allow an account to claim pending payment amounts due to the account. The pay claim they are claiming needs to be visible in the `pendingpay` table. transfers to the claimer via an inline transfer on the eosdactoken contract and then removes the pending payment record from the `pending_pay` table. The active auth of this claimer is required to complete this action. - -**TERM:** This action lasts for the duration of the time taken to process the transaction. - -

- rejectcuspay -

- -## ACTION: rejectcuspay -**PARAMETERS:** -* __payid__ the pay id for the arrange payment. - -**INTENT:** The intent of {{ rejectcuspay }} is to allow an account to reject a claim for pending payment amounts due to that account. The pay claim they are claiming needs to be visible in the `pendingpay` table and is then removed from the `pending_pay` table. The active auth of the intended claimer for the payment is required to complete this action. - -**TERM:** This action lasts for the duration of the time taken to process the transaction. - -

-transferobsv -

- -## ACTION: transferobsv -**PARAMETERS:** -* __from__ is an eosio account name for the source of the transaction. -* __to__ is an eosio account name for the destination of the transaction. -* __quantity__ is eos asset. -* __dac_id__ is an eosio account name to determine which DAC the transaction is associated with. - -**INTENT** The intent of transferobsv is to monitor an asset transaction to update current vote strengths. -#### Warning: This action will store the content on the chain in the history logs and the data cannot be deleted later so therefore should only store a unidentifiable hash of content rather than human readable content. - -

-capturestake -

- -## ACTION: capturestake -**PARAMETERS:** -* __from__ is an eosio account name for the source of the transaction. -* __quantity__ is eos asset. -* __dac_id__ is an eosio account name to determine which DAC the transaction is associated with. - -**INTENT** The intent of capturestake is to record an amount transferred to this contract for pending stake for a registering candidate. -#### Warning: This action will store the content on the chain in the history logs and the data cannot be deleted later so therefore should only store a unidentifiable hash of content rather than human readable content. - -

- setperm -

- -## ACTION: setperm -**PARAMETERS:** -* __cand__ The account registering a new permission, must be registered as a candidate. -* __permission__ The permission name that will be used in the multisig. - -**INTENT:** The intent of {{ setperm }} is to allow an account to set a permission name which will be used in the DAC multisig in place of the active permission. This allows an account to set up a permission specifically for administering the DAC. Setting the entry to active will delete the entry in the database and free RAM. - -**TERM:** This action lasts until the user sets the permission back to active. diff --git a/contracts/custodian/daccustodian.cpp b/contracts/custodian/daccustodian.cpp deleted file mode 100644 index a695503..0000000 --- a/contracts/custodian/daccustodian.cpp +++ /dev/null @@ -1,22 +0,0 @@ -#include -#include -#include -#include - -#include -#include -#include -#include "daccustodian.hpp" - -#include "update_member_details.cpp" -#include "registering.cpp" -#include "voting.cpp" -#include "privatehelpers.cpp" -#include "newperiod_components.cpp" -#include "pay_handling.cpp" -#include "external_observable_actions.cpp" -#include "config.cpp" -#include "migration.cpp" - -using namespace eosio; -using namespace std; diff --git a/contracts/custodian/daccustodian.hpp b/contracts/custodian/daccustodian.hpp deleted file mode 100644 index 4e3977d..0000000 --- a/contracts/custodian/daccustodian.hpp +++ /dev/null @@ -1,242 +0,0 @@ -#include -#include -#include -#include -#include - -#include "external_types.hpp" -#include "../_contract-shared-headers/eosdactokens_shared.hpp" -#include "../_contract-shared-headers/daccustodian_shared.hpp" -#include "../_contract-shared-headers/common_utilities.hpp" - -namespace eosdac { - - const eosio::name ONE_PERMISSION = "one"_n; - const eosio::name LOW_PERMISSION = "low"_n; - const eosio::name MEDIUM_PERMISSION = "med"_n; - const eosio::name HIGH_PERMISSION = "high"_n; - -#ifndef TRANSFER_DELAY -#define TRANSFER_DELAY 60*60 -#endif - -//old_start - - struct [[eosio::table("config"), eosio::contract("daccustodian")]] contr_config_old { - asset lockupasset; - uint8_t maxvotes = 5; - uint8_t numelected = 3; - uint32_t periodlength = 7 * 24 * 60 * 60; - name authaccount = name{0}; - name tokenholder = "eosdacthedac"_n; - name serviceprovider; - bool should_pay_via_service_provider; - uint32_t initial_vote_quorum_percent; - uint32_t vote_quorum_percent; - uint8_t auth_threshold_high; - uint8_t auth_threshold_mid; - uint8_t auth_threshold_low; - uint32_t lockup_release_time_delay; - asset requested_pay_max; - }; - - typedef singleton<"config"_n, contr_config_old> old_configscontainer; - //end_old - - struct contr_config; - typedef eosio::singleton<"config2"_n, contr_config> configscontainer; - - struct [[eosio::table("config2"), eosio::contract("daccustodian")]] contr_config { - // The amount of assets that are locked up by each candidate applying for election. - eosio::extended_asset lockupasset; - // The maximum number of votes that each member can make for a candidate. - uint8_t maxvotes = 5; - // Number of custodians to be elected for each election count. - uint8_t numelected = 3; - // Length of a period in seconds. - // - used for pay calculations if an eary election is called and to trigger deferred `newperiod` calls. - uint32_t periodlength = 7 * 24 * 60 * 60; - - // The contract will direct all payments via the service provider. - bool should_pay_via_service_provider; - - // Amount of voting value in votes required to trigger the initial set of custodians - uint32_t initial_vote_quorum_percent; - - // Amount of voting value in votes required to trigger the allow a new set of custodians to be set after the initial threshold has been achieved. - uint32_t vote_quorum_percent; - - // required number of custodians required to approve different levels of authenticated actions. - uint8_t auth_threshold_high; - uint8_t auth_threshold_mid; - uint8_t auth_threshold_low; - - // The time before locked up stake can be released back to the candidate using the unstake action - uint32_t lockup_release_time_delay; - - eosio::extended_asset requested_pay_max; - - static contr_config get_current_configs(eosio::name account, eosio::name scope) { - return configscontainer(account, scope.value).get_or_default(contr_config()); - } - - void save(eosio::name account, eosio::name scope, eosio::name payer = same_payer) { - configscontainer(account, scope.value).set(*this, payer); - } - }; - - struct contr_state; - typedef eosio::singleton<"state"_n, contr_state> statecontainer; - - struct [[eosio::table("state"), eosio::contract("daccustodian")]] contr_state { - eosio::time_point_sec lastperiodtime = time_point_sec(0); - int64_t total_weight_of_votes = 0; - int64_t total_votes_on_candidates = 0; - uint32_t number_active_candidates = 0; - bool met_initial_votes_threshold = false; - - static contr_state get_current_state(eosio::name account, eosio::name scope) { - return statecontainer(account, scope.value).get_or_default(contr_state()); - } - - void save(eosio::name account, eosio::name scope, eosio::name payer = same_payer) { - statecontainer(account, scope.value).set(*this, payer); - } - }; - - struct [[eosio::table("votes"), eosio::contract("daccustodian")]] vote { - name voter; - name proxy; - std::vector candidates; - - uint64_t primary_key() const { return voter.value; } - - uint64_t by_proxy() const { return proxy.value; } - }; - - typedef eosio::multi_index<"votes"_n, vote, - indexed_by<"byproxy"_n, const_mem_fun > - > votes_table; - - struct [[eosio::table("pendingpay"), eosio::contract("daccustodian")]] pay { - uint64_t key; - name receiver; - asset quantity; - string memo; - - uint64_t primary_key() const { return key; } - uint64_t byreceiver() const { return receiver.value; } - }; - - typedef multi_index<"pendingpay"_n, pay, - indexed_by<"byreceiver"_n, const_mem_fun > - > pending_pay_table; - - struct [[eosio::table("pendingstake"), eosio::contract("daccustodian")]] tempstake { - name sender; - asset quantity; - string memo; //deprecated - but needs mifgrations to get rid of it. - - uint64_t primary_key() const { return sender.value; } - }; - - typedef multi_index<"pendingstake"_n, tempstake> pendingstake_table_t; - - struct [[eosio::table("candperms"), eosio::contract("daccustodian")]] candperm { - name cand; - name permission; - - uint64_t primary_key() const { return cand.value; } - }; - - typedef multi_index<"candperms"_n, candperm> candperms_table; - - class daccustodian : public contract { - - public: - - daccustodian( name s, name code, datastream ds ) - :contract(s,code,ds) {} - - ACTION updateconfig(contr_config newconfig); - ACTION updateconfige(contr_config newconfig, name dac_id); - ACTION capturestake(name from, - asset quantity, - name dac_id); - ACTION transferobsv(name from, - name to, - asset quantity, - name dac_id); - ACTION nominatecand(name cand, eosio::asset requestedpay); - ACTION nominatecane(name cand, eosio::asset requestedpay, name dac_id); - ACTION withdrawcand(name cand); - ACTION withdrawcane(name cand, name dac_id); - ACTION firecand(name cand, bool lockupStake); - ACTION firecande(name cand, bool lockupStake, name dac_id); - ACTION resigncust(name cust); - ACTION resigncuste(name cust, name dac_id); - ACTION firecust(name cust); - ACTION firecuste(name cust, name dac_id); - ACTION updatebio(name cand, std::string bio); - ACTION updatebioe(name cand, std::string bio, name dac_id); - - [[eosio::action]] - inline void stprofile(name cand, std::string profile, name dac_id) { require_auth(cand); }; - - [[eosio::action]] - inline void stprofileuns(name cand, std::string profile) { require_auth(cand); }; - ACTION updatereqpay(name cand, eosio::asset requestedpay); - ACTION updatereqpae(name cand, eosio::asset requestedpay, name dac_id); - ACTION votecust(name voter, std::vector newvotes); - ACTION votecuste(name voter, std::vector newvotes, name dac_id); - // void voteproxy(name voter, name proxy); - ACTION newperiod(std::string message); - ACTION newperiode(std::string message, name dac_id); - ACTION runnewperiod(std::string message, name dac_id); - ACTION claimpay(uint64_t payid); - ACTION claimpaye(uint64_t payid, name dac_id); - ACTION removecuspay(uint64_t payid, name dac_id); - ACTION rejectcuspay(uint64_t payid, name dac_id); - ACTION unstake(name cand); - ACTION unstakee(name cand, name dac_id); - ACTION clearstake(name cand, asset new_value, name dac_id); - ACTION migrate(uint16_t batch_size); - ACTION clearold(uint16_t batch_size); - - - /** - * This action is used to register a custom permission that will be used in the multisig instead of active. - * - * ### Assertions: - * - The account supplied to cand exists and is a registered candidate - * - The permission supplied exists as a permission on the account - * - * @param cand - The account id for the candidate setting a custom permission. - * @param permission - The permission name to use. - * - * - * ### Post Condition: - * The candidate will have a record entered into the database indicating the custom permission to use. - */ - ACTION setperm(name cand, name permission, name dac_id); - - private: // Private helper methods used by other actions. - - void updateVoteWeight(name custodian, int64_t weight, name internal_dac_id); - void updateVoteWeights(const vector &votes, int64_t vote_weight, name internal_dac_id, contr_state ¤tState); - void modifyVoteWeights(name voter, vector oldVotes, vector newVotes, name internal_dac_id); - void assertPeriodTime(contr_config &configs, contr_state ¤tState); - void distributeMeanPay(name internal_dac_id); - void setCustodianAuths(name internal_dac_id); - void removeCustodian(name cust, name internal_dac_id); - void removeCandidate(name cust, bool lockupStake, name internal_dac_id); - void allocateCustodians(bool early_election, name internal_dac_id); - bool permissionExists(name account, name permission); - bool _check_transaction_authorization(const char* trx_data, uint32_t trx_size, - const char* pubkeys_data, uint32_t pubkeys_size, - const char* perms_data, uint32_t perms_size); - - permission_level getCandidatePermission(name account, name internal_dac_id); - void validateUnstake(name code, name cand, name dac_id); - }; -}; diff --git a/contracts/custodian/external_observable_actions.cpp b/contracts/custodian/external_observable_actions.cpp deleted file mode 100644 index e8fc6f8..0000000 --- a/contracts/custodian/external_observable_actions.cpp +++ /dev/null @@ -1,66 +0,0 @@ - -#include "../_contract-shared-headers/dacdirectory_shared.hpp" - -void daccustodian::capturestake(name from, - asset quantity, - name dac_id) { - - auto dac = dacdir::dac_for_id(dac_id); - auto token_contract = dac.symbol.get_contract(); - print("voting contract: ", token_contract); - require_auth(token_contract); - - candidates_table candidates(_self, dac_id.value); - auto cand = candidates.find(from.value); - if (cand != candidates.end()){ - candidates.modify(cand, _self, [&](candidate &c) { - c.locked_tokens += quantity; - }); - } else { - pendingstake_table_t pendingstake(_self, dac_id.value); - auto source = pendingstake.find(from.value); - if (source != pendingstake.end()) { - pendingstake.modify(source, _self, [&](tempstake &s) { - s.quantity += quantity; - }); - print("Modified exisiting stake record: ", from); - } else { - pendingstake.emplace(_self, [&](tempstake &s) { - s.sender = from; - s.quantity = quantity; - }); - print("Created stake record: ", from); - } - } -} - -void daccustodian::transferobsv(name from, - name to, - asset quantity, - name dac_id) { - - eosio::print("\n > transfer from : ", from, " to: ", to, " quantity: ", quantity); - - auto dac = dacdir::dac_for_id(dac_id); - auto token_contract = dac.symbol.get_contract(); - print("voting contract: ", token_contract); - require_auth(token_contract); - - votes_table votes_cast_by_members(_self, dac_id.value); - contr_state currentState = contr_state::get_current_state(_self, dac_id); - - auto existingVote = votes_cast_by_members.find(from.value); - if (existingVote != votes_cast_by_members.end()) { - updateVoteWeights(existingVote->candidates, -quantity.amount, dac_id, currentState); - currentState.total_weight_of_votes -= quantity.amount; - } - - // Update vote weight for the 'to' in the transfer if vote exists - existingVote = votes_cast_by_members.find(to.value); - if (existingVote != votes_cast_by_members.end()) { - updateVoteWeights(existingVote->candidates, quantity.amount, dac_id, currentState); - currentState.total_weight_of_votes += quantity.amount; - } - currentState.save(_self, dac_id); - -} diff --git a/contracts/custodian/external_types.hpp b/contracts/custodian/external_types.hpp deleted file mode 100644 index da64824..0000000 --- a/contracts/custodian/external_types.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include - -struct currency_stats { - eosio::asset supply; - eosio::asset max_supply; - eosio::name issuer; - bool transfer_locked = false; - - uint64_t primary_key() const { return supply.symbol.code().raw(); } -}; - -typedef eosio::multi_index<"stat"_n, currency_stats> stats; - -//Authority Structs -namespace eosiosystem { - - struct key_weight { - eosio::public_key key; - uint16_t weight; - }; - - struct permission_level_weight { - eosio::permission_level permission; - uint16_t weight; - }; - - struct wait_weight { - uint32_t wait_sec; - uint16_t weight; - }; - - struct authority { - - uint32_t threshold; - std::vector keys; - std::vector accounts; - std::vector waits; - }; -} diff --git a/contracts/custodian/newperiod_components.cpp b/contracts/custodian/newperiod_components.cpp deleted file mode 100644 index 9c9d6d9..0000000 --- a/contracts/custodian/newperiod_components.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#include "../_contract-shared-headers/migration_helpers.hpp" - -// void daccustodian::distributePay(name dac_id) { -// custodians_table custodians(_self, dac_id.value); -// pending_pay_table pending_pay(_self, dac_id.value); - -// //Find the median pay using a temporary vector to hold the requestedpay amounts. -// std::vector reqpays; -// for (auto cust: custodians) { -// reqpays.push_back(cust.requestedpay); -// } - -// // Using nth_element to just sort for the entry we need for the median value. -// size_t mid = reqpays.size() / 2; -// std::nth_element(reqpays.begin(), reqpays.begin() + mid, reqpays.end()); - -// asset medianAsset = reqpays[mid]; - -// if (medianAsset.amount > 0) { -// for (auto cust: custodians) { -// pending_pay.emplace(_self, [&](pay &p) { -// p.key = pending_pay.available_primary_key(); -// p.receiver = cust.cust_name; -// p.quantity = medianAsset; -// p.memo = "Custodian pay. Thank you."; -// }); -// } -// } - -// print("distribute pay"); -// } - -void daccustodian::distributeMeanPay(name dac_id) { - custodians_table custodians(get_self(), dac_id.value); - pending_pay_table pending_pay(get_self(), get_self().value); - contr_config configs = contr_config::get_current_configs(get_self(), dac_id); - name auth_account = dacdir::dac_for_id(dac_id).account_for_type(dacdir::AUTH); - - //Find the mean pay using a temporary vector to hold the requestedpay amounts. - asset total = asset{0, configs.requested_pay_max.quantity.symbol}; - int64_t count = 0; - for (auto cust: custodians) { - total += cust.requestedpay; - count += 1; - // print_f("cust % with amount %\n", cust.cust_name, cust.requestedpay); - } - - asset meanAsset = count == 0 ? total : total / count; - - // print_f("Calclulated mean is: %", meanAsset); - if (meanAsset.amount > 0) { - for (auto cust: custodians) { - pending_pay.emplace(auth_account, [&](pay &p) { - p.key = pending_pay.available_primary_key(); - p.receiver = cust.cust_name; - p.quantity = meanAsset; - p.memo = "Custodian pay. Thank you."; - }); - } - } - - print("distribute mean pay"); -} - -void daccustodian::assertPeriodTime(contr_config &configs, contr_state ¤tState) { - time_point_sec timestamp = time_point_sec(eosio::current_time_point()); - uint32_t periodBlockCount = (timestamp - currentState.lastperiodtime.sec_since_epoch()).sec_since_epoch(); - check(periodBlockCount > configs.periodlength, - "ERR::NEWPERIOD_EARLY::New period is being called too soon. Wait until the period has completed."); -} - -void daccustodian::allocateCustodians(bool early_election, name dac_id) { - - eosio::print("Configure custodians for the next period."); - - custodians_table custodians(get_self(), dac_id.value); - candidates_table registered_candidates(get_self(), dac_id.value); - contr_config configs = contr_config::get_current_configs(get_self(), dac_id); - name auth_account = dacdir::dac_for_id(dac_id).account_for_type(dacdir::AUTH); - - auto byvotes = registered_candidates.get_index<"byvotesrank"_n>(); - auto cand_itr = byvotes.begin(); - - int32_t electcount = configs.numelected; - uint8_t currentCustodianCount = 0; - - if (!early_election) { - eosio::print("Empty the custodians table to get a full set of new custodians based on the current votes."); - auto cust_itr = custodians.begin(); - while (cust_itr != custodians.end()) { - const auto ®_candidate = registered_candidates.get(cust_itr->cust_name.value, "ERR::NEWPERIOD_EXPECTED_CAND_NOT_FOUND::Corrupt data: Trying to set a lockup delay on candidate leaving office."); - registered_candidates.modify(reg_candidate, same_payer, [&](candidate &c) { - eosio::print("Lockup stake for release delay."); - c.custodian_end_time_stamp = time_point_sec(current_time_point().sec_since_epoch() + configs.lockup_release_time_delay); - }); - cust_itr = custodians.erase(cust_itr); - } - } - - eosio::print("Select only enough candidates to fill the gaps."); - for (auto itr = custodians.begin(); itr != custodians.end(); itr++) { ++currentCustodianCount; } - - while (currentCustodianCount < electcount) { - if (cand_itr == byvotes.end() || cand_itr->total_votes == 0) { - eosio::print("The pool of eligible candidates has been exhausted"); - return; - } - - // If the candidate is inactive or is already a custodian skip to the next one. - if (!cand_itr->is_active || custodians.find(cand_itr->candidate_name.value) != custodians.end()) { - cand_itr++; - } else { - custodians.emplace(auth_account, [&](custodian &c) { - c.cust_name = cand_itr->candidate_name; - c.requestedpay = cand_itr->requestedpay; - c.total_votes = cand_itr->total_votes; - }); - - byvotes.modify(cand_itr, same_payer, [&](candidate &c) { - eosio::print("Lockup stake for release delay."); - c.custodian_end_time_stamp = time_point_sec(current_time_point()) + configs.lockup_release_time_delay; - }); - - currentCustodianCount++; - cand_itr++; - } - } -} - -void daccustodian::setCustodianAuths(name dac_id) { - - custodians_table custodians(get_self(), dac_id.value); - contr_config current_config = contr_config::get_current_configs(get_self(), dac_id); - - auto dac = dacdir::dac_for_id(dac_id); - - name accountToChange = dac.account_for_type(dacdir::AUTH); - - vector accounts; - - print("setting auths for custodians\n\n"); - - for (auto it = custodians.begin(); it != custodians.end(); it++) { - eosiosystem::permission_level_weight account{ - .permission = getCandidatePermission(it->cust_name, dac_id), //eosio::permission_level(it->cust_name, "active"_n), - .weight = (uint16_t) 1, - }; - accounts.push_back(account); - } - - eosiosystem::authority high_contract_authority{ - .threshold = current_config.auth_threshold_high, - .keys = {}, - .accounts = accounts - }; - print("About to set the first one in auths for custodians\n\n"); - - action(permission_level{accountToChange, "owner"_n}, - "eosio"_n, "updateauth"_n, - std::make_tuple( - accountToChange, - HIGH_PERMISSION, - "active"_n, - high_contract_authority)) - .send(); - - print("After setting the first one in auths for custodians\n\n"); - - eosiosystem::authority medium_contract_authority{ - .threshold = current_config.auth_threshold_mid, - .keys = {}, - .accounts = accounts - }; - - action(permission_level{accountToChange, "owner"_n}, - "eosio"_n, "updateauth"_n, - std::make_tuple( - accountToChange, - MEDIUM_PERMISSION, - "high"_n, - medium_contract_authority)) - .send(); - - eosiosystem::authority low_contract_authority{ - .threshold = current_config.auth_threshold_low, - .keys = {}, - .accounts = accounts - }; - - action(permission_level{accountToChange, "owner"_n}, - "eosio"_n, "updateauth"_n, - std::make_tuple( - accountToChange, - LOW_PERMISSION, - "med"_n, - low_contract_authority)) - .send(); - - eosiosystem::authority one_contract_authority{ - .threshold = 1, - .keys = {}, - .accounts = accounts - }; - - action(permission_level{accountToChange, "owner"_n}, - "eosio"_n, "updateauth"_n, - std::make_tuple( - accountToChange, - ONE_PERMISSION, - "low"_n, - one_contract_authority)) - .send(); - print("Got to the end of setting permissions."); -} - -void daccustodian::newperiod(string message) { - newperiode(message, get_self()); -} - -void daccustodian::newperiode(string message, name dac_id) { - name auth_account = dacdir::dac_for_id(dac_id).account_for_type(dacdir::AUTH); - - eosio::action( - eosio::permission_level{ auth_account, "owner"_n }, - get_self(), "runnewperiod"_n, - make_tuple(message, dac_id) - ).send(); -} - -void daccustodian::runnewperiod(string message, name dac_id) { - - contr_config configs = contr_config::get_current_configs(get_self(), dac_id); - contr_state currentState = contr_state::get_current_state(get_self(), dac_id); - assertPeriodTime(configs, currentState); - - dacdir::dac found_dac = dacdir::dac_for_id(dac_id); - - - // Get the voting supply of the lockup asset voting (eg. EOSDAC) - auto tokenStats = stats( - found_dac.symbol.get_contract(), - found_dac.symbol.get_symbol().raw() - ).begin(); - uint64_t token_current_supply = tokenStats->supply.amount; - - double percent_of_current_voter_engagement = - double(currentState.total_weight_of_votes) / double(token_current_supply) * 100.0; - - eosio::print("\n\nToken current supply as decimal units: ", token_current_supply, " total votes so far: ", currentState.total_weight_of_votes); - eosio::print("\n\nNeed inital engagement of: ", configs.initial_vote_quorum_percent, "% to start the DAC."); - eosio::print("\n\nToken supply: ", token_current_supply * 0.0001, " total votes so far: ", currentState.total_weight_of_votes * 0.0001); - eosio::print("\n\nNeed initial engagement of: ", configs.initial_vote_quorum_percent, "% to start the DAC."); - eosio::print("\n\nNeed ongoing engagement of: ", configs.vote_quorum_percent, - "% to allow new periods to trigger after initial activation."); - eosio::print("\n\nPercent of current voter engagement: ", percent_of_current_voter_engagement, "\n\n"); - - check(currentState.met_initial_votes_threshold == true || - percent_of_current_voter_engagement > configs.initial_vote_quorum_percent, - "ERR::NEWPERIOD_VOTER_ENGAGEMENT_LOW_ACTIVATE::Voter engagement is insufficient to activate the DAC."); - currentState.met_initial_votes_threshold = true; - - check(percent_of_current_voter_engagement > configs.vote_quorum_percent, - "ERR::NEWPERIOD_VOTER_ENGAGEMENT_LOW_PROCESS::Voter engagement is insufficient to process a new period"); - - // Distribute pay to the current custodians. - distributeMeanPay(dac_id); - - // Set custodians for the next period. - allocateCustodians(false, dac_id); - - // Set the auths on the dacauthority account - setCustodianAuths(dac_id); - - currentState.lastperiodtime = current_block_time(); - currentState.save(get_self(), dac_id); - - -// Schedule the the next election cycle at the end of the period. -// transaction nextTrans{}; -// nextTrans.actions.emplace_back(permission_level(_self,N(active)), _self, N(newperiod), std::make_tuple("", false)); -// nextTrans.delay_sec = configs().periodlength; -// nextTrans.send(N(newperiod), false); -} diff --git a/contracts/custodian/pay_handling.cpp b/contracts/custodian/pay_handling.cpp deleted file mode 100644 index e848efa..0000000 --- a/contracts/custodian/pay_handling.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include - -void daccustodian::claimpay(uint64_t payid) { - claimpaye(payid, get_self()); -} - -void daccustodian::claimpaye(uint64_t payid, name dac_id) { - pending_pay_table pending_pay(get_self(), get_self().value); - - dacdir::dac dac = dacdir::dac_for_id(dac_id); - - contr_config configs = contr_config::get_current_configs(get_self(), dac_id); - const pay &payClaim = pending_pay.get(payid, "ERR::CLAIMPAY_INVALID_CLAIM_ID::Invalid pay claim id."); - assertValidMember(payClaim.receiver, dac_id); - - require_auth(payClaim.receiver); - - transaction deferredTrans{}; - - name payment_destination; - string memo; - - name service_account = dac.account_for_type(dacdir::SERVICE); - name token_holder = dac.account_for_type(dacdir::TREASURY); - - if (configs.should_pay_via_service_provider) { - memo = payClaim.receiver.to_string() + ":" + payClaim.memo + ":" + to_string(payid);; - print("constructed memo for the service contract: " + memo); - payment_destination = service_account; - } else { - memo = payClaim.memo + ":" + to_string(payid);; - print("constructed memo for the receiver contract: " + memo); - payment_destination = payClaim.receiver; - } - - deferredTrans.actions.emplace_back( - action(permission_level{token_holder, "xfer"_n}, - configs.requested_pay_max.contract, - "transfer"_n, - std::make_tuple(token_holder, payment_destination, payClaim.quantity, memo) - )); - - deferredTrans.actions.emplace_back( - action(permission_level{get_self(), "pay"_n}, - get_self(), "removecuspay"_n, - std::make_tuple(payid, dac_id) - )); - - deferredTrans.delay_sec = TRANSFER_DELAY; - deferredTrans.send(uint128_t(payid) << 64 | time_point_sec(current_time_point()).sec_since_epoch() , get_self()); -} - -void daccustodian::removecuspay(uint64_t payid, name dac_id) { - require_auth(get_self()); - - pending_pay_table pending_pay(get_self(), get_self().value); - const pay &payClaim = pending_pay.get(payid, "ERR::CLAIMPAY_INVALID_CLAIM_ID::Invalid pay claim id."); - - pending_pay.erase(payClaim); -} - -void daccustodian::rejectcuspay(uint64_t payid, name dac_id) { - pending_pay_table pending_pay(get_self(), get_self().value); - const pay &payClaim = pending_pay.get(payid, "ERR::CLAIMPAY_INVALID_CLAIM_ID::Invalid pay claim id."); - assertValidMember(payClaim.receiver, dac_id); - - require_auth(payClaim.receiver); - - pending_pay.erase(payClaim); -} diff --git a/contracts/custodian/privatehelpers.cpp b/contracts/custodian/privatehelpers.cpp deleted file mode 100644 index c5d0413..0000000 --- a/contracts/custodian/privatehelpers.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "../_contract-shared-headers/migration_helpers.hpp" - -void daccustodian::updateVoteWeight(name custodian, int64_t weight, name dac_id) { - if (weight == 0) { - print("\n Vote has no weight - No need to continue."); - return; - } - candidates_table registered_candidates(_self, dac_id.value); - - auto candItr = registered_candidates.find(custodian.value); - if (candItr == registered_candidates.end()) { - eosio::print("Candidate not found while updating from a transfer: ", custodian); - return; // trying to avoid throwing errors from here since it's unrelated to a transfer action.?!?!?!?! - } - - registered_candidates.modify(candItr, same_payer, [&](auto &c) { - c.total_votes += weight; - eosio::print("\nchanging vote weight: ", custodian, " by ", weight); - }); -} - -void daccustodian::updateVoteWeights(const vector &votes, int64_t vote_weight, name dac_id, contr_state ¤tState) { - - for (const auto &cust : votes) { - updateVoteWeight(cust, vote_weight, dac_id); - } - - currentState.total_votes_on_candidates += votes.size() * vote_weight; -} - -void daccustodian::modifyVoteWeights(name voter, vector oldVotes, vector newVotes, name dac_id) { - // This could be optimised with set diffing to avoid remove then add for unchanged votes. - later - eosio::print("Modify vote weights: ", voter, "\n"); - - dacdir::dac found_dac = dacdir::dac_for_id(dac_id); - accounts accountstable(found_dac.symbol.get_contract(), voter.value); - - const auto ac = accountstable.find(found_dac.symbol.get_symbol().code().raw()); - if (ac == accountstable.end()) { - print("Voter has no balance therefore no need to update vote weights"); - return; - } - int64_t vote_weight = ac->balance.amount; - contr_state currentState = contr_state::get_current_state(_self, dac_id); - - // New voter -> Add the tokens to the total weight. - if (oldVotes.size() == 0) - currentState.total_weight_of_votes += vote_weight; - - // Leaving voter -> Remove the tokens to the total weight. - if (newVotes.size() == 0) - currentState.total_weight_of_votes -= vote_weight; - - updateVoteWeights(oldVotes, -vote_weight, dac_id, currentState); - updateVoteWeights(newVotes, vote_weight, dac_id, currentState); - currentState.save(_self, dac_id); -} - -permission_level daccustodian::getCandidatePermission(name account, name dac_id){ - - candperms_table cand_perms(_self, dac_id.value); - auto perm = cand_perms.find(account.value); - if (perm == cand_perms.end()){ - return permission_level{account, "active"_n}; - } - else { - if (permissionExists(account, perm->permission)){ - return permission_level{account, perm->permission}; - } - else { - return permission_level{account, "active"_n}; - } - } -} - -/* - * TODO : replace with the native function once cdt 1.6.2 is released - */ -bool -daccustodian::_check_transaction_authorization( const char* trx_data, uint32_t trx_size, - const char* pubkeys_data, uint32_t pubkeys_size, - const char* perms_data, uint32_t perms_size ) { - auto res = internal_use_do_not_use::check_transaction_authorization( trx_data, trx_size, pubkeys_data, pubkeys_size, perms_data, perms_size ); - - return (res > 0); -} - -/* - * Check if a permission exists, the transaction attempts to create or modify a permission with the name of the - * permission we are checking, *but parent of owner*. If the permission exists (under active) then updateauth will - * assert with an error about not being able to change the parent of the existing permission, permission checks will pass. - * - * If the permission does NOT exist then updateauth will require auth of the parent permission (owner), but we only test - * against active. Auth checks will fail and the function will return false. - * - * This can be removed if https://github.com/EOSIO/eos/issues/6657 is fixed - */ -bool daccustodian::permissionExists(name account, name permission){ - transaction trx; - eosiosystem::authority authority{ - .threshold = {}, - .keys = {}, - .accounts = {} - }; - trx.actions.push_back( - action(permission_level{account, "active"_n}, - "eosio"_n, "updateauth"_n, - std::make_tuple(account, permission, "owner"_n, authority)) - ); - - auto packed_trx = pack(trx); - - auto check_perms = std::vector(); - check_perms.push_back(permission_level{account, "active"_n}); - - auto packed_perms = pack(check_perms); - - bool res = _check_transaction_authorization(packed_trx.data(), packed_trx.size(), (const char*)0, 0, packed_perms.data(), packed_perms.size()); - - return res; -} diff --git a/contracts/custodian/registering.cpp b/contracts/custodian/registering.cpp deleted file mode 100644 index 9bbceea..0000000 --- a/contracts/custodian/registering.cpp +++ /dev/null @@ -1,306 +0,0 @@ -#include -#include "../_contract-shared-headers/dacdirectory_shared.hpp" - -void daccustodian::nominatecand(name cand, asset requestedpay) { - nominatecane(cand, requestedpay, get_self()); -} - -void daccustodian::nominatecane(name cand, asset requestedpay, name dac_id) { - require_auth(cand); - assertValidMember(cand, dac_id); - contr_state currentState = contr_state::get_current_state(_self, dac_id); - contr_config configs = contr_config::get_current_configs(_self, dac_id); - - check(requestedpay.amount >= 0, "ERR::UPDATEREQPAY_UNDER_ZERO::Requested pay amount must not be negative."); - // This implicitly asserts that the symbol of requestedpay matches the configs.max pay. - check(requestedpay <= configs.requested_pay_max.quantity, - "ERR::NOMINATECAND_PAY_LIMIT_EXCEEDED::Requested pay limit for a candidate was exceeded."); - - currentState.number_active_candidates++; - currentState.save(_self, dac_id); - - pendingstake_table_t pendingstake(_self, dac_id.value); - auto pending = pendingstake.find(cand.value); - - candidates_table registered_candidates(_self, dac_id.value); - - auto reg_candidate = registered_candidates.find(cand.value); - if (reg_candidate != registered_candidates.end()) { - check(!reg_candidate->is_active, "ERR::NOMINATECAND_ALREADY_REGISTERED::Candidate is already registered and active."); - registered_candidates.modify(reg_candidate, cand, [&](candidate &c) { - c.is_active = 1; - c.requestedpay = requestedpay; - - if (pending != pendingstake.end()) { - c.locked_tokens += pending->quantity; - } - check(c.locked_tokens >= configs.lockupasset.quantity, "ERR::NOMINATECAND_INSUFFICIENT_FUNDS_TO_STAKE::Insufficient funds have been staked."); - }); - } else { - check(pending != pendingstake.end() && - pending->quantity >= configs.lockupasset.quantity, - "ERR::NOMINATECAND_STAKING_FUNDS_INCOMPLETE::A registering candidate must transfer sufficient tokens to the contract for staking."); - - registered_candidates.emplace(cand, [&](candidate &c) { - c.candidate_name = cand; - c.requestedpay = requestedpay; - c.locked_tokens = pending->quantity; - c.total_votes = 0; - c.is_active = 1; - }); - - //Temp block for migrations to new scope - if (dac_id == get_self()) { - candidates_table registered_candidates(_self, NEW_SCOPE.value); - - auto reg_candidate = registered_candidates.find(cand.value); - if (reg_candidate != registered_candidates.end()) { - check(!reg_candidate->is_active, "ERR::NOMINATECAND_ALREADY_REGISTERED::Candidate is already registered and active."); - registered_candidates.modify(reg_candidate, cand, [&](candidate &c) { - c.is_active = 1; - c.requestedpay = requestedpay; - - if (pending != pendingstake.end()) { - c.locked_tokens += pending->quantity; - } - check(c.locked_tokens >= configs.lockupasset.quantity, "ERR::NOMINATECAND_INSUFFICIENT_FUNDS_TO_STAKE::Insufficient funds have been staked."); - }); - } else { - check(pending != pendingstake.end() && - pending->quantity >= configs.lockupasset.quantity, - "ERR::NOMINATECAND_STAKING_FUNDS_INCOMPLETE::A registering candidate must transfer sufficient tokens to the contract for staking."); - - registered_candidates.emplace(cand, [&](candidate &c) { - c.candidate_name = cand; - c.requestedpay = requestedpay; - c.locked_tokens = pending->quantity; - c.total_votes = 0; - c.is_active = 1; - }); - } - } - //end Temp block - - pendingstake.erase(pending); - } -} - -void daccustodian::withdrawcand(name cand) { - withdrawcane(cand, get_self()); -} - -void daccustodian::withdrawcane(name cand, name dac_id) { - require_auth(cand); - removeCandidate(cand, false, dac_id); -} - -void daccustodian::firecand(name cand, bool lockupStake) { - firecande(cand, lockupStake, get_self()); -} - -void daccustodian::firecande(name cand, bool lockupStake, name dac_id) { - auto dac = dacdir::dac_for_id(dac_id); - require_auth(dac.account_for_type(dacdir::AUTH)); - removeCandidate(cand, lockupStake, dac_id); -} - -void daccustodian::unstake(name cand) { - unstakee(cand, get_self()); -} - -void daccustodian::unstakee(name cand, name dac_id) { - validateUnstake(get_self(), cand, dac_id); - - extended_asset lockup_asset = contr_config::get_current_configs(get_self(), dac_id).lockupasset; - - candidates_table registered_candidates(get_self(), dac_id.value); - const auto ®_candidate = registered_candidates.get(cand.value, "ERR::UNSTAKE_CAND_NOT_REGISTERED::Candidate is not already registered."); - - transaction deferredTrans{}; - - deferredTrans.actions.emplace_back( - action(permission_level{get_self(), "xfer"_n}, - lockup_asset.contract, - "transfer"_n, - make_tuple(get_self(), cand, reg_candidate.locked_tokens, - string("Returning locked up stake. Thank you.")) - ) - ); - deferredTrans.actions.emplace_back( - action(permission_level{get_self(), "pay"_n}, - get_self(), "clearstake"_n, - make_tuple(cand, asset(0, lockup_asset.quantity.symbol), dac_id) - ) - ); - - deferredTrans.delay_sec = TRANSFER_DELAY; - deferredTrans.send(cand.value, get_self()); - - //Temp block for migrations to new scope - if (dac_id == get_self()) { - candidates_table registered_candidates(_self, NEW_SCOPE.value); - const auto ®_candidate = registered_candidates.get(cand.value, "ERR::UNSTAKE_CAND_NOT_REGISTERED::Candidate is not already registered."); - check(!reg_candidate.is_active, "ERR::UNSTAKE_CANNOT_UNSTAKE_FROM_ACTIVE_CAND::Cannot unstake tokens for an active candidate. Call withdrawcand first."); - - check(reg_candidate.custodian_end_time_stamp < time_point_sec(eosio::current_time_point()), "ERR::UNSTAKE_CANNOT_UNSTAKE_UNDER_TIME_LOCK::Cannot unstake tokens before they are unlocked from the time delay."); - - registered_candidates.modify(reg_candidate, cand, [&](candidate &c) { - c.locked_tokens = asset(0, contr_config::get_current_configs(_self, dac_id).lockupasset.quantity.symbol); - }); - } - //end Temp block -} - -void daccustodian::clearstake(name cand, asset new_value, name dac_id) { - require_auth(get_self()); - - validateUnstake(get_self(), cand, dac_id); - - candidates_table registered_candidates(get_self(), dac_id.value); - const auto ®_candidate = registered_candidates.get(cand.value, "ERR::UNSTAKE_CAND_NOT_REGISTERED::Candidate is not already registered."); - - registered_candidates.modify(reg_candidate, cand, [&](candidate &c) { - c.locked_tokens = new_value; - }); -} - -void daccustodian::resigncust(name cust) { - resigncuste(cust, get_self()); -} - -void daccustodian::resigncuste(name cust, name dac_id) { - require_auth(cust); - removeCustodian(cust, dac_id); -} - -void daccustodian::firecust(name cust) { - firecuste(cust, get_self()); -} - -void daccustodian::firecuste(name cust, name dac_id) { - auto dac = dacdir::dac_for_id(dac_id); - require_auth(dac.account_for_type(dacdir::AUTH)); - removeCustodian(cust, dac_id); -} - - -void daccustodian::setperm(name cand, name permission, name dac_id) { - require_auth(cand); - assertValidMember(cand, dac_id); - - bool perm_exists = permissionExists(cand, permission); - - check(perm_exists, "ERR::PERMISSION_NOT_EXIST::Permission does not exist"); - candidates_table registered_candidates(_self, dac_id.value); - - registered_candidates.get(cand.value, "ERR::UNSTAKE_CAND_NOT_REGISTERED::Candidate is not already registered."); - - candperms_table cand_perms(_self, dac_id.value); - auto existing = cand_perms.find(cand.value); - - if (existing == cand_perms.end()){ - cand_perms.emplace(cand, [&](candperm &c) { - c.cand = cand; - c.permission = permission; - }); - } - else if (permission == "active"_n){ - cand_perms.erase(existing); - } - else { - cand_perms.modify(existing, same_payer, [&](candperm &c) { - c.permission = permission; - }); - } -} - -// private methods for the above actions - -void daccustodian::validateUnstake(name code, name cand, name dac_id){ - // Will assert if adc_id not found - dacdir::dac_for_id(dac_id); - candidates_table registered_candidates(code, dac_id.value); - const auto ®_candidate = registered_candidates.get(cand.value, "ERR::UNSTAKE_CAND_NOT_REGISTERED::Candidate is not already registered."); - extended_asset lockup_asset = contr_config::get_current_configs(code, dac_id).lockupasset; - check(!reg_candidate.is_active, "ERR::UNSTAKE_CANNOT_UNSTAKE_FROM_ACTIVE_CAND::Cannot unstake tokens for an active candidate. Call withdrawcand first."); - - check(reg_candidate.custodian_end_time_stamp < time_point_sec(eosio::current_time_point()), "ERR::UNSTAKE_CANNOT_UNSTAKE_UNDER_TIME_LOCK::Cannot unstake tokens before they are unlocked from the time delay."); -} - -void daccustodian::removeCustodian(name cust, name dac_id) { - - custodians_table custodians(_self, dac_id.value); - auto elected = custodians.find(cust.value); - check(elected != custodians.end(), "ERR::REMOVECUSTODIAN_NOT_CURRENT_CUSTODIAN::The entered account name is not for a current custodian."); - - eosio::print("Remove custodian from the custodians table."); - custodians.erase(elected); - - //Temp block for migrations to new scope - if (dac_id == get_self()) { - custodians_table custodians(_self, NEW_SCOPE.value); - auto elected = custodians.find(cust.value); - custodians.erase(elected); - } - //end Temp block - - // Remove the candidate from being eligible for the next election period. - removeCandidate(cust, true, dac_id); - - // Allocate the next set of candidates to only fill the gap for the missing slot. - allocateCustodians(true, dac_id); - - // Update the auths to give control to the new set of custodians. - setCustodianAuths(dac_id); -} - -void daccustodian::removeCandidate(name cand, bool lockupStake, name dac_id) { - contr_state currentState = contr_state::get_current_state(_self, dac_id); - contr_config configs = contr_config::get_current_configs(_self, dac_id); - - currentState.number_active_candidates--; - currentState.save(_self, dac_id); - - candidates_table registered_candidates(_self, dac_id.value); - candperms_table cand_perms(_self, dac_id.value); - - const auto ®_candidate = registered_candidates.get(cand.value, "ERR::REMOVECANDIDATE_NOT_CURRENT_CANDIDATE::Candidate is not already registered."); - - // remove entry for candperms - auto perm = cand_perms.find(cand.value); - if (perm != cand_perms.end()){ - cand_perms.erase(perm); - } - - auto end_time_stamp = eosio::current_time_point() + time_point_sec(configs.lockup_release_time_delay); - - eosio::print("Remove from nominated candidate by setting them to inactive."); - // Set the is_active flag to false instead of deleting in order to retain votes if they return to he dac. - registered_candidates.modify(reg_candidate, same_payer, [&](candidate &c) { - c.is_active = 0; - if (lockupStake) { - eosio::print("Lockup stake for release delay."); - c.custodian_end_time_stamp = end_time_stamp; - } - }); - - //Temp block for migrations to new scope - if (dac_id == get_self()) { - contr_state currentState = contr_state::get_current_state(_self, NEW_SCOPE); - currentState.number_active_candidates--; - currentState.save(_self, get_self()); - - candidates_table registered_candidates(_self, NEW_SCOPE.value); - - const auto ®_candidate = registered_candidates.get(cand.value, "ERR::REMOVECANDIDATE_NOT_CURRENT_CANDIDATE::Candidate is not already registered."); - - // Set the is_active flag to false instead of deleting in order to retain votes if they return to he dac. - registered_candidates.modify(reg_candidate, same_payer, [&](candidate &c) { - c.is_active = 0; - if (lockupStake) { - c.custodian_end_time_stamp = end_time_stamp; - } - }); - } - //end Temp block -} diff --git a/contracts/custodian/update_member_details.cpp b/contracts/custodian/update_member_details.cpp deleted file mode 100644 index 9cf1e82..0000000 --- a/contracts/custodian/update_member_details.cpp +++ /dev/null @@ -1,49 +0,0 @@ -#include "../_contract-shared-headers/migration_helpers.hpp" - -using namespace eosdac; - -void daccustodian::updatebio(name cand, string bio) { - updatebioe(cand, bio, get_self()); -} - -void daccustodian::updatebioe(name cand, string bio, name dac_id) { - - require_auth(cand); - assertValidMember(cand, dac_id); - candidates_table registered_candidates(_self, dac_id.value); - - const auto ®_candidate = registered_candidates.get(cand.value, "ERR::UPDATEBIO_NOT_CURRENT_REG_CANDIDATE::Candidate is not already registered."); - check(bio.size() < 256, "ERR::UPDATEBIO_BIO_SIZE_TOO_LONG::The bio should be less than 256 characters."); -} - -void daccustodian::updatereqpay(name cand, asset requestedpay) { - updatereqpae(cand, requestedpay, get_self()); -} - -void daccustodian::updatereqpae(name cand, asset requestedpay, name dac_id) { - - require_auth(cand); - assertValidMember(cand, dac_id); - - candidates_table registered_candidates(_self, dac_id.value); - - check(requestedpay.amount >= 0, "ERR::UPDATEREQPAY_UNDER_ZERO::Requested pay amount must not be negative."); - check(requestedpay <= contr_config::get_current_configs(_self, dac_id).requested_pay_max.quantity, "ERR::UPDATEREQPAY_EXCESS_MAX_PAY::Requested pay amount limit for a candidate was exceeded."); - const auto ®_candidate = registered_candidates.get(cand.value, "ERR::UPDATEREQPAY_NOT_CURRENT_REG_CANDIDATE::Candidate is not already registered."); - - registered_candidates.modify(reg_candidate, cand, [&](candidate &c) { - c.requestedpay = requestedpay; - }); - - //Temp block for migrations to new scope - if (dac_id == get_self()) { - candidates_table registered_candidates(_self, NEW_SCOPE.value); - - const auto ®_candidate = registered_candidates.get(cand.value, "ERR::UPDATEREQPAY_NOT_CURRENT_REG_CANDIDATE::Candidate is not already registered."); - - registered_candidates.modify(reg_candidate, cand, [&](candidate &c) { - c.requestedpay = requestedpay; - }); - } - //end Temp block -} \ No newline at end of file diff --git a/contracts/custodian/voting.cpp b/contracts/custodian/voting.cpp deleted file mode 100644 index 0f95c98..0000000 --- a/contracts/custodian/voting.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include "../_contract-shared-headers/migration_helpers.hpp" - -void daccustodian::votecust(name voter, vector newvotes) { - votecuste(voter, newvotes, get_self()); -} - -void daccustodian::votecuste(name voter, vector newvotes, name dac_id) { - - candidates_table registered_candidates(_self, dac_id.value); - contr_config configs = contr_config::get_current_configs(_self, dac_id); - - require_auth(voter); - assertValidMember(voter, dac_id); - - check(newvotes.size() <= configs.maxvotes, "ERR::VOTECUST_MAX_VOTES_EXCEEDED::Max number of allowed votes was exceeded."); - std::set dupSet{}; - for (name vote: newvotes) { - check(dupSet.insert(vote).second, "ERR::VOTECUST_DUPLICATE_VOTES::Added duplicate votes for the same candidate."); - auto candidate = registered_candidates.get(vote.value, "ERR::VOTECUST_CANDIDATE_NOT_FOUND::Candidate could not be found."); - check(candidate.is_active, "ERR::VOTECUST_VOTING_FOR_INACTIVE_CAND::Attempting to vote for an inactive candidate."); - } - - // Find a vote that has been cast by this voter previously. - votes_table votes_cast_by_members(_self, dac_id.value); - auto existingVote = votes_cast_by_members.find(voter.value); - if (existingVote != votes_cast_by_members.end()) { - modifyVoteWeights(voter, existingVote->candidates, newvotes, dac_id); - - if (newvotes.size() == 0) { - // Remove the vote if the array of candidates is empty - votes_cast_by_members.erase(existingVote); - eosio::print("\n Removing empty vote."); - } else { - votes_cast_by_members.modify(existingVote, voter, [&](vote &v) { - v.candidates = newvotes; - v.proxy = name(); - }); - } - } else { - modifyVoteWeights(voter, {}, newvotes, dac_id); - - votes_cast_by_members.emplace(voter, [&](vote &v) { - v.voter = voter; - v.candidates = newvotes; - }); - } - - // Start temp block - if (dac_id == get_self()) { - votes_table votes_cast_by_members(_self, NEW_SCOPE.value); - auto existingVote = votes_cast_by_members.find(voter.value); - if (existingVote != votes_cast_by_members.end()) { - modifyVoteWeights(voter, existingVote->candidates, newvotes, NEW_SCOPE); - - if (newvotes.size() == 0) { - // Remove the vote if the array of candidates is empty - votes_cast_by_members.erase(existingVote); - eosio::print("\n Removing empty vote."); - } else { - votes_cast_by_members.modify(existingVote, voter, [&](vote &v) { - v.candidates = newvotes; - v.proxy = name(); - }); - } - } else { - modifyVoteWeights(voter, {}, newvotes, NEW_SCOPE); - - votes_cast_by_members.emplace(voter, [&](vote &v) { - v.voter = voter; - v.candidates = newvotes; - }); - } - } - // end temp block -} - -//void daccustodian::voteproxy(name voter, name proxy) { -// -// require_auth(voter); -// assertValidMember(voter); -// -// string error_msg = "Member cannot proxy vote for themselves: " + voter.to_string(); -// check(voter != proxy, error_msg.c_str()); -// auto destproxy = votes_cast_by_members.find(proxy); -// if (destproxy != votes_cast_by_members.end()) { -// error_msg = "Proxy voters cannot vote for another proxy: " + voter.to_string(); -// check(destproxy->proxy == 0, error_msg.c_str()); -// } -// -// // Find a vote that has been cast by this voter previously. -// auto existingVote = votes_cast_by_members.find(voter); -// if (existingVote != votes_cast_by_members.end()) { -// -// votes_cast_by_members.modify(existingVote, _self, [&](vote &v) { -// v.candidates.clear(); -// v.proxy = proxy; -// }); -// } else { -// votes_cast_by_members.emplace(_self, [&](vote &v) { -// v.voter = voter; -// v.proxy = proxy; -// }); -// } -//} diff --git a/contracts/donation/CMakeLists.txt b/contracts/donation/CMakeLists.txt index 9144551..97563aa 100755 --- a/contracts/donation/CMakeLists.txt +++ b/contracts/donation/CMakeLists.txt @@ -1,8 +1,14 @@ add_contract(donation donation ${CMAKE_CURRENT_SOURCE_DIR}/src/donation.cpp) +find_path(LIBEOSDAC_INCLUDE_DIR NAMES libeosdac/libeosdac.hpp PATHS ${CMAKE_CURRENT_SOURCE_DIR}/../../external NO_CMAKE_FIND_ROOT_PATH) + +if((NOT LIBEOSDAC_INCLUDE_DIR) OR (NOT EXISTS ${LIBEOSDAC_INCLUDE_DIR})) + message("Unable to find libeosdac ${LIBEOSDAC_INCLUDE_DIR}") +endif() target_include_directories(donation PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${LIBEOSDAC_INCLUDE_DIR}) set_target_properties(donation PROPERTIES diff --git a/contracts/donation/donation.abi b/contracts/donation/donation.abi deleted file mode 100644 index 2d0f3d5..0000000 --- a/contracts/donation/donation.abi +++ /dev/null @@ -1,39 +0,0 @@ -{ - "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", - "version": "eosio::abi/1.1", - "types": [], - "structs": [ - { - "name": "receive", - "base": "", - "fields": [ - { - "name": "from", - "type": "name" - }, - { - "name": "to", - "type": "name" - }, - { - "name": "quantity", - "type": "asset" - }, - { - "name": "memo", - "type": "string" - } - ] - } - ], - "actions": [ - { - "name": "receive", - "type": "receive", - "ricardian_contract": "" - } - ], - "tables": [], - "ricardian_clauses": [], - "variants": [] -} \ No newline at end of file diff --git a/contracts/donation/donation.wasm b/contracts/donation/donation.wasm deleted file mode 100755 index 8018667..0000000 Binary files a/contracts/donation/donation.wasm and /dev/null differ diff --git a/contracts/donation/include/donation/common_utilities.hpp b/contracts/donation/include/donation/common_utilities.hpp deleted file mode 100644 index 944fbe3..0000000 --- a/contracts/donation/include/donation/common_utilities.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef COMMON_UTILITIES_H -#define COMMON_UTILITIES_H -#include - -namespace eosdac { - - using namespace eosio; - using namespace std; - - // Utility to combine ids to help with indexing. - uint128_t combine_ids(const uint8_t &boolvalue, const uint64_t &longValue) { - return (uint128_t{boolvalue} << 64) | longValue; - } - - uint128_t combine_ids(const uint16_t &value, const uint64_t &longValue) { - return (uint128_t{value} << 64) | longValue; - } - - static const uint128_t combine_ids(const uint64_t &x, const uint64_t &y) { - return (uint128_t{x} << 64) | y; - } - - static const __uint128_t raw_from_extended_symbol(const extended_symbol &symbol) { - return (uint128_t{symbol.get_contract().value} << 64) | symbol.get_symbol().code().raw(); - } -} - -#endif diff --git a/contracts/donation/include/donation/dacdirectory_shared.hpp b/contracts/donation/include/donation/dacdirectory_shared.hpp deleted file mode 100644 index 77aa0ed..0000000 --- a/contracts/donation/include/donation/dacdirectory_shared.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef DACDIRECTORY_SHARED_H -#define DACDIRECTORY_SHARED_H - -#include -#include -#include -#include "./common_utilities.hpp" - -namespace eosdac { - - namespace dacdir { - - enum account_type: uint8_t { - AUTH = 0, - TREASURY = 1, - CUSTODIAN = 2, - MSIGS = 3, - SERVICE = 5, - PROPOSALS = 6, - ESCROW = 7, - VOTE = 8, - EXTERNAL = 254, - OTHER = 255 - }; - - enum ref_type: uint8_t { - HOMEPAGE = 0, - LOGO_URL = 1, - DESCRIPTION = 2, - LOGO_NOTEXT_URL = 3, - BACKGROUND_URL = 4, - COLORS = 5, - CLIENT_EXTENSION = 6 - }; - - enum dac_state_type: uint8_t { - dac_state_typeINACTIVE = 0, - dac_state_typeACTIVE = 1 - }; - - struct [[eosio::table("dacs"), eosio::contract("dacdirectory")]] dac { - eosio::name owner; - eosio::name dac_id; - std::string title; - eosio::extended_symbol symbol; - std::map refs; - std::map accounts; - uint8_t dac_state; - - eosio::name account_for_type( uint8_t type) const { - eosio::print("\ngetting account for type: ", type,"\n"); - return accounts.at(type); - } - - uint64_t primary_key() const { return dac_id.value; } - uint64_t by_owner() const { return owner.value; } - uint128_t by_symbol() const { return eosdac::raw_from_extended_symbol(symbol); } - }; - - typedef eosio::multi_index< "dacs"_n, dac, - eosio::indexed_by<"byowner"_n, eosio::const_mem_fun>, - eosio::indexed_by<"bysymbol"_n, eosio::const_mem_fun> - > dac_table; - - - const dac dac_for_id(eosio::name id) { - dac_table dactable = dac_table("dacdirectory"_n, "dacdirectory"_n.value); - return dactable.get(id.value, "ERR::DAC_NOT_FOUND::DAC not found in directory"); - } - - const dac dac_for_symbol(eosio::extended_symbol sym) { - dac_table dactable = dac_table("dacdirectory"_n, "dacdirectory"_n.value); - auto index = dactable.get_index<"bysymbol"_n>(); - auto dac_idx = index.find(eosdac::raw_from_extended_symbol(sym)); - print("\ndac_for_symbol: ", sym, "\n"); - eosio::check(dac_idx != index.end() && dac_idx->symbol == sym, "ERR::DAC_NOT_FOUND_SYMBOL::DAC not found in directory for the given symbol"); - return *dac_idx; - } - } -} - -#endif diff --git a/contracts/donation/include/donation/donation.hpp b/contracts/donation/include/donation/donation.hpp index d7872e4..cd77a2d 100755 --- a/contracts/donation/include/donation/donation.hpp +++ b/contracts/donation/include/donation/donation.hpp @@ -2,7 +2,7 @@ #include #include -#include "dacdirectory_shared.hpp" +#include #include @@ -12,8 +12,6 @@ using namespace std; namespace eosdao { -// using namespace eosio; -// using namespace std; /** * @defgroup eosdaodonate donation * @ingroup eosiocontracts diff --git a/contracts/donation/src/donation.cpp b/contracts/donation/src/donation.cpp index 2062f1a..34e56d0 100755 --- a/contracts/donation/src/donation.cpp +++ b/contracts/donation/src/donation.cpp @@ -18,7 +18,7 @@ namespace eosdao { // Receiving donation, send inline action to issue tokens back to the sender const asset dao_quantity = asset(quantity.amount, symbol{symbol_code("DAO"), 4}); - auto dac = dacdir::dac_for_id("eosdao"_n); + auto dac = directory::dac_for_id("eosdao"_n); eosio::name token_contract = dac.symbol.get_contract(); string donate_memo = "Thank you for your donation to EOS DAO"; diff --git a/contracts/token/CMakeLists.txt b/contracts/token/CMakeLists.txt index 5855694..9e949da 100755 --- a/contracts/token/CMakeLists.txt +++ b/contracts/token/CMakeLists.txt @@ -1,8 +1,14 @@ add_contract(token token ${CMAKE_CURRENT_SOURCE_DIR}/src/token.cpp) +find_path(LIBEOSDAC_INCLUDE_DIR NAMES libeosdac/libeosdac.hpp PATHS ${CMAKE_CURRENT_SOURCE_DIR}/../../external NO_CMAKE_FIND_ROOT_PATH) + +if((NOT LIBEOSDAC_INCLUDE_DIR) OR (NOT EXISTS ${LIBEOSDAC_INCLUDE_DIR})) + message("Unable to find libeosdac ${LIBEOSDAC_INCLUDE_DIR}") +endif() target_include_directories(token PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${LIBEOSDAC_INCLUDE_DIR}) set_target_properties(token PROPERTIES diff --git a/contracts/token/include/token/common_utilities.hpp b/contracts/token/include/token/common_utilities.hpp deleted file mode 100644 index 944fbe3..0000000 --- a/contracts/token/include/token/common_utilities.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef COMMON_UTILITIES_H -#define COMMON_UTILITIES_H -#include - -namespace eosdac { - - using namespace eosio; - using namespace std; - - // Utility to combine ids to help with indexing. - uint128_t combine_ids(const uint8_t &boolvalue, const uint64_t &longValue) { - return (uint128_t{boolvalue} << 64) | longValue; - } - - uint128_t combine_ids(const uint16_t &value, const uint64_t &longValue) { - return (uint128_t{value} << 64) | longValue; - } - - static const uint128_t combine_ids(const uint64_t &x, const uint64_t &y) { - return (uint128_t{x} << 64) | y; - } - - static const __uint128_t raw_from_extended_symbol(const extended_symbol &symbol) { - return (uint128_t{symbol.get_contract().value} << 64) | symbol.get_symbol().code().raw(); - } -} - -#endif diff --git a/contracts/token/include/token/dacdirectory_shared.hpp b/contracts/token/include/token/dacdirectory_shared.hpp deleted file mode 100644 index 77aa0ed..0000000 --- a/contracts/token/include/token/dacdirectory_shared.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef DACDIRECTORY_SHARED_H -#define DACDIRECTORY_SHARED_H - -#include -#include -#include -#include "./common_utilities.hpp" - -namespace eosdac { - - namespace dacdir { - - enum account_type: uint8_t { - AUTH = 0, - TREASURY = 1, - CUSTODIAN = 2, - MSIGS = 3, - SERVICE = 5, - PROPOSALS = 6, - ESCROW = 7, - VOTE = 8, - EXTERNAL = 254, - OTHER = 255 - }; - - enum ref_type: uint8_t { - HOMEPAGE = 0, - LOGO_URL = 1, - DESCRIPTION = 2, - LOGO_NOTEXT_URL = 3, - BACKGROUND_URL = 4, - COLORS = 5, - CLIENT_EXTENSION = 6 - }; - - enum dac_state_type: uint8_t { - dac_state_typeINACTIVE = 0, - dac_state_typeACTIVE = 1 - }; - - struct [[eosio::table("dacs"), eosio::contract("dacdirectory")]] dac { - eosio::name owner; - eosio::name dac_id; - std::string title; - eosio::extended_symbol symbol; - std::map refs; - std::map accounts; - uint8_t dac_state; - - eosio::name account_for_type( uint8_t type) const { - eosio::print("\ngetting account for type: ", type,"\n"); - return accounts.at(type); - } - - uint64_t primary_key() const { return dac_id.value; } - uint64_t by_owner() const { return owner.value; } - uint128_t by_symbol() const { return eosdac::raw_from_extended_symbol(symbol); } - }; - - typedef eosio::multi_index< "dacs"_n, dac, - eosio::indexed_by<"byowner"_n, eosio::const_mem_fun>, - eosio::indexed_by<"bysymbol"_n, eosio::const_mem_fun> - > dac_table; - - - const dac dac_for_id(eosio::name id) { - dac_table dactable = dac_table("dacdirectory"_n, "dacdirectory"_n.value); - return dactable.get(id.value, "ERR::DAC_NOT_FOUND::DAC not found in directory"); - } - - const dac dac_for_symbol(eosio::extended_symbol sym) { - dac_table dactable = dac_table("dacdirectory"_n, "dacdirectory"_n.value); - auto index = dactable.get_index<"bysymbol"_n>(); - auto dac_idx = index.find(eosdac::raw_from_extended_symbol(sym)); - print("\ndac_for_symbol: ", sym, "\n"); - eosio::check(dac_idx != index.end() && dac_idx->symbol == sym, "ERR::DAC_NOT_FOUND_SYMBOL::DAC not found in directory for the given symbol"); - return *dac_idx; - } - } -} - -#endif diff --git a/contracts/token/include/token/token.hpp b/contracts/token/include/token/token.hpp index 95938f6..5a192df 100755 --- a/contracts/token/include/token/token.hpp +++ b/contracts/token/include/token/token.hpp @@ -7,192 +7,148 @@ #include #include -#include "dacdirectory_shared.hpp" +#include +#include +#include #include -using namespace eosio; -using namespace std; - namespace eosdao { - using std::string; - - /** - * @defgroup eosiotoken eosio.token - * @ingroup eosdaocontracts - * - * token.dao contract - * - * @details token.dao manages the DAO token. It is based on the standard eosio.token contract with modifications - * to allow for membership and vote decay. - * @{ - */ - class [[eosio::contract]] token : public contract { - public: - using contract::contract; - - /** - * Create action. - * - * @details Allows `issuer` account to create a token in supply of `maximum_supply`. - * @param issuer - the account that creates the token, - * @param maximum_supply - the maximum supply set for the token created. - * - * @pre Token symbol has to be valid, - * @pre Token symbol must not be already created, - * @pre maximum_supply has to be smaller than the maximum supply allowed by the system: 1^62 - 1. - * @pre Maximum supply must be positive; - * - * If validation is successful a new entry in statstable for token symbol scope gets created. - */ - [[eosio::action]] - void create( const name& issuer, - const asset& maximum_supply); - /** - * Issue action. - * - * @details This action issues to `to` account a `quantity` of tokens. - * - * @param to - the account to issue tokens to, it must be the same as the issuer, - * @param quntity - the amount of tokens to be issued, - * @memo - the memo string that accompanies the token issue transaction. - */ - [[eosio::action]] - void issue( const name& to, const asset& quantity, const string& memo ); - - /** - * Transfer action. - * - * @details Transfer is only used when issuing tokens, after that it is non-transferrable. - * - * @param from - the account to transfer from, - * @param to - the account to be transferred to, - * @param quantity - the quantity of tokens to be transferred, - * @param memo - the memo string to accompany the transaction. - */ - [[eosio::action]] - void transfer( const name& from, - const name& to, - const asset& quantity, - const string& memo ); - - /** - * Member register - * - * @details Allows a token holder to agree to the terms and conditions of being a member of the DAO - * - * @param sender - the account agreeing to the terms - * @param agreedterms - md5 hash of the terms document - * @param dac_id - the dac_id, used for scoping - */ - [[eosio::action]] - void memberrege(name sender, string agreedterms, name dac_id); - - - /** - * Register new terms - * - * @details Allows a token holder to agree to the terms and conditions of being a member of the DAO - * - * @param terms - URL of terms - * @param hash - md5 hash of the terms document - * @param dac_id - the dac_id, used for scoping - */ - [[eosio::action]] - void newmemtermse(string terms, string hash, name dac_id); - /** - * Get supply method. - * - * @details Gets the supply for token `sym_code`, created by `token_contract_account` account. - * - * @param token_contract_account - the account to get the supply for, - * @param sym_code - the symbol to get the supply for. - */ - static asset get_supply( const name& token_contract_account, const symbol_code& sym_code ) - { - stats statstable( token_contract_account, sym_code.raw() ); - const auto& st = statstable.get( sym_code.raw() ); + using namespace eosio; + using namespace std; + using namespace eosdac::token::tables; + using namespace eosdac::token::types; + + /** + * @defgroup eosiotoken eosio.token + * @ingroup eosdaocontracts + * + * token.dao contract + * + * @details token.dao manages the DAO token. It is based on the standard eosio.token contract with modifications + * to allow for membership and vote decay. + * @{ + */ + class [[eosio::contract]] token : public contract { + public: + using contract::contract; + + /** + * Create action. + * + * @details Allows `issuer` account to create a token in supply of `maximum_supply`. + * @param issuer - the account that creates the token, + * @param maximum_supply - the maximum supply set for the token created. + * + * @pre Token symbol has to be valid, + * @pre Token symbol must not be already created, + * @pre maximum_supply has to be smaller than the maximum supply allowed by the system: 1^62 - 1. + * @pre Maximum supply must be positive; + * + * If validation is successful a new entry in statstable for token symbol scope gets created. + */ + [[eosio::action]] + void create(const name &issuer, + const asset &maximum_supply); + + /** + * Issue action. + * + * @details This action issues to `to` account a `quantity` of tokens. + * + * @param to - the account to issue tokens to, it must be the same as the issuer, + * @param quntity - the amount of tokens to be issued, + * @memo - the memo string that accompanies the token issue transaction. + */ + [[eosio::action]] + void issue(const name &to, const asset &quantity, const string &memo); + + /** + * Transfer action. + * + * @details Transfer is only used when issuing tokens, after that it is non-transferrable. + * + * @param from - the account to transfer from, + * @param to - the account to be transferred to, + * @param quantity - the quantity of tokens to be transferred, + * @param memo - the memo string to accompany the transaction. + */ + [[eosio::action]] + void transfer(const name &from, + const name &to, + const asset &quantity, + const string &memo); + + /** + * Member register + * + * @details Allows a token holder to agree to the terms and conditions of being a member of the DAO + * + * @param sender - the account agreeing to the terms + * @param agreedterms - md5 hash of the terms document + * @param dac_id - the dac_id, used for scoping + */ + [[eosio::action]] + void memberrege(name sender, string agreedterms, name dac_id); + + + /** + * Register new terms + * + * @details Allows a token holder to agree to the terms and conditions of being a member of the DAO + * + * @param terms - URL of terms + * @param hash - md5 hash of the terms document + * @param dac_id - the dac_id, used for scoping + */ + [[eosio::action]] + void newmemtermse(string terms, string hash, name dac_id); + + /** + * Get supply method. + * + * @details Gets the supply for token `sym_code`, created by `token_contract_account` account. + * + * @param token_contract_account - the account to get the supply for, + * @param sym_code - the symbol to get the supply for. + */ + static asset get_supply(const name &token_contract_account, const symbol_code &sym_code) { + stat_table statstable(token_contract_account, sym_code.raw()); + const auto &st = statstable.get(sym_code.raw()); return st.supply; - } - - /** - * Get balance method. - * - * @details Get the balance for a token `sym_code` created by `token_contract_account` account, - * for account `owner`. - * - * @param token_contract_account - the token creator account, - * @param owner - the account for which the token balance is returned, - * @param sym_code - the token for which the balance is returned. - */ - static asset get_balance( const name& token_contract_account, const name& owner, const symbol_code& sym_code ) - { - accounts accountstable( token_contract_account, owner.value ); - const auto& ac = accountstable.get( sym_code.raw() ); + } + + /** + * Get balance method. + * + * @details Get the balance for a token `sym_code` created by `token_contract_account` account, + * for account `owner`. + * + * @param token_contract_account - the token creator account, + * @param owner - the account for which the token balance is returned, + * @param sym_code - the token for which the balance is returned. + */ + static asset get_balance(const name &token_contract_account, const name &owner, const symbol_code &sym_code) { + account_table accountstable(token_contract_account, owner.value); + const auto &ac = accountstable.get(sym_code.raw()); return ac.balance; - } - - /* TODO : Use a common eosdac include */ - struct account_balance_delta { - eosio::name account; - eosio::asset balance_delta; - }; - - private: - - // Standard voting contract tables - struct [[eosio::table]] account { - asset balance; - - uint64_t primary_key()const { return balance.symbol.code().raw(); } - }; - - struct [[eosio::table]] currency_stats { - asset supply; - asset max_supply; - name issuer; - - uint64_t primary_key()const { return supply.symbol.code().raw(); } - }; - - - - struct [[eosio::table]] termsinfo { - string terms; - string hash; - uint64_t version; - - termsinfo() : terms(""), hash(""), version(0) {} - - termsinfo(string _terms, string _hash, uint64_t _version) - : terms(_terms), hash(_hash), version(_version) {} - - uint64_t primary_key() const { return version; } - uint64_t by_latest_version() const { return UINT64_MAX - version; } - - EOSLIB_SERIALIZE(termsinfo, (terms)(hash)(version)) - }; - - - struct [[eosio::table]] member { - name sender; - uint64_t agreedtermsversion; + } - uint64_t primary_key() const { return sender.value; } - }; + private: - typedef multi_index<"members"_n, member> regmembers; - typedef eosio::multi_index< "accounts"_n, account > accounts; - typedef eosio::multi_index< "stat"_n, currency_stats > stats; - typedef eosio::multi_index<"memberterms"_n, termsinfo, - indexed_by<"bylatestver"_n, const_mem_fun > - > memterms; + /* We use the inherited eosdac definitions, but for abigen we need this. + * The struct name is used as table name. This is not used otherwise */ + struct [[eosio::table]] members : member_type {}; + struct [[eosio::table]] memterms : termsinfo_type {}; + struct [[eosio::table]] stats : stat_type {}; + struct [[eosio::table]] accounts : account_type {}; + // Internal methods + void sub_balance(const name &owner, const asset &value); - // Internal methods - void sub_balance( const name& owner, const asset& value ); - void add_balance( const name& owner, const asset& value, const name& ram_payer ); - }; + void add_balance(const name &owner, const asset &value, const name &ram_payer); + }; } /// namespace eosdao +//struct eosdao::member = eosdac::token::types::member_type; diff --git a/contracts/token/src/token.cpp b/contracts/token/src/token.cpp index 1f900dc..0b748c4 100755 --- a/contracts/token/src/token.cpp +++ b/contracts/token/src/token.cpp @@ -2,6 +2,7 @@ using namespace eosio; using namespace eosdac; +using namespace eosdac::token; namespace eosdao { @@ -15,9 +16,9 @@ namespace eosdao { check( maximum_supply.is_valid(), "invalid supply"); check( maximum_supply.amount > 0, "max-supply must be positive"); - stats statstable( get_self(), sym.code().raw() ); + stat_table statstable( get_self(), sym.code().raw() ); auto existing = statstable.find( sym.code().raw() ); - check( existing == statstable.end(), "voting with symbol already exists" ); + check( existing == statstable.end(), "token with symbol already exists" ); statstable.emplace( get_self(), [&]( auto& s ) { s.supply.symbol = maximum_supply.symbol; @@ -37,7 +38,7 @@ namespace eosdao { check( sym.is_valid(), "invalid symbol name" ); check( memo.size() <= 256, "memo has more than 256 bytes" ); - stats statstable( get_self(), sym.code().raw() ); + stat_table statstable( get_self(), sym.code().raw() ); auto existing = statstable.find( sym.code().raw() ); check( existing != statstable.end(), "voting with symbol does not exist, create voting before issue" ); const auto& st = *existing; @@ -76,7 +77,7 @@ namespace eosdao { require_auth( from ); check( is_account( to ), "to account does not exist"); auto sym = quantity.symbol.code(); - stats statstable( get_self(), sym.raw() ); + stat_table statstable( get_self(), sym.raw() ); const auto& st = statstable.get( sym.raw() ); require_recipient( to ); @@ -89,12 +90,18 @@ namespace eosdao { sub_balance( from, quantity ); add_balance( to, quantity, from ); - vector deltas; - deltas.push_back(account_balance_delta{to, quantity}); + struct account_balance_delta { + name account; + asset balance_delta; + }; + vector deltas; + deltas.push_back(notify::types::account_balance_delta{to, quantity}); // send inline action to the custodian contract to update any existing votes - dacdir::dac dac = dacdir::dac_for_symbol(extended_symbol{quantity.symbol, get_self()}); - eosio::name vote_contract = dac.account_for_type(dacdir::VOTE); + directory::types::dac dac = directory::dac_for_symbol(extended_symbol{quantity.symbol, get_self()}); + eosio::name vote_contract = dac.account_for_type(directory::types::ROUTER); +// auto obsv_action = notify::balanceobsv_action(vote_contract, { { get_self(), "notify"_n } }); +// obsv_action.send(deltas, "eosdao"_n); eosio::action( eosio::permission_level{ get_self(), "notify"_n }, vote_contract, "balanceobsv"_n, @@ -108,22 +115,22 @@ namespace eosdao { // agreedterms is expected to be the member terms document hash require_auth(sender); - memterms memberterms(_self, dac_id.value); + tables::member_terms_table memberterms(_self, dac_id.value); check(memberterms.begin() != memberterms.end(), "ERR::MEMBERREG_NO_VALID_TERMS::No valid member terms found."); auto latest_member_terms = (--memberterms.end()); check(latest_member_terms->hash == agreedterms, "ERR::MEMBERREG_NOT_LATEST_TERMS::Agreed terms isn't the latest."); - regmembers registeredgmembers = regmembers(_self, dac_id.value); + member_table registeredgmembers = member_table(_self, dac_id.value); auto existingMember = registeredgmembers.find(sender.value); if (existingMember != registeredgmembers.end()) { - registeredgmembers.modify(existingMember, sender, [&](member &mem) { + registeredgmembers.modify(existingMember, sender, [&](types::member_type &mem) { mem.agreedtermsversion = latest_member_terms->version; }); } else { - registeredgmembers.emplace(sender, [&](member &mem) { + registeredgmembers.emplace(sender, [&](types::member_type &mem) { mem.sender = sender; mem.agreedtermsversion = latest_member_terms->version; }); @@ -132,8 +139,8 @@ namespace eosdao { void token::newmemtermse(string terms, string hash, name dac_id) { - dacdir::dac dac = dacdir::dac_for_id(dac_id); - eosio::name auth_account = dac.account_for_type(dacdir::AUTH); + directory::types::dac dac = directory::dac_for_id(dac_id); + eosio::name auth_account = dac.account_for_type(directory::types::AUTH); require_auth(auth_account); // sample IPFS: QmXjkFQjnD8i8ntmwehoAHBfJEApETx8ebScyVzAHqgjpD @@ -143,7 +150,7 @@ namespace eosdao { check(!hash.empty(), "ERR::NEWMEMTERMS_EMPTY_HASH::Member terms document hash cannot be empty."); check(hash.length() <= 32, "ERR::NEWMEMTERMS_HASH_TOO_LONG::Member terms document hash should be less than 32 characters long."); - memterms memberterms(_self, dac_id.value); + tables::member_terms_table memberterms(get_self(), dac_id.value); // guard against duplicate of latest if (memberterms.begin() != memberterms.end()) { @@ -154,7 +161,7 @@ namespace eosdao { uint64_t next_version = (memberterms.begin() == memberterms.end() ? 0 : (--memberterms.end())->version) + 1; - memberterms.emplace(auth_account, [&](termsinfo &termsinfo) { + memberterms.emplace(auth_account, [&](types::termsinfo_type &termsinfo) { termsinfo.terms = terms; termsinfo.hash = hash; termsinfo.version = next_version; @@ -164,7 +171,7 @@ namespace eosdao { // Private methods void token::sub_balance( const name& owner, const asset& value ) { - accounts from_acnts( get_self(), owner.value ); + account_table from_acnts( get_self(), owner.value ); const auto& from = from_acnts.get( value.symbol.code().raw(), "no balance object found" ); check( from.balance.amount >= value.amount, "overdrawn balance" ); @@ -176,7 +183,7 @@ namespace eosdao { void token::add_balance( const name& owner, const asset& value, const name& ram_payer ) { - accounts to_acnts( get_self(), owner.value ); + account_table to_acnts( get_self(), owner.value ); auto to = to_acnts.find( value.symbol.code().raw() ); if( to == to_acnts.end() ) { to_acnts.emplace( ram_payer, [&]( auto& a ){ diff --git a/contracts/token/token.abi b/contracts/token/token.abi deleted file mode 100644 index 5244d62..0000000 --- a/contracts/token/token.abi +++ /dev/null @@ -1,231 +0,0 @@ -{ - "____comment": "This file was generated with eosio-abigen. DO NOT EDIT ", - "version": "eosio::abi/1.1", - "types": [], - "structs": [ - { - "name": "account", - "base": "", - "fields": [ - { - "name": "balance", - "type": "asset" - } - ] - }, - { - "name": "create", - "base": "", - "fields": [ - { - "name": "issuer", - "type": "name" - }, - { - "name": "maximum_supply", - "type": "asset" - } - ] - }, - { - "name": "currency_stats", - "base": "", - "fields": [ - { - "name": "supply", - "type": "asset" - }, - { - "name": "max_supply", - "type": "asset" - }, - { - "name": "issuer", - "type": "name" - } - ] - }, - { - "name": "issue", - "base": "", - "fields": [ - { - "name": "to", - "type": "name" - }, - { - "name": "quantity", - "type": "asset" - }, - { - "name": "memo", - "type": "string" - } - ] - }, - { - "name": "member", - "base": "", - "fields": [ - { - "name": "sender", - "type": "name" - }, - { - "name": "agreedtermsversion", - "type": "uint64" - } - ] - }, - { - "name": "memberrege", - "base": "", - "fields": [ - { - "name": "sender", - "type": "name" - }, - { - "name": "agreedterms", - "type": "string" - }, - { - "name": "dac_id", - "type": "name" - } - ] - }, - { - "name": "state_item", - "base": "", - "fields": [ - { - "name": "genesis", - "type": "time_point_sec" - } - ] - }, - { - "name": "termsinfo", - "base": "", - "fields": [ - { - "name": "terms", - "type": "string" - }, - { - "name": "hash", - "type": "string" - }, - { - "name": "version", - "type": "uint64" - } - ] - }, - { - "name": "transfer", - "base": "", - "fields": [ - { - "name": "from", - "type": "name" - }, - { - "name": "to", - "type": "name" - }, - { - "name": "quantity", - "type": "asset" - }, - { - "name": "memo", - "type": "string" - } - ] - }, - { - "name": "vote_weight", - "base": "", - "fields": [ - { - "name": "voter", - "type": "name" - }, - { - "name": "weight", - "type": "uint64" - } - ] - } - ], - "actions": [ - { - "name": "create", - "type": "create", - "ricardian_contract": "" - }, - { - "name": "issue", - "type": "issue", - "ricardian_contract": "" - }, - { - "name": "memberrege", - "type": "memberrege", - "ricardian_contract": "" - }, - { - "name": "transfer", - "type": "transfer", - "ricardian_contract": "" - } - ], - "tables": [ - { - "name": "accounts", - "type": "account", - "index_type": "i64", - "key_names": [], - "key_types": [] - }, - { - "name": "members", - "type": "member", - "index_type": "i64", - "key_names": [], - "key_types": [] - }, - { - "name": "memberterms", - "type": "termsinfo", - "index_type": "i64", - "key_names": [], - "key_types": [] - }, - { - "name": "stat", - "type": "currency_stats", - "index_type": "i64", - "key_names": [], - "key_types": [] - }, - { - "name": "state", - "type": "state_item", - "index_type": "i64", - "key_names": [], - "key_types": [] - }, - { - "name": "weights", - "type": "vote_weight", - "index_type": "i64", - "key_names": [], - "key_types": [] - } - ], - "ricardian_clauses": [], - "variants": [] -} \ No newline at end of file diff --git a/contracts/token/token.wasm b/contracts/token/token.wasm deleted file mode 100755 index e0b8030..0000000 Binary files a/contracts/token/token.wasm and /dev/null differ diff --git a/contracts/voting/CMakeLists.txt b/contracts/voting/CMakeLists.txt index bea909a..57c1d3b 100755 --- a/contracts/voting/CMakeLists.txt +++ b/contracts/voting/CMakeLists.txt @@ -1,8 +1,15 @@ add_contract(voting voting ${CMAKE_CURRENT_SOURCE_DIR}/src/voting.cpp) +find_path(LIBEOSDAC_INCLUDE_DIR NAMES libeosdac/libeosdac.hpp PATHS ${CMAKE_CURRENT_SOURCE_DIR}/../../external NO_CMAKE_FIND_ROOT_PATH) +message(STATUS "libeosdac => ${LIBEOSDAC_INCLUDE_DIR}") + +if((NOT LIBEOSDAC_INCLUDE_DIR) OR (NOT EXISTS ${LIBEOSDAC_INCLUDE_DIR})) + message("Unable to find libeosdac ${LIBEOSDAC_INCLUDE_DIR} in voting contract. Please make sure you have run git submodule update --init)") +endif() target_include_directories(voting PUBLIC - ${CMAKE_CURRENT_SOURCE_DIR}/include) + ${CMAKE_CURRENT_SOURCE_DIR}/include + ${LIBEOSDAC_INCLUDE_DIR}) set_target_properties(voting PROPERTIES diff --git a/contracts/voting/include/voting/common_utilities.hpp b/contracts/voting/include/voting/common_utilities.hpp deleted file mode 100644 index 944fbe3..0000000 --- a/contracts/voting/include/voting/common_utilities.hpp +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef COMMON_UTILITIES_H -#define COMMON_UTILITIES_H -#include - -namespace eosdac { - - using namespace eosio; - using namespace std; - - // Utility to combine ids to help with indexing. - uint128_t combine_ids(const uint8_t &boolvalue, const uint64_t &longValue) { - return (uint128_t{boolvalue} << 64) | longValue; - } - - uint128_t combine_ids(const uint16_t &value, const uint64_t &longValue) { - return (uint128_t{value} << 64) | longValue; - } - - static const uint128_t combine_ids(const uint64_t &x, const uint64_t &y) { - return (uint128_t{x} << 64) | y; - } - - static const __uint128_t raw_from_extended_symbol(const extended_symbol &symbol) { - return (uint128_t{symbol.get_contract().value} << 64) | symbol.get_symbol().code().raw(); - } -} - -#endif diff --git a/contracts/voting/include/voting/dacdirectory_shared.hpp b/contracts/voting/include/voting/dacdirectory_shared.hpp deleted file mode 100644 index 77aa0ed..0000000 --- a/contracts/voting/include/voting/dacdirectory_shared.hpp +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef DACDIRECTORY_SHARED_H -#define DACDIRECTORY_SHARED_H - -#include -#include -#include -#include "./common_utilities.hpp" - -namespace eosdac { - - namespace dacdir { - - enum account_type: uint8_t { - AUTH = 0, - TREASURY = 1, - CUSTODIAN = 2, - MSIGS = 3, - SERVICE = 5, - PROPOSALS = 6, - ESCROW = 7, - VOTE = 8, - EXTERNAL = 254, - OTHER = 255 - }; - - enum ref_type: uint8_t { - HOMEPAGE = 0, - LOGO_URL = 1, - DESCRIPTION = 2, - LOGO_NOTEXT_URL = 3, - BACKGROUND_URL = 4, - COLORS = 5, - CLIENT_EXTENSION = 6 - }; - - enum dac_state_type: uint8_t { - dac_state_typeINACTIVE = 0, - dac_state_typeACTIVE = 1 - }; - - struct [[eosio::table("dacs"), eosio::contract("dacdirectory")]] dac { - eosio::name owner; - eosio::name dac_id; - std::string title; - eosio::extended_symbol symbol; - std::map refs; - std::map accounts; - uint8_t dac_state; - - eosio::name account_for_type( uint8_t type) const { - eosio::print("\ngetting account for type: ", type,"\n"); - return accounts.at(type); - } - - uint64_t primary_key() const { return dac_id.value; } - uint64_t by_owner() const { return owner.value; } - uint128_t by_symbol() const { return eosdac::raw_from_extended_symbol(symbol); } - }; - - typedef eosio::multi_index< "dacs"_n, dac, - eosio::indexed_by<"byowner"_n, eosio::const_mem_fun>, - eosio::indexed_by<"bysymbol"_n, eosio::const_mem_fun> - > dac_table; - - - const dac dac_for_id(eosio::name id) { - dac_table dactable = dac_table("dacdirectory"_n, "dacdirectory"_n.value); - return dactable.get(id.value, "ERR::DAC_NOT_FOUND::DAC not found in directory"); - } - - const dac dac_for_symbol(eosio::extended_symbol sym) { - dac_table dactable = dac_table("dacdirectory"_n, "dacdirectory"_n.value); - auto index = dactable.get_index<"bysymbol"_n>(); - auto dac_idx = index.find(eosdac::raw_from_extended_symbol(sym)); - print("\ndac_for_symbol: ", sym, "\n"); - eosio::check(dac_idx != index.end() && dac_idx->symbol == sym, "ERR::DAC_NOT_FOUND_SYMBOL::DAC not found in directory for the given symbol"); - return *dac_idx; - } - } -} - -#endif diff --git a/contracts/voting/include/voting/voting.hpp b/contracts/voting/include/voting/voting.hpp index 37adc64..470ce73 100755 --- a/contracts/voting/include/voting/voting.hpp +++ b/contracts/voting/include/voting/voting.hpp @@ -4,12 +4,21 @@ #include #include #include -#include "./dacdirectory_shared.hpp" + +#include +#include +#include +#include using namespace eosdac; +using namespace eosdac::token::tables; using namespace eosio; using namespace std; +using eosdac::notify::types::account_balance_delta; +using eosdac::notify::types::account_weight_delta; +using eosdac::custodian::types::vote_weight_type; + namespace eosdao { /** @@ -43,29 +52,14 @@ namespace eosdao { }; // Standard voting contract tables - struct [[eosio::table]] vote_weight { - name voter; - uint64_t weight; - - uint64_t primary_key()const { return voter.value; } - }; + struct [[eosio::table]] weights : vote_weight_type {}; - typedef eosio::multi_index< "weights"_n, vote_weight > weights; void update_vote_weight(name owner, asset new_tokens, name dac_id); uint64_t get_vote_weight(asset quantity, name dac_id); public: using contract::contract; - /* TODO : Use a common eosdac include */ - struct account_balance_delta { - eosio::name account; - eosio::asset balance_delta; - }; - struct account_weight_delta { - eosio::name account; - uint64_t weight_delta; - }; /** @@ -82,11 +76,31 @@ namespace eosdao { * If validation is successful the weight of each account will be updated to reflect the weight at the time * of the balance delta. */ + ACTION balanceobsv(vector account_balance_deltas, name dac_id); - ACTION balanceobsv(vector account_balance_deltas, name dac_id); - - ACTION resetweights(name dac_id); - + /** + * Assert unlock action + * + * @details Asserts if the DAC is still locked and has not crossed activation threshold + * + * @param dac_id - The dac_id as set in the dacdirectory contract + * + * If the DAC has crossed the activation threshold then this action will do nothing. + */ + ACTION assertunlock(name dac_id); + +#ifdef DEBUG + /** + * Reset weights action + * + * @details Development action to clear the weights + * + * @param dac_id - The dac_id as set in the dacdirectory contract + * + * If the DAC has crossed the activation threshold then this action will do nothing. + */ + ACTION resetweights(name dac_id); +#endif }; } /// namespace eosdao diff --git a/contracts/voting/ricardian/voting.contracts.md.in b/contracts/voting/ricardian/voting.contracts.md.in index e69de29..5eaac9c 100644 --- a/contracts/voting/ricardian/voting.contracts.md.in +++ b/contracts/voting/ricardian/voting.contracts.md.in @@ -0,0 +1,21 @@ +

balanceobsv

+ +--- +spec_version: "0.2.0" +title: Internal Use +summary: 'External notification of balance change' +icon: @ICON_BASE_URL@/@TRANSFER_ICON_URI@ +--- + +Internal use only DO NOT USE + +

resetweights

+ +--- +spec_version: "0.2.0" +title: Admin Use +summary: 'Clears weights table' +icon: @ICON_BASE_URL@/@TRANSFER_ICON_URI@ +--- + +Admin use only diff --git a/contracts/voting/src/voting.cpp b/contracts/voting/src/voting.cpp index 8015228..f8fcf12 100755 --- a/contracts/voting/src/voting.cpp +++ b/contracts/voting/src/voting.cpp @@ -1,11 +1,13 @@ #include using namespace eosio; +using namespace eosdac; +using eosdac::custodian::tables::weight_table; namespace eosdao { void voting::balanceobsv(vector account_balance_deltas, name dac_id) { - auto dac = dacdir::dac_for_id(dac_id); + auto dac = directory::dac_for_id(dac_id); auto token_contract = dac.symbol.get_contract(); require_auth(token_contract); auto dac_symbol = dac.symbol.get_symbol(); @@ -18,7 +20,23 @@ namespace eosdao { } } + void voting::assertunlock(name dac_id) { + auto dac = directory::dac_for_id(dac_id); + stat_table statsTable = stat_table( + dac.symbol.get_contract(), + dac.symbol.get_symbol().code().raw() + ); + auto tokenStats = statsTable.begin(); + check(tokenStats != statsTable.end(), "ERR::STATS_NOT_FOUND::Stats table not found"); + + uint64_t token_current_supply = tokenStats->supply.amount; + + check(token_current_supply > 100'000'0000, "ERR::NEWPERIOD_VOTER_ENGAGEMENT_LOW_ACTIVATE::Voter engagement is insufficient to activate the DAC."); + } + +#ifdef DEBUG void voting::resetweights(name dac_id) { + require_auth(get_self()); weights voter_weights( get_self(), dac_id.value ); auto w = voter_weights.begin(); @@ -26,12 +44,13 @@ namespace eosdao { w = voter_weights.erase(w); } } +#endif void voting::update_vote_weight(name owner, asset new_tokens, name dac_id){ uint64_t vote_weight = get_vote_weight(new_tokens, dac_id); // update vote weights table - weights voter_weights( get_self(), dac_id.value ); + weight_table voter_weights( get_self(), dac_id.value ); auto existing = voter_weights.find( owner.value ); uint64_t old_weight = 0; if( existing == voter_weights.end() ) { @@ -50,18 +69,18 @@ namespace eosdao { } vector account_weights; - account_weights.push_back(account_weight_delta{owner, vote_weight}); - - auto dac = dacdir::dac_for_id(dac_id); - eosio::name custodian_contract = dac.account_for_type(dacdir::CUSTODIAN); + account_weights.push_back(account_weight_delta{owner, static_cast( vote_weight )}); -// eosio::action( -// eosio::permission_level{ get_self(), "notify"_n }, -// custodian_contract, "weightobsv"_n, -// make_tuple(account_weights, dac.dac_id) -// ).send(); + auto dac = directory::dac_for_id(dac_id); + eosio::name custodian_contract = dac.account_for_type(directory::types::CUSTODIAN); print("notifying weight change to ", custodian_contract, "::weightobsv"); + eosio::action( + eosio::permission_level{ get_self(), "notify"_n }, + custodian_contract, "weightobsv"_n, + make_tuple(account_weights, dac.dac_id) + ).send(); + } diff --git a/custodian_config.json b/custodian_config.json deleted file mode 100644 index 9dc72b4..0000000 --- a/custodian_config.json +++ /dev/null @@ -1,21 +0,0 @@ -[{ - "lockupasset": { - "quantity": "0.0000 DAC", - "contract": "oadtestingtk" - }, - "maxvotes": 2, - "numelected": 5, - "periodlength": 300, - "should_pay_via_service_provider": 1, - "initial_vote_quorum_percent": 0, - "vote_quorum_percent": 3, - "auth_threshold_high": 4, - "auth_threshold_mid": 3, - "auth_threshold_low": 3, - "lockup_release_time_delay": 600, - "requested_pay_max": { - "quantity": "5.0000 EOS", - "contract": "eosio.token" - } -} -, "eosdao"] diff --git a/docker/run_compile.sh b/docker/run_compile.sh index 06d299b..4bc2b7d 100755 --- a/docker/run_compile.sh +++ b/docker/run_compile.sh @@ -5,7 +5,9 @@ GREEN='\033[0;32m' NC='\033[0m' cd /src -./build.sh -y +./scripts/build.sh -y printf "\t${GREEN}=========== Running tests ===========${NC}\n\n" ./build/tests/unit_test +# For debugging +#./build/tests/unit_test -- --verbose diff --git a/external/libeosdac b/external/libeosdac new file mode 160000 index 0000000..116b56e --- /dev/null +++ b/external/libeosdac @@ -0,0 +1 @@ +Subproject commit 116b56eeb4248c8b3ab85ef6517146e69376adc6 diff --git a/memberterms.json b/memberterms.json deleted file mode 100644 index 28df873..0000000 --- a/memberterms.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - "https://raw.githubusercontent.com/eosdac/eosdac-constitution/master/boilerplate_constitution.md", - "1df37bdb72c0be963ef2bdfe9b7ef10b", - "eosdao" -] diff --git a/scripts/.environment b/scripts/.environment deleted file mode 100755 index 5f5b773..0000000 --- a/scripts/.environment +++ /dev/null @@ -1,12 +0,0 @@ -export SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -export REPO_ROOT="${SCRIPT_DIR}/.." -export TEST_DIR="${REPO_ROOT}/tests" - -export EOSIO_MIN_VERSION_MAJOR=$(cat $TEST_DIR/CMakeLists.txt | grep -E "^[[:blank:]]*set[[:blank:]]*\([[:blank:]]*EOSIO_VERSION_MIN" | tail -1 | sed 's/.*EOSIO_VERSION_MIN //g' | sed 's/ //g' | sed 's/"//g' | cut -d\) -f1 | cut -f1 -d '.') -export EOSIO_MIN_VERSION_MINOR=$(cat $TEST_DIR/CMakeLists.txt | grep -E "^[[:blank:]]*set[[:blank:]]*\([[:blank:]]*EOSIO_VERSION_MIN" | tail -1 | sed 's/.*EOSIO_VERSION_MIN //g' | sed 's/ //g' | sed 's/"//g' | cut -d\) -f1 | cut -f2 -d '.') -export EOSIO_SOFT_MAX_MAJOR=$(cat $TEST_DIR/CMakeLists.txt | grep -E "^[[:blank:]]*set[[:blank:]]*\([[:blank:]]*EOSIO_VERSION_SOFT_MAX" | tail -1 | sed 's/.*EOSIO_VERSION_SOFT_MAX //g' | sed 's/ //g' | sed 's/"//g' | cut -d\) -f1 | cut -f1 -d '.') -export EOSIO_SOFT_MAX_MINOR=$(cat $TEST_DIR/CMakeLists.txt | grep -E "^[[:blank:]]*set[[:blank:]]*\([[:blank:]]*EOSIO_VERSION_SOFT_MAX" | tail -1 | sed 's/.*EOSIO_VERSION_SOFT_MAX //g' | sed 's/ //g' | sed 's/"//g' | cut -d\) -f1 | cut -f2 -d '.') -export EOSIO_MAX_VERSION=$(cat $TEST_DIR/CMakeLists.txt | grep -E "^[[:blank:]]*set[[:blank:]]*\([[:blank:]]*EOSIO_VERSION_HARD_MAX" | tail -1 | sed 's/.*EOSIO_VERSION_HARD_MAX //g' | sed 's/ //g' | sed 's/"//g' | cut -d\) -f1) -export EOSIO_MAX_VERSION="${EOSIO_MAX_VERSION:-$(echo $EOSIO_SOFT_MAX_MAJOR.999)}" -export EOSIO_MAX_VERSION_MAJOR=$(echo $EOSIO_MAX_VERSION | cut -f1 -d '.') -export EOSIO_MAX_VERSION_MINOR=$(echo $EOSIO_MAX_VERSION | cut -f2 -d '.') \ No newline at end of file diff --git a/build.sh b/scripts/build.sh similarity index 66% rename from build.sh rename to scripts/build.sh index 12e0477..bf13cb4 100755 --- a/build.sh +++ b/scripts/build.sh @@ -1,6 +1,12 @@ #!/usr/bin/env bash set -eo pipefail +NONINTERACTIVE=true +PROCEED=true + +# Fix for broken eosio include file +cp tests/symbol.hpp /usr/local/eosio/include/eosio/chain/ + printf "\t=========== Building eosdao.contracts ===========\n\n" RED='\033[0;31m' NC='\033[0m' diff --git a/scripts/helper.sh b/scripts/helper.sh deleted file mode 100755 index 1ced68e..0000000 --- a/scripts/helper.sh +++ /dev/null @@ -1,135 +0,0 @@ -# Ensures passed in version values are supported. -function check-version-numbers() { - CHECK_VERSION_MAJOR=$1 - CHECK_VERSION_MINOR=$2 - - if [[ $CHECK_VERSION_MAJOR -lt $EOSIO_MIN_VERSION_MAJOR ]]; then - exit 1 - fi - if [[ $CHECK_VERSION_MAJOR -gt $EOSIO_MAX_VERSION_MAJOR ]]; then - exit 1 - fi - if [[ $CHECK_VERSION_MAJOR -eq $EOSIO_MIN_VERSION_MAJOR ]]; then - if [[ $CHECK_VERSION_MINOR -lt $EOSIO_MIN_VERSION_MINOR ]]; then - exit 1 - fi - fi - if [[ $CHECK_VERSION_MAJOR -eq $EOSIO_MAX_VERSION_MAJOR ]]; then - if [[ $CHECK_VERSION_MINOR -gt $EOSIO_MAX_VERSION_MINOR ]]; then - exit 1 - fi - fi - exit 0 -} - - -# Handles choosing which EOSIO directory to select when the default location is used. -function default-eosio-directories() { - REGEX='^[0-9]+([.][0-9]+)?$' - ALL_EOSIO_SUBDIRS=($(ls ${HOME}/eosio | sort -V)) - for ITEM in "${ALL_EOSIO_SUBDIRS[@]}"; do - if [[ "$ITEM" =~ $REGEX ]]; then - DIR_MAJOR=$(echo $ITEM | cut -f1 -d '.') - DIR_MINOR=$(echo $ITEM | cut -f2 -d '.') - if $(check-version-numbers $DIR_MAJOR $DIR_MINOR); then - PROMPT_EOSIO_DIRS+=($ITEM) - fi - fi - done - for ITEM in "${PROMPT_EOSIO_DIRS[@]}"; do - if [[ "$ITEM" =~ $REGEX ]]; then - EOSIO_VERSION=$ITEM - fi - done -} - - -# Prompts or sets default behavior for choosing EOSIO directory. -function eosio-directory-prompt() { - if [[ -z $EOSIO_DIR_PROMPT ]]; then - default-eosio-directories; - echo 'No EOSIO location was specified.' - while true; do - if [[ $NONINTERACTIVE != true ]]; then - if [[ -z $EOSIO_VERSION ]]; then - echo "No default EOSIO installations detected..." - PROCEED=n - else - printf "Is EOSIO installed in the default location: $HOME/eosio/$EOSIO_VERSION (y/n)" && read -p " " PROCEED - fi - fi - echo "" - case $PROCEED in - "" ) - echo "Is EOSIO installed in the default location?";; - 0 | true | [Yy]* ) - break;; - 1 | false | [Nn]* ) - if [[ $PROMPT_EOSIO_DIRS ]]; then - echo "Found these compatible EOSIO versions in the default location." - printf "$HOME/eosio/%s\n" "${PROMPT_EOSIO_DIRS[@]}" - fi - printf "Enter the installation location of EOSIO:" && read -e -p " " EOSIO_DIR_PROMPT; - break;; - * ) - echo "Please type 'y' for yes or 'n' for no.";; - esac - done - fi - export EOSIO_INSTALL_DIR="${EOSIO_DIR_PROMPT:-${HOME}/eosio/${EOSIO_VERSION}}" -} - - -# Prompts or default behavior for choosing EOSIO.CDT directory. -function cdt-directory-prompt() { - if [[ -z $CDT_DIR_PROMPT ]]; then - echo 'No EOSIO.CDT location was specified.' - while true; do - if [[ $NONINTERACTIVE != true ]]; then - printf "Is EOSIO.CDT installed in the default location? /usr/local/eosio.cdt (y/n)" && read -p " " PROCEED - fi - echo "" - case $PROCEED in - "" ) - echo "Is EOSIO.CDT installed in the default location?";; - 0 | true | [Yy]* ) - break;; - 1 | false | [Nn]* ) - printf "Enter the installation location of EOSIO.CDT:" && read -e -p " " CDT_DIR_PROMPT; - break;; - * ) - echo "Please type 'y' for yes or 'n' for no.";; - esac - done - fi - export CDT_INSTALL_DIR="${CDT_DIR_PROMPT:-/usr/local/eosio.cdt}" -} - - -# Ensures EOSIO is installed and compatible via version listed in tests/CMakeLists.txt. -function nodeos-version-check() { - INSTALLED_VERSION=$(echo $($EOSIO_INSTALL_DIR/bin/nodeos --version)) - INSTALLED_VERSION_MAJOR=$(echo $INSTALLED_VERSION | cut -f1 -d '.' | sed 's/v//g') - INSTALLED_VERSION_MINOR=$(echo $INSTALLED_VERSION | cut -f2 -d '.' | sed 's/v//g') - - if [[ -z $INSTALLED_VERSION_MAJOR || -z $INSTALLED_VERSION_MINOR ]]; then - echo "Could not determine EOSIO version. Exiting..." - exit 1; - fi - - if $(check-version-numbers $INSTALLED_VERSION_MAJOR $INSTALLED_VERSION_MINOR); then - if [[ $INSTALLED_VERSION_MAJOR -gt $EOSIO_SOFT_MAX_MAJOR ]]; then - echo "Detected EOSIO version is greater than recommended soft max: $EOSIO_SOFT_MAX_MAJOR.$EOSIO_SOFT_MAX_MINOR. Proceed with caution." - fi - if [[ $INSTALLED_VERSION_MAJOR -eq $EOSIO_SOFT_MAX_MAJOR && $INSTALLED_VERSION_MINOR -gt $EOSIO_SOFT_MAX_MINOR ]]; then - echo "Detected EOSIO version is greater than recommended soft max: $EOSIO_SOFT_MAX_MAJOR.$EOSIO_SOFT_MAX_MINOR. Proceed with caution." - fi - echo "Using EOSIO installation at: $EOSIO_INSTALL_DIR" - echo "Using EOSIO.CDT installation at: $CDT_INSTALL_DIR" - else - echo "Supported versions are: $EOSIO_MIN_VERSION_MAJOR.$EOSIO_MIN_VERSION_MINOR - $EOSIO_MAX_VERSION_MAJOR.$EOSIO_MAX_VERSION_MINOR" - echo "Invalid EOSIO installation. Exiting..." - exit 1; - fi - export CMAKE_FRAMEWORK_PATH="${EOSIO_INSTALL_DIR}:${CDT_INSTALL_DIR}:${CMAKE_PREFIX_PATH}" -} \ No newline at end of file diff --git a/scripts/run_docker_tests.sh b/scripts/run_docker_tests.sh new file mode 100755 index 0000000..16e0e7f --- /dev/null +++ b/scripts/run_docker_tests.sh @@ -0,0 +1,6 @@ +#!/usr/bin/env bash + +TAG=v1 + +docker build -t eosdao/contracts:$TAG . +docker run -t eosdao/contracts:$TAG diff --git a/scripts/run_tests.sh b/scripts/run_tests.sh new file mode 100755 index 0000000..07343b0 --- /dev/null +++ b/scripts/run_tests.sh @@ -0,0 +1,18 @@ +#!/usr/bin/env bash +set -eo pipefail + +printf "\t=========== Building eosdao.contracts ===========\n\n" +RED='\033[0;31m' +NC='\033[0m' + +# set to installed dir +export eosio_DIR=~/eosio/1.8 + +CPU_CORES=$(getconf _NPROCESSORS_ONLN) +mkdir -p build +pushd build &> /dev/null +cmake ../ +make -j $CPU_CORES +printf "\t=========== Running tests ===========\n\n" +./tests/unit_test -- --verbose +popd &> /dev/null diff --git a/tests/actions.hpp b/tests/actions.hpp new file mode 100644 index 0000000..215fa98 --- /dev/null +++ b/tests/actions.hpp @@ -0,0 +1,125 @@ + +void issue( const asset& amount, const name& manager = config::system_account_name ) { + base_tester::push_action( N(eosio.token), N(issue), manager, mutable_variant_object() + ("to", manager ) + ("quantity", amount ) + ("memo", "") + ); +} + +void issue_dao( const name& to, const asset& quantity, const name& manager = config::system_account_name ) { + base_tester::push_action( N(token.dao), N(issue), manager, mutable_variant_object() + ("to", to ) + ("quantity", quantity ) + ("memo", "") + ); +} + +void transfer( const name& from, const name& to, const asset& amount, const name& manager = config::system_account_name ) { + base_tester::push_action( N(eosio.token), N(transfer), manager, mutable_variant_object() + ("from", from) + ("to", to ) + ("quantity", amount) + ("memo", "") + ); +} + +void transfer_dao( const name& from, const name& to, const asset& amount, const name& manager = config::system_account_name ) { + base_tester::push_action( N(token.dao), N(transfer), manager, mutable_variant_object() + ("from", from) + ("to", to ) + ("quantity", amount) + ("memo", "") + ); +} + +void vote( const name& voter, const vector& votes, const name& dac_id, const name& manager = config::system_account_name ) { + base_tester::push_action( N(steward.dao), N(votecuste), manager, mutable_variant_object() + ("voter", voter) + ("newvotes", votes ) + ("dac_id", dac_id ) + ); +} + +void regdac( const name& owner, const name& dac_id, const extended_symbol& dac_symbol, const std::string& title, const name& manager = config::system_account_name ) { + + vector> refs; + vector> accounts; + + base_tester::push_action( N(dacdirectory), N(regdac), manager, mutable_variant_object() + ("owner", owner) + ("dac_id", dac_id ) + ("dac_symbol", dac_symbol ) + ("title", title ) + ("refs", refs ) + ("accounts", accounts ) + ); +} + +void regaccount( const name& dac_id, const name& account, const uint8_t type, const name& manager = config::system_account_name ) { + base_tester::push_action( N(dacdirectory), N(regaccount), manager, mutable_variant_object() + ("dac_id", dac_id ) + ("account", account ) + ("type", type ) + ); +} + +void configure_custodian( const contr_config newconfig, const name& dac_id, const name& manager = config::system_account_name ) { + + + base_tester::push_action( N(steward.dao), N(updateconfige), manager, mutable_variant_object() + ("newconfig", + mutable_variant_object() + ("lockupasset", newconfig.lockupasset) + ("maxvotes", newconfig.maxvotes) + ("numelected", newconfig.numelected) + ("periodlength", newconfig.periodlength) + ("should_pay_via_service_provider", newconfig.should_pay_via_service_provider) + ("initial_vote_quorum_percent", newconfig.initial_vote_quorum_percent) + ("vote_quorum_percent", newconfig.vote_quorum_percent) + ("auth_threshold_high", newconfig.auth_threshold_high) + ("auth_threshold_mid", newconfig.auth_threshold_mid) + ("auth_threshold_low", newconfig.auth_threshold_low) + ("lockup_release_time_delay", newconfig.lockup_release_time_delay) + ("requested_pay_max", newconfig.requested_pay_max) + + ) + ("dac_id", dac_id ) + ); +} + +void updatememterms(const string& terms, const string& hash, const name& dac_id, const name& manager = config::system_account_name){ + base_tester::push_action( N(token.dao), N(newmemtermse), manager, mutable_variant_object() + ("terms", terms ) + ("hash", hash ) + ("dac_id", dac_id ) + ); +} + +void memberreg(const name& sender, const string& agreedterms, const name& dac_id, const name& manager = config::system_account_name){ + base_tester::push_action( N(token.dao), N(memberrege), manager, mutable_variant_object() + ("sender", sender ) + ("agreedterms", agreedterms ) + ("dac_id", dac_id ) + ); +} + +void nominatecand(const name& cand, const asset& requestedpay, const name& dac_id, const name& manager = config::system_account_name){ + base_tester::push_action( N(steward.dao), N(nominatecane), manager, mutable_variant_object() + ("cand", cand ) + ("requestedpay", requestedpay ) + ("dac_id", dac_id ) + ); +} + +action_result newperiod(const string& message, const name& dac_id, const name& manager = config::system_account_name){ + action act; + act.account = N(steward.dao); + act.name = N(newperiode); + + string action_type_name = custodian_abi_ser.get_action_type(act.name); + variant_object data = mutable_variant_object()("message", message)("dac_id", dac_id); + act.data = custodian_abi_ser.variant_to_binary(action_type_name , data, abi_serializer_max_time ); + + return base_tester::push_action(std::move(act), uint64_t(manager)); +} diff --git a/tests/contracts.hpp.in b/tests/contracts.hpp.in index 2f5d263..07b9ca6 100644 --- a/tests/contracts.hpp.in +++ b/tests/contracts.hpp.in @@ -12,7 +12,6 @@ namespace eosdao { namespace testing { static std::vector token_abi() { return read_abi("${CMAKE_BINARY_DIR}/../contracts/token/token.abi"); } static std::vector voting_wasm() { return read_wasm("${CMAKE_BINARY_DIR}/../contracts/voting/voting.wasm"); } static std::vector voting_abi() { return read_abi("${CMAKE_BINARY_DIR}/../contracts/voting/voting.abi"); } - static std::vector xvoting_abi() { return read_abi("${CMAKE_BINARY_DIR}/../contracts/voting/voting.abi"); } struct util { static std::vector system_token_wasm() { return read_wasm("${CMAKE_SOURCE_DIR}/test_contracts/eosio.token.wasm"); } diff --git a/tests/eosdao_tester.hpp b/tests/eosdao_tester.hpp index ff65bec..58a93b3 100644 --- a/tests/eosdao_tester.hpp +++ b/tests/eosdao_tester.hpp @@ -28,407 +28,10 @@ namespace eosdao { class eosdao_tester : public TESTER { public: +#include "setup.hpp" +#include "utils.hpp" +#include "actions.hpp" - void basic_setup() { - printf("Running basic_setup...\n"); - produce_blocks( 2 ); - - create_accounts({ N(eosio.token), N(eosio.ram), N(eosio.ramfee), N(eosio.stake), - N(eosio.bpay), N(eosio.vpay), N(eosio.saving), N(eosio.names), N(eosio.rex) }); - - create_accounts({ N(auth.dao), N(token.dao), N(donation.dao), N(voting.dao), N(steward.dao), N(dacdirectory) }); - - produce_blocks( 100 ); - set_code( N(eosio.token), testing::contracts::util::system_token_wasm()); - set_abi( N(eosio.token), testing::contracts::util::system_token_abi().data() ); - { - const auto& accnt = control->db().get( N(eosio.token) ); - abi_def abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); - system_token_abi_ser.set_abi(abi, abi_serializer_max_time); - } - - set_code( N(eosio), testing::contracts::util::system_wasm()); - set_abi( N(eosio), testing::contracts::util::system_abi().data() ); - - - { - const auto& accnt = control->db().get( N(eosio) ); - abi_def abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); - system_abi_ser.set_abi(abi, abi_serializer_max_time); - } - - // create and issue core voting - FC_ASSERT( core_symbol.decimals() == 4, "create_core_token assumes core voting has 4 digits of precision" ); - create_currency( N(eosio.token), config::system_account_name, asset(100000000000000, core_symbol) ); - issue( asset(10000000000000, core_symbol) ); - BOOST_REQUIRE_EQUAL( asset(10000000000000, core_symbol), get_system_balance( "eosio", core_symbol ) ); - - - base_tester::push_action(config::system_account_name, N(init), - config::system_account_name, mutable_variant_object() - ("version", 0) - ("core", CORE_SYM_STR) - ); - } - - void deploy_contracts() { - printf("Running deploy_contracts...\n"); - - set_code( N(token.dao), testing::contracts::token_wasm() ); - set_abi( N(token.dao), testing::contracts::token_abi().data() ); - { - const auto& accnt = control->db().get( N(token.dao) ); - abi_def abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); - token_abi_ser.set_abi(abi, abi_serializer_max_time); - } - - - set_code( N(donation.dao), testing::contracts::donation_wasm() ); - set_abi( N(donation.dao), testing::contracts::donation_abi().data() ); - { - const auto& accnt = control->db().get( N(donation.dao) ); - abi_def abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); - donation_abi_ser.set_abi(abi, abi_serializer_max_time); - } - - - set_code( N(voting.dao), testing::contracts::voting_wasm() ); - set_abi( N(voting.dao), testing::contracts::voting_abi().data() ); - { - const auto& accnt = control->db().get( N(voting.dao) ); - abi_def abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); - voting_abi_ser.set_abi(abi, abi_serializer_max_time); - } - - - set_code( N(steward.dao), testing::contracts::eosdac::custodian_wasm() ); - set_abi( N(steward.dao), testing::contracts::eosdac::custodian_abi().data() ); - { - const auto& accnt = control->db().get( N(steward.dao) ); - abi_def abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); - custodian_abi_ser.set_abi(abi, abi_serializer_max_time); - } - - - set_code( N(dacdirectory), testing::contracts::eosdac::directory_wasm() ); - set_abi( N(dacdirectory), testing::contracts::eosdac::directory_abi().data() ); - - { - const auto& accnt = control->db().get( N(dacdirectory) ); - abi_def abi; - BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); - directory_abi_ser.set_abi(abi, abi_serializer_max_time); - } - } - - void set_permissions(){ - printf("Running set_permissions...\n"); - // Add code rights for the donation contract so it can call issue - set_code_perms(N(token.dao), N(donation.dao), N(issue)); - link_perms(N(token.dao), N(token.dao), N(issue), N(issue)); - // Add code rights to the token contract so that it can transfer from itself - set_code_perms(N(token.dao), N(token.dao), N(xfer)); - link_perms(N(token.dao), N(token.dao), N(transfer), N(xfer)); - // Notify from voting contract to custodian - set_code_perms(N(token.dao), N(token.dao), N(notify)); - link_perms(N(token.dao), N(voting.dao), N(balanceobsv), N(notify)); -// link_perms(N(token.dao), N(voting.dao), N(weightobsv), N(notify)); - - // so the voting contract can notify the custodian contract - set_code_perms(N(voting.dao), N(voting.dao), N(notify)); - link_perms(N(voting.dao), N(steward.dao), N(weightobsv), N(notify)); - - } - - void create_dao_token(){ - printf("Running create_dao_token...\n"); - FC_ASSERT( DAO_SYM_PRECISION == 4, "create_dao_token assumes DAO voting has 4 digits of precision" ); - create_currency( N(token.dao), N(token.dao), dao_sym::from_string("1000000000000.0000") ); - } - - void setup_directory(){ - printf("Running setup_directory...\n"); - -// AUTH = 0, -// TREASURY = 1, -// CUSTODIAN = 2, -// MSIGS = 3, -// SERVICE = 5, -// PROPOSALS = 6, -// ESCROW = 7, - - extended_symbol es = extended_symbol{symbol(4, "DAO"), N(token.dao)}; - regdac( N(auth.dao), N(eosdao), es, "EOS DAO", N(auth.dao) ); - regaccount( N(eosdao), N(auth.dao), 0, N(auth.dao) ); - regaccount( N(eosdao), N(steward.dao), 2, N(auth.dao) ); - regaccount( N(eosdao), N(voting.dao), 8, N(auth.dao) ); - } - - void remaining_setup() { - printf("Running remaining_setup...\n"); - produce_blocks(); - - create_account_with_resources( N(donor1.dao), config::system_account_name, core_sym::from_string("1000.0000"), false ); - create_account_with_resources( N(donor2.dao), config::system_account_name, core_sym::from_string("1000.0000"), false ); - create_account_with_resources( N(donor3.dao), config::system_account_name, core_sym::from_string("1000.0000"), false ); - create_account_with_resources( N(donor4.dao), config::system_account_name, core_sym::from_string("1000.0000"), false ); - create_account_with_resources( N(donor5.dao), config::system_account_name, core_sym::from_string("1000.0000"), false ); - -// create_accounts({ N(donor1.dao), N(donor2.dao), N(donor3.dao), N(donor4.dao), N(donor5.dao) }); - - transfer(config::system_account_name, N(donor1.dao), core_sym::from_string("1000.0000")); - transfer(config::system_account_name, N(donor2.dao), core_sym::from_string("10000.0000")); - transfer(config::system_account_name, N(donor3.dao), core_sym::from_string("100000.0000")); - transfer(config::system_account_name, N(donor4.dao), core_sym::from_string("1000000.0000")); - transfer(config::system_account_name, N(donor5.dao), core_sym::from_string("10000000.0000")); - - BOOST_REQUIRE_EQUAL( asset(10000000, core_symbol), get_system_balance( "donor1.dao", core_symbol ) ); - BOOST_REQUIRE_EQUAL( asset(100000000, core_symbol), get_system_balance( "donor2.dao", core_symbol ) ); - BOOST_REQUIRE_EQUAL( asset(1000000000, core_symbol), get_system_balance( "donor3.dao", core_symbol ) ); - BOOST_REQUIRE_EQUAL( asset(10000000000, core_symbol), get_system_balance( "donor4.dao", core_symbol ) ); - BOOST_REQUIRE_EQUAL( asset(100000000000, core_symbol), get_system_balance( "donor5.dao", core_symbol ) ); - } - - enum class setup_level { - none, - minimal, - deploy_contracts, - set_permissions, - dao_token, - directory, - full - }; - - eosdao_tester( setup_level l = setup_level::full ) { - if( l == setup_level::none ) return; - - basic_setup(); - if( l == setup_level::minimal ) return; - produce_blocks(); - - deploy_contracts(); - if( l == setup_level::deploy_contracts ) return; - produce_blocks(); - - set_permissions(); - if( l == setup_level::set_permissions ) return; - produce_blocks(); - - create_dao_token(); - if( l == setup_level::dao_token ) return; - produce_blocks(); - - setup_directory(); - if( l == setup_level::directory ) return; - produce_blocks(); - - remaining_setup(); - } - - template - eosdao_tester(Lambda setup) { - setup(*this); - - basic_setup(); - deploy_contracts(); - set_permissions(); - create_dao_token(); - setup_directory(); - remaining_setup(); - } - - - uint32_t last_block_time() const { - return time_point_sec( control->head_block_time() ).sec_since_epoch(); - } - - uint64_t get_voter_weight( const name& voter ){ - vector data = get_row_by_account( N(token.dac), N(token.dac), N(weights), voter.value ); - if (data.empty()){ - printf("DATA empty in get_voter_weight\n"); - } - return data.empty() ? 0 : token_abi_ser.binary_to_variant( "vote_weight", data, abi_serializer_max_time )["weight"].as(); - } - - asset get_system_balance( const account_name& act, symbol balance_symbol = symbol{CORE_SYM} ) { - vector data = get_row_by_account( N(eosio.token), act, N(accounts), balance_symbol.to_symbol_code().value ); - return data.empty() ? asset(0, balance_symbol) : system_token_abi_ser.binary_to_variant("account", data, abi_serializer_max_time)["balance"].as(); - } - - asset get_dao_balance( const account_name& act, symbol balance_symbol = symbol{DAO_SYM} ) { - vector data = get_row_by_account( N(token.dao), act, N(accounts), balance_symbol.to_symbol_code().value ); - return data.empty() ? asset(0, balance_symbol) : token_abi_ser.binary_to_variant("account", data, abi_serializer_max_time)["balance"].as(); - } - - - transaction_trace_ptr create_account_with_resources( account_name a, account_name creator, asset ramfunds, bool multisig, - asset net = core_sym::from_string("10.0000"), asset cpu = core_sym::from_string("10.0000") ) { - signed_transaction trx; - set_transaction_headers(trx); - - authority owner_auth; - if (multisig) { - // multisig between account's owner key and creators active permission - owner_auth = authority(2, {key_weight{get_public_key( a, "owner" ), 1}}, {permission_level_weight{{creator, config::active_name}, 1}}); - } else { - owner_auth = authority( get_public_key( a, "owner" ) ); - } - - trx.actions.emplace_back( vector{{creator,config::active_name}}, - newaccount{ - .creator = creator, - .name = a, - .owner = owner_auth, - .active = authority( get_public_key( a, "active" ) ) - }); - - trx.actions.emplace_back( get_action( config::system_account_name, N(buyram), vector{{creator,config::active_name}}, - mvo() - ("payer", creator) - ("receiver", a) - ("quant", ramfunds) ) - ); - - trx.actions.emplace_back( get_action( config::system_account_name, N(delegatebw), vector{{creator,config::active_name}}, - mvo() - ("from", creator) - ("receiver", a) - ("stake_net_quantity", net ) - ("stake_cpu_quantity", cpu ) - ("transfer", 0 ) - ) - ); - - set_transaction_headers(trx); - trx.sign( get_private_key( creator, "active" ), control->get_chain_id() ); - return push_transaction( trx ); - } - - - void set_code_perms( name account, name code, name permission, name manager = config::system_account_name ) { - - permission_level code_perm{code, N(eosio.code)}; - permission_level_weight code_perm_weight{code_perm, 1}; - - vector key_weights{}; - vector permission_weights{code_perm_weight}; - vector wait_weights{}; - - authority code_authority( - 1, - key_weights, - permission_weights, - wait_weights); - - auto auth_act = mutable_variant_object() - ("account", account ) - ("permission", permission ) - ("parent", "active" ) - ("auth", code_authority ); - - base_tester::push_action(N(eosio), N(updateauth), account, auth_act ); - - produce_block(); - } - - void link_perms( name account, name code, name type, name requirement, name manager = config::system_account_name ){ - - auto link_act = mutable_variant_object() - ("account", account ) - ("code", code ) - ("type", type ) - ("requirement", requirement ); - - base_tester::push_action(N(eosio), N(linkauth), account, link_act ); - } - - void create_currency( name contract, name manager, asset maxsupply ) { - auto act = mutable_variant_object() - ("issuer", manager ) - ("maximum_supply", maxsupply ); - - base_tester::push_action(contract, N(create), contract, act ); - } - - void issue( const asset& amount, const name& manager = config::system_account_name ) { - base_tester::push_action( N(eosio.token), N(issue), manager, mutable_variant_object() - ("to", manager ) - ("quantity", amount ) - ("memo", "") - ); - } - - void transfer( const name& from, const name& to, const asset& amount, const name& manager = config::system_account_name ) { - base_tester::push_action( N(eosio.token), N(transfer), manager, mutable_variant_object() - ("from", from) - ("to", to ) - ("quantity", amount) - ("memo", "") - ); - } - - void transfer_dao( const name& from, const name& to, const asset& amount, const name& manager = config::system_account_name ) { - base_tester::push_action( N(token.dao), N(transfer), manager, mutable_variant_object() - ("from", from) - ("to", to ) - ("quantity", amount) - ("memo", "") - ); - } - - void vote( const name& voter, const vector& votes, const name& manager = config::system_account_name ) { - base_tester::push_action( N(steward.dao), N(votecust), manager, mutable_variant_object() - ("voter", voter) - ("votes", votes ) - ); - } - - void regdac( const name& owner, const name& dac_id, const extended_symbol& dac_symbol, const std::string& title, const name& manager = config::system_account_name ) { - - vector> refs; - vector> accounts; - - base_tester::push_action( N(dacdirectory), N(regdac), manager, mutable_variant_object() - ("owner", owner) - ("dac_id", dac_id ) - ("dac_symbol", dac_symbol ) - ("title", title ) - ("refs", refs ) - ("accounts", accounts ) - ); - } - - void regaccount( const name& dac_id, const name& account, const uint8_t type, const name& manager = config::system_account_name ) { - base_tester::push_action( N(dacdirectory), N(regaccount), manager, mutable_variant_object() - ("dac_id", dac_id ) - ("account", account ) - ("type", type ) - ); - } - - fc::variant get_system_stats( const string& symbolname ) { - auto symb = eosio::chain::symbol::from_string(symbolname); - auto symbol_code = symb.to_symbol_code().value; - vector data = get_row_by_account( N(eosio.token), symbol_code, N(stat), symbol_code ); - return data.empty() ? fc::variant() : system_token_abi_ser.binary_to_variant( "currency_stats", data, abi_serializer_max_time ); - } - - fc::variant get_dao_stats( const string& symbolname ) { - auto symb = eosio::chain::symbol::from_string(symbolname); - auto symbol_code = symb.to_symbol_code().value; - vector data = get_row_by_account( N(token.dao), symbol_code, N(stat), symbol_code ); - return data.empty() ? fc::variant() : token_abi_ser.binary_to_variant( "currency_stats", data, abi_serializer_max_time ); - } - - uint64_t microseconds_since_epoch_of_iso_string( const fc::variant& v ) { - return static_cast( time_point::from_iso_string( v.as_string() ).time_since_epoch().count() ); - } abi_serializer donation_abi_ser; abi_serializer token_abi_ser; diff --git a/tests/eosdao_tests.cpp b/tests/eosdao_tests.cpp index ab7e4b5..79ee7e3 100644 --- a/tests/eosdao_tests.cpp +++ b/tests/eosdao_tests.cpp @@ -14,67 +14,125 @@ using namespace eosdao; -BOOST_AUTO_TEST_SUITE( eosdao_token_tests ) +BOOST_AUTO_TEST_SUITE( eosdao_tests ) BOOST_FIXTURE_TEST_CASE( donate_receive, eosdao_tester ) try { printf("Running donate_receive...\n"); BOOST_REQUIRE_EQUAL( core_sym::from_string("1000.0000"), get_system_balance( "donor1.dao" ) ); - BOOST_REQUIRE_EQUAL( core_sym::from_string("10000.0000"), get_system_balance( "donor2.dao" ) ); - BOOST_REQUIRE_EQUAL( core_sym::from_string("100000.0000"), get_system_balance( "donor3.dao" ) ); transfer( "donor1.dao", "donation.dao", core_sym::from_string("1000.0000"), "donor1.dao" ); - transfer( "donor2.dao", "donation.dao", core_sym::from_string("1000.0000"), "donor2.dao" ); - transfer( "donor3.dao", "donation.dao", core_sym::from_string("1000.0000"), "donor3.dao" ); produce_blocks(2); - BOOST_REQUIRE_EQUAL( core_sym::from_string("3000.0000"), get_system_balance( "donation.dao" ) ); + BOOST_REQUIRE_EQUAL( core_sym::from_string("1000.0000"), get_system_balance( "donation.dao" ) ); -// BOOST_REQUIRE_EQUAL( dao_sym::from_string("1000.0000"), get_dao_balance( "donor1.dao" ) ); -// BOOST_REQUIRE_EQUAL( dao_sym::from_string("1000.0000"), get_dao_balance( "donor2.dao" ) ); -// BOOST_REQUIRE_EQUAL( dao_sym::from_string("1000.0000"), get_dao_balance( "donor3.dao" ) ); -printf("Running donate_receive complete...\n"); + BOOST_REQUIRE_EQUAL( dao_sym::from_string("1000.0000"), get_dao_balance( "donor1.dao" ) ); +//printf("Running donate_receive complete...\n"); } FC_LOG_AND_RETHROW() -//BOOST_FIXTURE_TEST_CASE( vote_weight_decay, eosdao_tester ) try { -// -// BOOST_REQUIRE_EQUAL( core_sym::from_string("1000.0000"), get_system_balance( "donor1.dao" ) ); -// BOOST_REQUIRE_EQUAL( core_sym::from_string("10000.0000"), get_system_balance( "donor2.dao" ) ); -// BOOST_REQUIRE_EQUAL( core_sym::from_string("100000.0000"), get_system_balance( "donor3.dao" ) ); -// -// transfer( "donor1.dao", "donation.dao", core_sym::from_string("1000.0000"), "donor1.dao" ); -// transfer( "donor2.dao", "donation.dao", core_sym::from_string("1000.0000"), "donor2.dao" ); -// transfer( "donor3.dao", "donation.dao", core_sym::from_string("1000.0000"), "donor3.dao" ); -// -// produce_blocks(2); -// -// BOOST_REQUIRE_EQUAL( dao_sym::from_string("1000.0000"), get_dao_balance( "donor1.dao" ) ); -// BOOST_REQUIRE_EQUAL( dao_sym::from_string("1000.0000"), get_dao_balance( "donor2.dao" ) ); -// BOOST_REQUIRE_EQUAL( dao_sym::from_string("1000.0000"), get_dao_balance( "donor3.dao" ) ); -// -//// const uint64_t six_months_blocks = 60 * 60 * 24 * 30 * 6 * 2; -// const uint64_t six_months_blocks = 60 * 60 * 2; -// -// printf("Waiting 6 months...\n"); -// produce_blocks(six_months_blocks); -// printf("That was fast...\n"); -// -// transfer( "donor2.dao", "donation.dao", core_sym::from_string("1000.0000"), "donor2.dao" ); -// -// produce_blocks(2); -// -// uint64_t weight_1 = get_voter_weight(N(donor1.dao)); -// printf("Weight : %d\n", (int)weight_1); -// -// BOOST_REQUIRE_EQUAL(get_voter_weight(N(donor1.dao)), 10000000); -// BOOST_REQUIRE(get_voter_weight(N(donor2.dao)) > 10000000); -// -// // Weight of donor1 should now be 1.5x the weight of donor2 -// BOOST_REQUIRE_EQUAL((double)get_voter_weight(N(donor2.dao)), (double)get_voter_weight(N(donor1.dao)) * 1.5); -// -//} FC_LOG_AND_RETHROW() +BOOST_FIXTURE_TEST_CASE( vote_weight_decay, eosdao_tester ) try { + printf("Running vote_weight_decay...\n"); + + BOOST_REQUIRE_EQUAL( core_sym::from_string("1000.0000"), get_system_balance( "donor1.dao" ) ); + BOOST_REQUIRE_EQUAL( core_sym::from_string("10000.0000"), get_system_balance( "donor2.dao" ) ); + BOOST_REQUIRE_EQUAL( core_sym::from_string("100000.0000"), get_system_balance( "donor3.dao" ) ); + + const uint64_t one_hour_blocks = 60 * 60 * 2; + // const uint64_t six_months_blocks = 60 * 60 * 2; + + transfer( "donor2.dao", "donation.dao", core_sym::from_string("1000.0000"), "donor2.dao" ); + + produce_blocks(one_hour_blocks); + + transfer( "donor3.dao", "donation.dao", core_sym::from_string("1000.0000"), "donor3.dao" ); + + BOOST_REQUIRE_EQUAL( core_sym::from_string("2000.0000"), get_system_balance( "donation.dao" ) ); + + uint64_t weight_2 = get_voter_weight(N(donor2.dao)); + uint64_t weight_3 = get_voter_weight(N(donor3.dao)); + + BOOST_REQUIRE_EQUAL(weight_2, 10000000); + // Weight of donor3 should include 1 hour of vote weight increase + BOOST_REQUIRE_EQUAL(weight_3, 10001604); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( direct_issue, eosdao_tester ) try { + printf("Running direct_issue...\n"); + + issue_dao( "donor5.dao", dao_sym::from_string("1000.0000"), N(token.dao) ); + + BOOST_REQUIRE_EQUAL( dao_sym::from_string("1000.0000"), get_dao_balance( "donor5.dao" ) ); + + uint64_t weight_5 = get_voter_weight(N(donor5.dao)); + + BOOST_REQUIRE_EQUAL(weight_5, 10000000); + +} FC_LOG_AND_RETHROW() + + +BOOST_FIXTURE_TEST_CASE( voting, eosdao_tester ) try { + printf("Running voting...\n"); + + transfer( "donor1.dao", "donation.dao", core_sym::from_string("1000.0000"), "donor1.dao" ); + transfer( "donor2.dao", "donation.dao", core_sym::from_string("1000.0000"), "donor2.dao" ); + transfer( "donor3.dao", "donation.dao", core_sym::from_string("1000.0000"), "donor3.dao" ); + transfer( "donor4.dao", "donation.dao", core_sym::from_string("1000.0000"), "donor4.dao" ); + + BOOST_REQUIRE_EQUAL( dao_sym::from_string("1000.0000"), get_dao_balance( "donor1.dao" ) ); + BOOST_REQUIRE_EQUAL( dao_sym::from_string("1000.0000"), get_dao_balance( "donor2.dao" ) ); + BOOST_REQUIRE_EQUAL( dao_sym::from_string("1000.0000"), get_dao_balance( "donor3.dao" ) ); + BOOST_REQUIRE_EQUAL( dao_sym::from_string("1000.0000"), get_dao_balance( "donor4.dao" ) ); + + uint64_t weight_1 = get_voter_weight(N(donor1.dao)); + + BOOST_REQUIRE_EQUAL(weight_1, 10000000); + + memberreg(N(donor1.dao), "1df37bdb72c0be963ef2bdfe9b7ef10b", N(eosdao), N(donor1.dao)); + memberreg(N(donor2.dao), "1df37bdb72c0be963ef2bdfe9b7ef10b", N(eosdao), N(donor2.dao)); + memberreg(N(donor3.dao), "1df37bdb72c0be963ef2bdfe9b7ef10b", N(eosdao), N(donor3.dao)); + memberreg(N(donor4.dao), "1df37bdb72c0be963ef2bdfe9b7ef10b", N(eosdao), N(donor4.dao)); + memberreg(N(donor5.dao), "1df37bdb72c0be963ef2bdfe9b7ef10b", N(eosdao), N(donor5.dao)); + + vector votes1 = {N(steward1.dao), N(steward2.dao), N(steward3.dao)}; + vector votes2 = {N(steward1.dao), N(steward2.dao), N(steward3.dao)}; + vector votes3 = {N(steward1.dao), N(steward3.dao), N(steward5.dao)}; + vector votes4 = {N(steward1.dao), N(steward3.dao), N(steward4.dao)}; + vector votes5 = {N(steward1.dao), N(steward3.dao), N(steward4.dao)}; + + BOOST_REQUIRE_EQUAL( + error("assertion failure with message: ERR::NEWPERIOD_VOTER_ENGAGEMENT_LOW_ACTIVATE::Voter engagement is insufficient to activate the DAC."), + newperiod(string("My newperiod message"), N(eosdao), N(donor1.dao)) + ); + + vote(N(donor1.dao), votes1, N(eosdao), N(donor1.dao)); + vote(N(donor2.dao), votes2, N(eosdao), N(donor2.dao)); + vote(N(donor3.dao), votes3, N(eosdao), N(donor3.dao)); + vote(N(donor4.dao), votes4, N(eosdao), N(donor4.dao)); + + BOOST_REQUIRE_EQUAL( + error("assertion failure with message: ERR::NEWPERIOD_VOTER_ENGAGEMENT_LOW_ACTIVATE::Voter engagement is insufficient to activate the DAC."), + newperiod(string("My newperiod message"), N(eosdao), N(donor1.dao)) + ); + + // donor5 is required to unlock + transfer( "donor5.dao", "donation.dao", core_sym::from_string("10000000.0000"), "donor5.dao" ); + BOOST_REQUIRE_EQUAL( dao_sym::from_string("10000000.0000"), get_dao_balance( "donor5.dao" ) ); + + uint64_t weight_5 = get_voter_weight(N(donor5.dao)); + BOOST_REQUIRE_EQUAL(weight_5, 10'000'000'0000); + + vote(N(donor5.dao), votes5, N(eosdao), N(donor5.dao)); + + BOOST_REQUIRE_EQUAL( + success(), + newperiod(string("My newperiod message"), N(eosdao), N(donor1.dao)) + ); + +} FC_LOG_AND_RETHROW() BOOST_AUTO_TEST_SUITE_END() diff --git a/tests/setup.hpp b/tests/setup.hpp new file mode 100644 index 0000000..c8907e1 --- /dev/null +++ b/tests/setup.hpp @@ -0,0 +1,293 @@ + +struct contr_config { + extended_asset lockupasset; + uint8_t maxvotes = 5; + uint8_t numelected = 3; + uint32_t periodlength = 7 * 24 * 60 * 60; + bool should_pay_via_service_provider; + uint32_t initial_vote_quorum_percent; + uint32_t vote_quorum_percent; + uint8_t auth_threshold_high; + uint8_t auth_threshold_mid; + uint8_t auth_threshold_low; + uint32_t lockup_release_time_delay; + extended_asset requested_pay_max; +}; + +void basic_setup() { +// printf("Running basic_setup...\n"); + produce_blocks(); + + create_accounts({ N(eosio.token), N(eosio.ram), N(eosio.ramfee), N(eosio.stake), + N(eosio.bpay), N(eosio.vpay), N(eosio.saving), N(eosio.names), N(eosio.rex) }); + + create_accounts({ N(auth.dao), N(token.dao), N(donation.dao), N(voting.dao), N(steward.dao), N(dacdirectory) }); + + produce_blocks(); + set_code( N(eosio.token), testing::contracts::util::system_token_wasm()); + set_abi( N(eosio.token), testing::contracts::util::system_token_abi().data() ); + { + const auto& accnt = control->db().get( N(eosio.token) ); + abi_def abi; + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + system_token_abi_ser.set_abi(abi, abi_serializer_max_time); + } + + set_code( N(eosio), testing::contracts::util::system_wasm()); + set_abi( N(eosio), testing::contracts::util::system_abi().data() ); + + + { + const auto& accnt = control->db().get( N(eosio) ); + abi_def abi; + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + system_abi_ser.set_abi(abi, abi_serializer_max_time); + } + + // create and issue core voting + FC_ASSERT( core_symbol.decimals() == 4, "create_core_token assumes core voting has 4 digits of precision" ); + create_currency( N(eosio.token), asset(100000000000000, core_symbol) ); + issue( asset(10000000000000, core_symbol) ); + BOOST_REQUIRE_EQUAL( asset(10000000000000, core_symbol), get_system_balance( "eosio", core_symbol ) ); + + + base_tester::push_action(config::system_account_name, N(init), + config::system_account_name, mutable_variant_object() + ("version", 0) + ("core", CORE_SYM_STR) + ); +} + +void deploy_contracts() { +// printf("Running deploy_contracts...\n"); + + set_code( N(token.dao), testing::contracts::token_wasm() ); + set_abi( N(token.dao), testing::contracts::token_abi().data() ); + { + const auto& accnt = control->db().get( N(token.dao) ); + abi_def abi; + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + token_abi_ser.set_abi(abi, abi_serializer_max_time); + } + + + set_code( N(donation.dao), testing::contracts::donation_wasm() ); + set_abi( N(donation.dao), testing::contracts::donation_abi().data() ); + { + const auto& accnt = control->db().get( N(donation.dao) ); + abi_def abi; + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + donation_abi_ser.set_abi(abi, abi_serializer_max_time); + } + + + set_code( N(voting.dao), testing::contracts::voting_wasm() ); + set_abi( N(voting.dao), testing::contracts::voting_abi().data() ); + { + const auto& accnt = control->db().get( N(voting.dao) ); + abi_def abi; + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + voting_abi_ser.set_abi(abi, abi_serializer_max_time); + } + + + set_code( N(steward.dao), testing::contracts::eosdac::custodian_wasm() ); + set_abi( N(steward.dao), testing::contracts::eosdac::custodian_abi().data() ); + { + const auto& accnt = control->db().get( N(steward.dao) ); + abi_def abi; + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + custodian_abi_ser.set_abi(abi, abi_serializer_max_time); + } + + + set_code( N(dacdirectory), testing::contracts::eosdac::directory_wasm() ); + set_abi( N(dacdirectory), testing::contracts::eosdac::directory_abi().data() ); + + { + const auto& accnt = control->db().get( N(dacdirectory) ); + abi_def abi; + BOOST_REQUIRE_EQUAL(abi_serializer::to_abi(accnt.abi, abi), true); + directory_abi_ser.set_abi(abi, abi_serializer_max_time); + } +} + +void configure_contracts() { +// printf("Running configure_contracts...\n"); + + extended_asset lockup = extended_asset(asset(0, symbol(4, "DAO")), N(token.dao)); + extended_asset reqpay = extended_asset(asset(1, symbol(4, "EOS")), N(eosio.token)); + + contr_config config = { + lockup, // extended_asset lockupasset + 3, //uint8_t maxvotes = 5; + 5, //uint8_t numelected = 3; + 24 * 60 * 60, //uint32_t periodlength = 7 * 24 * 60 * 60; + false, //bool should_pay_via_service_provider; + 1, //uint32_t initial_vote_quorum_percent; + 1, //uint32_t vote_quorum_percent; + 4, //uint8_t auth_threshold_high; + 3, //uint8_t auth_threshold_mid; + 3, //uint8_t auth_threshold_low; + 600, //uint32_t lockup_release_time_delay; + reqpay //extended_asset requested_pay_max; + }; + + configure_custodian(config, N(eosdao), N(auth.dao)); + produce_blocks(); + + string terms_url = "https://raw.githubusercontent.com/eosdac/eosdac-constitution/master/boilerplate_constitution.md"; + string terms_hash = "1df37bdb72c0be963ef2bdfe9b7ef10b"; + updatememterms(terms_url, terms_hash, N(eosdao), N(auth.dao)); +} + +void set_permissions(){ + // Add code rights for the donation contract so it can call issue + set_code_perms(N(token.dao), N(donation.dao), N(issue)); + link_perms(N(token.dao), N(token.dao), N(issue), N(issue)); + // Add code rights to the token contract so that it can transfer from itself + set_code_perms(N(token.dao), N(token.dao), N(xfer)); + link_perms(N(token.dao), N(token.dao), N(transfer), N(xfer)); + // Notify from token contract to voting + set_code_perms(N(token.dao), N(token.dao), N(notify)); + link_perms(N(token.dao), N(voting.dao), N(balanceobsv), N(notify)); + + // so the voting contract can notify the custodian contract (and vice versa) + vector codes = { N(steward.dao), N(voting.dao) }; + set_code_perms(N(voting.dao), codes, N(notify)); + link_perms(N(voting.dao), N(steward.dao), N(weightobsv), N(notify)); + link_perms(N(voting.dao), N(voting.dao), N(assertunlock), N(notify)); + + // create auth permissions + vector perms = { {N(auth.dao), N(owner)} }; + set_code_perms(N(auth.dao), N(steward.dao), N(owner), "", perms); + set_code_perms(N(auth.dao), N(steward.dao), N(high), "active"); + set_code_perms(N(auth.dao), N(steward.dao), N(med), "high"); + set_code_perms(N(auth.dao), N(steward.dao), N(low), "med"); + set_code_perms(N(auth.dao), N(steward.dao), N(one), "low"); + + // permissions on custodian account + +} + +void create_dao_token(){ +// printf("Running create_dao_token...\n"); + FC_ASSERT( DAO_SYM_PRECISION == 4, "create_dao_token assumes DAO token has 4 digits of precision" ); + create_currency( N(token.dao), dao_sym::from_string("1000000000000.0000"), N(token.dao) ); +} + +void setup_directory(){ +// printf("Running setup_directory...\n"); + extended_symbol es = extended_symbol{symbol(4, "DAO"), N(token.dao)}; + regdac( N(auth.dao), N(eosdao), es, "EOS DAO", N(auth.dao) ); + regaccount( N(eosdao), N(auth.dao), 0, N(auth.dao) ); + regaccount( N(eosdao), N(steward.dao), 2, N(auth.dao) ); + regaccount( N(eosdao), N(voting.dao), 8, N(auth.dao) ); + regaccount( N(eosdao), N(voting.dao), 9, N(auth.dao) ); +} + +void register_candidates(){ + create_account_with_resources( N(steward1.dao), config::system_account_name, core_sym::from_string("1000.0000"), false ); + create_account_with_resources( N(steward2.dao), config::system_account_name, core_sym::from_string("1000.0000"), false ); + create_account_with_resources( N(steward3.dao), config::system_account_name, core_sym::from_string("1000.0000"), false ); + create_account_with_resources( N(steward4.dao), config::system_account_name, core_sym::from_string("1000.0000"), false ); + create_account_with_resources( N(steward5.dao), config::system_account_name, core_sym::from_string("1000.0000"), false ); + + memberreg(N(steward1.dao), "1df37bdb72c0be963ef2bdfe9b7ef10b", N(eosdao), N(steward1.dao)); + memberreg(N(steward2.dao), "1df37bdb72c0be963ef2bdfe9b7ef10b", N(eosdao), N(steward2.dao)); + memberreg(N(steward3.dao), "1df37bdb72c0be963ef2bdfe9b7ef10b", N(eosdao), N(steward3.dao)); + memberreg(N(steward4.dao), "1df37bdb72c0be963ef2bdfe9b7ef10b", N(eosdao), N(steward4.dao)); + memberreg(N(steward5.dao), "1df37bdb72c0be963ef2bdfe9b7ef10b", N(eosdao), N(steward5.dao)); + + nominatecand( N(steward1.dao), core_sym::from_string("0.0000"), N(eosdao), N(steward1.dao) ); + nominatecand( N(steward2.dao), core_sym::from_string("0.0000"), N(eosdao), N(steward2.dao) ); + nominatecand( N(steward3.dao), core_sym::from_string("0.0000"), N(eosdao), N(steward3.dao) ); + nominatecand( N(steward4.dao), core_sym::from_string("0.0000"), N(eosdao), N(steward4.dao) ); + nominatecand( N(steward5.dao), core_sym::from_string("0.0000"), N(eosdao), N(steward5.dao) ); + +} + +void remaining_setup() { +// printf("Running remaining_setup...\n"); + produce_blocks(); + + create_account_with_resources( N(donor1.dao), config::system_account_name, core_sym::from_string("1000.0000"), false ); + create_account_with_resources( N(donor2.dao), config::system_account_name, core_sym::from_string("1000.0000"), false ); + create_account_with_resources( N(donor3.dao), config::system_account_name, core_sym::from_string("1000.0000"), false ); + create_account_with_resources( N(donor4.dao), config::system_account_name, core_sym::from_string("1000.0000"), false ); + create_account_with_resources( N(donor5.dao), config::system_account_name, core_sym::from_string("1000.0000"), false ); + + transfer(config::system_account_name, N(donor1.dao), core_sym::from_string("1000.0000")); + transfer(config::system_account_name, N(donor2.dao), core_sym::from_string("10000.0000")); + transfer(config::system_account_name, N(donor3.dao), core_sym::from_string("100000.0000")); + transfer(config::system_account_name, N(donor4.dao), core_sym::from_string("1000000.0000")); + transfer(config::system_account_name, N(donor5.dao), core_sym::from_string("10000000.0000")); + + BOOST_REQUIRE_EQUAL( asset(10000000, core_symbol), get_system_balance( "donor1.dao", core_symbol ) ); + BOOST_REQUIRE_EQUAL( asset(100000000, core_symbol), get_system_balance( "donor2.dao", core_symbol ) ); + BOOST_REQUIRE_EQUAL( asset(1000000000, core_symbol), get_system_balance( "donor3.dao", core_symbol ) ); + BOOST_REQUIRE_EQUAL( asset(10000000000, core_symbol), get_system_balance( "donor4.dao", core_symbol ) ); + BOOST_REQUIRE_EQUAL( asset(100000000000, core_symbol), get_system_balance( "donor5.dao", core_symbol ) ); +} + + + +enum class setup_level { + none, + minimal, + deploy_contracts, + set_permissions, + dao_token, + directory, + configure_contracts, + register_candidates, + full +}; + +eosdao_tester( setup_level l = setup_level::full ) { + if( l == setup_level::none ) return; + + basic_setup(); + if( l == setup_level::minimal ) return; + produce_blocks(); + + deploy_contracts(); + if( l == setup_level::deploy_contracts ) return; + produce_blocks(); + + set_permissions(); + if( l == setup_level::set_permissions ) return; + produce_blocks(); + + create_dao_token(); + if( l == setup_level::dao_token ) return; + produce_blocks(); + + setup_directory(); + if( l == setup_level::directory ) return; + produce_blocks(); + + configure_contracts(); + if( l == setup_level::configure_contracts ) return; + produce_blocks(); + + register_candidates(); + if( l == setup_level::register_candidates ) return; + produce_blocks(); + + remaining_setup(); +} + +template +eosdao_tester(Lambda setup) { + setup(*this); + + basic_setup(); + deploy_contracts(); + set_permissions(); + create_dao_token(); + setup_directory(); + configure_contracts(); + register_candidates(); + remaining_setup(); +} diff --git a/tests/symbol.hpp b/tests/symbol.hpp new file mode 100644 index 0000000..69b30fa --- /dev/null +++ b/tests/symbol.hpp @@ -0,0 +1,193 @@ +/** + * @file + * @copyright defined in eos/LICENSE + */ +#pragma once +#include +#include +#include +#include +#include + +namespace eosio { + namespace chain { + + /** + class symbol represents a token and contains precision and name. + When encoded as a uint64_t, first byte represents the number of decimals, remaining bytes + represent token name. + Name must only include upper case alphabets. + from_string constructs a symbol from an input a string of the form "4,EOS" + where the integer represents number of decimals. Number of decimals must be larger than zero. + */ + + static constexpr uint64_t string_to_symbol_c(uint8_t precision, const char* str) { + uint32_t len = 0; + while (str[len]) ++len; + + uint64_t result = 0; + // No validation is done at compile time + for (uint32_t i = 0; i < len; ++i) { + result |= (uint64_t(str[i]) << (8*(1+i))); + } + + result |= uint64_t(precision); + return result; + } + +#define SY(P,X) ::eosio::chain::string_to_symbol_c(P,#X) + + static uint64_t string_to_symbol(uint8_t precision, const char* str) { + try { + uint32_t len = 0; + while(str[len]) ++len; + uint64_t result = 0; + for (uint32_t i = 0; i < len; ++i) { + // All characters must be upper case alphabets + EOS_ASSERT (str[i] >= 'A' && str[i] <= 'Z', symbol_type_exception, "invalid character in symbol name"); + result |= (uint64_t(str[i]) << (8*(i+1))); + } + result |= uint64_t(precision); + return result; + } FC_CAPTURE_LOG_AND_RETHROW((str)) + } + + struct symbol_code { + uint64_t value; + + operator uint64_t()const { return value; } + }; + + class symbol { + public: + + static constexpr uint8_t max_precision = 18; + + explicit symbol(uint8_t p, const char* s): m_value(string_to_symbol(p, s)) { + EOS_ASSERT(valid(), symbol_type_exception, "invalid symbol: ${s}", ("s",s)); + } + explicit symbol(uint64_t v = CORE_SYMBOL): m_value(v) { + EOS_ASSERT(valid(), symbol_type_exception, "invalid symbol: ${name}", ("name",name())); + } + static symbol from_string(const string& from) + { + try { + string s = fc::trim(from); + EOS_ASSERT(!s.empty(), symbol_type_exception, "creating symbol from empty string"); + auto comma_pos = s.find(','); + EOS_ASSERT(comma_pos != string::npos, symbol_type_exception, "missing comma in symbol"); + auto prec_part = s.substr(0, comma_pos); + uint8_t p = fc::to_int64(prec_part); + string name_part = s.substr(comma_pos + 1); + EOS_ASSERT( p <= max_precision, symbol_type_exception, "precision ${p} should be <= 18", ("p", p)); + return symbol(string_to_symbol(p, name_part.c_str())); + } FC_CAPTURE_LOG_AND_RETHROW((from)) + } + uint64_t value() const { return m_value; } + bool valid() const + { + const auto& s = name(); + return decimals() <= max_precision && valid_name(s); + } + static bool valid_name(const string& name) + { + return all_of(name.begin(), name.end(), [](char c)->bool { return (c >= 'A' && c <= 'Z'); }); + } + + uint8_t decimals() const { return m_value & 0xFF; } + uint64_t precision() const + { + EOS_ASSERT( decimals() <= max_precision, symbol_type_exception, "precision ${p} should be <= 18", ("p", decimals()) ); + uint64_t p10 = 1; + uint64_t p = decimals(); + while( p > 0 ) { + p10 *= 10; --p; + } + return p10; + } + string name() const + { + uint64_t v = m_value; + v >>= 8; + string result; + while (v > 0) { + char c = v & 0xFF; + result += c; + v >>= 8; + } + return result; + } + + symbol_code to_symbol_code()const { return {m_value >> 8}; } + + explicit operator string() const + { + uint64_t v = m_value; + uint8_t p = v & 0xFF; + string ret = eosio::chain::to_string(p); + ret += ','; + ret += name(); + return ret; + } + + string to_string() const { return string(*this); } + template + friend DataStream& operator<< (DataStream& ds, const symbol& s) + { + return ds << s.to_string(); + } + + void reflector_init()const { + EOS_ASSERT( decimals() <= max_precision, symbol_type_exception, "precision ${p} should be <= 18", ("p", decimals()) ); + EOS_ASSERT( valid_name(name()), symbol_type_exception, "invalid symbol: ${name}", ("name",name())); + } + + private: + uint64_t m_value; + friend struct fc::reflector; + }; // class symbol + + struct extended_symbol { + symbol symbol; + account_name contract; + }; + + inline bool operator== (const symbol& lhs, const symbol& rhs) + { + return lhs.value() == rhs.value(); + } + inline bool operator!= (const symbol& lhs, const symbol& rhs) + { + return lhs.value() != rhs.value(); + } + inline bool operator< (const symbol& lhs, const symbol& rhs) + { + return lhs.value() < rhs.value(); + } + inline bool operator> (const symbol& lhs, const symbol& rhs) + { + return lhs.value() > rhs.value(); + } + + } // namespace chain +} // namespace eosio + +namespace fc { + inline void to_variant(const eosio::chain::symbol& var, fc::variant& vo) { vo = var.to_string(); } + inline void from_variant(const fc::variant& var, eosio::chain::symbol& vo) { + vo = eosio::chain::symbol::from_string(var.get_string()); + } +} + +namespace fc { + inline void to_variant(const eosio::chain::symbol_code& var, fc::variant& vo) { + vo = eosio::chain::symbol(var.value << 8).name(); + } + inline void from_variant(const fc::variant& var, eosio::chain::symbol_code& vo) { + vo = eosio::chain::symbol(0, var.get_string().c_str()).to_symbol_code(); + } +} + +FC_REFLECT(eosio::chain::symbol_code, (value)) +FC_REFLECT(eosio::chain::symbol, (m_value)) +FC_REFLECT(eosio::chain::extended_symbol, (symbol)(contract)) diff --git a/tests/test_contracts/daccustodian.abi b/tests/test_contracts/daccustodian.abi index 9b57914..fa410e7 100644 --- a/tests/test_contracts/daccustodian.abi +++ b/tests/test_contracts/daccustodian.abi @@ -3,6 +3,62 @@ "version": "eosio::abi/1.1", "types": [], "structs": [ + { + "name": "account_balance_delta", + "base": "", + "fields": [ + { + "name": "account", + "type": "name" + }, + { + "name": "balance_delta", + "type": "asset" + } + ] + }, + { + "name": "account_stake_delta", + "base": "", + "fields": [ + { + "name": "account", + "type": "name" + }, + { + "name": "stake_delta", + "type": "asset" + } + ] + }, + { + "name": "account_weight_delta", + "base": "", + "fields": [ + { + "name": "account", + "type": "name" + }, + { + "name": "weight_delta", + "type": "int64" + } + ] + }, + { + "name": "balanceobsv", + "base": "", + "fields": [ + { + "name": "account_balance_deltas", + "type": "account_balance_delta[]" + }, + { + "name": "dac_id", + "type": "name" + } + ] + }, { "name": "candidate", "base": "", @@ -465,6 +521,20 @@ } ] }, + { + "name": "stakeobsv", + "base": "", + "fields": [ + { + "name": "account_stake_deltas", + "type": "account_stake_delta[]" + }, + { + "name": "dac_id", + "type": "name" + } + ] + }, { "name": "stprofile", "base": "", @@ -699,6 +769,20 @@ } ] }, + { + "name": "weightobsv", + "base": "", + "fields": [ + { + "name": "account_weight_deltas", + "type": "account_weight_delta[]" + }, + { + "name": "dac_id", + "type": "name" + } + ] + }, { "name": "withdrawcand", "base": "", @@ -725,6 +809,11 @@ } ], "actions": [ + { + "name": "balanceobsv", + "type": "balanceobsv", + "ricardian_contract": "" + }, { "name": "capturestake", "type": "capturestake", @@ -825,6 +914,11 @@ "type": "setperm", "ricardian_contract": "" }, + { + "name": "stakeobsv", + "type": "stakeobsv", + "ricardian_contract": "" + }, { "name": "stprofile", "type": "stprofile", @@ -890,6 +984,11 @@ "type": "votecuste", "ricardian_contract": "---\nspec_version: \"0.2.0\"\ntitle: Vote for Custodian\nsummary: 'Vote for custodians with account {{ nowrap voter }} to {{ nowrap requestedpay }} for DAC ID {{ nowrap dac_id }}'\nicon: https://eosdac.io/assets/contracts/generic.png#00da1afc6464028359b3a02ffbdb59e1ea79fa261b5523ce7ac174cc0ef27bbd\n---\n\nThis action is casting a vote for the following candidates:\n\n{{#each newvotes}}\n**{{ this }}**\n{{/each}}\n\nAny previous votes will be removed by these" }, + { + "name": "weightobsv", + "type": "weightobsv", + "ricardian_contract": "" + }, { "name": "withdrawcand", "type": "withdrawcand", diff --git a/tests/test_contracts/daccustodian.wasm b/tests/test_contracts/daccustodian.wasm index 99a68a2..9a4d786 100755 Binary files a/tests/test_contracts/daccustodian.wasm and b/tests/test_contracts/daccustodian.wasm differ diff --git a/tests/test_contracts/eosio.system.abi b/tests/test_contracts/eosio.system.abi index 364e95b..715552c 100644 --- a/tests/test_contracts/eosio.system.abi +++ b/tests/test_contracts/eosio.system.abi @@ -17,16 +17,6 @@ } ] }, - { - "name": "activate", - "base": "", - "fields": [ - { - "name": "feature_digest", - "type": "checksum256" - } - ] - }, { "name": "authority", "base": "", @@ -1567,11 +1557,6 @@ } ], "actions": [ - { - "name": "activate", - "type": "activate", - "ricardian_contract": "---\nspec_version: \"0.2.0\"\ntitle: Activate Protocol Feature\nsummary: 'Activate protocol feature {{nowrap feature_digest}}'\nicon: http://127.0.0.1/ricardian_assets/eosio.contracts/icons/admin.png#9bf1cec664863bd6aaac0f814b235f8799fb02c850e9aa5da34e8a004bd6518e\n---\n\n{{$action.account}} activates the protocol feature with a digest of {{feature_digest}}." - }, { "name": "bidname", "type": "bidname", diff --git a/tests/test_contracts/eosio.system.wasm b/tests/test_contracts/eosio.system.wasm index a549cd9..83d7c99 100755 Binary files a/tests/test_contracts/eosio.system.wasm and b/tests/test_contracts/eosio.system.wasm differ diff --git a/tests/utils.hpp b/tests/utils.hpp new file mode 100644 index 0000000..c1c2c57 --- /dev/null +++ b/tests/utils.hpp @@ -0,0 +1,146 @@ + +uint32_t last_block_time() const { + return time_point_sec( control->head_block_time() ).sec_since_epoch(); +} + +uint64_t get_voter_weight( const name& voter ){ + vector data = get_row_by_account( N(voting.dao), N(eosdao), N(weights), voter ); + if (data.empty()){ + printf("DATA empty in get_voter_weight\n"); + } + return data.empty() ? 0 : voting_abi_ser.binary_to_variant( "vote_weight_type", data, abi_serializer_max_time )["weight"].as(); +} + +asset get_system_balance( const account_name& act, symbol balance_symbol = symbol{CORE_SYM} ) { + vector data = get_row_by_account( N(eosio.token), act, N(accounts), balance_symbol.to_symbol_code().value ); + return data.empty() ? asset(0, balance_symbol) : system_token_abi_ser.binary_to_variant("account", data, abi_serializer_max_time)["balance"].as(); +} + +asset get_dao_balance( const account_name& act, symbol balance_symbol = symbol{DAO_SYM} ) { + vector data = get_row_by_account( N(token.dao), act, N(accounts), balance_symbol.to_symbol_code().value ); + return data.empty() ? asset(0, balance_symbol) : token_abi_ser.binary_to_variant("account_type", data, abi_serializer_max_time)["balance"].as(); +} + + +transaction_trace_ptr create_account_with_resources( account_name a, account_name creator, asset ramfunds, bool multisig, + asset net = core_sym::from_string("10.0000"), asset cpu = core_sym::from_string("10.0000") ) { + signed_transaction trx; + set_transaction_headers(trx); + + authority owner_auth; + if (multisig) { + // multisig between account's owner key and creators active permission + owner_auth = authority(2, {key_weight{get_public_key( a, "owner" ), 1}}, {permission_level_weight{{creator, config::active_name}, 1}}); + } else { + owner_auth = authority( get_public_key( a, "owner" ) ); + } + + trx.actions.emplace_back( vector{{creator,config::active_name}}, + newaccount{ + .creator = creator, + .name = a, + .owner = owner_auth, + .active = authority( get_public_key( a, "active" ) ) + }); + + trx.actions.emplace_back( get_action( config::system_account_name, N(buyram), vector{{creator,config::active_name}}, + mvo() + ("payer", creator) + ("receiver", a) + ("quant", ramfunds) ) + ); + + trx.actions.emplace_back( get_action( config::system_account_name, N(delegatebw), vector{{creator,config::active_name}}, + mvo() + ("from", creator) + ("receiver", a) + ("stake_net_quantity", net ) + ("stake_cpu_quantity", cpu ) + ("transfer", 0 ) + ) + ); + + set_transaction_headers(trx); + trx.sign( get_private_key( creator, "active" ), control->get_chain_id() ); + return push_transaction( trx ); +} + + +void set_code_perms( name account, name code, name permission, string parent = "active", const vector perms = {} ) { + vector codes{code}; + return set_code_perms(account, codes, permission, parent, perms); +} + +void set_code_perms( name account, vector codes, name permission, string parent = "active", const vector perms = {} ) { + + vector key_weights{}; + vector permission_weights; + vector wait_weights{}; + + for (name code: codes){ + permission_level code_perm{code, N(eosio.code)}; + permission_level_weight code_perm_weight{code_perm, 1}; + permission_weights.push_back(code_perm_weight); + } + + authority code_authority( + 1, + key_weights, + permission_weights, + wait_weights); + + variant_object auth_act; + auth_act = mutable_variant_object() + ("account", account ) + ("permission", permission ) + ("parent", parent.c_str() ) + ("auth", code_authority ); + + + if (perms.size()){ + base_tester::push_action(N(eosio), N(updateauth), perms, auth_act, 6, 0 ); + } + else { + base_tester::push_action(N(eosio), N(updateauth), account, auth_act ); + } + + produce_block(); +} + +void link_perms( name account, name code, name type, name requirement, name manager = config::system_account_name ){ + + auto link_act = mutable_variant_object() + ("account", account ) + ("code", code ) + ("type", type ) + ("requirement", requirement ); + + base_tester::push_action(N(eosio), N(linkauth), account, link_act ); +} + +void create_currency( name contract, asset maxsupply, name manager = config::system_account_name ) { + auto act = mutable_variant_object() + ("issuer", manager ) + ("maximum_supply", maxsupply ); + + base_tester::push_action(contract, N(create), contract, act ); +} + + +fc::variant get_system_stats( const string& symbolname ) { + auto symb = eosio::chain::symbol::from_string(symbolname); + auto symbol_code = symb.to_symbol_code().value; + vector data = get_row_by_account( N(eosio.token), symbol_code, N(stat), symbol_code ); + return data.empty() ? fc::variant() : system_token_abi_ser.binary_to_variant( "currency_stats", data, abi_serializer_max_time ); +} + +fc::variant get_dao_stats( const string& symbolname ) { + auto symb = eosio::chain::symbol::from_string(symbolname); + auto symbol_code = symb.to_symbol_code().value; + vector data = get_row_by_account( N(token.dao), symbol_code, N(stat), symbol_code ); + return data.empty() ? fc::variant() : token_abi_ser.binary_to_variant( "currency_stats", data, abi_serializer_max_time ); +} + +uint64_t microseconds_since_epoch_of_iso_string( const fc::variant& v ) { + return static_cast( time_point::from_iso_string( v.as_string() ).time_since_epoch().count() ); +}