Created
December 28, 2018 18:20
-
-
Save witoldsz/1ce270046d6d531eb939c24870bbcb60 to your computer and use it in GitHub Desktop.
Semux Pool calc (concept)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const DELEGATE_ADDR = '0x…' | |
const TXS_URL = `https://semux.online/v2.1.0/account/transactions?address=${DELEGATE_ADDR}&from=${0}&to=99999` | |
const BURNED_SEM_OWNER = shortHash('0x…') | |
const BURNED_SEM_PAID_DATE = new Date('2018-10-15T00:00:00Z') | |
const IGNORE_ADDR = '0x…' | |
const ALIASES = { | |
'xyz1': '🔜', | |
'xyz2': '🐋', | |
'xyz3': '💎', | |
'xyz4': 'WS', | |
} | |
async function main() { | |
const txs = await fetchTxs() | |
const initialState = { | |
stakes: {}, // { owner1: semAmount }, { owner2: semAmount }, …} | |
gains: {}, // { owner1: semAmount }, { owner2: semAmount }, …} | |
} | |
const finalState = txs | |
.reduce((state, tx) => ( | |
isVoteIn(tx) ? voteAction(state, tx, 1) | |
: isVoteOut(tx) ? voteAction(state, tx, -1) | |
: isProfitOut(tx) ? profitOutAction(state, tx) | |
: isSelfVote(tx) ? selfVoteAction(state, tx) | |
: state | |
), initialState) | |
return { | |
stakesSum: Math.round($objSumValues(finalState.stakes)), | |
gainsSum: Math.round($objSumValues(finalState.gains)), | |
stakes: roundValues(finalState.stakes), | |
gains: roundValues(finalState.gains), | |
} | |
} | |
main().then(console.log, console.warn) | |
// --------------------------------------------------------------- | |
// actions | |
function voteAction(state, tx, multiplier/* in=1 out=-1**/) { | |
const owner = shortHash(tx.from) | |
const amount = nanoToSem(tx.value) * multiplier | |
const icon = amount > 0 ? '📥' : '📤' | |
const newStakes = $updateKey(owner, (oldVal) => (oldVal || 0) + amount, state.stakes) | |
const newGains = $updateKey(owner, (oldVal) => (oldVal || 0), state.gains) | |
console.log(`${showDate(tx)},${icon},${showAmount(amount)},${showAddr(owner)},${showStakesAndGains(newStakes, newGains)}`) | |
return { ...state, stakes: newStakes, gains: newGains } | |
} | |
function profitOutAction(state, tx) { | |
const recipient = shortHash(tx.to) | |
const amount = nanoToSem(tx.value) | |
const newStakes = $updateKey(recipient, (oldVal) => oldVal - amount, state.stakes) | |
const newGains = $updateKey(recipient, (oldVal) => oldVal - amount, state.gains) | |
console.log(`${showDate(tx)},💰,${showAmount(amount)},${showAddr(recipient)}`) | |
return { ...state, stakes: newStakes, gains: newGains } | |
} | |
function selfVoteAction(state, tx) { | |
const amount = nanoToSem(tx.value) | |
const oldStakes = state.stakes | |
const totalStake = totalStakeOf(oldStakes, tx) | |
const gainOf_ = (owner, stake) => gainOf(totalStake, tx, owner, stake) | |
const newStakes = $mapObj( | |
({ key: owner, val: stake }) => stake + gainOf_(owner, stake), | |
oldStakes | |
) | |
const newGains = $mapObj( | |
({ key: owner, val }) => val + gainOf_(owner, oldStakes[owner]), | |
state.gains | |
) | |
console.log(`${showDate(tx)},🌟,${showAmount(amount)},··,${showStakesAndGains(newStakes, newGains)}`) | |
return { ...state, stakes: newStakes, gains: newGains } | |
} | |
function isVoteIn(tx) { | |
return tx.type === 'VOTE' | |
&& tx.from !== DELEGATE_ADDR | |
&& tx.from !== IGNORE_ADDR | |
&& tx.to === DELEGATE_ADDR | |
} | |
function isVoteOut(tx) { | |
return tx.type === 'UNVOTE' | |
&& tx.from !== DELEGATE_ADDR | |
&& tx.from !== IGNORE_ADDR | |
&& tx.to === DELEGATE_ADDR | |
} | |
function isProfitOut(tx) { | |
return tx.type === 'TRANSFER' | |
&& tx.from === DELEGATE_ADDR | |
&& tx.to !== DELEGATE_ADDR | |
} | |
function isSelfVote(tx) { | |
return tx.type === 'VOTE' | |
&& tx.from === DELEGATE_ADDR | |
&& tx.to === DELEGATE_ADDR | |
} | |
// --------------------------------------------------------------- | |
// utils | |
function totalStakeOf(stakes, tx) { | |
return $objSumValues(stakes) + (dateOf(tx) < BURNED_SEM_PAID_DATE ? 1000 : 0) | |
} | |
function gainOf(totalStake, tx, owner, stake_) { | |
const amount = nanoToSem(tx.value) | |
const stake = stake_ + (owner === BURNED_SEM_OWNER && dateOf(tx) < BURNED_SEM_PAID_DATE ? 1000 : 0) | |
return stake * amount / totalStake | |
} | |
function roundValues(obj) { | |
return $mapObj(({ val }) => Math.round(val), obj) | |
} | |
async function fetchTxs() { | |
const a = await fetch(TXS_URL) | |
const b = await a.json() | |
return b.result | |
} | |
function dateOf(tx) { | |
return new Date(parseInt(tx.timestamp)) | |
} | |
function showDate(tx) { | |
return dateOf(tx).toISOString().substr(0, 10) | |
} | |
function showAddr(a) { | |
return ALIASES[a] || a | |
} | |
function showAmount(a, minDigits = 5, padSide = 'left') { | |
const pad = str => str.length >= minDigits ? str : pad(padSide === 'left' ? ` ${str}` : `${str} `) | |
return pad(`${a | 0}`) | |
} | |
function showAmounts(obj) { | |
return `"{ ${Object.keys(obj).map((k) => `${showAddr(k)}:${showAmount(obj[k])}`).join(',') } }"` | |
} | |
function showStakesAndGains(stakes, gains) { | |
const amounts = (key) => `${showAmount(stakes[key])}﹩${showAmount(gains[key], 4, 'right')}` | |
return Object.keys(stakes).map((k) => `${showAddr(k)}:${amounts(k)}`).join(' ') | |
} | |
function nanoToSem(nano) { | |
return nano / 1e9 | |
} | |
function shortHash(hash) { | |
return hash.substr(2, 4) | |
} | |
// --------------------------------------------------------------- | |
// libs | |
function $objValues(obj) { | |
return Object.keys(obj).map((k) => obj[k]) | |
} | |
function $mapObj(keyValFn, objIn) { | |
const objOut = {} | |
for (const key in objIn) { | |
const val = objIn[key] | |
objOut[key] = keyValFn({ key, val }) | |
} | |
return objOut | |
} | |
function $objSumValues(obj) { | |
const initialVal = 0 | |
return $objValues(obj) | |
.reduce((v1, v2) => v1 + v2, initialVal) | |
} | |
function $updateKey(key, valFn, obj) { | |
return { | |
...obj, | |
[key]: valFn(obj[key]) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment