Prevent reputation from decreasing below zero
This commit is contained in:
parent
e777a0ee85
commit
83f9007401
|
@ -0,0 +1,50 @@
|
||||||
|
Reputation Tokens
|
||||||
|
|
||||||
|
Minting
|
||||||
|
|
||||||
|
Suppose it's possible to mint a reputation token.
|
||||||
|
Say we have a contract that keeps track of all the reputation tokens.
|
||||||
|
Suppose the reputation contract implements ERC720 (NFT).
|
||||||
|
Assume a validation pool always corresponds to a post.
|
||||||
|
A single token could be minted for each validation pool.
|
||||||
|
That token could be subdivided so that each winning voter gets some.
|
||||||
|
Perhaps voters get tokens that are specifically identifiable as governance reputation tokens.
|
||||||
|
Then the main token can be awarded to the post author.
|
||||||
|
Each token should carry a specific value.
|
||||||
|
The forum will update the value of the token for the post author as well as posts affected by its chain of references.
|
||||||
|
|
||||||
|
Then, when participants wish to stake reputation (for voting or for availability),
|
||||||
|
they must specify the amount and the token address which carries that reputation.
|
||||||
|
The token should probably then lock that reputation, preventing it from being staked concurrently for another purpose.
|
||||||
|
|
||||||
|
Perhaps our interface can allow staking reputation from multiple tokens at the same time.
|
||||||
|
And/or we can provide a mechanism for consolidating tokens.
|
||||||
|
|
||||||
|
Or maybe, if reputation is staked via a given token, then the reputation awards should go to that same token.
|
||||||
|
In that case, when should new tokens be minted?
|
||||||
|
|
||||||
|
Maybe a token should be minted for each validation pool, but not subdivided.
|
||||||
|
Voter rewards can just add value to the existing tokens from which reputation was staked.
|
||||||
|
|
||||||
|
Maybe a new token should only be minted if the author did not provide a token from which to stake reputation on their own post.
|
||||||
|
This supports the case of a new author earning their first reputation.
|
||||||
|
In that case the author may need to pay a fee to buy in to the DAO.
|
||||||
|
Or perhaps they can be sponsored by one or more existing reputation token holders.
|
||||||
|
Existing reputation holders could grant some reputation to a new member.
|
||||||
|
Perhaps there could be a contract that allows sponsoring a new member, such that whatever fee is given,
|
||||||
|
that fee will automatically be repaid from the new member's earnings, before the new member starts receiving their share of earnings.
|
||||||
|
This could be a multi-party action, or could just be a generic operation that can be performed multiple times.
|
||||||
|
|
||||||
|
However, this effectively allows buying reputation, which goes against the core concept of reputation as evidence of performance.
|
||||||
|
|
||||||
|
It could make more sense for new members to submit some sort of petition, i.e. to make a post.
|
||||||
|
|
||||||
|
Maybe rather than submitting fees, existing members can grant some of their own reputation to a new member, and receive some sort of compensation if the new member does well.
|
||||||
|
|
||||||
|
So far the only workable model seems to be the idea that a new member must submit a post along with a fee, in order to be considered, and if the post is approved, they gain their first reputation.
|
||||||
|
The amount of this fee can be up to the applicant, and/or can be regulated by soft protocols within the DAO.
|
||||||
|
|
||||||
|
If this is the only scenario in which new rep tokens are minted, and from then on their value is updated as a result of each validation pool,
|
||||||
|
then we probably want each token to store information about the history of its value.
|
||||||
|
At a minimum this can be a list where each item includes the identifier of the corresponding validation pool, and the resulting increment/decrement of token value.
|
||||||
|
Each validation pool can then keep a record of the reputation staked by each voter, and the identifier of the corresponding post.
|
|
@ -83,44 +83,47 @@ export class Forum extends Actor {
|
||||||
|
|
||||||
const post = this.getPost(postId);
|
const post = this.getPost(postId);
|
||||||
post.setStatus('Validated');
|
post.setStatus('Validated');
|
||||||
const rewards = await this.propagateValue(pool, post, initialValue);
|
|
||||||
|
// Compute rewards
|
||||||
|
const rewards = new Map();
|
||||||
|
await this.propagateValue(rewards, pool, post, initialValue);
|
||||||
// Apply computed rewards
|
// Apply computed rewards
|
||||||
for (const [id, value] of rewards) {
|
for (const [id, value] of rewards) {
|
||||||
bench.reputations.addTokens(id, value);
|
bench.reputations.addTokens(id, value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async propagateValue(fromActor, post, increment, depth = 0) {
|
async propagateValue(rewards, fromActor, post, increment, depth = 0) {
|
||||||
if (params.referenceChainLimit >= 0 && depth > params.referenceChainLimit) {
|
if (params.referenceChainLimit >= 0 && depth > params.referenceChainLimit) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
this.actions.propagateValue.log(fromActor, post, `(${increment})`);
|
this.actions.propagateValue.log(fromActor, post, `(${increment})`);
|
||||||
|
|
||||||
// Apply leaching value
|
|
||||||
const adjustedIncrement = increment * (1 - params.leachingValue * post.totalCitationWeight);
|
|
||||||
|
|
||||||
const rewards = new Map();
|
|
||||||
const addReward = (id, value) => rewards.set(id, (rewards.get(id) ?? 0) + value);
|
|
||||||
const addRewards = (r) => {
|
|
||||||
for (const [id, value] of r) {
|
|
||||||
addReward(id, value);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Increment the value of the post
|
|
||||||
await this.setPostValue(post, post.value + adjustedIncrement);
|
|
||||||
|
|
||||||
// Award reputation to post author
|
|
||||||
console.log(`reward for post author ${post.authorPublicKey}`, adjustedIncrement);
|
|
||||||
addReward(post.authorPublicKey, adjustedIncrement);
|
|
||||||
|
|
||||||
// Recursively distribute reputation to citations, according to weights
|
// Recursively distribute reputation to citations, according to weights
|
||||||
|
let downstreamRefund = 0;
|
||||||
for (const { postId: citedPostId, weight } of post.citations) {
|
for (const { postId: citedPostId, weight } of post.citations) {
|
||||||
const citedPost = this.getPost(citedPostId);
|
const citedPost = this.getPost(citedPostId);
|
||||||
addRewards(await this.propagateValue(post, citedPost, weight * increment, depth + 1));
|
downstreamRefund += await this.propagateValue(rewards, post, citedPost, weight * increment, depth + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return rewards;
|
// Apply leaching value
|
||||||
|
const adjustedIncrement = increment * (1 - params.leachingValue * post.totalCitationWeight) + downstreamRefund;
|
||||||
|
|
||||||
|
// Prevent value from decreasing below zero
|
||||||
|
const rawNewValue = post.value + adjustedIncrement;
|
||||||
|
const newValue = Math.max(0, rawNewValue);
|
||||||
|
const upstreamRefund = rawNewValue < 0 ? rawNewValue : 0;
|
||||||
|
const appliedIncrement = newValue - post.value;
|
||||||
|
|
||||||
|
// Increment the value of the post
|
||||||
|
await this.setPostValue(post, newValue);
|
||||||
|
|
||||||
|
// Award reputation to post author
|
||||||
|
console.log(`reward for post author ${post.authorPublicKey}`, appliedIncrement);
|
||||||
|
|
||||||
|
rewards.set(post.authorPublicKey, appliedIncrement);
|
||||||
|
|
||||||
|
return upstreamRefund;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -224,7 +224,7 @@ export class ValidationPool extends Actor {
|
||||||
const tokensForWinners = getTotalStaked(!votePasses);
|
const tokensForWinners = getTotalStaked(!votePasses);
|
||||||
const winningVotes = this.listVotes(({ position, isSystemVote }) => position === votePasses && !isSystemVote);
|
const winningVotes = this.listVotes(({ position, isSystemVote }) => position === votePasses && !isSystemVote);
|
||||||
|
|
||||||
// Reward the winning voters, in proportion to their stakes
|
// Compute rewards for the winning voters, in proportion to their stakes
|
||||||
const rewards = new Map();
|
const rewards = new Map();
|
||||||
for (const [signingPublicKey, { stake }] of winningVotes) {
|
for (const [signingPublicKey, { stake }] of winningVotes) {
|
||||||
const { reputationPublicKey } = this.voters.get(signingPublicKey);
|
const { reputationPublicKey } = this.voters.get(signingPublicKey);
|
||||||
|
@ -233,6 +233,7 @@ export class ValidationPool extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
const authorReputationPublicKey = this.voters.get(this.authorSigningPublicKey).reputationPublicKey;
|
const authorReputationPublicKey = this.voters.get(this.authorSigningPublicKey).reputationPublicKey;
|
||||||
|
|
||||||
// Distribute awards to voters other than the author
|
// Distribute awards to voters other than the author
|
||||||
for (const [id, value] of rewards) {
|
for (const [id, value] of rewards) {
|
||||||
if (id !== authorReputationPublicKey) {
|
if (id !== authorReputationPublicKey) {
|
||||||
|
@ -241,6 +242,8 @@ export class ValidationPool extends Actor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: revoke staked reputation from losing voters
|
||||||
|
|
||||||
if (votePasses) {
|
if (votePasses) {
|
||||||
// Distribute awards to author via the forum
|
// Distribute awards to author via the forum
|
||||||
const tokensForAuthor = this.tokensMinted * params.stakeForAuthor + rewards.get(authorReputationPublicKey);
|
const tokensForAuthor = this.tokensMinted * params.stakeForAuthor + rewards.get(authorReputationPublicKey);
|
||||||
|
|
|
@ -5,15 +5,22 @@
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<h2>Tests</h2>
|
<h2>Tests</h2>
|
||||||
|
<h3>Primary</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/tests/validation-pool.html">Validation Pool</a></li>
|
||||||
|
<li><a href="/tests/availability.html">Availability + Business</a></li>
|
||||||
|
<li><a href="/tests/forum.html">Forum</a></li>
|
||||||
|
</ul>
|
||||||
|
<h3>Secondary</h3>
|
||||||
|
<ul>
|
||||||
|
<li><a href="/tests/forum-network.html">Forum Network</a></li>
|
||||||
|
</ul>
|
||||||
|
<h3>Tertiary</h3>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="/tests/basic.html">Basic</a></li>
|
<li><a href="/tests/basic.html">Basic</a></li>
|
||||||
<li><a href="/tests/forum-network.html">Forum Network</a></li>
|
|
||||||
<li><a href="/tests/graph.html">Graph</a></li>
|
|
||||||
<li><a href="/tests/validation-pool.html">Validation Pool</a></li>
|
|
||||||
<li><a href="/tests/mermaid.html">Mermaid</a></li>
|
<li><a href="/tests/mermaid.html">Mermaid</a></li>
|
||||||
|
<li><a href="/tests/graph.html">Graph</a></li>
|
||||||
<li><a href="/tests/debounce.html">Debounce</a></li>
|
<li><a href="/tests/debounce.html">Debounce</a></li>
|
||||||
<li><a href="/tests/availability.html">Availability</a></li>
|
|
||||||
<li><a href="/tests/forum.html">Forum</a></li>
|
|
||||||
<li><a href="/tests/flowchart.html">Flowchart</a></li>
|
<li><a href="/tests/flowchart.html">Flowchart</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -21,7 +21,7 @@ const params = {
|
||||||
/* Forum parameters */
|
/* Forum parameters */
|
||||||
initialPostValue: () => 1, // q1
|
initialPostValue: () => 1, // q1
|
||||||
revaluationLimit: 1, // q2
|
revaluationLimit: 1, // q2
|
||||||
maxPropagationDepth: 3, // q3
|
referenceChainLimit: 3, // q3
|
||||||
leachingValue: 1, // q4
|
leachingValue: 1, // q4
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -122,9 +122,9 @@
|
||||||
forum,
|
forum,
|
||||||
new PostContent({ hello: "y'all" })
|
new PostContent({ hello: "y'all" })
|
||||||
.setTitle("Post 3")
|
.setTitle("Post 3")
|
||||||
.addCitation(postId2, 0.5),
|
.addCitation(postId2, -0.5),
|
||||||
{
|
{
|
||||||
fee: 10,
|
fee: 100,
|
||||||
duration: 1000,
|
duration: 1000,
|
||||||
tokenLossRatio: 1,
|
tokenLossRatio: 1,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue