Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CIP-0071 | Non-Fungible Token (NFT) Proxy Voting Standard #351

Merged
merged 8 commits into from
Nov 9, 2022

Conversation

thaddeusdiamond
Copy link
Contributor

@thaddeusdiamond thaddeusdiamond commented Oct 13, 2022

This is the initial commit for a proposed Cardano Improvement Proposal (CIP) that would enable NFT projects that did not want to use a governance or other native fungible asset to perform verifiable on-chain votes.

[ Documentation: README.md, example/ ]


see rendered Markdown

@thaddeusdiamond thaddeusdiamond marked this pull request as draft October 13, 2022 05:20
@thaddeusdiamond
Copy link
Contributor Author

This is not ready for review yet I am just putting it up here so I have a pointer to get the process started. When I am ready for formal reviews, I will move it out of Draft status.

@thaddeusdiamond thaddeusdiamond marked this pull request as ready for review October 13, 2022 21:19
@thaddeusdiamond
Copy link
Contributor Author

This is now ready for community review. I will engage key participants to determine how best to get the ball rolling!

@rphair
Copy link
Collaborator

rphair commented Oct 14, 2022

thanks @thaddeusdiamond - I've added a direct link in your OP to the README.md file in your branch, in the form we use when new CIP PR's are submitted... please modify the text as you like & keep the link updated if your CIP draft changes path (I see your "example" is linked from the CIP text).

If you can indicate here which individuals or groups you are already addressing as "key participants" we can ask for feedback from any others who have submitted or reviewed proposals about voting, governance, and/or authentication by NFT.

The proposal looks technically sound to me, and complete with all formalities including the Path to Active that we would expect to eventually merge this with Proposed status, but I'm not qualified to review the technical details: therefore hoping that @KtorZ @SebastienGllmt and perhaps @michaelpj will be interested in reading & reviewing soon.

