Token Curated Registries

A common requirement I have seen, is the need of having a mechanism that allows listing elements, and manage the presency of these, in a way that promoves that these elements are aligned with the goal of the list:

  • For #core-dapps, a descentralized way to control the publishing of DApps.
  • For #core-infra, a way to register and discover trusted server nodes.

Probably more requeriments like these will appear in the future, hence the importance of having a form of handling this kind of behaviours, and also to add additional use cases for SNT.

Lots of research have been done already for Token Curated Registries

  • https: // medium . com/@tokencuratedregistry/the-token-curated-registry-whitepaper-bd2fb29299d6 A huge list of resources related to TCRs
  • https: // github . com/skmgoldin/tcr Generic TCR

Leveraging the work already done on these resources, as well as the ProposalCuration contract 3esmit developed, I have adapted the code from the generic trc to use Status’ set of Democracy contracts for the voting mechanism

https: // github . com/status-im/contracts/tree/000-tcr

The intended flow for using these contracts is the following:

  • The controller of a contract (right now a multisig address, in the future a DAO) after instantiating the contract, proceeds to set the TCR parameters:
    – Proposal application period: number of blocks before an unchallenged application may be whitelisted and included in the TCR
    – Proposal voting period: number of blocks where voting for a challenged proposal is allowed
    – Submission price: minimum number of tokens to allow a proposal to be added to a list, and a challenge to be made. (Can be set to specific addreses too)

For the proposal creation,

  • A token holder that wishes to submit a proposal, needs to inquiry the submission price via getSubmitPrice, and then, to submit a proposal, for this, the function submitProposal is invoked, with a _data parameter that will include the information that represents an element in the TCR (think of IPFS/Swarm hashed, Metadata, etc.), and also a _depositAmount which will be used as a balance for the proposal. This needs to be greater or equal to the submission price, and the TCR contracts needs an allowance equals to this amount for the correct execution of this function.
  • This balance can be increased and decreased, the only requirement is to keep the balance greater than the submission price
  • After the whitelisting period ends, the owner of the proposal can invoke processProposal to proceed to whitelist it, however, a proposal may be challenged as soon as it is submitted, so it may not be able to be whitelisted.

For the curation of proposals

  • When a proposal is created, a different token holder might challenge it to remove the proposal if he feels it doesn’t belong to the TCR (or if he wants to be a bad actor, but the TCR has a mechanism to handle this case).
  • The challenger invokes the challenge function specifying which proposal he wants to challenge. He needs to give an allowance to the TCR contract of at least the submission price.
  • When the proposal is challenged, a votation process is created, and any token holder may vote to allow the proposal to be included in the TCR or not. This voting is done using the ProposalManager contract and specifying the challenge ID:
let receipt1 = await ProposalManager.methods.voteProposal(challengeId, vote).send({from: accounts[0]});      
let receipt2 = await ProposalManager.methods.tabulateVote(challengeId, accounts[0]).send({from: accounts[0]});
  • After the voting period is complete, and the votes tabulated, processProposal can be invoked by anyone to proceed to determine the voting outcome

  • If the proposal is rejected, the submission price is deducted from the proposal balance, the remainder is transferred to the proposal owner, and the proposal is delisted.

  • The proposal balance that was deducted is divided among the challenger and the holders who voted to reject the proposal in a proportion determined by the rewardPercentage variable.

  • The same behavior is applied when the proposal is approved, but instead of the balance being deducted, is the challenger stake that is deducted, and divided in the same proportion for the tokens holder who approved the proposal, and increasing the balance of the proposal.

  • The token holders that voted may claim their reward after voting ends and proposal is processed using the claimReward function.

