Completed: ICDevs.org - Bounty #17 - A DAO for Cycles - $10,000 - ht: cycle_dao

Special Note: Thanks to @Arthur and cycle_dao for accelerating this bounty. I’ve called it “A DAO for cycles” to avoid confusion, but this is a cycle dao, and THE cycle_dao has collaboratively agreed to be a sponsor on the bounty.

Special Note 2: This idea was inspired by a conversation I had with @Maxfinity about how we could use the IC’s properties to fund the actual operation of the IC(cycles).

A DAO for Cycles - #17

Current Status: Discussion

  • Discussion (02/25/2022)
  • Ratification
  • Open for application
  • Assigned
  • In Review
  • Closed

Official Link

Bounty Details

  • Bounty Amount: $10,000 USD of ICP at award date - Standard ICP Match Available
  • ICDevs.org DFINITY Foundation Grant Match: This grant was accelerated with by Arthur Falls and the cycle_dao for an additional $5,000. For every ICP sent to 596b5cdecdae9a8ba967d3bdc448d829f353c40c40a284b5f51a6ca283249e02 we will add .25 ICP to this issue and .75 ICP to fund other ICDevs.org initiatives.
  • Project Type: Team
  • Opened: 02/24/2021
  • Time Commitment: Days
  • Project Type: Library
  • Experience Type: Beginer - Motoko;
  • Issue Type: Motoko Canister

Description

This bounty gives the opportunity to

  • learn about DAOs and how they work
  • learn about cycles on the IC
  • learn about governance on the IC

The goal of this bounty is to create a DAO that collects cycles for a particular application’s canisters and rewards users that supply those cycles with DAO tokens that can be used to manage the DAO.

Functions:

wallet_recieve()-> async () - must be called from a cycle wallet that can pass cycles. Use the experimental_cycles functionality to deposit the cycles and mint new DAOTokens according to the following schedule:

  • If the Canister has more than MAX_CYCLES, refund all cycles.
  • Consult the CYCLE_EXCHANGE_CONFIG and reward enough cycles to get to MAX_CYCLES and reward with minted tokens according to the menu levls. If MAX_CYCLES is greater than the highest configuration should use the highest configuration as max_cycles. Default Config:
MAX_CYCLES = 150_000_000_000_000;

[
    {min:0; max:2_000_000_000_000; rate_per_T: 1: float;}
    {min:2_000_000_000_000; max:10_000_000_000_000; rate_per_T: 0.8: float;}
    {min:10_000_000_000_000; max:50_000_000_000_000; rate_per_T: 0.4: float;}
    {min:50_000_000_000_000; max:150_000_000_000_000; rate_per_T: 0.2: float;}
]

The canister should support calling the DIP20, EXT(Fungible), and Ledger canister standards for the issued DAOToken for fungible tokens and DIP721, EXT(non-fungible), and the nft_origyn standard for NFTs. The canister should be a minting canister for the DAO token canister and call mint on the DAO token canister passing in the determined value as the token value(fungible) or the meta_data variable if an NFT.

The canister should implement:

configure_dao(command: ConfigureDAOCommand) → async Nat;

Each command must be submitted to the configured governance canister for approval. The governance canister should return a Nat that is a unique proposal id. The governance canister is free to implement any approval or voting scheme that it would like.

type ConfigureDAOCommand:{
    #UpdateMaxCycles: Nat;
    #UpdateMintConfig: [ExchangeLevel];
    #DistributeBalance: { //sends any balance of a token/NFT to the provided principal;
        to: Principal
        token_principal: Principal;
        amount: Nat; //1 for NFT
        id: ?{#Text: Text; #Nat: Nat}; //used for nfts
        standard: Text;
    };
    #DistributeCycles; //cycle through the allow list and distributes cycles to bring tokens up to the required balance
    #DistributeRequestedCycles; //cycle through the request list and distributes cycles to bring tokens up to the required balance
    #ConfigureDAOToken: {
        principal: Principal;
    };
    #AddAllowList: {
        principal: Principal;
        min_cycles: Nat;
    };
    #RequestTopUp: { //lets canister pull cycles
        principal: Principal;
    };
    #RemoveAllowList: {
        principal: Principal;
    }
    #ConfigureGovernanceCanister: {
        principal: Principal;
    };
};

type ExchangeLevel {
    min: Nat;
    max: Nat;
    rate_per_T: Float;
};

The canister should implement execute_proposal(Nat) → Result<Bool, Error> that runs a proposal and should only be callable by the governance canister.

As an implementation detail of DistributeCycles, it should likely be wired up via timer function and automatically approved and executed by the governance canister. The internal logic should keep it from running too often.

The canister should implement query cycle_balance() → Nat that returns the current number of cycles in the canister.