CIP-0071/README.md Outdated Show resolved Hide resolved
}
```

There is no requirement that the "ballot counter" redeem all "ballots" from the "ballot box" and send them back to the respective voters, but we anticipate that this is what will happen in practice. We encourage further open-sourced code versions that enforce this requirement at the smart contract level.
Copy link

@yHSJ yHSJ Oct 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it makes more sense to burn the "ballots" instead of returning them to their respective voters. There are a few reasons for this:
First, in the motivation section, you discuss the issues with governance tokens. Although the ballots are no longer usable (defined by the time in the policy), it seems unnecessarily confusing for users to receive what seems like a governance token. That could result in confusion and even scams.
Secondly, burning the asset will make it much easier for third parties to tabulate votes or at on the results. While all the data is provided to tabulate votes as a third party already, it is quite simple, and already done for other applications, to track the -1 mint transactions of all assets under a policy ID. Any on chain voting should allow vote results to be verified as simply as possible, which would be done through burning the assets.

Of course, once you burn the asset, there is the minUTXO that was previously attached to the asset. It is important to do SOMETHING with that minUTXO, so we don't end up with up to ~10.5K ADA locked in the contract address. I propose that it is all sent back to the voters, so the only cost associated with voting is the transaction fee, but I would also support splitting it with the developer(s) in some capacity to pay for their time.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the ideal here is to have the default ballot box perform:

  • Burn ballot
  • Send minUTxO back to address indicated by datum
  • Only do the above after verifying on chain that polls are closed

I would still like to leave this as optional if possible, though it will definitely be an initial implementation in the voting.js file in cardano-nft-mint-frontend.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would still like to leave this as optional if possible, though it will definitely be an initial implementation in the voting.js file in cardano-nft-mint-frontend.

I disagree with this. I think that if there is going to be a standard, it should be just that -- standard. A 3rd party should not have to change their tabulation system in order to verify votes. Ballots should always be burned once the polls have closed. If a voting organizer wants to give out a commemorative voting NFT (think the "I voted" sticker), they should then mint assets on a new policy ID and send those to the voters.

Leaving it up to choice in this situation seems like the worst choice, it removes the benefits of standardizing this.

Copy link
Contributor Author

@thaddeusdiamond thaddeusdiamond Oct 14, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As we discussed in the chat, because the minting of the "ballot" HAS TO occur in the same transaction as the "send-to-ballot-box" (to prevent a "mint now, vote later" attack) I think this is less of a concern. Monitoring for a burn transaction would actually duplicate the cost to viewers who would have to find the burn transaction per the standard.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Upon further discussion with @thaddeusdiamond in a Discord server (and re-reading the proposal), I now tend to agree that the burning shouldn't be required, at least not for the sake of a developer. I still do believe that burning the token makes the most sense (release the minUTXO, prevent confusion on the side of users), but it certainly isn't mandatory.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not sure about it being optional or not but I do agree that burning the token makes the most sense instead of sending it back to the voter.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it makes more sense to burn the "ballots" instead of returning them to their respective voters.

Yes.

I think that if there is going to be a standard, it should be just that -- standard.

Also.

Upon further discussion with @thaddeusdiamond in a Discord server (and re-reading the proposal)

I'd love to see the outcome of that discussion in the 'Rationale'. In particular, the rationale for burning/not burning token needs to be stated clearly as this is clearly a point everyone seems to be tripping on.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's mostly about the use case around commemorative NFTs. Will add a Rationale section with our offline discussion.

@thaddeusdiamond
Copy link
Contributor Author

thanks @thaddeusdiamond - I've added a direct link in your OP to the README.md file in your branch, in the form we use when new CIP PR's are submitted... please modify the text as you like & keep the link updated if your CIP draft changes path (I see your "example" is linked from the CIP text).

What is OP? I can update any forms you need I just was not sure of them.

If you can indicate here which individuals or groups you are already addressing as "key participants" we can ask for feedback from any others who have submitted or reviewed proposals about voting, governance, and/or authentication by NFT.

I have talked to several NFT projects that want this kind of functionality (especially for newer/smaller projects), but they may not be technical. I've reached out to encourage them to comment here.

The proposal looks technically sound to me, and complete with all formalities including the Path to Active that we would expect to eventually merge this with Proposed status, but I'm not qualified to review the technical details: therefore hoping that @KtorZ @SebastienGllmt and perhaps @michaelpj will be interested in reading & reviewing soon.

@rphair
Copy link
Collaborator

rphair commented Oct 14, 2022

What is OP?

it's a common Internet abbreviation for Original Post, i.e. your comment here. When new proposals come in I generally link them from there to the working proposal to make it easy for me & others to review, especially when on the spot at our biweekly CIP meetings:

see rendered Markdown

Since you had already mentioned the README.md in your own text I also thought you might want to merge these.

@KtorZ KtorZ changed the title CIP-0071(?): Non-Fungible Token (NFT) Proxy Voting Standard CIP-????: Non-Fungible Token (NFT) Proxy Voting Standard Oct 25, 2022
@KtorZ KtorZ changed the title CIP-????: Non-Fungible Token (NFT) Proxy Voting Standard CIP-???? | Non-Fungible Token (NFT) Proxy Voting Standard Oct 25, 2022
Copy link
Member

@KtorZ KtorZ left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall, the idea looks okay but I am missing a bit some perspective with existing similar voting systems such as SundaeSwap's (cc @Quantumplation) or Voteaire's (cc @codefly) from whom I'd appreciate their review / comments on this proposal.

I am also not fully sure to understand the need for a Ballot NFT and counting ballot contract altogether. Indeed, the counting seems to happen entirely off-chain, by fetching and ordering UTxO entries, and counting assets within them. The contract merely states the rule by which the ballot can be returned to their original voters. But, why bother minting and sending ballots in the first place then?

What is the added benefit compared to a more simple vote done by sending a transaction to oneself with the associated metadata? An off-chain process can easily fetch transactions with the appropriate metadata tag and count votes in a very similar fashion. If there's a clear benefit over doing that, it should be stated in the motivation (what problem this approach has) and the rationale (how this specification solves issues of that simple approach);


## Abstract

This proposal defines a standard mechanism for performing verifiable on-chain voting in NFT projects that do not have a governance token by using datums, plutus minting policies, and smart contracts.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

by using datums, plutus minting policies, and smart contracts.

This is implied by the fact that this is a verifiable on-chain voting solution. It would be nice in the abstract to get instead a high-level sense of how these are put to use.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will reword.


This standard provides a simple solution for this by using the underlying NFT to mint a one-time "ballot" token that can be used only for a specific voting topic. Projects that adopt this standard will be able to integrate with web viewers that track projects' governance votes and will also receive the benefits of verifiable on-chain voting without requiring issuance of a new native token.

It is not intended for complex voting applications or for governance against fungible tokens that cannot be labeled individually.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd love to see perhaps a concrete use case unfold in this section. Do you have a particular project as an example?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added 3. I can name names or leave it generic just let me know which is preferred in CIPs.

Mint => {
polls_are_still_open(tx.time_range)
&& assets_were_spent(tx.minted, minted_policy, tx.outputs)
&& assets_locked_in_script(tx, tx.minted)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the minting policy expected to check that the wallet has at least one NFT of the required policy in order to mint a ballot?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes that is assets_were_spent let me rename the pseudocode better


#### "Vote" -> eUTxO Datum

To cast the vote, the user sends the ballot NFT just created to a "ballot box". Note that for reasons specified in [the "attacks" section below](#Creation-of-Ballot-Without-Casting-a-Vote) this needs to occur during the same transaction that the ballot was minted in.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What enforces that? The minting policy?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes that is the assets_locked_in_script function above (will rename to assets_locked_in_ballot_box).

};
```