I made some changes to the ProposalManager contract to add some additional view functions, and also to be able to specify the voting period and the quorum percentage. I’m not sure that having 50% + 1 votes (Majority rule) is the ideal way to determine a proposal result, since we may fall into a classic tirany of the majority result where the other 50% - 1 token holders disagree with the voting result (and this represents a big part of the token holders, as well as the difficulty of getting that amount of votes (even if we use delegates), because of the dificulty of gathering the amount of SNT required due to the existing supply and the interest a token holder may have or not in voting for proposals.

My idea is to have a bonded curve token that can be bought with SNT, and this token is created along with the TCR. The idea is that in order to publish and vote for elements in a TCR, you need to acquire the bonded curve token (BCToken from now on) (where the higher the supply of tokens is, the higher the cost. See https: // medium . com/@simondlr/tokens-2-0-curved-token-bonding-in-curation-markets-1764a2e0bee5), and the BCToken can be sold back for SNT in any moment. Also, since the supply of the BCToken will be way less than the SNT supply, we can change the quorum percentage to a value that is more representative to the wish of the majority. (This is something I’m currently researching).

Also something that caught my attention in https : //github . com/skmgoldin/tcr is that they have a token curated registry for handling the parameters too, which I believe it will not be necessary in the current TCR contract, since in the future these parameters can be handled with the democracy contracts.

The TCR contract can be used without requiring the BCToken, tho. So in case we do not require this, we can use SNT token or any other token without problems.

If you guys wish to see the TCR in action, assuming you have embark installed, you can execute

embark test test/tokenCuratedRegistry.js

2 Likes

This is a nice use case for a TCR as well as perhaps a bonded token.

Something I’m curious about:

Where should meta discussions about TCR application/challenges take place? Presenting evidence to support challenges or inclusions are an important part of the mechanism. Should they be in a dedicated Status chat channel? Where is it easiest to minimize noise/spam while not excluding any relevant voices?

I believe having a specific channel for discussing the challenges is ideal, specially when the “Indicators of Trust” use case from the whitepaper is implemented: https://wiki.status.im/The_Status_Network_Whitepaper#Indicators_of_Trust

That use case would let us have an scenario where an user would need to acquire N bonded tokens in order to join a semi-private group chat to discuss the proposal inclusions and challenges, and also this would open the possibility to allow any users to besides having private group chats, to be able to moderate their own TCRs inside the Status app.

I think we can use Voting Dapp for that, we just need create specific topics to certain addresses. We could setup lower quorums for approve/reject to make easier actions and so lower gas consumption needed to approve proposal.

EDIT: If you’re reading this as the intro to a problem statement, I have updated the naming in the contract and spreadsheet to just use the word “vote” instead of “token”. See the contract gist for more.

I’ve been thinking about this thing we want to do called Optimised for Status which is sort of like a rewards/loyalty program for devs and teams who make sure their DApp works well in Status. I want to be creative with this, and I see an opportunity to link v1 of a TCR for DApp curation with this idea of being Optimised for Status.

Basically, we build an incredibly simple TCR, because my worry is that all the challenges, votes and other nuances associated with the above are just too complex, and that people will just not use it, or find some way of manipulating the fact that it is difficult for most to grok.

  1. We give each DApp that is Optimised for Status 10,000SNT for e.g.
  2. We build a beautiful page in our new technical docs where they can submit this as a stake to the proto-TCR in one click.
  3. The stake signals a vote, most likely for their own DApp.
  4. The DApps that appear first in Status (split into appropriate categories) are the ones with the highest number of tokens staked.
  5. Staked tokens produce a bonded curve token specific to that DApp.
  6. This means the more someone stakes, the cheaper it will be for others to buy their specific token.
  7. Key Insight. the more you’re willing to spend to get to the top of the list in your category, the cheaper it is for people to influence that position.
  8. To re-iterate: The more tokens they have staked, the cheaper the DApps bonded curve tokens are to buy.
  9. Community members who own those tokens can vote with them, either positive or negative.
  10. The DApp’s position in the list depends on both raw staked SNT and community votes.
  11. Can be at first by as simple an algorithm as, every 5% over 50% of negative community votes equates to -1 absolute position in the registry.

The biggest challenge with this stuff, summarised nicely here from a balanced, techno-economic stance, is “How do we actually get people to care?”. How do we get them to vote, challenge and keep up with it all?

I’m not sure we can, which is why I propose that we simplify vastly, and implement as free a market as we can for v1, with this twist that the more you spend, the cheaper it is for others to influence that position, and by making sure that the people we make it easy for to enter the registry and actually vote are the DApp developers themselves, who are likely to always, and almost exclusively, be the only interested parties.

I also don’t think challenge is the right word. But, if you care enough, you can buy some of the bonded tokens to signal your point of view. That way we get radical free markets, with what could be a pretty robust check of community input if we make it easy enough to express an opinion. Like Engelbart’s knowledge repositories, you want the top down structure of a pure price discovery mechanism operating in some categories we define for UI purposes, but then you also want ground-up, grassroots feedback that becomes an indelible part of the history of that DApp in the registry.

                                     . . .  

You also don’t need to incentivize people to buy the bonded token, and rewards complicate things enormously imo. This is the other shift in what I’m saying.

We’re designing things as if everyone is going to take part and vote, and be a good citizen etc etc, but I just don’t think that maps to reality at all.

Why, really, would you want to buy tokens and vote at all, then? Well, a number of real world reasons:

  1. You’re actually a DApp developer and care about where your product sits. Therefore, you’re willing to spend some SNT to get a better ranking.
  2. You are a competing DApp developer (or a malicious actor) and want to buy a lot of your competitors/targets tokens in order to downvote them and get them lower in the registry.

The case of 2 is why we use a bonded curve, rather than something completely simple. If you want to downvote your competitor effectively, you’ll need more than (say) 50% of their bonded tokens, and this should cost you considerably.

We also don’t need governance around this, because the SNT you use to buy the the bonded tokens can go directly back to their owner - the more the community is using their tokens to vote, the less SNT DApps actually need staked. (The same value they staked would stay staked, it’s just that they receive other SNT back from addresses interested in voting on their DApp’s position).

@barry made the interesting point/action item that it would be great to have a simulator a la MakerDAO, and I totally agree that getting these percentages just right is key and an simulator would be amazing to test some ideas in.

I object to the some thinking among fans of TCRs that we are “building these for the community”. Count me incredulous. Which community? From Status’ perspective, we are really interested (in our DApp curation discussions) in the community of DApp developers only. The wider community of DApps users can provide skin-in-the-game feedback if they like, but my guess is that most won’t because people generally don’t really know what they want till you give it to them.

Basically, imo, the goal of cryptoeconomics is decidedly not “more egalitarian systems”. The goal of cryptoeconomics is to design systems without any single actor, or small group of actors, at the top, in control.

Greater decentralisation - which lies on a continuum - is the goal. Egalitarianism is a great success metric for it, but not the other way around.

1 Like

One interesting side-effect of this proposal is that it allows for a really easy upvoting/downvoting system, which is actually just a donation mechanism for the DApps themselves.

If you “upvote” a DApp in Status, it’s essentially equivalent to buying some of the bonded tokens and voting positively. Your SNT goes back to the developer as a “donation” that they can use for further development.

If you “downvote” a DApp in Status, that’s fine, it’s easy to do for whatever reason, but you have to pay to have your say AND it becomes more and more expensive to do so the closer the community votes get to actually moving the DApp in the registry. It’s a weird game-theoretic donate-to-manipulate equilibria afaict.

The point is that these are side-effects, effective success metrics etc, NOT the design goal of the cryptoeconomic system itself, which simply optimises for the most decentralised and free market possible.

P.S. The SNT that is staked by DApps to get into the registry also need not concern us at a design level. It’s presence is a skin-in-the-game sign of commitment that the DApp works, so this staked, static SNT is - much like burned Ether - a donation to the whole community of SNT users. Less tokens roughly equals more demand, hence more value for all other holders. It’s a great, natural token sink for SNT.

Yes, malicious users can pump their apps by buying in, but they’ll always end up giving more to the community than the community needs to spend to lower their ranking sufficiently. I don’t think we should build any backdoors into this either, users can easily petition Status or other larger holders of SNT if they think an app has turned malicious, which should be enough to remove it from the first page. Status itself can also provide warnings in the UI independent of the functioning of the registry as well, for instance.

The most obvious way I can see to attack this is for a DApp to stake itself with X SNT, then buy most of it’s bonded tokens to vote for itself. This is especially easy when prices are low at the start - another security feature of the amount of SNT staked in registry, incidentally. The more SNT staked, the more confident we can be of the legitimacy of the DApps at the top.

I’ve thought about alternatives for solving this, like making Optimised for Status include an NFC or other token that grants rights in an emergency vote, but - honestly - even that is introducing too much complexity, expecting too much of users, and generally brings with it a host of other problems.

The easiest and simplest imo is to say that the DApp curation contract is an open standard, but in exceptional cases, Status reserves the right to clean up a smudge on our window into Ethereum if the community clearly thinks it is and can justify why. It’s important this justification is formal and falsifiable - so it could take the form of an RFC through this forum, for instance. We can hold votes by other, separate means if necessary, but I don’t think it should be included in the initial design, and would likely be one of many functions through our eventual DAO.

Hey @cryptowanderer
I think I finally grok what all this is about.
Are you available to jump on a call maybe?
Thinking that the bancor approach to a bonded curve may be helpful in this scenario.
I wanna understand why you chose the curve you did, more about point #11 above, and whether being a bonded token users could sell their tokens back again, even though the price is always sinking.
-Billy

@cryptowanderer you are one seriously smart dude! HA! :slight_smile:

Ok, my response to this has been stalled for weeks but I’m ready to get back into thinking through this wild scenario @cryptowanderer. Because I’m dense I still feel the need to try to break down your proposal into pieces in order to speak about it effectively.

On a macro level, the goal of your proposal is to design a system with the following features:

  • It produces a list of ordered dapps (with potentially far reaching alternative applications)
  • Anyone can attempt to join the list, however there will be some cost to make it on
  • Dapp creators, who have the most interest in being on the list, should primarily bear the costs in making it onto the list
  • Users of the dapp should be able to effect the list in a significant way
  • The system should be sybil attack resistant (inflated reviews from imaginary customers)

In order to achieve these goals there is a smart contract which accepts SNT in order to manage this list of dapps. Please correct me if these are out of date @rramos, but the functions within this contract are:

  • createDApp()
    • This function is used for registering new dapps within the list. It accepts a parameter for _amountToStake which is the amount of SNT staked towards the dapp by the dapp creator
    • The value of dapp.SNTBalance is increased and the SNT is held by the contract
  • stake()
    • This function lets you stake more SNT to a specific dapp.
    • The value of dapp.SNTBalance is increased and the SNT is held by the contract
  • upvote()
    • This function calculates a number of votes which can be used to “upvote” an app. The cost of creating these upvotes is calculated with costOfMinting(), and the SNT used in upvoting is sent to the dapp creator
    • The positive votes are stored in dapp.votes
  • downVote()
    • This function calculates a number of votes which can be used to “downvote” an app. The cost of creating these downvotes is calculated with costOfMinting(), and the SNT used in downvoting is sent to the dapp owner
    • The value dapp.effectiveBalance is decreased
    • The negative votes are stored in dapp.votes
  • withdrawStake()
    • This function allows the dapp developer to withdraw some amount of SNT that was previously staked from the contract. The SNT is sent to the dapp creator
    • The value of dapp.SNTBalance is decreased
  • costOfMinting()
    • This function is used within upvote() as well as downvote() in order to determine how many upvotes or downvotes can be generated with some amount of SNT
    • The details of the math become are a little confusing but the bottom line is that the more SNT that is staked to an app, the cheaper it is to mint new (positive or negative) votes

All of this is explicit but what is missing is how these values are interpereted and used to display the order of the Apps. The position on the list is determined by some combination of dapp.SNTBalance, dapp.votes (both positive and negative) and I assume the dapp.effectiveBalance. @cryptowanderer said:

  1. Can be at first by as simple an algorithm as, every 5% over 50% of negative community votes equates to -1 absolute position in the registry.

TBH I don’t understand what that means, but I think it has something to do with not calculating it as simply as positionOnList = dapp.SNTBalance - totalNegativeVotes + totalPositiveVotes. I assume that’s to avoid some of the possible attack vectors, for example the following:

A dapp buys in big at the begining (making votes very cheap to mint) and mints positive votes for their own dapp over and over (the SNT goes back to them anyway), then withdraws their stake (making votes expensive to mint) again. This would result in a high rank that is difficult to move. Furthermore the dapp creator does not need to keep a lot of stake tied up in the contract.

Now it’s not outlined in the contract code, but I remember from a call I had w @cryptowanderer that he had considered similar scenarios. I think his proposal was that a percentage of the SNT used in upvotes or downvotes should be burned. This would create diminishing returns on that sort of self-serving behavior. And the calculation of dapp position would have to rely much more heavily on the current amount of SNT staked to prevent the “lock in” of a high position from positive votes followed by a withdrawn stake.

The flip side of self-serving behavior would be adversarial behavior from competing Dapps:

A dapp may spend as much or more downvoting competitors as upvoting itself. The contract says this downvote SNT stake should go to the downvoted developer. They may in turn counteract that downvote with an upvote—the results of which would depend on whether the SNT is burned or delivered straight to the dapp developper, and whether or not the number of votes also effects the cost of minting new votes. This process could repeat ad infinitum or until counter-espionage budgets are depleted.

There is a very rich excel file describing possible parameters on the vote minting and dapp placement that also needs a thorough grok. Maybe it’s better to stop first and ping @cryptowanderer. Could you parse my understanding of the proposal so far and please amend, append, reject and rejoice the parts that are correct and the parts which have sorely missed the mark? Afterwards maybe you could gently introduce the math used in costOfMinting() as well as the ranking algorithm that combines all of these stakes and votes?

Welcome back @okwme - as humble and self-effacing as ever, even after an ECF grant. It is truly a pleasure to behold :heart:

You are spot on right up until the end of the bullet point list.

After this, things get hairy. Very simply: only negative votes affect the effectiveBalance, which is also the value actually used to rank DApps, and the value that would be displayed in the Store in Status.

So, upvotes make it more expensive to cast further votes, but they don’t move the DApp in the store (which is exactly equivalent to saying "they don’t have an effect on the effectiveBalance").

There is no % that is ever burned. What I did say was that only a % of the SNT staked per DApp is actually available to mint votes with, because of a specific attack. Consider: I stake some SNT to get my DApp in the store. I then buy all the votes I can and upvote with them - remembering that the cost for doing this comes directly back to me as the developer. So, if 100% of what I had staked was available to mint votes with, I could get 100% back AND ensure that no-one could move my DApp.

  1. We have a social mechanism which says, if you can prove vote manipulation (seeing as it is on chain it should be possible to prove), then the Status UI does not need to honour contractual reality.
  2. This is why only ~60% of the SNT staked is available for voting. i.e. Developers could still attack in this way, but they would only be able to retrieve 60% of what they staked AND would face the very strong possibility of still having their DApp removed from the UI (which is actually what matters most in the end. It also means other people can use our DApp Store, but show different rankings if they honour different social conventions.)

As for the maths used in costOfMinting - it comes from that curve defined in the spreadsheet, the key insights there being that the exponent needs to be fractional to result in an exponential curve under integration, and that you can set the fraction used = to the % SNT staked that is available for minting.

There is an updated spreadsheet here (look under Copy of Curve for the latest work) and I did a tech talk with some slides, which you can watch here for further insight.