The inspiration for this kind of generic infrastructure was inspired by Max and the InfinitySwap team that is implementing something similar to support their canisters.

To apply for this bounty you should:

  • Include links to previous work writing tutorials and any other open-source contributions(ie. your github).
  • Include a brief overview of how you will complete the task. This can include things like which dependencies you will use, how you will make it self-contained, the sacrifices you would have to make to achieve that, or how you will make it simple. Anything that can convince us you are taking a thoughtful and expert approach to this design.
  • Give an estimated timeline on completing the task.
  • Post your application text to the Bounty Thread

Selection Process

The ICDevs.org developer’s advisors will propose a vote to award the bounty and the Developer Advisors will vote.

Bounty Completion

Please keep your ongoing code in a public repository(fork or branch is ok). Please provide regular (at least weekly) updates. Code commits count as updates if you link to your branch/fork from the bounty thread. We just need to be able to see that you are making progress.

The balance of the bounty will be paid out at completion.

Once you have finished, please alert the dev forum thread that you have completed work and where we can find that work. We will review and award the bounty reward if the terms have been met. If there is any coordination work(like a pull request) or additional documentation needed we will inform you of what is needed before we can award the reward.

Bounty Abandonment and Re-awarding

If you cease work on the bounty for a prolonged(at the Developer Advisory Board’s discretion) or if the quality of work degrades to the point that we think someone else should be working on the bounty we may re-award it. We will be transparent about this and try to work with you to push through and complete the project, but sometimes, it may be necessary to move on or to augment your contribution with another resource which would result in a split bounty.

Funding

The bounty was generously funded by the DFINITY Foundation and cycle_dao. If you would like to turbocharge this bounty you can seed additional donations of ICP to 596b5cdecdae9a8ba967d3bdc448d829f353c40c40a284b5f51a6ca283249e02. ICDevs will match the bounty 0.25:1. All donations will be tax deductible for US Citizens and Corporations. If you send a donation and need a donation receipt, please email the hash of your donation transaction, physical address, and name to donations@icdevs.org. More information about how you can contribute can be found at our donations page.

General Bounty Process

Discussion

The draft bounty is posted to the DFINITY developer’s forum for discussion

Ratification

The developer advisor’s board will propose a bounty be ratified and a vote will take place to ratify the bounty. Until a bounty is ratified by the Dev it hasn’t been officially adopted. Please take this into consideration if you are considering starting early.

Open for application

Developers can submit applications to the Dev Forum post. The council will consider these as they come in and propose a vote to award the bounty to one of the applicants. If you would like to apply anonymously you can send an email to austin at icdevs dot org or sending a PM on the dev forum.

Assigned

A developer is currently working on this bounty, you are free to contribute, but any splitting of the award will need to be discussed with the currently assigned developer.

In Review

The Dev Council is reviewing the submission

Awarded

The award has be been given and the bounty is closed.

Matches

DFINITY Foundation Grant: - $5,000 USD of ICP at award date
cycle_dao - 40 ICP accelerator Matched with $5,000 from the DFINITY Foundatin Grant

Other ICDevs.org Bounties

8 Likes

Hello Skilesare,

I am quite interested to work on this! I have a few questions to see if I understood correctly the thing before I submit my application:

  • The cycle DAO canister does not directly mint its own DAO token. The “DAO token” comes from another canister (called here DAO token canister), that can be fongible OR non-fongible, and shall respect one of the six mentionned standards. It’s up to the cycle DAO canister to be configured with any canister performing as its DAO token canister.

  • For the configure_dao function signature, here it seems that a unique proposal ID will be created every time. Shouldn’t the function configure_dao rather return a ?Nat or Result<Nat, Error> instead of a Nat so that no usless propasal ID is generated (e.g. in case the caller doesn’t have any DAO tokens so no right to suggest a proposal)?

  • In #DistributeBalance I guess the token_principal is required in case the configured DAO token canister changes before the #DistributeBalance is adopted by the gouvernance canister, so the distribution can still be performed?

  • #DistributeCycles; //cycle through the allow list and distributes cycles to bring tokens up to the required balance. The required balance is the min_cycles specified in #AddAllowList right? Does the cycles DAO canister just refill up to each allowed canisters’ minimum ?

  • I am not sure to understand, will the #DistributeCycles be called periodically by a kind of controller canister or user (and in this case the cycles DAO needs to keep track of the last call and only perform the actual distribution if enough time has passed), or shall the cycles canister itself call the distributeCycles method in a loop (which I think is not possible) ?

  • I guess the reason there is a requestedList of canisters in addition of the allowedList periodically refed up, is in the case one or many canister are more needy than others and the DistributeCycles is not called frequently enough, to urgently provide cycles to these greedy canisters?

1 Like

Yes. For example, the standard ledger(ICP) has a “minting canister” and that canister has the rights to produce tokens. In this instance, the DAO canister would be the minting canister for whatever token you’d set up.

Good point! The spec is meant as a suggestion and I think this is a great change.

This is there so that if the DAO ends up with a balance of some other kind of IC based token, it can distribute those tokens. So for instance, if the DAO generates some NFTs as part of its configuration, this function could be used to distribute those. Or if it collects XICP it could distribute some to developers wanting to undertake a project.

Yes. Fill up to the specified amount. Maybe the vocab needs to be less confusing. Suggestions welcomed.

I thought about heartbeat, but that chews cycles. I figured it was better to let each application decide how it wants to call this. We just need to create the handle to be pulled by the dapp/dao.

That was my thinking. One method is a push and one is a pull.

1 Like

Thanks for your answers.

So for instance, if the DAO generates some NFTs as part of its configuration, this function could be used to distribute those. Or if it collects XICP it could distribute some to developers wanting to undertake a project.

So you suggest that the wallet_receive() functions could trade cycles against DAO tokens and optionally other token/NFT ? In this case the cycle DAO canister not only keep a reference to the canister for DAO tokens but optionally to a list of other token/NFT canisters?

That was my thinking. One method is a push and one is a pull.

I was wondering why do we even need a push method. But you seem to suggest the push (DistributeCycles) would be automatically accepted by the gouvernance DAO, but not the pull ? My understanding is the danger if you automatically approve every application’s canisters pulls is that you might open the door to DoS attacks that could burn all your DAO cycles, am I right?

  1. Yes. 2. Yes…something like that. A well-thought-out exercise in attack vectors would be warranted.

Ok cool, sorry for all the questions, I am quite new in this space :smiley:

So I’d definitly like to work on that!

Previous work: I don’t have much to show to justify my background, but I can say that

  • I have six years of programming experience in C++
  • I went through all the motoko tutorials, hands-on
  • I started my own motoko project a few months ago (that I’d like to keep private for now), I kinda hit a wall, then I started again experimenting with the sudograph library. Taking a step back I think it would be wise for me to try to get more involved in this space and becoming more knowledgeable before trying to build my own stuff. I have a lot of interest into DAOs.
  • I am part time available (around 20 hours per week), happy to work in a team or alone.
  • My goal is to be able to quit my current job and be able to sustain on working on community projects or my own projects, so this is very exciting for me

Overview how I will complete the task: right know I am thinking about creating a “minimum viable” cycles DAO canister that could only use a DIP20 canister for its DAO token, implementing the inner logic of the cycle DAO canister, and then playing around with a dummy “mocked” gouvernance canister. Then improving the inner logic, find workarounds for potential limitations, and finally implementing the other standards.

Timeline: I’d say 3 months if I’m alone, but this might be underestimated, as I say I’m quite new in this space. By exemple I only checked one token standard so far.

Post your application on the bounty thread: I hope this is it!

Basic Dao :: Internet Computer :eyes:

1 Like

Awesome! This bounty just got easier. :slight_smile:

2 Likes

I implemented the cycle_balance, wallet_receive, and set_token_dao functions. For now only the DIP20 token can be used as DAO token. Note that I slightly changed the data structure used for cycle_exchange_config, because the one suggested duplicates the information (max from interval N = min from interval N+1). Also the MAX_CYCLES is the last threshold in the array, again to avoid duplicates.

I added some tests (see srs/CyclesDAO/test/add_cycles.test.sh), it seems to work well so far.

Any comment will be appreciated.

The next steps for me if nobody has comment will be:

  • implementing a function that allows to modify the cycles_exchange_config: func update_config(interval: Types.ExchangeInterval).
  • plug the BasicDAO canister and start implementing execute_proposal
1 Like

Awesome! Will Review.

1 Like

Small update. I started to implement the configure_dao method. In the current implementation, the configure_dao method actually executes the command (but only if it is called by the governance canister), and I don’t have an execute_proposal method. I did that because to be able to submit a proposal to the governance BasicDAO canister, you need to have an account, otherwise an error is returned. And to me it didn’t make a whole lot of sense that the CyclesDAO canister gets an account to the governance canister (IMO the CyclesDAO shall be 100% the slave of the governance canister). Having a single method also removes some (I think) some unecessary logic for keeping a list of proposal IDs inside the CyclesDAO, because everything is handled internally by the Governance canister anyway.

For example, to submit a proposal to configure the CyclesDAO so it uses the “dip20” canister to mint the tokens (in ic-repl commands, see prelude.sh in tests):

call basicDAO.submit_proposal(
  record {
    method = "configure_dao";
    canister_id = cyclesDAO;
      message = encode cyclesDAO.configure_dao(
        variant {
          configureDAOToken = record {
            canister = dip20;
        }
      }
    );
  }
);

If the vote passes, the configure_dao method is called on the CyclesDAO canister which gets updated as intended.

Let me know what you guys think!

Great adjustment! Looks like you are making great progress. I’ll make sure you are marked as assigned on this bounty! I’ll do a deeper dive into the code when I get a chance.

1 Like

Hello, long time no see, I am coming back with another round of questions :slight_smile:

Concerning the token standards to interface with, I added the link I found to each of them. Could you confirm it’s the right ones? Also I have some remarks on some of them.

DIP20: GitHub - Psychedelic/DIP20: DIP20: A fungible token standard for the DFINITY Internet Computer.
→ The single standard implemented so far

EXT (fungible and non-fungible): extendable-token/ext-core.mo at main · Toniq-Labs/extendable-token · GitHub
→ There is a mintNFT function in the ERC721 (non-fungible) interface, but for the standard fungible interface I could not find any mint function. I am not sure what how am I supposed to mint then.

DIP721: GitHub - Psychedelic/DIP721 at main
→ Looks allright

nft-origyn: OGY Ledger Candid Interface · GitHub
→ I could just find the interface, so I won’t be able to test by creating a nft-origyn canister on my side. Other than that looks allright.

ledger: ic/ledger.did at master · dfinity/ic · GitHub and example here: examples/main.mo at master · dfinity/examples · GitHub
→ The problem I think I will have is that only the internet computer gov canister can mint ICP? With my current design one needs to give the ownership of the minting canister to the CyclesDAO canister to mint the tokens which is impossible here. So I think there is probably something off with my design.

Maybe instead of giving the ownership of the token canister to the CyclesDAO canister it shall still belong to the governance canister, and the CyclesDAO shall (periodically or when it does not have anymore tokens?) ask the governance canister to refill it ? Or is there a possibility to give authorization to mint to another canister? (but looking at each token standard, it does not seem possible)

Hey…Origyn just uses the NNS/SNS ledger standard for the OGY token. There are a couple extra dfx calls, but nothing that should be relevant. Here is the current branch: ic/rs/rosetta-api/ledger_canister at dip-20-passthrough · ORIGYN-SA/ic · GitHub

The NFT standard is not published in full yet but the first test canisters are coming out this week and you’ll be able to preview the API.

1 Like

I don’t think that this canister is going to be able to run with ICP. Like you say, it needs to be the minting canister to be able to mint. You don’t need to integrate it with the ICP ledger specifically. Since the SNS will use that interface it will be good to have the interface working so those tokens can set the minting canister to the DAO canister. Maybe something in the SNS would preclude this, we can ask @lara.

Hi all! just read through the thread a bit and keen to try to help, but I don’t think I understand the concrete question you have just now.
Can you maybe try to reformulate the question that I might be able to help with?

Do I understand correctly that in your setting there are the following canisters: governance, ledger / token canister, and cyclesDAO canister (that trades cycles for governance tokens)?
I currently don’t fully understand whether you imagine to use the SNS canisters for governance / ledger?
Is the question whether for some of them you could use the SNS cansiters and be compatible? Or how the control / minting cansiters are set in the SNS design?

1 Like

I think the question is: Will the SNS be its own minting canister, or can we have a configurable one?

If it is configurable then this CycleDao canister could be used as a minting canister and set on the SNS ledger as the minting canister

1 Like

Got it, thanks for clarifying!

If you just want to use the SNS canisters as a basis but implement you own additions, you should be able to set the minting account to anything when you deploy the ledger canister. If you chose any canister other than the governance canister it might however be trickier to implement voting rewards (if you want them to consist of freshly minted tokens).

If you want to use the SNS “out of the box”, then by the Carbon milestone it will have a first simple version of a reward scheme, where the SNS governance canister will indeed be set as the minting account (minting canister). The design is however still in the discussion phase (see here). This is just a first version however, so it might be possible to make this configurable later (e.g., for SNSs that don’t want voting rewards, with voting rewards that are minted this would be harder).

We will also have a community conversation about SNS rewards next week if you are interested! It will be held by @bjoernek who leads the work on the SNS reward scheme.
(Bjoern, please also feel free to add details that I might have missed here).

Hope that helps, please let me know if you have further questions!

1 Like

Thanks for the reply! Maybe it makes sense in the future to have multiple minting accounts if you have different functions that you want to mint tokens for. Example: A network that rewards for 1. Voting 2. Sending cycles to the network 3.

1 Like

Thank you both for your answers! So for now I am gonna do as you suggest lara, put the minting account to the CyclesDAO when I deploy it.