The `voter` element is extremely important in this datum so that we know who minted the ballot NFT and who we should return it to. At the end of the ballot counting process, this user will receive their ballot NFT back.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not burn the ballot?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See the new Rationale section around commemorative NFTs.

1. Ensure the polls are closed (can be either on or off-chain)
2. Iterate through all UTxOs in forward-time-order locked in the "ballot box" and for each:
* Determine which assets are inside of that UTxO
* Mark their most recent vote to match the `vote` object in the UTxOs datum
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this last point answer a question I was about to ask about multiple votes. What happens when a voter mints multiple ballots and votes multiple time? This suggests only the most recent vote is counted.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct. Only the most recent vote is counted, although all votes are technically "valid". Basically we are allowing voters to change their mind.

}
```

There is no requirement that the "ballot counter" redeem all "ballots" from the "ballot box" and send them back to the respective voters, but we anticipate that this is what will happen in practice. We encourage further open-sourced code versions that enforce this requirement at the smart contract level.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it makes more sense to burn the "ballots" instead of returning them to their respective voters.

Yes.

I think that if there is going to be a standard, it should be just that -- standard.

Also.

Upon further discussion with @thaddeusdiamond in a Discord server (and re-reading the proposal)

I'd love to see the outcome of that discussion in the 'Rationale'. In particular, the rationale for burning/not burning token needs to be stated clearly as this is clearly a point everyone seems to be tripping on.


### Returning the "Ballot" NFTs to the Wrong User

During the construction of the ballot NFTs we allow the user to specify their vote alongside a `voter` field indicating where their "ballot" NFT should be returned to once the vote is fully counted. Unfortunately, this is not strictly checked inside the Plutus minting policy's code (largely due to CPU/memory constraints). So, we rely on the user to provide an accurate return address, which means that there is the potential for someone who has not actually voted to receive a commemorative NFT. This does not impact the protocol though, as the "ballot" NFT was legally minted, just returned to the incorrect location. That user actually received a gift, as they can now burn the ballot and receive some small amount of dust.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unfortunately, this is not strictly checked inside the Plutus minting policy's code (largely due to CPU/memory constraints).

I am not sure that this is a valid justification. Nor do I understand actually how one would check the voter field? Surely, there isn't much that the minting policy can do besides verifying that it's indeed a well-formed public key.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's fair, we can remove it (or make the standard be that the user's return address is checked). Basically you would have to check that the datum.vote.address field matches the pubkeyhash of one of the signatories of ballot creation. Should we change it to that?


The Helios code above simply checks that during a burn (as indicated by the Plutus minting policy's `redeemer`), the user is not attempting to mint a positive number of any assets. With this code, *any Cardano wallet* can burn *any ballot* minted as part of this protocol. Why so permissive? We want to ensure that each vote is bringing the minimal costs possible to the user. In providing this native burning mechanism we can free up the minUTxO that had been locked with the ballot, and enable the user to potentially participate in more votes they might not have otherwise. In addition, users who really do not like the specific commemorative NFTs or projects that choose to skip the "commemorative" aspect of ballot creation now have an easy way to dispose of "junk" assets.

## Potential Attacks and Mitigations
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please nest this under a Rationale section.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

- The mapping function described in the [Plutus minting policy for ballots](#Ballot---Plutus-Minting-Policy) should likely be some sort of prefixing or suffixing (e.g., "Ballot #1 - <REFERENCE NFT>"), and NOT the identity function. Although the asset will be different than the reference NFT due to its differing policy ID, users are likely to be confused when viewing these assets in a token viewer.
- The "ballot" NFT should have some sort of unique metadata (if using [CIP-25](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0025)), datum (if using [CIP-68](https://github.com/cardano-foundation/CIPs/tree/master/CIP-0068)) or other identification so that the users can engage with the ballot in a fun, exciting way and to ensure there is no confusion with the reference NFT.
- The "vote" represented by a datum will be easier to debug and analyze in real-time if it uses the new "inline datum" feature from Vasil, but the protocol will still work on Alonzo era transactions.
- The "ballot box" smart contract should likely enforce that the datum's "voter" field is respected when returning the ballots to users after voting has ended to provide greater transparency and trust for project participants.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I am missing from the justifications here is why this solution addresses the problem stated in the motivation. As far as I understood, one of the core motivation was the cost of minting governance tokens (min ada value), but this proposal also proposes to mint ballot NFT and only, optionally return the ballot NFT to the voter. So arguably, the proposal only optionally addresses the motivation. Since this is stated as a motivation, I think the proposal should better describes how it solves this issue systematically.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair. I think we can then enforce that the ballot has to be either:

a) Returned to the user
b) Burned and the ADA unlocked returned to the user

Thoughts?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd have a slight preference for "Burned and ADA returned to the user" because most users wouldn't probably know how to do that in the first place. Plus, it makes the protocol complete this way by cleaning up after oneself.

@thaddeusdiamond
Copy link
Contributor Author

Thank you so much for the review. A couple of your comments I have follow-up questions on. I am also pushing a new revision that hopefully addresses some of your thoughts. More feedback welcome (especially @codefly and @Quantumplation seeing as they have governance experience.

Overall, the idea looks okay but I am missing a bit some perspective with existing similar voting systems such as SundaeSwap's (cc @Quantumplation) or Voteaire's (cc @codefly) from whom I'd appreciate their review / comments on this proposal.

I am not familiar with SundaeSwap but Voteaire uses metadata (correct me if I am wrong @codefly I am going off of https://github.com/voteaire/voteaire-onchain-spec). I would like to move the procedure on-chain to allow for on-chain verification of vote authenticity (e.g., enforcing RCV/vote selection in a Smart Contract).

I am also not fully sure to understand the need for a Ballot NFT and counting ballot contract altogether. Indeed, the counting seems to happen entirely off-chain, by fetching and ordering UTxO entries, and counting assets within them. The contract merely states the rule by which the ballot can be returned to their original voters. But, why bother minting and sending ballots in the first place then?

The creation of the ballot and simultaneously sending in the vote is the entire process. Counting can occur off-chain (just like NFT viewers retrieve IPFS images off-chain), but the validity of the votes are enforced on-chain. I am going to add a few use cases in the "Motivation" section that hopefully clear up why you need to mint the ballots on-chain.

What is the added benefit compared to a more simple vote done by sending a transaction to oneself with the associated metadata?

For dApps, it is much simpler (and more cost effective against service providers) to look for UTxOs at a single address (the ballot box). Otherwise we would have to snapshot the addresses who held and look at all their txn's during a voting period to find the specific vote. We also could not enforce vote authenticity on-chain.

An off-chain process can easily fetch transactions with the appropriate metadata tag and count votes in a very similar fashion. If there's a clear benefit over doing that, it should be stated in the motivation (what problem this approach has) and the rationale (how this specification solves issues of that simple approach);

I will update the motivation and rationale for why this needs to be on-chain.

@victormundi
Copy link

victormundi commented Oct 29, 2022

Thank you @thaddeusdiamond for writing up this proposal. We would certainly be adopting and using this standard at ApeWatch in order to let holders of our NFT - to be released - vote on various topics, e.g. which feature to build next - since we're aspiring to form a decentralized business council behind our platform.

@rphair rphair changed the title CIP-???? | Non-Fungible Token (NFT) Proxy Voting Standard CIP-0071? | Non-Fungible Token (NFT) Proxy Voting Standard Nov 8, 2022
Copy link
Member

@KtorZ KtorZ left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As outlined in the editors meeting today, we're happy to see this proposal merged as Proposed while implementations are being worked on.

Please come back with updates on the proposal (might it simply be changing the status to 'Active') as the implementation progresses.

Copy link
Collaborator

@rphair rphair left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only noticed this from a recent review comment but there are several misspellings of ada, using instead the trading symbol ADA (according to Cardano Foundation long standing documentation, a style error):

[edit: p.s. for verification, please see this official page about Ada; since the trading symbol is never mentioned, note that it is never written in all-capitals: https://cardano.org/what-is-ada]

CIP-0071/README.md Outdated Show resolved Hide resolved
CIP-0071/README.md Outdated Show resolved Hide resolved
CIP-0071/README.md Outdated Show resolved Hide resolved
CIP-0071/README.md Outdated Show resolved Hide resolved
CIP-0071/example/voting.html Outdated Show resolved Hide resolved
This is the initial commit for a proposed Cardano Improvement Proposal
(CIP) that would enable NFT projects that did not want to use a
governance or other native fungible asset to perform verifiable on-chain
votes.

[ Documentation: README.md, example/ ]
See changes in README.md
[See rendered Markdown](CIP-0071/README.md)
@thaddeusdiamond
Copy link
Contributor Author

Thank you @rphair. I have updated the CIP to to have the appropriate spelling for ADA -> ada. Can you advise on how to complete path to Active? So far I have:

  • Considerations for ranked-choice voting if projects wish to have it
  • Minimal reference implementation making use of Lucid (off-chain), Plutus Core using Helios (on-chain): Implementation
  • Open-source implementations from other NFT projects that make use of this CIP
  1. RCV is listed in this README as an add-on that can be verified through the datum
  2. Reference implementation is checked in here
  3. I open-sourced the full example (which we conducted and completed at wildtangz.com/vote) as part of the cardano-nft-js-toolkit repository I have.

Should I add those references somehow?

one final update for ada vs. ADA typographical convention
Copy link
Collaborator

@rphair rphair left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@thaddeusdiamond I'll go ahead & merge this since it already satisfies the requirements laid out at yesterday's meeting and more. Since you'll be updating this to Active soon enough, perhaps you'd link to the "full example" (cardano-nft-js-toolkit) then (when you list your active implementations). 😎

@rphair rphair merged commit 5b50794 into cardano-foundation:master Nov 9, 2022
@nftguild
Copy link

nftguild commented Apr 9, 2023

In the 20th NFT Guild Roundtable we explored CIP 71 with its author @thaddeusdiamond

Watch the entire Roundtable:
https://youtu.be/Yyx4EEFXRh4

Or read through the transcript:
https://drive.google.com/file/d/1gz9z0Gp0nh2uK6OgdmhmQHg-8OzAJbvS/view?usp=sharing

@rphair rphair changed the title CIP-0071? | Non-Fungible Token (NFT) Proxy Voting Standard CIP-0071 | Non-Fungible Token (NFT) Proxy Voting Standard May 24, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants