---
# System prepended metadata

title: DoinGud DAO requirements

---

# DoinGud DAO requirements

The DoinGud DAO is a main organizational mechanism behind the DoinGud project. It manages and organizes the project's financial streams and funding allocation. The DoinGud DAO consists of several elements, including the main MetaDAO, multiple Guild DAOs, tokens and staking mechanisms, etc.

**These elements are proposed to be implemented in three stages:**
1. Basic tokenomic primitives such as $AMOR, $dAMOR, $AMORxGuild, etc.
2. Implementation of MetaDAO, the main mechanism to manage funds and create guilds.
3. The development of Guild and guild-related mechanics.

![](https://i.imgur.com/KzGxk6h.png)

## First stage. Tokenomics

### Tokens

#### Proxy research

Check if it's possible to change proxy implementation without loosing data stored in the contract(for AMOR).

#### AMOR

The AMOR token is a standard ERC20 token used in MetaDAO for liquidity. AMOR should be implemented as a simple token with a predefined supply. Each token transfer is taxed (to the MetaDAO and also distributed between all guilds), but by default tax is 0. The maximum taxation should be 5%. The tax from AMOR transfers is distributed between MetaDAO and all of the guilds.

Important functions:
- `transfer(address to, uint256 amount)` - this function call get taxed based on the defined tax percentage. Afterwards contract calls MetaDAO controller to distribute the fees propperly between MetaDAO and all of the Guilds.
- `setTaxRate(uint256 newRate)` - this function call sets tax percentage for the AMOR Token. It is limited up to 5% with 0.01 step.


#### AMORxGuild Curve

 It can also be used to exchange with AMORxGuild token. AMOR token is a standard ERC20 token. AMORxGuild is received with the help of the quadratic Curve. The more AMOR tokens are locked in the Curve, the less AMORxGuild will be received from the Curve from the stake. The relationship will be like this: $X$ AMOR tokens - $\sqrt X$ AMORxGuild.

The Curve contract is also an ERC20, but it also should have two special functions:
- `setTax(uint256 tax) onlyOwner`. Function allows to set the tax of the AMORxGuild Curve token. Some percentage of the Tax goes to the DoinGud fiundation(Avatar contract).
- `stake(uint256 amount)`. Stake function receives some amount of AMOR tokens. Based on this amount curve is minting some amount of tokens. Total supply of AMORxGuild tokens is regulated with this invariant: $$I(S,R)=\frac{S^2}{R}$$Where $I(S,R)$ is a definition of an invariant, which is equal to some constant coefficient, for example $I(S,R) = 1$. $S$ is a token supply of AMORxGuild. $R$ is an amount of staked AMOR tokens. So basically formula with $I(S,R)=1$ means that if we want to have $N$ tokens in supply we need to have $N^2$ tokens staked. From the formula higher we can derive:
  1. $S^2 = I(S,R) R$
  2. $S^2 = R$
  3. $S = \sqrt{R}$
    When we stake AMOR tokens, it becomes easy to calculate the amount of produced AMORxGuild tokens--this is basically a defined integral. For example, for $I(S,R)=1$ and we have $n$ tokens already staked(which means we have $\sqrt{n}$ tokens minted). 
We're going to stake $k$ tokens. The amount of minted tokens is calculated using this formula: $$N_{minted} =  (\sqrt{(n+k)} - \sqrt{n})$$
Stake function is getting taxed up to 20%, tax goes as a donation to the Avatar.

This function call increases the weight of the guild inside of the MetaDAO controller by the number of staked tokens.

- `withdraw(uint256 amount)`. Withdraw function  receives some amount of AMORxGuild token, burns it and withdraws a corresponding amount of AMOR token. The formula for amount of withdrawn AMOR tokens looks like this: $$N_{withdrawn} =  ({TotalSupply}^2 - {(TotalSupply-amount)}^2)$$
Where $TotalSupply$ is an amount of AMORxGuild tokens minted, and $amount$ - amount of burned AMORxGuild tokens. 
This function call decreases the weight of the guild inside of the MetaDAO controller by the number of withdrawn tokens.
- `setTaxRate(uint256 newRate)` - this function call sets tax percentage for the AMORxGuild Token. It is limited up to 20% with 0.01 step.

#### dAMOR Token (dAMORxGuild)

dAMOR token is non-transferrable ERC20  governance token for MetaDAO which is used for delegations and voting. dAMOR is received by staking AMOR token. dAMORxGuild is a governance token for a specific guild. It is obtained based on the AMORxGuild token.
Important variables:
- `mapping(address => uint256) stakes`
- `mapping(address => [address]) delegators` - this mapping is needed to know all of the people that delegated their tokens to the address, to calculate VP.
- `mapping(address => address) delegations` - this mapping is needed when user withdraws tokens. So we'll know all of the user's delegations, that we'll need to close. It is also useful to withdraw delegation.

Important functions:
- `stake(uint256 amount, uint256 time)` This function allows msg.sender to stake amount of AMORxGuild tokens, which will mint them the amount of dAMORxGuild tokens. The tokens are staked for the time, where $1\ week \leq time \leq 1\ year$. The more time passes, the more tokens will be given to the user. The simplest way to distribute tokens is as such(): $$ f(t)^2 = 2  $$ 
for t = 365, t - is an amount of days for which the tokens are staked.
The resulting amount of tokens is: $$ N_{dAMOR} = f(t)^2 n_{AMOR} $$
    
This function is impossible to call if the stake is already done.
    
    
- `increaseStake(uint256 amount)` - msg.sender receives funds, based on the amount of time remaining until the end of his stake. 
- `withdraw()` After the end of staking time user can withdraw their `AMORxGuild` tokens. This function will burn `dAMORxGuild` tokens of user and will return a corresponding amount of `AMORxGuild`. We burn all of `dAMORxGuild`, and return the amount of AMOR we get from mapping(`stakes`);
- `delegate(address account, uint256 amount)` delegates the `amount` of voting power from the msg.sender to another specific address.
- `undelegate(address account, uint256 amount)` undelegates the `amount` voting power away from the `account` address.

#### FXAMORxGuild Token (REP)

FXAMORxGuild token is a token used to represent the impact. This is a non-transferrable standard ERC20 token. It has mint and burn functions, where mint is can be called by GuildController, which receives a donation from a user, and afterwards 20% of the donation is getting reimbursed with FXAMORxGuild token. FXAMORxGuild can be used to support some report, which will result in burning of those FXAMORxGuild tokens. 
- `mapping(address => [address]) delegators`
- `mapping(address => address) delegations`
Important supported functions:
- `stake(address account, uint256 amount) OnlyOwner` where owner is GuildController contract. This contract receives ERC20 AMORxGuild tokens, which are getting locked and generate FXAMORxGuild tokens in return. Tokens are minted 1:1.
- `burn(address account, uint256 amount) OnlyOwner` - function burns FXAMORxGuild tokens if they are being used for voting. When this tokens are burned, staked AMORxGuild is being transfered to the controller(contract that has a voting function). If account has delegates, we burn tokens of the delegates first, if all tokens of the delegate are removed, we remove delegate from an array in `delegators`.
- `balanceOf(address account)`
- `delegate(address account)` - function that allows some external account to vote with your FXAMORxGuild tokens
- `undelegate(address account)` - function that removes token delegation and returns remaining tokens for vote.

This contract also shouldn't have `transfer` and `transferFrom` functions implementation.



## Second stage. Tokenomics management

### MetaDAO Controller. 

Controller contract controls the all of the deployed contracts of the MetaDAO. It mints FXAMOR tokens, disperses the flows of donations to the different pools both for specific Guilds. It is also responsible for deploying another guilds using factory contracts. MetaDAO contract is owned by DoinGud, and is a very important part of managing guilds. The difference between MetaDAO Controller and Guild controller, is that MetaDAO controller can create guilds, distribute funds between guilds(from the fees) and doesn't have a reputation system.  

In the sync of 2022/08/05 there was discussion around the donate function of the MetaDAO Controller. There was a proposal to add a `whitelistToken` function. During internal discussion it was questioned if it is necessary, as the donation goes to the MetaDAO itself in any case.  

Important variables:
- `guilds address []` Array of guild addresses, guild id is an index of the guild in the array.
- `guildDistribution uint256 []` Array guilds token amounts they can claim.
- `distributedTaxes uint256` - amount of already distributed, staked AMOR tokens.
- `distributedDonations mapping(address => uint256)` - amount of already distributed ERC20 donations.
- `tokenDistributions mapping(address => uint256 => uint256)` - Mapping of token distributions for specific token to specific guild.
- `guildDonationDistribution mapping(address => [uint256])` - mapping from token address to the array of guilds, showing the specific amount of tokens each guild can claim.
- `mapping(bytes32 => Index) public indexes` Mapping of all of the indexes in the guild(Set of guild weights).


It has following important functions:
- `donate(uint256 amount, address token, uint256 index)` - function that allows to donate AMOR tokens to the MetaDAO. It automatically distributes tokens between all of the guilds in which are supported by the MetaDAO based on the id of the provided index(which is a set of the weights). 
- `allocateByIndex(address token, uint256 amount, uint256 index)` - function gets an amount of AMOR tokens, which gets staked in the Controller contract, and calculates the distribution of the token between the guilds. It iterates through the `guildDistribution` array, and calculates the amount of distribution based on the this calculation: $$N_{i_{distr}} = \frac{indexTotalWeight}{indexWeight(i)}\ amount$$ Where $N_{i_{distr}}$ is an amount, with which `guildDistribution[i]` is going to be increased(the guild's share). This amount is equal to the rate of the guild weight, comparing to the all guilds weight, multiplied by the distributed amount.
- `setWeightDistribution(uint256 [] weights)` - sets weight distribution for all of the guilds in the contract. If guild is added it's weight is automatically set to a 0.
- `claim(uint16 guild)` - this function claims AMOR tokens from fees for a specific guild, then it swaps AMOR with AMORxGuild and transfers those funds to the guild organisational treasury(avatar, it's  basically GnosisSafe). 
- `claimToken(address token)` - this function claims AMOR tokens for a specific guild(needs to be called from it), then it swaps AMOR with AMORxGuild and donates those funds to the guild organisational pool.
- `createGuild(address realityModule, address initialGuardian, string memory name, string memory tokenSymbol) onlyOwner` - this function calls factory contracts to create guild Controller, and guild tokens. Guild name and tokenSymbol base are provided as a two strings.
- `distributeFees()` - this function is needed to distribute the fees obtained from the AMOR transfers between the guilds and DoinGud itself.
- `addIndex(bytes[] calldata weights)` - this function allows to add the custom index to set the donation distribution between the guilds.
- `addGuild(address controller) onlyOwner` - function allows to add guild by the `Controller` address. Because the Guild factory can be called by anyone, not only by the metaDAO itself.
- `removeGuild(uint256 id)` - function allows to remove guild from the Controller by it's id.




### GuildFactory (united with Token Factory)

GuildFactory deploys tokens, pools and controller contract, and connects them together into functioning guild.

Important function is:
- `deployGuild(string name, string tokenSymbol)`, which creates a Controller contract, and calls tokenFactory, which creates AMORxGuild token contract, dAMORxGuild token and FXAmorxGuild with Guild exchanged with a token Symbol.

### Guild Controller contract

Controller contract controls the all of the deployed contracts of the guild. It mints FXAMOR tokens, disperses the flows of donations to the different pools both for specific Guilds.
Important variables:
- `reportsWeight int256[]` - this is an array, which describes the amount of the weight of each report.(So the reports will later receive payments based on this weight)
- `votes mapping(uint report => mapping(address voter => int256 vote))`
- `voters mapping(uint report => address [] voters)`
- `reportsVoting [int]` - results of the vote for the report with specific id
- `totalReportsWeight [uint256]` - total Weight of all of reports
- `impactMakers[address]` - list of impactMakers of this DAO
- `claimableTokens mapping(address => uint)` - amount of tokens each specific address(impactMaker) can claim.
- `weights mapping(address => uint)` - weight of each specific Impact Maker/Builder.
- `totalWeight` - total Weight of all of the impact makers
- `timeVoting [uint256]` - time of the forst vote for the report with specific id
- `reportsAuthors mapping(uint report => address author)`

Important functions:

- `donate(uint256 amount, address token)` - function that allows to donate ERC20 tokens to the Guild. It automatically distributes tokens between Impact makers. 10% of the tokens in the impact pool are getting:
    1. Exchanged in the pool to AMOR(if it's not AMOR or AMORxGuild)
    2. Exchanged from AMOR to AMORxGuild using staking contract( if it's not AMORxGuild)
    3. Staked in the FXAMORxGuild tokens, which are going to be owned by the user. 
- `gatherDonation(address token)` - function that gathers donation from `MetadaoController` in specific token and calles `distribute` function for the whole amount of gathered tokens.

- `distribute(uint256 amount, address token)` - function called by donate and gatherDonation, distributes amount of tokens between all of the impact makers based on their weight.
    
    Afterwards, based on the weights distribution, tokens will be automatically redirected to the impact makers.
- `addImpactMaker(address impactMaker, uint weight) onlyOwner` - this function allows to add impactMaker with a specific weight. Only avatar can add one, based on the popular vote.
- `claim(address impact)` - allows to claim tokens for specific ImpactMaker address.
- `changeImpactMaker(address impactMaker, uint weight) onlyOwner` - this function allows to add change impactMaker weight, it will also result in `totalWeight = totalWeight +( weights[impactMaker] - weight)`. ATTENTION TO THE UINT ARITHMETICS!.
- `removeImpactMaker(address impactMaker) onlyOwner` - this function allows to remove impactMaker with specific address. Avatar will also do `totalWeight = totalWeight - weights[impactMaker]`.
- `setImpactMakers(address[] impactMakers, uint[] weight) onlyOwner` - function removes impact makers, resets mapping and array, and creates new array, mapping, and sets weights.
- `addReport(bytes report, bytes signature)` - function which adds another element to the reportsWeight, with weight 0, and starts voting on it. It also checks if a report is ok to start voting on(based on signature). Reports are getting collected, up until the point there are at least ten untouched reports. If there are ten reports, then the week is given to vote on the 10 reports. During the vote reports can be added, but they will be waiting and vote for them won't start. When the voting for the 10 reports is finished, and there are $\geq$ 10 reports in the queue, than the vote for the next report set instantly starts. The vote starts for all of the untouched reports in the queue. 
- `voteForReport(uint256 id, uint256 amount, bool sign)` - function burns the `amount` of FXTokens, and changes a report weight, based on a sign provided. It also sets a vote info for a specific voter. As soon as the first vote goes for report, we create a time limit for vote(a week). There is also an additional time limitation for the voting: $$(t_{vote} > 2\ days) \land (vote_{end} =\ 12AM(00:00)\ on\ Sunday)$$
- `finalizeReportVoting(uint256 id)` - function distributes funds, depending on the report ids, for which votings were conducted. If report has positive voting weight, then funds go 50-50%, 50% go to the report creater, and 50% goes to the people who voted positively. If report has negative voting weight, then 50% goes to the people who voted negatively. and 50%  gets redistributed between the passed reports based on their weights. The idea around this is to loop on all reports, and at first process negative ones, and redistribute funds to the positive reports, by redistributing weights. Afterwards we'll need tp loop again, and distribute tokens for the positive reports. Distribution between passed reports goes by formula: $$amountToSendReport = \frac{fiftyPercent * weight}{100}$$
Where $weight = \frac{reportsWeight[i]}{totalReportsWeight}$, $fiftyPercent = \frac{reportsWeight[id] * 50}{100}$.

If all of the reports were voted against, then the 50% of the funds are locked and going to be distributed after the next voting round to the successful reports.  

### Vesting Contract  
Early contributors are rewarded with dAMOR tokens which have voting power. By rewarding these contributors with vested dAMOR they are on the same footing as other community members (i.e. they aren't seen to receive preferential tokens or treatment).  

The vesting contract is funded with an AMOR allocation set aside at deployment. Any additional vesting will require more AMOR tokens to be locked in the contract.  

The initial vesting occurs over a set period of time with tokens vesting continuously in a straight-line. Addresses with a vested allocation will be able to claim their allocation according to the formula.  

Allowance must be made for additional vested tokens. The MetaDAO should be able to vest more tokens and reserve these for other addresses.  

The MetaDAO should also be able to change the vesting duration and vested amount allocated to an address.  

The Vesting Contract should allow the contributors to manage their tokens.  

The important functions are:  

~~`delegate(address delegatee, uint256 amount)`: allow an address that has vested tokens allocated to it to delegate that voting power to another address.~~
~~`undelegate(address delegatee, uint256 amount)`: undelegate the voting power that has been granted to another address.
`withdraw(uint256 amount)`: allow an address to withdraw dAMOR that has been allocated to it by DoinGud, provided the vesting conditions have been met~~ these functions are taken care of with `snapshot`
`withdrawAmor(uint256)`: allow an address to withdraw AMOR allocated to it that has reached maturity. This will swap dAMOR for AMOR.
`allocateVestedTokens(address target, uint256 amount, uint256 cliff, uint256 vestingDate)`: allocate an amount of tokens to a specific address.  
`vestAMOR(address target, uint256 amount, uint256 cliff, uint256 vestingDate)`: transfers AMOR to the vesting contract and allocates the dAMOR to the specified target address, with the specified parameters. This function is to be used when allocating new vested tokens to a target after initiation of the vesting contract.    
`modifyAllocation(address target, uint256 newTargetDate)`: changes the vesting end date of the target address. Note that this influences the vesting rate for that target address.  

# Third stage. Governance Execution

## Governance steps
Any proposal that is needed to be executed on both avatars needs:
1. Guardian adds proposal to the snapshot vote.
2. If proposal snapshot vote is passed, then a proposal is created on-chain for guardians vote.
3. If guardians approve(at least 20% vote, with >50% approvalss), then the proposal can be executed.

## AvatarxGuild(Avatar) contract

AvatarxGuild contract is needed to manage the funds of the guild, receive and execute the proposals, attach modules and interact with external voting contracts(for example attach snapshot vote). This avatar contract will implement Voting abstract contract, which will define the voting method used. 
It will require the following variables:

`mapping(address => bool) voters`- mapping to define voters.

It will require the following functions:

<!-- - `addProposal(bytes proposal, address to) returns (uint256 id)` - function adds a proposal to execute a transaction(encoded in bytes) to the specific address. Returns id of the proposal -->
- `executeProposal(address target, uit256 value, bytes proposal) onlyOwner` - executes the proposal, which was approved as a result of the vote(by the Governor).
<!-- - `voteForTheProposal(uint256 id, bool approval) onlyVoter` - allows to vote for the specific proposal, and approve or disapprove it.
- `addVoter(address voter) onlyAdmin` - allows to add voters to the Avatar contract -->
- `addModule(address module)` - this function is needed to add Zodiac or any other module to interact with DAO.(this will work as GnosisSafe modules)
- `execTransactionFromModule(address to, uint256 value, bytes data, Enum.Operations operation)` - allows to execute functions from the module(it will send the passed proposals from the snapshot to the Governor).

Contract should implement IAvatar interface:
https://github.com/ethereum/EIPs/blob/master/EIPS/eip-5005.md


## Governor contract
Governor contract will be based on the [IGovernor](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/governance/IGovernor.sol) interface, and will allow to add and vote for the proposals. Proposal execution will happen throught the Avatar contract. It has those important variables:
- `GUARDIANS_LIMIT` - amount of guardians for contract to function propperly, until this limit is reached, governor contract will only be able to execute decisions to add more guardians to itself.
- `proposals [uint256]` - it's an array of proposals hashes to execute. After proposal was voted for, an executor provides a complete data about the proposal, which gets hashed and if hashes correspond, then the proposal is executed.
- `guardians [address]` - this is an array guardians who are allowed to vote for the proposals.
Governor contract should support this important functions:
- `modifier GuardianLimitReached()`- this modifier is needed to validate that amount of the Guardians is sufficient to vote and approve the "Many" decision.
- `setGuardians([address] guardians) onlySnapshot` - this function resets guardians array, and adds new guardian to the system.
- `addGuardian(address guardian) onlySnapshot` - this function adds new guardian to the system.
- `changeGuardians(uint current, address newGuardian ) onlySnapshot` - this function changes guardian as a result of the vote.
- `propose(address[] memory targets, uint256[] memory values,      bytes[] memory calldatas) onlySnapshot` - this function will add a proposal for a guardians(from the AMORxGuild token) vote. Only Avatar(as a result of the Snapshot) contract can add a proposal for voting.
- `function castVote(uint256 proposalId, uint8 support) onlyGuardian` - function allows guardian to vote for the proposal. Proposal should achieve at least 20% approval of all guardians, to be accepted. Proposal should achieve at least 51% approval of voted guardians, to be accepted.
- `function execute(address[] memory targets, uint256[] memory values, bytes[] memory calldatas, bytes32 descriptionHash)` function allows anyone to execute specific proposal, based on the vote.

By the default on the contract creation, the person who inflicted the creation of the contract is set as the only guardian of the system.


## Snapshot proposal voting(for guilds and MetaDAO)

Snapshot voting is going to use Quadratic voting approach. All the votes for snapshot can be created only by Guardians. After the vote for proposal is passed, if it is succesful, we call an Avatar contract, using intermediary plugin contract for function execution in Snapshot.

Snapshot is created for each guild and each MetaDao. We need to check the possibility to create Snapshot automatically(by using APIs for ENS and Snapshot).