Implement leaching value
This commit is contained in:
parent
e4566d1a45
commit
675fd17734
|
@ -83,3 +83,5 @@ Tokens staked for and against a post.
|
||||||
Token loss ratio
|
Token loss ratio
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
parameter q_4 -- what is c_n?
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Actor } from './actor.js';
|
import { Actor } from './actor.js';
|
||||||
import { Reputations } from './reputation.js';
|
import { Reputations } from './reputation.js';
|
||||||
import { ValidationPool } from './validation-pool.js';
|
import { ValidationPool } from './validation-pool.js';
|
||||||
import params from './params.js';
|
import params from '../params.js';
|
||||||
import { Action } from './action.js';
|
import { Action } from './action.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
/**
|
||||||
|
* ERC-721 Non-Fungible Token Standard
|
||||||
|
* See https://eips.ethereum.org/EIPS/eip-721
|
||||||
|
* and https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol
|
||||||
|
*
|
||||||
|
* This implementation is currently incomplete. It lacks the following:
|
||||||
|
* - Token approvals
|
||||||
|
* - Operator approvals
|
||||||
|
* - Emitting events
|
||||||
|
*/
|
||||||
|
|
||||||
|
export class ERC721 /* is ERC165 */ {
|
||||||
|
constructor(name, symbol) {
|
||||||
|
this.name = name;
|
||||||
|
this.symbol = symbol;
|
||||||
|
this.balances = new Map(); // owner address --> token count
|
||||||
|
this.owners = new Map(); // token id --> owner address
|
||||||
|
// this.tokenApprovals = new Map(); // token id --> approved addresses
|
||||||
|
// this.operatorApprovals = new Map(); // owner --> operator approvals
|
||||||
|
|
||||||
|
this.events = {
|
||||||
|
// Transfer: (_from, _to, _tokenId) => {},
|
||||||
|
// Approval: (_owner, _approved, _tokenId) => {},
|
||||||
|
// ApprovalForAll: (_owner, _operator, _approved) => {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
incrementBalance(owner, increment) {
|
||||||
|
const balance = this.balances.get(owner) ?? 0;
|
||||||
|
this.balances.set(owner, balance + increment);
|
||||||
|
}
|
||||||
|
|
||||||
|
mint(to, tokenId) {
|
||||||
|
if (this.owners.get(tokenId)) {
|
||||||
|
throw new Error('ERC721: token already minted');
|
||||||
|
}
|
||||||
|
this.incrementBalance(to, 1);
|
||||||
|
this.owners.set(tokenId, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
burn(tokenId) {
|
||||||
|
const owner = this.owners.get(tokenId);
|
||||||
|
this.incrementBalance(owner, -1);
|
||||||
|
this.owners.delete(tokenId);
|
||||||
|
}
|
||||||
|
|
||||||
|
balanceOf(owner) {
|
||||||
|
if (!owner) {
|
||||||
|
throw new Error('ERC721: address zero is not a valid owner');
|
||||||
|
}
|
||||||
|
return this.balances.get(owner) ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ownerOf(tokenId) {
|
||||||
|
const owner = this.owners.get(tokenId);
|
||||||
|
if (!owner) {
|
||||||
|
throw new Error('ERC721: invalid token ID');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
transferFrom(from, to, tokenId) {
|
||||||
|
const owner = this.owners.get(tokenId);
|
||||||
|
if (owner !== from) {
|
||||||
|
throw new Error('ERC721: transfer from incorrect owner');
|
||||||
|
}
|
||||||
|
this.incrementBalance(from, -1);
|
||||||
|
this.incrementBalance(to, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// @notice Enable or disable approval for a third party ("operator") to manage
|
||||||
|
/// all of `msg.sender`'s assets
|
||||||
|
/// @dev Emits the ApprovalForAll event. The contract MUST allow
|
||||||
|
/// multiple operators per owner.
|
||||||
|
/// @param _operator Address to add to the set of authorized operators
|
||||||
|
/// @param _approved True if the operator is approved, false to revoke approval
|
||||||
|
// setApprovalForAll(_operator, _approved) {}
|
||||||
|
|
||||||
|
/// @notice Get the approved address for a single NFT
|
||||||
|
/// @dev Throws if `_tokenId` is not a valid NFT.
|
||||||
|
/// @param _tokenId The NFT to find the approved address for
|
||||||
|
/// @return The approved address for this NFT, or the zero address if there is none
|
||||||
|
// getApproved(_tokenId) {}
|
||||||
|
|
||||||
|
/// @notice Query if an address is an authorized operator for another address
|
||||||
|
/// @param _owner The address that owns the NFTs
|
||||||
|
/// @param _operator The address that acts on behalf of the owner
|
||||||
|
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
|
||||||
|
// isApprovedForAll(_owner, _operator) {}
|
||||||
|
}
|
|
@ -2,7 +2,7 @@ import { Actor } from './actor.js';
|
||||||
import { Graph } from './graph.js';
|
import { Graph } from './graph.js';
|
||||||
import { Action } from './action.js';
|
import { Action } from './action.js';
|
||||||
import { CryptoUtil } from './crypto.js';
|
import { CryptoUtil } from './crypto.js';
|
||||||
import params from './params.js';
|
import params from '../params.js';
|
||||||
|
|
||||||
class Post extends Actor {
|
class Post extends Actor {
|
||||||
constructor(forum, authorPublicKey, postContent) {
|
constructor(forum, authorPublicKey, postContent) {
|
||||||
|
@ -23,15 +23,6 @@ class Post extends Actor {
|
||||||
throw new Error('Each citation weight must be in the range [-1, 1]');
|
throw new Error('Each citation weight must be in the range [-1, 1]');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async setPostValue(value) {
|
|
||||||
await this.setValue('value', value);
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
getPostValue() {
|
|
||||||
return this.value;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -43,6 +34,7 @@ export class Forum extends Actor {
|
||||||
this.posts = new Graph(scene);
|
this.posts = new Graph(scene);
|
||||||
this.actions = {
|
this.actions = {
|
||||||
addPost: new Action('add post', scene),
|
addPost: new Action('add post', scene),
|
||||||
|
propagateValue: new Action('propagate value', this.scene),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,8 +42,14 @@ export class Forum extends Actor {
|
||||||
const post = new Post(this, authorId, postContent);
|
const post = new Post(this, authorId, postContent);
|
||||||
await this.actions.addPost.log(this, post);
|
await this.actions.addPost.log(this, post);
|
||||||
this.posts.addVertex(post.id, post, post.title);
|
this.posts.addVertex(post.id, post, post.title);
|
||||||
|
if (this.scene.flowchart) {
|
||||||
|
this.scene.flowchart.log(`${post.id} -- value --> ${post.id}_value[0]`);
|
||||||
|
}
|
||||||
for (const { postId: citedPostId, weight } of post.citations) {
|
for (const { postId: citedPostId, weight } of post.citations) {
|
||||||
this.posts.addEdge('citation', post.id, citedPostId, { weight });
|
this.posts.addEdge('citation', post.id, citedPostId, { weight });
|
||||||
|
if (this.scene.flowchart) {
|
||||||
|
this.scene.flowchart.log(`${post.id} -- ${weight} --> ${citedPostId}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return post.id;
|
return post.id;
|
||||||
}
|
}
|
||||||
|
@ -61,14 +59,39 @@ export class Forum extends Actor {
|
||||||
}
|
}
|
||||||
|
|
||||||
getPosts() {
|
getPosts() {
|
||||||
return this.posts.getVertices();
|
return this.posts.getVerticesData();
|
||||||
}
|
}
|
||||||
|
|
||||||
async propagateValue(postId, increment, depth = 0) {
|
async setPostValue(postId, value) {
|
||||||
if (depth > params.maxPropagationDepth) {
|
const post = this.getPost(postId);
|
||||||
|
post.value = value;
|
||||||
|
await post.setValue('value', value);
|
||||||
|
if (this.scene.flowchart) {
|
||||||
|
this.scene.flowchart.log(`${post.id}_value[${value}]`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getPostValue(postId) {
|
||||||
|
const post = this.getPost(postId);
|
||||||
|
return post.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
getTotalValue() {
|
||||||
|
return this.getPosts().reduce((total, { value }) => total += value, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
async propagateValue(fromActor, postId, increment, depth = 0) {
|
||||||
|
if (depth === 0 && this.scene.flowchart) {
|
||||||
|
const randomId = CryptoUtil.randomUUID().replaceAll('-', '').slice(0, 8);
|
||||||
|
this.scene.flowchart.log(`${postId}_initial_value_${randomId}[${increment}] -- initial value --> ${postId}`);
|
||||||
|
}
|
||||||
|
if (params.maxPropagationDepth >= 0 && depth > params.maxPropagationDepth) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
const post = this.getPost(postId);
|
const post = this.getPost(postId);
|
||||||
|
|
||||||
|
this.actions.propagateValue.log(fromActor, post, `(increment: ${increment})`);
|
||||||
|
|
||||||
const rewards = new Map();
|
const rewards = new Map();
|
||||||
const addReward = (id, value) => rewards.set(id, (rewards.get(id) ?? 0) + value);
|
const addReward = (id, value) => rewards.set(id, (rewards.get(id) ?? 0) + value);
|
||||||
const addRewards = (r) => {
|
const addRewards = (r) => {
|
||||||
|
@ -77,9 +100,11 @@ export class Forum extends Actor {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Increment the value of the given post
|
// Increment the value of the post
|
||||||
const postValue = post.getPostValue();
|
// Apply leaching value
|
||||||
await post.setPostValue(postValue + increment);
|
const currentValue = this.getPostValue(postId);
|
||||||
|
const newValue = currentValue + increment * (1 - params.leachingValue * post.totalCitationWeight);
|
||||||
|
await this.setPostValue(postId, newValue);
|
||||||
|
|
||||||
// Award reputation to post author
|
// Award reputation to post author
|
||||||
console.log('reward for post author', post.authorPublicKey, increment);
|
console.log('reward for post author', post.authorPublicKey, increment);
|
||||||
|
@ -87,7 +112,7 @@ export class Forum extends Actor {
|
||||||
|
|
||||||
// Recursively distribute reputation to citations, according to weights
|
// Recursively distribute reputation to citations, according to weights
|
||||||
for (const { postId: citedPostId, weight } of post.citations) {
|
for (const { postId: citedPostId, weight } of post.citations) {
|
||||||
addRewards(await this.propagateValue(citedPostId, weight * increment, depth + 1));
|
addRewards(await this.propagateValue(post, citedPostId, weight * increment, depth + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
return rewards;
|
return rewards;
|
||||||
|
|
|
@ -58,7 +58,7 @@ export class Graph {
|
||||||
return this.getVertex(id)?.data;
|
return this.getVertex(id)?.data;
|
||||||
}
|
}
|
||||||
|
|
||||||
getVertices() {
|
getVerticesData() {
|
||||||
return Array.from(this.vertices.values()).map(({ data }) => data);
|
return Array.from(this.vertices.values()).map(({ data }) => data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -84,9 +84,6 @@ export class Graph {
|
||||||
this.setEdge(label, from, to, edge);
|
this.setEdge(label, from, to, edge);
|
||||||
this.getVertex(from).edges.from.push(edge);
|
this.getVertex(from).edges.from.push(edge);
|
||||||
this.getVertex(to).edges.to.push(edge);
|
this.getVertex(to).edges.to.push(edge);
|
||||||
if (this.scene.flowchart) {
|
|
||||||
this.scene.flowchart.log(`${from} --> ${to}`);
|
|
||||||
}
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import mermaid from 'https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.min.mjs';
|
import mermaid from 'https://unpkg.com/mermaid@9.2.2/dist/mermaid.esm.min.mjs';
|
||||||
import { Actor } from './actor.js';
|
import { Actor } from './actor.js';
|
||||||
import { Action } from './action.js';
|
import { Action } from './action.js';
|
||||||
import { debounce } from '../util.js';
|
import { debounce, hexToRGB } from '../util.js';
|
||||||
|
|
||||||
class MermaidDiagram {
|
class MermaidDiagram {
|
||||||
constructor(box, logBox) {
|
constructor(box, logBox) {
|
||||||
|
@ -11,6 +11,7 @@ class MermaidDiagram {
|
||||||
this.renderBox = this.box.addBox('Render');
|
this.renderBox = this.box.addBox('Render');
|
||||||
this.box.addBox('Spacer').setInnerHTML(' ');
|
this.box.addBox('Spacer').setInnerHTML(' ');
|
||||||
this.logBox = logBox;
|
this.logBox = logBox;
|
||||||
|
this.inSection = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
async log(msg, render = true) {
|
async log(msg, render = true) {
|
||||||
|
@ -23,7 +24,10 @@ class MermaidDiagram {
|
||||||
|
|
||||||
async render() {
|
async render() {
|
||||||
const render = async () => {
|
const render = async () => {
|
||||||
const innerText = this.logBox.getInnerText();
|
let innerText = this.logBox.getInnerText();
|
||||||
|
for (let i = 0; i < this.inSection; i++) {
|
||||||
|
innerText += '\nend';
|
||||||
|
}
|
||||||
const graph = await mermaid.mermaidAPI.render(
|
const graph = await mermaid.mermaidAPI.render(
|
||||||
this.element.getId(),
|
this.element.getId(),
|
||||||
innerText,
|
innerText,
|
||||||
|
@ -104,11 +108,22 @@ export class Scene {
|
||||||
return dv;
|
return dv;
|
||||||
}
|
}
|
||||||
|
|
||||||
deactivateAll() {
|
async deactivateAll() {
|
||||||
for (const actor of this.actors.values()) {
|
for (const actor of this.actors.values()) {
|
||||||
while (actor.active) {
|
while (actor.active) {
|
||||||
actor.deactivate();
|
await actor.deactivate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async startSection(color = '#08252c') {
|
||||||
|
const { r, g, b } = hexToRGB(color);
|
||||||
|
this.sequence.inSection++;
|
||||||
|
this.sequence.log(`rect rgb(${r}, ${g}, ${b})`, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
async endSection() {
|
||||||
|
this.sequence.inSection--;
|
||||||
|
this.sequence.log('end');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { CryptoUtil } from './crypto.js';
|
||||||
import { Vote } from './vote.js';
|
import { Vote } from './vote.js';
|
||||||
import { Voter } from './voter.js';
|
import { Voter } from './voter.js';
|
||||||
import { Actor } from './actor.js';
|
import { Actor } from './actor.js';
|
||||||
import params from './params.js';
|
import params from '../params.js';
|
||||||
|
|
||||||
const ValidationPoolStates = Object.freeze({
|
const ValidationPoolStates = Object.freeze({
|
||||||
OPEN: 'OPEN',
|
OPEN: 'OPEN',
|
||||||
|
@ -195,7 +195,8 @@ export class ValidationPool extends Actor {
|
||||||
this.setStatus(`Resolved - ${result ? 'Won' : 'Lost'}`);
|
this.setStatus(`Resolved - ${result ? 'Won' : 'Lost'}`);
|
||||||
this.scene.sequence.log(`note over ${this.name} : ${result ? 'Win' : 'Lose'}`);
|
this.scene.sequence.log(`note over ${this.name} : ${result ? 'Win' : 'Lose'}`);
|
||||||
this.applyTokenLocking();
|
this.applyTokenLocking();
|
||||||
await this.distributeTokens(result);
|
await this.distributeReputation(result);
|
||||||
|
// TODO: distribute fees
|
||||||
} else {
|
} else {
|
||||||
this.setStatus('Resolved - Quorum not met');
|
this.setStatus('Resolved - Quorum not met');
|
||||||
this.scene.sequence.log(`note over ${this.name} : Quorum not met`);
|
this.scene.sequence.log(`note over ${this.name} : Quorum not met`);
|
||||||
|
@ -206,7 +207,7 @@ export class ValidationPool extends Actor {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
async distributeTokens({ votePasses }) {
|
async distributeReputation({ votePasses }) {
|
||||||
const rewards = new Map();
|
const rewards = new Map();
|
||||||
const addReward = (id, value) => rewards.set(id, (rewards.get(id) ?? 0) + value);
|
const addReward = (id, value) => rewards.set(id, (rewards.get(id) ?? 0) + value);
|
||||||
|
|
||||||
|
@ -223,20 +224,20 @@ export class ValidationPool extends Actor {
|
||||||
const { reputationPublicKey } = this.voters.get(signingPublicKey);
|
const { reputationPublicKey } = this.voters.get(signingPublicKey);
|
||||||
const reward = (tokensForWinners * stake) / getTotalStaked(votePasses);
|
const reward = (tokensForWinners * stake) / getTotalStaked(votePasses);
|
||||||
addReward(reputationPublicKey, reward);
|
addReward(reputationPublicKey, reward);
|
||||||
console.log('reward for winning voter', reputationPublicKey, reward);
|
console.log(`reward for winning voter ${reputationPublicKey}:`, reward);
|
||||||
}
|
}
|
||||||
|
|
||||||
const awardsFromVoting = Array.from(rewards.values()).reduce((total, value) => total += value, 0);
|
const awardsFromVoting = Array.from(rewards.values()).reduce((total, value) => total += value, 0);
|
||||||
console.log('awardsFromVoting', awardsFromVoting);
|
console.log('total awards from voting:', awardsFromVoting);
|
||||||
|
|
||||||
if (votePasses && !!this.forum) {
|
if (votePasses && !!this.forum) {
|
||||||
// Recurse through forum to determine reputation effects
|
// Recurse through forum to determine reputation effects
|
||||||
const forumReputationEffects = await this.forum.propagateValue(this.postId, this.tokensMinted);
|
const forumReputationEffects = await this.forum.propagateValue(this, this.postId, this.tokensMinted);
|
||||||
for (const [id, value] of forumReputationEffects) {
|
for (const [id, value] of forumReputationEffects) {
|
||||||
addReward(id, value);
|
addReward(id, value);
|
||||||
}
|
}
|
||||||
const awardsFromForum = Array.from(forumReputationEffects.values()).reduce((total, value) => total += value, 0);
|
const awardsFromForum = Array.from(forumReputationEffects.values()).reduce((total, value) => total += value, 0);
|
||||||
console.log('awardsFromForum', awardsFromForum);
|
console.log('total awards from forum:', awardsFromForum);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow for possible attenuation of total value of post, e.g. based on degree of contention
|
// Allow for possible attenuation of total value of post, e.g. based on degree of contention
|
||||||
|
@ -245,9 +246,11 @@ export class ValidationPool extends Actor {
|
||||||
// Scale all rewards so that the total is correct
|
// Scale all rewards so that the total is correct
|
||||||
// TODO: Add more precise assertions; otherwise this operation could mask errors.
|
// TODO: Add more precise assertions; otherwise this operation could mask errors.
|
||||||
const currentTotal = Array.from(rewards.values()).reduce((total, value) => total += value, 0);
|
const currentTotal = Array.from(rewards.values()).reduce((total, value) => total += value, 0);
|
||||||
console.log('currentTotal', currentTotal);
|
console.log('total awards before normalization:', currentTotal);
|
||||||
for (const [id, value] of rewards) {
|
for (const [id, value] of rewards) {
|
||||||
rewards.set(id, (value * initialPostValue) / currentTotal);
|
const normalizedValue = (value * initialPostValue) / currentTotal;
|
||||||
|
console.log(`normalized reward for ${id}: ${value} -> ${normalizedValue}`);
|
||||||
|
rewards.set(id, normalizedValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply computed rewards
|
// Apply computed rewards
|
||||||
|
|
|
@ -22,6 +22,7 @@ const params = {
|
||||||
initialPostValue: () => 1, // q1
|
initialPostValue: () => 1, // q1
|
||||||
revaluationLimit: 1, // q2
|
revaluationLimit: 1, // q2
|
||||||
maxPropagationDepth: 3, // q3
|
maxPropagationDepth: 3, // q3
|
||||||
|
leachingValue: 1, // q4
|
||||||
};
|
};
|
||||||
|
|
||||||
export default params;
|
export default params;
|
|
@ -17,6 +17,7 @@
|
||||||
import { Forum } from "/classes/forum.js";
|
import { Forum } from "/classes/forum.js";
|
||||||
import { Public } from "/classes/public.js";
|
import { Public } from "/classes/public.js";
|
||||||
import { PostContent } from "/classes/post-content.js";
|
import { PostContent } from "/classes/post-content.js";
|
||||||
|
import params from "/params.js";
|
||||||
|
|
||||||
const DEFAULT_DELAY_INTERVAL = 500;
|
const DEFAULT_DELAY_INTERVAL = 500;
|
||||||
|
|
||||||
|
@ -27,6 +28,9 @@
|
||||||
scene.withSequenceDiagram();
|
scene.withSequenceDiagram();
|
||||||
scene.withFlowchart();
|
scene.withFlowchart();
|
||||||
|
|
||||||
|
scene.addDisplayValue("leachingValue").set(params.leachingValue);
|
||||||
|
scene.addDisplayValue(" ");
|
||||||
|
|
||||||
const experts = (window.experts = []);
|
const experts = (window.experts = []);
|
||||||
const newExpert = async () => {
|
const newExpert = async () => {
|
||||||
const index = experts.length;
|
const index = experts.length;
|
||||||
|
@ -50,6 +54,7 @@
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
await bench.setValue("total rep", bench.getTotalReputation());
|
await bench.setValue("total rep", bench.getTotalReputation());
|
||||||
|
await forum.setValue("total value", forum.getTotalValue());
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateDisplayValuesAndDelay = async (delayMs) => {
|
const updateDisplayValuesAndDelay = async (delayMs) => {
|
||||||
|
@ -59,6 +64,8 @@
|
||||||
|
|
||||||
await updateDisplayValuesAndDelay();
|
await updateDisplayValuesAndDelay();
|
||||||
|
|
||||||
|
await scene.startSection();
|
||||||
|
|
||||||
const { postId: postId1, pool: pool1 } = await expert1.submitPostWithFee(
|
const { postId: postId1, pool: pool1 } = await expert1.submitPostWithFee(
|
||||||
bench,
|
bench,
|
||||||
forum,
|
forum,
|
||||||
|
@ -77,6 +84,9 @@
|
||||||
await pool1.evaluateWinningConditions();
|
await pool1.evaluateWinningConditions();
|
||||||
await updateDisplayValuesAndDelay();
|
await updateDisplayValuesAndDelay();
|
||||||
|
|
||||||
|
await scene.endSection();
|
||||||
|
await scene.startSection();
|
||||||
|
|
||||||
const { postId: postId2, pool: pool2 } = await expert2.submitPostWithFee(
|
const { postId: postId2, pool: pool2 } = await expert2.submitPostWithFee(
|
||||||
bench,
|
bench,
|
||||||
forum,
|
forum,
|
||||||
|
@ -97,6 +107,9 @@
|
||||||
await pool2.evaluateWinningConditions();
|
await pool2.evaluateWinningConditions();
|
||||||
await updateDisplayValuesAndDelay();
|
await updateDisplayValuesAndDelay();
|
||||||
|
|
||||||
|
await scene.endSection();
|
||||||
|
await scene.startSection();
|
||||||
|
|
||||||
const { pool: pool3 } = await expert3.submitPostWithFee(
|
const { pool: pool3 } = await expert3.submitPostWithFee(
|
||||||
bench,
|
bench,
|
||||||
forum,
|
forum,
|
||||||
|
@ -116,4 +129,6 @@
|
||||||
|
|
||||||
await pool3.evaluateWinningConditions();
|
await pool3.evaluateWinningConditions();
|
||||||
await updateDisplayValuesAndDelay();
|
await updateDisplayValuesAndDelay();
|
||||||
|
|
||||||
|
await scene.endSection();
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -18,3 +18,13 @@ export const delay = async (delayMs) => {
|
||||||
setTimeout(resolve, delayMs);
|
setTimeout(resolve, delayMs);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const hexToRGB = (input) => {
|
||||||
|
if (input.startsWith('#')) {
|
||||||
|
input = input.slice(1);
|
||||||
|
}
|
||||||
|
const r = parseInt(`${input[0]}${input[1]}`, 16);
|
||||||
|
const g = parseInt(`${input[2]}${input[3]}`, 16);
|
||||||
|
const b = parseInt(`${input[4]}${input[5]}`, 16);
|
||||||
|
return { r, g, b };
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue